FBO一个最常见的应用就是:渲染到纹理(render to texture),通过这项技术可以实现发光效果,环境映射,阴影映射等很炫的效果。
OpenGL中的Frame Buffer Object(FBO)扩展,被推荐用于把数据渲染到纹理对像。相对于其它同类技术,如数据拷贝或交换缓冲区等,使用FBO技术会更高效并且更容易实现。
在OpenGL渲染管线中,几何数据和纹理最终都是以2d像素绘制到屏幕上。最后一步的渲染目标在OpenGL渲染管线中被称为帧缓存(frame buffer)。帧缓存是颜色缓存、深度缓存、模板缓存、累积缓存的集合。默认情况下, OpenGL使用的帧缓存是由窗体系统创建和管理的。
在OpenGL扩展中,GL_EXT_framebuffer_object扩展提供了一个创建额外帧缓存对象(FBO)的接口。这个帧缓存的创建和控制完全是由OpenGL完成的,有别于窗体系统创建的默认的帧缓存。与系统默认的帧缓存类似,一个FBO也是颜色缓存、深度缓存、模板缓存的集合(FBO不包括累积缓存),然后OpenGL程序就可以把渲染重定向到FBO中。
这里有一个新的概念需要注意,那就是renderbuffer object。这个对象是通过GL_EXT_framebuffer_object扩展创建。它被用来在渲染过程中为一个2D图像提供渲染目标。
下图展示了FBO和renderbuffer object与texture object之间的关系。从图中我们可以看出:多个renderbuffer object和texture object可以通过挂接点挂接到FBO上。需要主要的是FBO并没有实际存储数据的地方,它只是一个数据的壳,它只有挂接点。
一个FBO对象包含多个颜色挂接点和一个深度挂接点以及一个模板挂接点。不同的显卡支持的颜色挂接点的数目是不同的,可以通过查询GL_MAX_COLOR_ATTACHMENTS_EXT获取支持的最大的挂接点的数目。支持多个颜色挂接点的原因是FBO可以在同一时间内将颜色缓存渲染到多个目标中去,这种能力被称为MRT(multiple render targets)。通过GL_ARB_draw_buffers扩展可以实现该功能。
帧缓冲提供了一种有效的切换机制,使得挂接和卸载一个可挂接的图像非常之迅速。FBO使用glFramebufferTexture2DEXT()来进行texturebuffer对象的切换,使用glFramebufferRenderbufferEXT()来进行renderbuffer对象的切换。
让我们看一下其使用过程:
1.FBO对象的创建与销毁
void glGenFramebuffersEXT(GLsizei n, GLuint* ids);
void glDeleteFramebuffersEXT(GLsizei n, const GLuint* ids);
第一个参数是要创建的FBO的个数,第二个保存创建的FBO的ID。
2.一旦FBO对象创建完毕,即要进行绑定
void glBindFramebufferEXT(GLenum target, GLuint id);
第一个参数必须是GL_FRAMEBUFFER_EXT
第二个参数是第一步创建FBO对象中获取的id号,该id号是一个非0值,因为系统默认的FBO的id号是0,所以如果你想取消FBO的绑定,将id等于0作为id参数传递给该函数即可。
3.创建Renderbuffer Object
void glGenRenderbuffersEXT(GLsizei n, GLuint* ids);
void glDeleteRenderbuffersEXT(GLsizei n, const GLuint* ids);
4.同样的,Renderbuffer Object创建好之后,要记得绑定。
void glBindRenderbufferEXT(GLenum target, GLuint id)
5.确定Renderbuffer Object的数据格式和尺寸。
这一步很重要,因为我们只是创建了Renderbuffer Object,还没有为它提供一个存储数据的地方,也没有指定其存储数据的地方存储什么格式的数据,所以接下来就要做这个事情了。
void glRenderbufferStorageEXT(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);
第一个参数必须是GL_RENDERBUFFER_EXT
第二个参数可以是可渲染的颜色格式,如(GL_RGB, GL_RGBA, etc.),可渲染的深度格式(GL_DEPTH_COMPONENT)或者是可渲染的模板格式(GL_STENCIL_INDEX)。
最后两个参数就是以像素为单位指定数据所要占用内存的大小。
这里需要注意两点:
1。 指定的内存区域的宽和高应该都小于GL_MAX_RENDERBUFFER_SIZE_EXT,否则将产生GL_INVALID_VALUE错误。
2。 所有的texture object也好,renderbuffer object也好,其宽和高都是严格一致的。
6.指定挂接的对象
glFramebufferTexture2DEXT函数挂接一个texture图像到FBO
glFramebufferRenderbufferEXT函数挂接一个Renderbuffer图像到FBO
7.检测FBO状态
该挂接的对象都挂接好了,在使用FBO之前,必须要检查FBO的状态是否处于完成状态。如果FBO处于未完成状态,那么绘制操作就会失败。如何获取FBO的完成状态呢?使用
glCheckFramebufferStatusEXT函数。