帧缓冲的基础知识

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); 

widthheight 应与渲染窗口大小相同。纹理数据指针是 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);

就这样! 如果你已正确完成所有操作,则应该看到与之前相同的场景,但在全屏四边形上渲染。视觉输出与以前相同,但现在只需编辑片段着色器即可轻松添加后处理效果。 (我将在另一个例子中添加效果并在此处链接)