OpenGL系列教程之十:OpenGL帧缓冲区对象(FBO)
http://blog.****.net/u012501459/article/details/12945167
相关主题:像素缓冲区对象(PBO)
下载: fbo.zip, fboDepth.zip, fboStencil.zip
glGenFramebuffers()
- void glGenFramebuffers(GLsizei n, GLuint* ids)
- void glDeleteFramebuffers(GLsizei n, const GLuint* ids)
glBindFramebuffer()
- void glBindFramebuffer(GLenum target, GLuint id)
- void glGenRenderbuffers(GLsizei n, GLuint* ids)
- void glDeleteRenderbuffers(GLsizei n, const Gluint* ids)
- void glRenderbufferStorage(GLenum target,
- GLenum internalFormat,
- GLsizei width,
- GLsizei height)
当一个渲染缓冲区对象被创建以后,它不包含任何的数据存储区,因此我们必须为它分配空间。这可以使用glRenderbufferStorage()来实现。第一个参数必须是GL_RENDERBUFFER。第二个参数可以是渲染颜色(GL_RGB,GL_RGBA,等),渲染深度(GL_DEPTH_COMPONENT),或者渲染模板格式(GL_STENCIL_INDEX)。width和height是渲染缓冲区镜像的尺寸(以像素为单位)。
- void glGetRenderbufferParameteriv(GLenum target,
- GLenum param,
- GLint* value)
- GL_RENDERBUFFER_WIDTH
- GL_RENDERBUFFER_HEIGHT
- GL_RENDERBUFFER_INTERNAL_FORMAT
- GL_RENDERBUFFER_RED_SIZE
- GL_RENDERBUFFER_GREEN_SIZE
- GL_RENDERBUFFER_BLUE_SIZE
- GL_RENDERBUFFER_ALPHA_SIZE
- GL_RENDERBUFFER_DEPTH_SIZE
- GL_RENDERBUFFER_STENCIL_SIZE
- glFramebufferTexture2D(GLenum target,
- GLenum attachmentPoint,
- GLenum textureTarget,
- GLuint textureId,
- GLint level)
- void glFramebufferRenderbuffer(GLenum target,
- GLenum attachmentPoint,
- GLenum renderbufferTarget,
- GLuint renderbufferId)
- GLenum glCheckFramebufferStatus(GLenum target)
glCheckFramebufferStatus()检查附加到当前被绑定的FBO的镜像和帧缓冲的参数。并且这个函数不能在glBegin()和glEnd()之间。target参数必须是GL_FRAMEBUFFER。检查完FBO后会返回一个非零的值。如果所有的条件和规则都满足,会返回GL_FRAMEBUFFER_COMPLETE。否则,它返回一个有关的错误值,这个错误值会告诉违反了那条规则。
- 附加到帧缓冲区镜像的width和height必须是非零的。
- 如果一个镜像被附加到颜色附加点,那么这个镜像必须有一个可渲染颜色(color-renderable)的内部格式。(GL_RGBA,GL_DEPTH_COMPONENT,GL_LUMINANCE等)
- 如果一个镜像被附加到GL_DEPTH_ATTACHMENT,那么这个镜像必须有可渲染深度(depth-renderable)的内部格式。(GL_DEPTH_COMONENT,GL_DEPTH_COPONENT24等)
- 如果一个镜像被附加到GL_STENCIL_ATTACHMENT,那么这个镜像必须有可渲染模板(stencil-renderable)的内部格式。(GL_STENCIL_INDEX,GL_STENCIL_INDEX8等)
- FBO必须至少有一个附加的镜像。
- 所有附加到FBO的镜像必须有相同的宽体和高度。
- 所有附加到颜色附加点的镜像必须有相同的内部格式。
注意即使上面所有的条件都满足,你的OpenGL驱动(GPU)可能不支持某些内部的格式或参数。如果某个特殊的实现不被OpenGL驱动支持,那么glCheckFramebufferStatus()会返回GL_FRAMEBUFFER_UNSUPPORTED。
sample code提供了一些工具函数来显示当前FBO的信息:printFramebufferInfo()和checkFramebufferStatus()。
下载源文件和可执行文件:fbo.zip(更新:2012-07-08)
附加:
-只渲染到深度缓冲区: fboDepth.zip
-使用模板缓冲区渲染物体的轮廓: fboStencil.zip
-使用glBlitFramebuffer()在两个FBO中传输:fboBlit.zip
有时,你需要生成动态的纹理。最普遍的例子是生成镜像/反射的效果,动态的立方体/环境映射和阴影映射。动态的纹理可以使用渲染场景到纹理中来生成。一个传统的渲染到纹理的方法是按照正常的步骤绘制一个场景到帧缓冲区中,然后使用glCopyTexSubImage2D()将帧缓冲区中的镜像复制到纹理中。
使用FBO,我们可以直接将一个场景渲染到纹理中,因此我们完全不需要使用”窗口系统提供的“帧缓冲区。除此之外,我们还可以减少额外的数据复制(从帧缓冲区到纹理)。
这个例子程序使用用FBO和不用FBO两种方式执行了渲染到纹理的操作,并比较了它们的性能差异。除了性能方面的区别外,FBO还有另外一个优点,在传统的渲染到纹理的模式中如果纹理的分辨率比渲染窗口大,那么纹理中窗口区域之外的部分会被裁剪掉,然后使用FBO不会产生这种裁剪的问题。你可以创建一个比显示窗口大的可渲染的帧缓冲区镜像。
下面的代码在渲染操作开始之前设置了一个FBO和附加到帧缓冲区的镜像。注意不仅纹理镜像可以附加到FBO中,渲染缓冲区镜像(深度缓冲,模板缓冲)也可以被附加到FBO。我们不会实际使用深度缓冲,然而FBO需要它进行深度测试。如果我们不将深度缓冲附加到FBO中,那么渲染输出的结果会被破坏因为缺少了深度测试。如果在FBO渲染时需要模板测试,那么额外的模板缓冲需要附加到GL_STENCIL_ATTACHMENT附加点。
- ...
- // 创建一个纹理对象
- GLuint textureId;
- glGenTextures(1, &textureId);
- glBindTexture(GL_TEXTURE_2D, textureId);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // 自动贴图
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, 0);
- glBindTexture(GL_TEXTURE_2D, 0);
- // 创建一个渲染缓冲区对象来存储深度信息
- GLuint rboId;
- glGenRenderbuffers(1, &rboId);
- glBindRenderbuffer(GL_RENDERBUFFER, rboId);
- glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
- TEXTURE_WIDTH, TEXTURE_HEIGHT);
- glBindRenderbuffer(GL_RENDERBUFFER, 0);
- // 创建一个帧缓冲区对象
- GLuint fboId;
- glGenFramebuffers(1, &fboId);
- glBindFramebuffer(GL_FRAMEBUFFER, fboId);
- // 将纹理对象附加到FBO的颜色附加点上
- glFramebufferTexture2D(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
- GL_COLOR_ATTACHMENT0, // 2. attachment point
- GL_TEXTURE_2D, // 3. tex target: GL_TEXTURE_2D
- textureId, // 4. tex ID
- 0); // 5. mipmap level: 0(base)
- // 将渲染缓冲区对象附加到FBO的深度附加点上
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, // 1. fbo target: GL_FRAMEBUFFER
- GL_DEPTH_ATTACHMENT, // 2. attachment point
- GL_RENDERBUFFER, // 3. rbo target: GL_RENDERBUFFER
- rboId); // 4. rbo ID
- // 检查FBO的状态
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if(status != GL_FRAMEBUFFER_COMPLETE)
- fboUsed = false;
- // 切换到窗口系统提供的帧缓冲区中
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- ...
- ...
- // 设置FBO为渲染的目的地
- glBindFramebuffer(GL_FRAMEBUFFER, fboId);
- // 清除缓冲区
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- // 直接绘制一个场景到纹理中
- draw();
- // 解除FBO的绑定
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- // 贴图生成
- // 注意:如果GL_GENERATE_MIPMAP被设置成 GL_TRUE, 那么glCopyTexSubImage2D()
- //会自动生成贴图. 然而, 附加到FBO的纹理需要使用glGenerateMipmap()来生成贴图
- glBindTexture(GL_TEXTURE_2D, textureId);
- glGenerateMipmap(GL_TEXTURE_2D);
- glBindTexture(GL_TEXTURE_2D, 0);
- ...
注意为了在修改纹理镜像的基础级别后显示地生成贴图glGenerateMipmap()也被当做FBO的扩展。如果GL_GENERATE_MIPMAP被设置为GL_TRUE,那么glTex{Sub}Image2D()和glCopyTex{Sub}Image2D()会自动触发生成贴图(在OpenGL 1.4版本或更高的版本中)。然而,FBO当它的基础纹理被更改后不会自动生成它的贴图这是因为FBO不会调用glCopyTex{Sub}Image2D()来修改纹理。因此,glGenerateMipmap()必须被显示地调用。
如果你需要快速地出来纹理,可以联合像素缓冲区对象(PBO)一起修改纹理。