当前位置: 首页 > article >正文

LearnOpenGL学习(高级OpenGL --> 帧缓冲,立方体贴图,高级数据)

完整代码见:zaizai77/Cherno-OpenGL: OpenGL 小白学习之路

帧缓冲

帧缓冲(FrameBuffer)是所有屏幕缓冲(包括颜色缓冲,深度缓冲,模板缓冲)的集合。它被存储在GPU内存中,我们可以定义自己的帧缓冲

我们目前所做的所有操作都是在默认帧缓冲的渲染缓冲上进行的。默认的帧缓冲是在你创建窗口的时候生成和配置的(GLFW帮我们做了这些)。通过创建我们自己的帧缓冲,我们可以获得额外的渲染目标(target)。

创建一个帧缓冲

unsigned int fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);

创建一个帧缓冲对象并绑定

在绑定到GL_FRAMEBUFFER目标之后,所有的读取写入帧缓冲的操作将会影响当前绑定的帧缓冲。我们也可以使用GL_READ_FRAMEBUFFERGL_DRAW_FRAMEBUFFER,将一个帧缓冲分别绑定到读取目标或写入目标。

一个完整的帧缓冲需要满足以下的条件:

  • 附加至少一个缓冲(颜色、深度或模板缓冲)。
  • 至少有一个颜色附件(Attachment)。
  • 所有的附件都必须是完整的(保留了内存)。
  • 每个缓冲都应该有相同的样本数(sample)。

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) // 执行胜利的舞蹈

//检查缓冲是否完整

之后所有的渲染操作将会渲染到当前绑定帧缓冲的附件中。

由于我们的帧缓冲不是默认帧缓冲,渲染指令将不会对窗口的视觉输出有任何影响。出于这个原因,渲染到一个不同的帧缓冲被叫做离屏渲染(Off-screen Rendering)。要保证所有的渲染操作在主窗口中有视觉效果,我们需要再次激活默认帧缓冲,将它绑定到 0 

glBindFramebuffer(GL_FRAMEBUFFER, 0);

//完成渲染之后删除帧缓冲对象

glDeleteFramebuffers(1, &fbo);

附件是一个内存位置,它能够作为帧缓冲的一个缓冲,可以将它想象为一个图像。当创建一个附件的时候我们有两个选项:纹理或渲染缓冲对象(Renderbuffer Object)。

纹理附件

当把一个纹理附加到帧缓冲的时候,所有的渲染指令将会写入到这个纹理中,就像它是一个普通的颜色/深度或模板缓冲一样。使用纹理的优点是,所有渲染操作的结果将会被储存在一个纹理图像中,我们之后可以在着色器中很方便地使用它。

为帧缓冲创建一个纹理和创建一个普通的纹理差不多:

unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
//无图片数据,data参数为NULL
//若附加深度缓冲纹理,Format和Internalformat参数应当为GL_DEPTH_COMPONENT
//若附加模板缓冲纹理,则为GL_STENCIL_INDEX
//若同时附加深度和模板缓冲纹理,Format为GL_DEPTH24_STENCIL8,Internalformat为GL_DEPTH_STENCIL,Type为GL_UNSIGNED_INT_24_8
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
//纹理附件的大小总是为屏幕大小,所以不关心环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);

glFrameBufferTexture2D有以下的参数:

  • target :帧缓冲的目标(绘制、读取或者两者皆有)
  • attachment :我们想要附加的附件类型。除了颜色缓冲外,还有GL_DEPTH_ATTACHMENT,GL_STENCIL_ATTACHMENT,GL_DEPTH_STENCIL_ATTACHMENT
  • textarget: 你希望附加的纹理类型
  • texture: 要附加的纹理本身
  • level: 多级渐远纹理的级别。我们将它保留为0。

渲染缓冲对象附件

渲染缓冲对象(Renderbuffer Object,RBO)是真正的缓冲(相对于纹理等通用数据缓冲, General Purpose Data Buffer),它相比于纹理缓冲具有更快的读取速度。

渲染缓冲对象是只写的,可以通过 glReadPixels 函数来读取当前绑定的帧缓冲中的特定像素

unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);

当我们不需要从这些缓冲中采样的时候,通常选择渲染缓冲对象,因为它更优化一些创建一个深度和模板渲染缓冲对象可以通过调用glRenderbufferStorage函数来完成:

