帧缓冲的基础知识
Framebuffer 是一种缓冲区,用于存储内存中像素的颜色值,深度和模板信息。当你在 OpenGL 中绘制某些内容时,输出将存储在默认的帧缓冲区中,然后你实际上会在屏幕上看到此缓冲区的颜色值。你也可以制作自己的帧缓冲,可用于很多很酷的后期处理效果,如灰度,模糊,景深,失真,反射 ……
首先,你需要创建一个 framebuffer 对象( FBO )并像 OpenGL 中的任何其他对象一样绑定它:
unsigned int FBO;
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
现在,你必须向帧缓冲区添加至少一个附件 (颜色,深度或模板)。附件是一个内存位置,充当帧缓冲区的缓冲区。它可以是纹理或渲染缓冲对象。使用纹理的优点是你可以在后处理着色器中轻松使用此纹理。创建纹理与普通纹理类似:
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, 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);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
width
和 height
应与渲染窗口大小相同。纹理数据指针是 NULL
,因为你只想分配内存而不用任何数据填充纹理。纹理已准备就绪,因此你可以将其实际附加到帧缓冲区:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
你的帧缓冲区现在应该可以使用了,但你可能还想添加深度附件或深度和模板附件。如果你想将它们添加为纹理附件(并将它们用于某些处理),你可以创建上面的其他纹理。唯一的区别在于这些方面:
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, width, height, 0,
GL_DEPTH_COMPONENT, GL_FLOAT, NULL
);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
或者如果你想在单个纹理中使用深度和模板附件:
glTexImage2D(
GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL
);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
如果以后不想处理值,也可以使用 renderbuffer 而不是纹理作为深度和模板缓冲区的附件。 (将在另一个例子中解释……)
你可以检查帧缓冲是否已成功创建并完成且没有任何错误:
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
// do something...
最后不要忘记取消绑定帧缓冲区,以免意外渲染到它:
glBindFramebuffer(GL_FRAMEBUFFER, 0);
范围
通过使用参数 GL_MAX_COLOR_ATTACHMENTS
,可以通过 OGL 函数 glGetIntegerv 确定可附加到单个帧缓冲区的最大颜色缓冲区数 :
GLint maxColAttchments = 0;
glGetIntegerv( GL_MAX_COLOR_ATTACHMENTS, &maxColAttchments );
使用帧缓冲
用法非常简单。首先绑定帧缓冲区并将场景渲染到其中。但是你实际上还没有看到任何东西,因为你的渲染缓冲区是不可见的。所以第二部分是将帧缓冲区渲染为全屏四边形的纹理到屏幕上。你可以按原样渲染它或执行一些后处理效果。
以下是全屏四边形的顶点:
float vertices[] = {
// positions texture coordinates
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
你需要将它们存储在 VBO 中或使用属性指针进行渲染。你还需要一些基本着色器程序来渲染具有纹理的全屏四边形。
顶点着色器:
in vec2 position;
in vec2 texCoords;
out vec2 TexCoords;
void main()
{
gl_Position = vec4(position.x, position.y, 0.0, 1.0);
TexCoords = texCoords;
}
片段着色器:
in vec2 TexCoords;
out vec4 color;
uniform sampler2D screenTexture;
void main()
{
color = texture(screenTexture, TexCoords);
}
注意: 你可能需要调整你的 GLSL 版本的着色器。
现在你可以进行实际渲染。如上所述,首先是将场景渲染到你的 FBO 中。要做到这一点,你只需绑定你的 FBO,清除它并绘制场景:
glBindFramebuffer(GL_FRAMEBUFFER, FBO);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// draw your scene here...
注意: 在 glClear
功能中,你应指定你正在使用的所有帧缓冲附件(在此示例中为颜色和深度附件)。
现在,你可以将 FBO 渲染为默认帧缓冲区上的全屏四边形,以便你可以看到它。要做到这一点,你只需取消绑定你的 FBO 并渲染四边形:
glBindFramebuffer(GL_FRAMEBUFFER, 0); // unbind your FBO to set the default framebuffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.Use(); // shader program for rendering the quad
glBindTexture(GL_TEXTURE_2D, texture); // color attachment texture
glBindBuffer(GL_ARRAY_BUFFER, VBO); // VBO of the quad
// You can also use VAO or attribute pointers instead of only VBO...
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindBuffer(GL_ARRAY_BUFFER, 0);
就这样! 如果你已正确完成所有操作,则应该看到与之前相同的场景,但在全屏四边形上渲染。视觉输出与以前相同,但现在只需编辑片段着色器即可轻松添加后处理效果。 (我将在另一个例子中添加效果并在此处链接)