OpenGL学习笔记 帧缓冲
帧缓存
到目前为止,我们已经使用了很多屏幕缓冲了:用于写入颜色值的颜色缓冲、用于写入深度信息的深度缓冲和允许我们根据一些条件丢弃特定片段的模板缓冲。***这些缓冲结合起来叫做帧缓冲(Framebuffer)***,它被储存在内存中。OpenGL允许我们定义我们自己的帧缓冲,也就是说我们能够定义我们自己的颜色缓冲,甚至是深度缓冲和模板缓冲。
帧缓存实现效果:
啊这,好像没能体现什么特别的。
那我们再在代码中加入
//线框模式绘制
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
那么发现了实际上默认的帧缓冲只是画了一条线,说明我们渲染出的效果实际上就是我们自己定义的帧缓冲做到的。
回过头好好研究代码。
unsigned int framebuffer;//定义帧缓冲
glGenFramebuffers(1, &framebuffer);//创建一个帧缓冲
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);//绑定帧缓冲
和VAO,VBO类似,我们需要绑定好我们的帧缓冲
一个完整的帧缓冲需要满足以下的条件:
- 附加至少一个缓冲(颜色、深度或模板缓冲)。
- 至少有一个颜色附件(Attachment)。
- 所有的附件都必须是完整的(保留了内存)。
- 每个缓冲都应该有相同的样本数。
如果准备就绪执行
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) //检测帧缓冲是否通过
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
之后所有的渲染操作将会渲染到当前绑定帧缓冲的附件中。由于我们的帧缓冲不是默认帧缓冲,渲染指令将不会对窗口的视觉输出有任何影响。出于这个原因,渲染到一个不同的帧缓冲被叫做离屏渲染(Off-screen Rendering)。要保证所有的渲染操作在主窗口中有视觉效果,我们需要再次**默认帧缓冲,将它绑定到0。
glBindFramebuffer(GL_FRAMEBUFFER, 0);
当一切做完之后应该删除。
glDeleteFramebuffers(1, &fbo);
接下来我们来处理中间部分。
我们需要给帧缓冲附加一个附件。附件是一个内存位置,它能够作为帧缓冲的一个缓冲,可以将它想象为一个图像。当创建一个附件的时候我们有两个选项:纹理或渲染缓冲对象(Renderbuffer Object)。
首先我们看看纹理附件
unsigned int textureColorbuffer; //定义纹理缓冲
//在完整性检查执行之前,我们需要给帧缓冲附加一个附件。
//附件是一个内存位置,它能够作为帧缓冲的一个缓冲,可以将它想象为一个图像。
//当创建一个附件的时候我们有两个选项:纹理或渲染缓冲对象(Renderbuffer Object)。
glGenTextures(1, &textureColorbuffer);//定义纹理缓冲对象
glBindTexture(GL_TEXTURE_2D, textureColorbuffer);//创建纹理缓冲对象
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 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, textureColorbuffer, 0);
接下来是渲染缓冲对象
渲染缓冲对象(Renderbuffer Object)是在纹理之后引入到OpenGL中,作为一个可用的帧缓冲附件类型的,所以在过去纹理是唯一可用的附件。和纹理图像一样,渲染缓冲对象是一个真正的缓冲,即一系列的字节、整数、像素等。渲染缓冲对象附加的好处是,它会将数据储存为OpenGL原生的渲染格式,它是为离屏渲染到帧缓冲优化过的。
// create a renderbuffer object for depth and stencil attachment (we won't be sampling these)
//渲染缓冲对象附件
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT); // use a single renderbuffer object for both a depth AND stencil buffer.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // now actually attach it
// now that we actually created the framebuffer and added all attachments we want to check if it is actually complete now
至此为止属于我们的帧缓冲就创建好了。
在渲染hook中要注意有以下几个步骤
// 第一处理阶段(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);
所以这个有什么用处呢?因为我们能够以一个纹理图像的方式访问已渲染场景中的每个像素,我们可以在片段着色器中创建出非常有趣的效果。这些有趣效果统称为***后期处理(Post-processing)*** 效果。
插一句我的理解,我们能以一个纹理图像的方式访问已渲染场景之中的每个像素,换句话说我们可以在着色器中通过颜色的变换做一些特殊的效果。
奇怪的效果增加了
补充
离屏渲染(offscreen-rendering)顾名思义为屏幕外的渲染,即渲染的结果不会直接呈现到当前屏幕上,而是等待合适的时机才会被显示。
我查了很久,没能找到为什么这篇文章会有两个shader的解释,就自己说说看法。我觉得,第一个shader用来渲染出来所有的物体,计算光照(如果有),法线(如果有),颜色。然后这些作为一整个纹理传入第二个shader中,所以第二个shader可以直接对全局进行后期处理,很符合后期处理这一说明。自己暂时先这么理解,后续如果有新的想法再补充。
推荐阅读
-
OpenGL学习笔记 帧缓冲
-
OpenGL帧缓冲
-
opengl使用帧缓冲区及模板缓存记录
-
学习OpenGL ES for Android(二十二)— 帧缓冲
-
黑马Android76期学习笔记01基础--day07--广播,有、无序广播、特殊广播接受者、样式和主题,this与context的区别、普通对话框,进度条对话框、帧动画
-
OpenGL学习笔记(六):创建第一个VS2015 OpenGL工程模板(与平台无关)
-
OpenGL学习笔记(七):创建第一个Qt5.9.3 OpenGL工程模版(与平台无关)
-
OpenGL学习笔记--配置VS环境
-
DirectX 11 学习笔记(4)- 创建渲染目标视图、创建深度缓冲区以及相关的深度视图并绑定到渲染管线的输出合并阶段
-
【OpenGL学习笔记】2.创建窗口