//创建一个深度和模板渲染缓冲对象可以通过调用glRenderbufferStorage函数来完成:

glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);

//附加这个渲染缓冲对象:

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

创建一个渲染缓冲对象和纹理对象类似,不同的是这个对象是专门被设计作为帧缓冲附件使用的,而不是纹理那样的通用数据缓冲(General Purpose Data Buffer)。这里我们选择GL_DEPTH24_STENCIL8作为内部格式,它封装了24位的深度和8位的模板缓冲。

渲染到纹理

我们将会将场景渲染到一个附加到帧缓冲对象上的颜色纹理中,之后将在一个横跨整个屏幕的四边形上绘制这个纹理。这样视觉输出和没使用帧缓冲时是完全一样的,但这次是打印到了一个四边形上。

//创建一个帧缓冲对象并绑定

unsigned int framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

//创建一个纹理图像,我们将它作为一个颜色附件附加到帧缓冲上
//我们将纹理的维度设置为窗口的宽度和高度,并且不初始化它的数据:


// 生成纹理
unsigned int texColorBuffer;
glGenTextures(1, &texColorBuffer);
glBindTexture(GL_TEXTURE_2D, texColorBuffer);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);

// 将它附加到当前绑定的帧缓冲对象
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texColorBuffer, 0);  

//添加一个深度(和模板)附件到帧缓冲中

unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo); 
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);  
glBindRenderbuffer(GL_RENDERBUFFER, 0);

//当我们为渲染缓冲对象分配了足够的内存之后,我们可以解绑这个渲染缓冲。
//接下来,作为完成帧缓冲之前的最后一步,我们将渲染缓冲对象附加到帧缓冲的深度和模板附件上:

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

//我们希望检查帧缓冲是否是完整的,如果不是,我们将打印错误信息。

if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);

要想绘制场景到一个纹理上,我们需要采取以下的步骤:

  1. 将新的帧缓冲绑定为激活的帧缓冲,和往常一样渲染场景
  2. 绑定默认的帧缓冲
  3. 绘制一个横跨整个屏幕的四边形,将帧缓冲的颜色缓冲作为它的纹理。

帧缓冲的一个渲染迭代将会有以下的结构:

// 第一处理阶段(Pass)
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 我们现在不使用模板缓冲
glEnable(GL_DEPTH_TEST);
DrawScene();    

// 第二处理阶段
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 返回默认
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 
glClear(GL_COLOR_BUFFER_BIT);

screenShader.use();  
glBindVertexArray(quadVAO);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
glDrawArrays(GL_TRIANGLES, 0, 6);  

后期处理

参考:LearnOpenGL学习笔记(九) - 面剔除、帧缓冲、CubeMap与高级数据 - Yoi's Home

帧缓冲 - LearnOpenGL CN 


http://www.kler.cn/a/429289.html

相关文章:

  • 51单片机——DS18B20温度传感器
  • 增广卡尔曼滤波AKF的要点分析
  • QT 如何禁止QComboBox鼠标滚轮
  • 如何使用策略模式并让spring管理
  • 鸿蒙打包发布
  • 神经网络常见操作(卷积)输入输出
  • 策略模式实战 - 猜拳游戏
  • 如何配置Jackson以忽略Java类中为null或空(empty)的字段
  • 避大坑!Vue3中reactive丢失响应式的问题
  • guava 整合springboot 自定义注解实现接口鉴权调用保护
  • 题海拾贝:力扣 231. 2 的幂
  • 使用Python和OpenGL实现3D立方体的交互式显示
  • 康托展开和逆康托展开
  • java-数组—acwing
  • 【C语言】数据库事物的ACID属性
  • 在Ubuntu上使用IntelliJ IDEA:开启你的Java开发之旅!
  • osi七层模型
  • 电子商务人工智能指南 6/6 - 人工智能生成的产品图像
  • Linux DNS之进阶篇bind-chroot企业级部署方式
  • Electron小案例
  • 超详细搭建PhpStorm+PhpStudy开发环境
  • git提交时出现merge branch main of xxx
  • Win11 配置 TeXstudio 编辑器教程
  • C# Winform飞机大战小游戏源码
  • docker的网络类型和使用方式
  • 【计算机图形学】实验2:橡皮筋技术及拾取操作