1. 引言
在上一章节讲解FBO时,使用纹理来存储颜色缓存附件、深度缓存附件、模板缓存附件,但纹理并不是唯一的选择。尤其是针对深度缓存附件、模板缓存附件这类不需要在着色器中读取的缓存数据,OpenGL 还提供了另一种更加高效的缓存区附件——渲染缓冲对象(Renderbuffer Object, RBO)附件,用于存储渲染结果。
2. 渲染缓冲对象?
渲染缓冲对象(RBO)是 OpenGL 提供的一种存储渲染结果的帧缓冲对象(FrameBuffer Object,FBO)附件,与帧缓冲对象(FBO)配合使用。
与可以在着色器中采样的纹理附件不同,渲染缓冲对象的不能被直接读取。由于其不可被直接读取的特性,给了OpenGL很多优化空间:RBO直接存储渲染数据,无需进行额外的向纹理特定格式的转换,从而减少了内存带宽的占用。而深度缓冲区和模板缓冲区这类不需要在后续的着色器阶段中被读取和处理的数据,正是RBO的绝佳应用场景。
由于RBO不能被直接读取,所以无法像操作纹理一样从 RBO 中直接获取(采样)数据。但这并不意味着不可以读取RBO中缓存数据,可以借助 glReadPixels接口获得指定区域内的数据,该接口的详细叙述如下:
//// 从帧缓冲区中读取像素数据
/// x: 从帧缓冲区读取的像素的左下角 x 坐标
/// y: 从帧缓冲区读取的像素的左下角 y 坐标
/// width: 从帧缓冲区读取的像素的宽度
/// height: 从帧缓冲区读取的像素的高度
/// format: 像素数据的格式,GL_STENCIL_INDEX,
/// GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL,
/// GL_BGR, GL_RGBA, and GL_BGRA, etc
/// type: 像素数据的类型,GL_UNSIGNED_BYTE, GL_BYTE,
/// GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT,
/// GL_INT,
void glReadPixels(GLint x, GLint y,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
void *pixels);
尽管渲染缓冲对象和纹理都能作为 FBO 的附件,用于存储渲染结果,但它们的功能和性能有所不同。
3. 相关接口
RBO的相关接口涉及到其创建、绑定、分配存储空间、附加到FBO等操作,下面是相关接口的详细说明:
// 创建渲染缓冲对象
// n: 要创建的 RBO 数量
// renderbuffers: 返回的 RBO ID 数组
void glGenRenderbuffers(GLsizei n, GLuint *renderbuffers);
// 绑定渲染缓冲对象
// target: 要绑定的目标,必须是 GL_RENDERBUFFER
// renderbuffer: 要绑定的 RBO ID
void glBindRenderbuffer(GLenum target, GLuint renderbuffer);
// 删除渲染缓冲对象
// n: 要删除的 RBO 数量
// renderbuffers: 要删除的 RBO ID 数组
void glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers);
// 为 RBO 分配存储空间
// target: 要绑定的目标,必须是 GL_RENDERBUFFER
// internalformat: RBO 的内部格式,例如 GL_DEPTH_COMPONENT,
// GL_DEPTH24_STENCIL8, GL_RGBA8 等
// width: RBO 的宽度
// height: RBO 的高度
void glRenderbufferStorage(GLenum target, GLenum internalformat,
GLsizei width, GLsizei height);
// 将 RBO 附加到帧缓冲对象
// target: 要绑定的目标,必须是 GL_FRAMEBUFFER
// attachment: 要附加的附件类型,例如 GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT, GL_DEPTH_STENCIL_ATTACHMENT 等
// renderbuffertarget: RBO 的目标类型,必须是 GL_RENDERBUFFER
// renderbuffer: 要附加的 RBO ID
void glFramebufferRenderbuffer(GLenum target, GLenum attachment,
GLenum renderbuffertarget, GLuint renderbuffer);
相应的代码实操案例如下:
void CreateAndBindFBO(GLuint& fbo,GLuint &colorTexture, GLuint &rbo, int width, int height)
{
// 创建 FBO
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
// 创建并绑定颜色缓冲纹理
glGenTextures(1, &colorTexture);
glBindTexture(GL_TEXTURE_2D, colorTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
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, colorTexture, 0);
// 创建并绑定深度模板纹理
glGenRenderbuffers(1,&rbo);
glBindRenderbuffer(GL_RENDERBUFFER,rbo);
glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH24_STENCIL8,width,height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT,GL_RENDERBUFFER,rbo);
// 检查 FBO 是否完整
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// 解除绑定 FBO
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
在这个例子中,首先我们创建了一个渲染缓冲对象 `rbo`,并为它分配了深度-模板格式的存储空间。然后,我们将这个 RBO 附加到帧缓冲对象的深度-模板附件上。
4. 总结
本文在前一章节(帧缓冲)的基础上,介绍了渲染缓冲对象,并通过对比渲染缓冲对象附件和纹理附件,详细说明了它们的区别和适用场景。然后介绍了渲染缓冲对象的相关接口,并给出了相应的代码实操案例。