OpenGL允许我们定义我们自己的帧缓冲,也就是说我们能够定义我们自己的颜色缓冲,甚至是深度缓冲和模板缓冲。
创建一个帧缓冲
1.创建一个帧缓冲对象(Framebuffer Object, FBO)
创建语法同VAO,VBO。
unsigned int fbo;
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);//绑定
GL_FRAMEBUFFER绑定后所有的
读取
和
写入
帧缓冲的操作将会影响当前绑定的帧缓冲
glBindFramebuffer(GL_READ_FRAMEBUFFER,fbo);
//绑定只读
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo);
//绑定只写
2.纹理附件
颜色附件
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
//data == 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(fbo的类型,颜色附件,纹理类型,纹理,纹理多级渐远级别);
函数原型
glFrameBufferTexture2D(target,attachment,textarget,texture,level);
target:帧缓冲的目标(绘制、读取或者两者皆有)
attachment:我们想要附加的附件类型。当前我们正在附加一个颜色附件。注意最后的0意味着我们可以附加多个颜色附件。我们将在之后的教程中提到。
textarget:你希望附加的纹理类型
texture:要附加的纹理本身
level:多级渐远纹理的级别。我们将它保留为0。
attachment附件类型有
颜色纹理
GL_COLOR_ATTACHMENT0+i
, 纹理格式为
GL_RGB
深度缓冲
GL_DEPTH_ATTACHMENT
,纹理格式为
GL_DEPTH_COMPONENT
模板缓冲
GL_STENCIL_ATTACHMENT
,纹理格式为
GL_STENCIL_INDEX
将深度和模板缓冲附加为一个纹理
GL_DEPTH_STENCIL_ATTACHMENT
,纹理格式为
GL_DEPTH24_STENCIL8
,纹理的每32位数值将包含24位的深度信息和8位的模板信息。
使用样例
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, 800, 600, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture, 0);
3.渲染缓冲对象附件
渲染缓冲对象附加的好处是,它会将数据储存为OpenGL原生的渲染格式,它是为离屏渲染到帧缓冲优化过的。
渲染缓冲对象直接将所有的渲染数据储存到它的缓冲中,不会做任何针对纹理格式的转换,让它变为一个更快的可写储存介质。
创建一个渲染缓冲对象
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
创建一个深度和模板渲染缓冲对象可以通过调用glRenderbufferStorage函数来完成
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600);
附加这个渲染缓冲对象
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
4.选用 纹理附件 还是 渲染缓冲对象附件
通常的规则是,如果你不需要从一个缓冲中采样数据,那么对这个缓冲使用渲染缓冲对象会是明智的选择。如果你需要从缓冲中采样颜色或深度值等数据,那么你应该选择纹理附件。性能方面它不会产生非常大的影响的。
5.调用glCheckFramebufferStatus,检查帧缓冲是否完整。
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
渲染到纹理
我们将会将场景渲染到一个附加到帧缓冲对象上的颜色纹理中,之后将在一个横跨整个屏幕的四边形上绘制这个纹理。
// framebuffer configuration
// -------------------------
unsigned int framebuffer;//创建一个帧缓冲对象
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);//绑定
// 创建一个颜色附件
unsigned int textureColorbuffer;
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);// 将它附加到当前绑定的帧缓冲对象
// 添加一个深度(和模板)附件到帧缓冲中。
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT); // 创建为一个深度和模板附件渲染缓冲对象
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); // 附加到帧缓冲的深度和模板附件上
// 检查帧缓冲是否是完整的
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
//解绑帧缓冲,保证我们不会不小心渲染到错误的帧缓冲上。
glBindFramebuffer(GL_FRAMEBUFFER, 0);
在该帧缓存对象中,颜色缓冲绑定为附件(texture),而深度和模板缓冲绑定为渲染缓冲对象。
在循环中使用
// 绑定到framebuffer并绘制场景,就像我们通常给纹理着色一样
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glEnable(GL_DEPTH_TEST); // 启用深度测试(在渲染屏幕空间四边形时禁用)
// 确保我们清除了framebuffer的内容
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
绘制内容,将绘制在该帧缓存中.................
glBindFramebuffer(GL_FRAMEBUFFER, 0); // 返回默认
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
//绘制到屏幕
screenShader.use();
glBindVertexArray(quadVAO);
glBindTexture(GL_TEXTURE_2D, textureColorbuffer); // use the color attachment texture as the texture of the quad plane
glDrawArrays(GL_TRIANGLES, 0, 6);
运行结果
线框模式下为
帧缓存将3D模型转换为2D纹理。