OpenGL里的帧缓存FBO(OpenGL Frame BufferObject)

本文详细介绍了OpenGL FrameBufferObject (FBO) 的使用方法,包括创建、绑定、关联纹理和渲染缓存,以及如何进行渲染到纹理和使用已渲染的纹理。同时,阐述了FBO与传统渲染方式的区别,强调了FBO在提高渲染效率和灵活性方面的优势。通过示例程序,进一步展示了FBO在实际应用中的操作流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近用到纹理,由于纹理是浮点的,所以采用直接纹理贴图会有锯齿,如果采用gluBuild2DMipmaps,显得模糊且效率比较低,所以学下下FBO。以下是摘录的两篇文章

第一篇摘自:http://blog.sina.com.cn/s/blog_4d035b080100kw4w.html 

GPU深度发掘(二)::OpenGL FrameBuffer Object 101

作者:by Rob 'phantom' Jones                译者:华文广

    本译文可以自由转载,要求保留原作者信息并注明文章出自物理开发网:http://www.physdev.com/

 

 

介绍

Frame Buffer Object(FBO)扩展,被推荐用于把数据渲染到纹理对像。相对于其它同类技术,如数据拷贝或交换缓冲区等,使用FBO技术会更高效并且更容易实现。
在这篇文章中,我将会快速地讲解一下如何来使用这一扩展,同时会介绍一些在使用过程中我们要注意的地方。学会该技术后,你便可以把一些渲染到纹理(render to texture)的功能加入到你的程序中,实现更快速的运行。

建立

和OpenGL中的其它对像一样,如纹理对像(texture object), 像素缓冲对像(pixel buffer objects) , 顶点缓冲对像(vertex buffer object)等,在使用一个FBO对像之前,你必须先要生成该对像,并取得一个有效的对像标识。

GLuint fbo; glGenFramebuffersEXT(1, &fbo);

要对一个FBO进行任何的操作,你必须先要对它进行绑定。这一步骤与我们平时使用VBO或者纹理的过程很像。绑定对像后,我们便可以对FBO进行各种操作了,以下代码演示如何进行绑定。

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);

第一个参数是“目标(target)”,指的是你要把FBO与哪个帧缓冲区进行绑定,目前来说,我个参数就只有一些预定义的选择(GL_FRAMEBUFFER_EXT),但将来扩展的发展,可能会来现其它的选择,让你把FBO与其它的目标进行绑定。整型变量fbo,是用来保存FBO对像标识的,这个标识我们已在前面生成了。要实现任何与FBO有关的操作,我们必须有一个FBO被绑定,否则调用就会出错

加入一个深度缓存(Depth Buffer)

一个FBO它本身其实没有多大用处,要想让它能被更有效的利用,我们需要把它与一些可被渲染的缓冲区绑定在一起,这样的缓冲区可以是纹理,也可以是下面我们将要介绍的渲染缓冲区(renderbuffers)。

一个渲染缓冲区,其实就是一个用来支持离屏渲染的缓冲区。通常是帧缓冲区的一部份,一般不具有纹理格式。常见的模版缓冲和深度缓冲就是这样一类对像。

在这里,我们要为我们的FBO指定一个渲染缓冲区。这样,当我们渲染的时候,我们便把这个渲染缓冲区作为FBO的一个深度缓存来使用。

和FBO的生成一样,我们首先也要为渲染缓冲区指定一个有效的标识。

GLuint depthbuffer; glGenRenderbuffersEXT(1, &depthbuffer);

成功完成上面一步之后,我们就要对该缓冲区进行绑定,让它成为当前渲染缓冲,下面是实现代码。

glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);

和FBO的绑定函数一样,第一个参数是“目标(target)”,指的是你要与哪个目标进行绑定,目前来说,只能是一些预定义好的目标。变量dephtbuffer用来保存对像标识。

这里有一个关键的地方,也就是我们生成的渲染缓冲对像,它本身并不会自动分配内存空间。因此我们要调用OpenGL的函数来给它分配指定大小的内存空间,在这里,我们分配一个固定大小的深度缓显空间。

glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height);

上面这一函数成功运行之后,OpenGL将会为我们分配好一个大小为width x height的深度缓冲区。注意的是,这里用了GL_DEPTH_COMPONENT,就是指我们的空间是用来保存深度值的,但除了这个之外,渲染缓冲区 还可以用来保存普通的RGB/RGBA格式的数据或者是模板缓冲的信息。

准被好了深度缓存的显存空间后,接下来要做的工作就是把它与前面我们准备好了的FBO对像绑定在一起。

glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);

这个函数看起来有点复杂,但其实它很好理解的。它要做的全部工作就是把把前面我们生成的深度缓存对像与当前的FBO对像进行绑定,当然我们要注意一个FBO有多个不同绑定点,这里是要绑定在FBO的深度缓冲绑定点上。

加入用于渲染的纹理

到现在为止,我们还没有办法往FBO中写入颜色信息。这也是我们接下来正要讨论的,我们有以下两种方法来实现它:

  1. 把一个颜色渲染缓冲与FBO绑定。
  2. 把一个纹理与FBO绑定。

前者在某些地方会用到,后面的章节我们会深入讨论。现在我们先来说说第二种方法。

在你想要把纹理与一个FBO进行绑定之前,我们得先要生成这个纹理。这个生成纹理的过程种我们平时见到的纹理生成没什么区别。

GLuint img; glGenTextures(1, &img); glBindTexture(GL_TEXTURE_2D, img); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

这个实例中,我们生成一个普通的RGBA图像,大小是width x height,与前面我们生成的渲染缓冲区的大小是一样的,这一点很重要,也就是FBO中所有的绑定对像,都必须要有相同的宽度和高度。还有要注意的就是:这里我们没有上传任何的数据,只是让OpenGL保留分配好的空间,稍后我们将会用到。

生成好纹理之后,接下来的工作就是把这个纹理与FBO绑定在一起,以便我们可以把数据渲染到纹理空间中去。

glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, img, 0);

这里再次看到这个看起来非常可怕的函数,当然它也并没有我们想像中那么难理解。参数GL_COLOR_ATTACHMENT0_EXT是告诉OpenGL把纹理对像绑定到FBO的0号绑定点(一个FBO在同一个时间内可以绑定多个颜色缓冲区,每个对应FBO的一个绑定点),参数GL_TEXTURE_2D是指定纹理的格式,img保存的是纹理标识,指向一个之前就准备好了的纹理对像。纹理可以是多重映射的图像,最后一个参数指定级级为0,指的是使用原图像。

最后还有一步要做的工作,就是检查一下FBO的准备工作是否全部完成,是否以经能被正确使用了。

这个测试工作由下面一个函数来完成,它会返回一个当前绑定的FBO是否正确的状态信息。

GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);

如果所有工作都已经做好,那么返回的状态值是GL_FRAMEBUFFER_COMPLETE_EXT,也就是说你的FBO已经准备好,并可以用来作为渲染对像了。否则就会返回其它一个错误码,通过查找定义文档,可以找到相关的错误信息,从而了角错误大概是在哪一步骤中产生的。
 

渲染到纹理

所有困难的工作就是前面建立FBO环境的部份,剩下来的工作就相当简单了,相关的事情就只是调用一下以下这个函数:glBindFramebufferEXT().

当我们要把数据渲染并输出到FBO的时候,我们只需要用这个函数来把一个FBO对像进行绑定。当我们要停止输出到FBO,我们只要把参数设为0,再重新调用一次该函数就可以了。当然,停止向FBO输出,这也是很重要的,当我们完成了FBO的工作,就得停止FBO,让图像可以在屏幕上正确输出。

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo); glPushAttrib(GL_VIEWPORT_BIT); glViewport(0,0,width, height); // Render as normal here // output goes to the FBO and it's attached buffers glPopAttrib(); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

上面另外三行代码glPushAttrib/glPopAttrib 及 glViewport,是用来确保在你跳出FBO渲染的时候可以返回原正常的渲染路径。glViewport在这里的调用是十分必要的,我们不要常试把数据渲染到一个大于或小于FBO大小的区域。 函数glPushAtrrib 和 glPopAttrib 是用来快速保存视口信息。这一步也是必要的,因为FBO会共享主上下文的所有信息。任何的变动,都会同时影响到FBO及主上下文,当然也就会直接影响到你的正常屏幕渲染。

这里一个重要信息,你可能也注意到了,我们只是在绘制的时候绑定或解除FBO,但是我们没有重新绑定纹理或渲染缓冲区,这里因为在FBO中会一直保存了这种绑定关系,除非你要把它们分开或FBO对像被销毁了。

使用已渲染出来的纹理

来到这里,我们已经把屏幕的数据渲染到了一个图像纹理上。现在我们来看一看如何来使用这张已经渲染好了的图像纹理。这个操作的本身其实是很简单的,我们只要把这张图像纹理当作普通纹理一样,绑定为当前纹理就可以了。

glBindTexture(GL_TEXTURE_2D, img);

以上这一函数调用完成之后,这张图像纹理就成了一个在绘图的时候用于被读取的普通纹理。

根据你在初始化时所指定的不同纹理滤波方式,你也许会希望为该纹理生成多重映像(mipmap)信息。如果要建立多重映像信息,多数的人都是在上传纹理数据的时候,通过调用函数gluBuild2DMipmaps()来实现,当然有些朋友可能会知道如何使用自动生成多重映像的扩展,但是在FBO扩展中,我们增加了第三种生成映像的方法,也就是使用GenerateMipmapEXT()函数。

这个函数的作用就是让OpenGL帮你自动创建多重映像信息。中间实现的过程,根据不同的显卡会有所不同,我们只关心它们最终的结果是一样就行了。值得注意的是:对于这种通过FBO渲染出来的纹理,要实现多重映像的话,只有这一种方法是正确的,这里你不可以使用自动生成函数来生成多重映像,这其中的原因有很多,如果你想深入了解的话,可以查看一下技术文档。

使用这一函数使方便,你所要做的就是先把该纹理对像绑定为当前纹理,然后调用一次该函数就可以了。

glGenerateMipmapEXT(GL_TEXTURE_2D);

OpenGL将会自动为我们生成所需要的全部信息,到现在我们的纹理便可以正常使用了。

一个重点要注意的地方:如果你打算使用多重映像(如 GL_LINEAR_MIPMAP_LINEAR),该函数glGenerateMipmapEXT()必须要在执行渲染到纹理之前调用。

在创建纹理的时候,我们可以按以下代码来做。

glGenTextures(1, &img); glBindTexture(GL_TEXTURE_2D, img); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 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_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glGenerateMipmapEXT(GL_TEXTURE_2D);

到现在,这张纹理和普通纹理没什么区别,我们就按处理普通纹理的方法来使用就可以了。

清理

最后,当你完成了所有的FBO操作之后,请别忘了要清理或删除掉那些不要了的FBO对像,和清理纹理对像相似,这一步只要以下一个函数就可以完成:

glDeleteFramebuffersEXT(1, &fbo);

同样的,你如果分配了渲染缓冲对像,也别忘了要把它清理掉。本实例中我们分配的是深度缓存渲染对像,我们用以下函数来清除它:

glDeleteRenderbuffersEXT(1, &depthbuffer);

到这里,所有的FBO对像及渲染缓冲都被释放掉了,我们的清理工作也就完成了。

最后的思考

这一篇文章只是对FBO扩展的一个初步介绍,希望对你有所帮助,更多详细的知识,可以查看一下FBO spec ,或者看一下《More OpenGL Game Programming》这本书中关于扩展部分的章节。

问题的返溃及相关技术的讨论,可以登陆物理开发网的GPGPU/CUDA论坛进行交流。

在文档结束之前,我要说一下在使用FBO来写程序的过程中,一些值得我们去注意的地方: 

  1. 就目前来说,你没办法得到模版缓冲的绑定点。虽然在技术上是定义了这么一种深度模版的纹理格式,目的是让我们可以渲染到模版,但这一技术到目前为止还缺乏硬件的支持。
  2. 不要频繁地创建及销毁FBO对像。好的做法应该是在程序建立的同时生成FBO对像,然后在我们需要用到的地方使用它。 
  3. 一个纹理,如果被定义为用于做渲染纹理,那么我们就要尽量避免使用glTexImage之类的函数来修改该纹理的数据,这样做多数情况下会让你的程序出现问题。

本文示例程序中要注意的地方

对应这篇文章所讨论的内容,我们写了一个相应的程序,其功能就是给FBO加入一个深度缓冲对像及一个纹理对像。我们发现,在ATI的显卡中有一个bug,也就是当我们给同时FBO加入一个深度缓冲及一个纹理的时候,就会出现严重的冲突。从这里也告诉我们,当我们在写好一个FBO相关的程序的时候,一定要在不同的硬件及不同的驱动下进行广泛的测试,直到没有任何渲染问题为止。

I'd also like to put out a big thanks to Rick Appleton for helping me test out and debug the code on NVIDA hardware, couldn't have done it without you mate :)

本程序需要有GLUT函数库的支持才能正确运行,我使用的是FreeGLUT.

FBO_Example.zip程序下载



以下是第二篇文章:

【OpenGL】OpenGL帧缓存对象(FBO:Frame Buffer Object)

摘自:http://blog.youkuaiyun.com/xiajun07061225/article/details/7283929 (同是csdn的)

翻译的,如果不正确,敬请谅解和指正。

OpenGL Frame BufferObject(FBO)

Overview:

    在OpenGL渲染管线中,几何数据和纹理经过多次转化和多次测试,最后以二维像素的形式显示在屏幕上。OpenGL管线的最终渲染目的地被称作帧缓存(framebuffer)。帧缓冲是一些二维数组和OpenG所使用的存储区的集合:颜色缓存、深度缓存、模板缓存和累计缓存。一般情况下,帧缓存完全由window系统生成和管理,由OpenGL使用。这个默认的帧缓存被称作“window系统生成”(window-system-provided)的帧缓存。

在OpenGL扩展中,GL_EXT_framebuffer_object提供了一种创建额外的不能显示的帧缓存对象的接口。为了和默认的“window系统生成”的帧缓存区别,这种帧缓冲成为应用程序帧缓存(application-createdframebuffer)。通过使用帧缓存对象(FBO),OpenGL可以将显示输出到引用程序帧缓存对象,而不是传统的“window系统生成”帧缓存。而且,它完全受OpenGL控制。

相似于window系统提供的帧缓存,一个FBO也包含一些存储颜色、深度和模板数据的区域。(注意:没有累积缓存)我们把FBO中这些逻辑缓存称之为“帧缓存关联图像”,它们是一些能够和一个帧缓存对象关联起来的二维数组像素。

有两种类型的“帧缓存关联图像”:纹理图像(texture images)和渲染缓存图像(renderbuffer images)。如果纹理对象的图像数据关联到帧缓存,OpenGL执行的是“渲染到纹理”(render to texture)操作。如果渲染缓存的图像数据关联到帧缓存,OpenGL执行的是离线渲染(offscreen rendering)。

这里要提到的是,渲染缓存对象是在GL_EXT_framebuffer_object扩展中定义的一种新的存储类型。在渲染过程中它被用作存储单幅二维图像。

下面这幅图显示了帧缓存对象、纹理对象和渲染缓存对象之间的联系。多多个纹理对象或者渲染缓存对象能够通过关联点关联到一个帧缓存对象上。


在一个帧缓存对象中有多个颜色关联点(GL_COLOR_ATTACHMENT0_EXT,...,GL_COLOR_ATTACHMENTn_EXT),一个深度关联点(GL_DEPTH_ATTACHMENT_EXT),和一个模板关联点(GL_STENCIL_ATTACHMENT_EXT)。每个FBO中至少有一个颜色关联点,其数目与实体显卡相关。可以通过GL_MAX_COLOR_ATTACHMENTS_EXT来查询颜色关联点的最大数目。FBO有多个颜色关联点的原因是这样可以同时将颜色而换成渲染到多个FBO关联区。这种“多渲染目标”(multiple rendertargets,MRT)可以通过GL_ARB_draw_buffers扩展实现。需要注意的是:FBO本身并没有任何图像存储区,只有多个关联点。

FBO提供了一种高效的切换机制;将前面的帧缓存关联图像从FBO分离,然后把新的帧缓存关联图像关联到FBO。在帧缓存关联图像之间切换比在FBO之间切换要快得多。FBO提供了glFramebufferTexture2DEXT()来切换2D纹理对象和glFramebufferRenderbufferEXT()来切换渲染缓存对象。

 

创建FBO

创建FBO和产生VBO类似。

glGenFramebuffersEXT()

Void gelGenFramebuffersEXT(GLsizei n,GLuint* ids);

void glDeleteFramebuffersEXT(GLsizei n, const GLuint* ids);

glGenFramebuffersEXT()需要两个参数:第一个是要创建的帧缓存的数目,第二个是指向存储一个或者多个ID的变量或数组的指针。它返回未使用的FBO的ID。ID为0表示默认帧缓存,即window系统提供的帧缓存。

当FBO不再被使用时,FBO可以通过调用glDeleteFrameBuffersEXT()来删除。

glBindFramebufferEXT()

一旦一个FBO被创建,在使用它之前必须绑定。

void glBindFramebufferEXT(GLenum target, GLuint id)

第一个参数target应该是GL_FRAMEBUFFER_EXT,第二个参数是FBO的ID号。一旦FBO被绑定,之后的所有的OpenGL操作都会对当前所绑定的FBO造成影响。ID号为0表示缺省帧缓存,即默认的window提供的帧缓存。因此,在glBindFramebufferEXT()中将ID号设置为0可以解绑定当前FBO。

 

渲染缓存对象(Renderbuffer Object)

另外,渲染缓存是为离线渲染而新引进的。它允许将一个场景直接渲染到一个渲染缓存对象中,而不是渲染到纹理对象中。渲染缓存对象是用于存储单幅图像的数据存储区域。该图像按照一种可渲染的内部格式存储。它用于存储没有相关纹理格式的OpenGL逻辑缓存,比如模板缓存或者深度缓存。

glGenRenderbuffersEXT()

void glGenRenderbuffersEXT(GLsizei n, GLuint* ids)

void glDeleteRenderbuffersEXT(GLsizei n, const Gluint* ids)

一旦一个渲染缓存被创建,它返回一个非零的正整数。ID为0是OpenGL保留值。

glBindRenderbufferEXT()

void glBindRenderbufferEXT(GLenum target, GLuint id)

和OpenGL中其他对象一样,在引用渲染缓存之前必须绑定当前渲染缓存对象。他target参数应该是GL_RENDERBUFFER_EXT。

glRenderbufferStorageEXT()

void glRenderbufferStorageEXT(GLenum target, GLenum internalFormat,

                             GLsizei width, GLsizei height)

当一个渲染缓存被创建,它没有任何数据存储区域,所以我们还要为他分配空间。这可以通过用glRenderbufferStorageEXT()实现。第一个参数必须是GL_RENDERBUFFER_EXT。第二个参数可以是用于颜色的(GL_RGB,GL_RGBA,etc.),用于深度的(GL_DEPTH_COMPONENT),或者是用于模板的格式(GL_STENCIL_INDEX)。Width和height是渲染缓存图像的像素维度。

width和height必须比GL_MAX_RENDERBUFFER_SIZE_EXT小,否则将会产生GL_UNVALID_VALUE错误。

glGetRenderbufferParameterivEXT()

void glGetRenderbufferParameterivEXT(GLenum target, GLenum param,GLint* value);

我们也可以得到当前绑定的渲染缓存对象的一些参数。Target应该是GL_RENDERBUFFER_EXT,第二个参数是所要得到的参数名字。最后一个是指向存储返回值的整型量的指针。渲染缓存的变量名有如下:

GL_RENDERBUFFER_WIDTH_EXT

GL_RENDERBUFFER_HEIGHT_EXT

GL_RENDERBUFFER_INTERNAL_FORMAT_EXT

GL_RENDERBUFFER_RED_SIZE_EXT

GL_RENDERBUFFER_GREEN_SIZE_EXT

GL_RENDERBUFFER_BLUE_SIZE_EXT

GL_RENDERBUFFER_ALPHA_SIZE_EXT

GL_RENDERBUFFER_DEPTH_SIZE_EXT

GL_RENDERBUFFER_STENCIL_SIZE_EXT

将图像和FBO关联

FBO本身没有图像存储区。我们必须帧缓存关联图像(纹理或渲染对象)关联到FBO。这种机制允许FBO快速地切换(分离和关联)帧缓存关联图像。切换帧缓存关联图像比在FBO之间切换要快得多。而且,它节省了不必要的数据拷贝和内存消耗。比如,一个纹理可以被关联到多个FBO上,图像存储区可以被多个FBO共享。

把2D纹理图像关联到FBO

glFramebufferTexture2DEXT(GLenum target,

                          GLenumattachmentPoint,

                         GLenum textureTarget,

                         GLuint textureId,

                         GLint  level)

glFramebufferTexture2DEXT()把一幅纹理图像关联到一个FBO。第一个参数一定是GL_FRAMEBUFFER_EXT,第二个参数是关联纹理图像的关联点。第三个参数textureTarget在多数情况下是GL_TEXTURE_2D。第四个参数是纹理对象的ID号。最后一个参数是要被关联的纹理的mipmap等级

如果参数textureId被设置为0,那么纹理图像将会被从FBO分离。如果纹理对象在依然关联在FBO上时被删除,那么纹理对象将会自动从当前帮的FBO上分离。然而,如果它被关联到多个FBO上然后被删除,那么它将只被从绑定的FBO上分离,而不会被从其他非绑定的FBO上分离。

把渲染缓存对象关联到FBO

void glFramebufferRenderbufferEXT(GLenum target,

                                 GLenum attachmentPoint,

                                 GLenum renderbufferTarget,

                                 GLuint renderbufferId)

通过调用glFramebufferRenderbufferEXT()可以关联渲染缓存图像。前两个参数和glFramebufferTexture2DEXT()一样。第三个参数只能是GL_RENDERBUFFER_EXT,最后一个参数是渲染缓存对象的ID号。

如果参数renderbufferId被设置为0,渲染缓存图像将会从FBO的关联点分离。如果渲染缓存图像在依然关联在FBO上时被删除,那么纹理对象将会自动从当前绑定的FBO上分离,而不会从其他非绑定的FBO上分离。

检查FBO状态

一旦关联图像(纹理和渲染缓存)被关联到FBO上,在执行FBO的操作之前,你必须检查FBO的状态,这可以通过调用glCheckFramebufferStatusEXT()实现。如果这个FBObuilding完整,那么任何绘制和读取命令(glBegin(),glCopyTexImage2D(), etc)都会失败。

GLenum glCheckFramebufferStatusEXT(GLenum target)

glCheckFramebufferStatusEXT()检查当前帧缓存的关联图像和帧缓存参数。这个函数不能在glBegin()/glEnd()之间调用。Target参数必须为GL_FRAMEBUFFER_EXT。它返回一个非零值。如果所有要求和准则都满足,它返回GL_FRAMEBUFFER_COMPLETE_EXT。否则,返回一个相关错误代码告诉我们哪条准则没有满足。

FBO完整性准则有:

(1)帧缓存关联图像的宽度和高度必须非零。

(2)如果一幅图像被关联到一个颜色关联点,那么这幅图像必须有颜色可渲染的内部格式(GL_RGBA, GL_DEPTH_COMPONENT, GL_LUMINANCE, etc)。

(3)如果一幅被图像关联到GL_DEPTH_ATTACHMENT_EXT,那么这幅图像必须有深度可渲染的内部格式(GL_DEPTH_COMPONENT,GL_DEPTH_COMPONENT24_EXT, etc)。

(4)如果一幅被图像关联到GL_STENCIL_ATTACHMENT_EXT,那么这幅图像必须有模板可渲染的内部格式(GL_STENCIL_INDEX,GL_STENCIL_INDEX8_EXT, etc)。

(5)FBO至少有一幅图像关联。

(6)被关联到FBO的缩影图像必须有相同的宽度和高度。

(7)被关联到颜色关联点上的所有图像必须有相同的内部格式。

注意:即使以上所有条件都满足,你的OpenGL驱动也可能不支持某些格式和参数的组合。如果一种特别的实现不被OpenGL驱动支持,那么glCheckFramebufferStatusEXT()返回GL_FRAMEBUFFER_UNSUPPORTED_EXT。


示例:渲染到纹理


源代码下载:http://www.songho.ca/opengl/gl_fbo.html

包括渲染到纹理、只渲染到深度缓存和使用模板缓存渲染对象的轮廓。

有时候,你需要产生动态纹理。比较常见的例子是产生镜面反射效果、动态环境贴图和阴影等效果。动态纹理可以通过把场景渲染到纹理来实现。渲染到纹理的一种传统方式是将场景绘制到普通的帧缓存上,然后调用glCopyTexSubImage2D()拷贝帧缓存图像至纹理。

使用FBO,我们能够将场景直接渲染到纹理,所以我们不必使用window系统提供的帧缓存。并且,我们能够去除额外的数据拷贝(从帧缓存到纹理);。

这个demo实现了使用FBO和不使用FBO两种情况下渲染到纹理的操作,并且比较了性能差异。除了能够获得性能上的提升,使用FBO的还有另外一个优点。在传统的渲染到纹理的模式中(不使用FBO),如果纹理分辨率比渲染窗口的尺寸大,超出窗口区域的部分将被剪切掉。然后,使用FBO就不会有这个问题。你可以产生比显示窗口大的帧缓存渲染图像。

以下代码在渲染循环开始之前,对FBO和帧缓存关联图像进行了初始化。注意只有一幅纹理图像被关联到FBO,但是,一个深度渲染图像被关联到FBO的深度关联点。实际上我们并没有使用这个深度缓存,但是FBO本身需要它进行深度测试。如果我们不把这个深度可渲染的图像关联到FBO,那么由于缺少深度测试渲染输出结果是不正确的。如果在FBO渲染期间模板测试也是必要的,那么也需要把额外的渲染图像和GL_STENCIL_ATTACHMENT_EXT关联起来。

  1. // create a texture object  
  2. GLuint textureId;  
  3. glGenTextures(1, &textureId);  
  4. glBindTexture(GL_TEXTURE_2D, textureId);  
  5. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  
  6. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);  
  7. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  
  8. glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  
  9. glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap  
  10. glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0,  
  11.              GL_RGBA, GL_UNSIGNED_BYTE, 0);  
  12. glBindTexture(GL_TEXTURE_2D, 0);  
  13.   
  14. // create a renderbuffer object to store depth info  
  15. GLuint rboId;  
  16. glGenRenderbuffersEXT(1, &rboId);  
  17. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId);  
  18. glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,  
  19.                          TEXTURE_WIDTH, TEXTURE_HEIGHT);  
  20. glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);  
  21.   
  22. // create a framebuffer object  
  23. GLuint fboId;  
  24. glGenFramebuffersEXT(1, &fboId);  
  25. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);  
  26.   
  27. // attach the texture to FBO color attachment point  
  28. glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,  
  29.                           GL_TEXTURE_2D, textureId, 0);  
  30.   
  31. // attach the renderbuffer to depth attachment point  
  32. glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,  
  33.                              GL_RENDERBUFFER_EXT, rboId);  
  34.   
  35. // check FBO status  
  36. GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);  
  37. if(status != GL_FRAMEBUFFER_COMPLETE_EXT)  
  38.     fboUsed = false;  
  39.   
  40. // switch back to window-system-provided framebuffer  
  41. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);  
  42. ...  

渲染到纹理的过程和普通的绘制过程基本一样。我们只需要把渲染的目的地由window系统提供的帧缓存改成不可显示的应用程序创建的帧缓存(FBO)就可以了。

  1. ...  
  2. // set rendering destination to FBO  
  3. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);  
  4.   
  5. // clear buffers  
  6. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  
  7.   
  8. // draw a scene to a texture directly  
  9. draw();  
  10.   
  11. // unbind FBO  
  12. glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);  
  13.   
  14. // trigger mipmaps generation explicitly  
  15. // NOTE: If GL_GENERATE_MIPMAP is set to GL_TRUE, then glCopyTexSubImage2D()  
  16. // triggers mipmap generation automatically. However, the texture attached  
  17. // onto a FBO should generate mipmaps manually via glGenerateMipmapEXT().  
  18. glBindTexture(GL_TEXTURE_2D, textureId);  
  19. glGenerateMipmapEXT(GL_TEXTURE_2D);  
  20. glBindTexture(GL_TEXTURE_2D, 0);  
  21. ...  

注意到,glGenerateMipmapEXT()也是作为FBO扩展的一部分,用来在改变了纹理图像的基级之后显式生成mipmap的。如果GL_GENERATE_MIPMAP被设置为GL_TRUE,那么glTex{Sub}Image2D()和 glCopyTex{Sub}Image2D()将会启用自动mipmap生成(在OpenGL版本1.4或者更高版本中)。然后,当纹理基级被改变时,FBO操作不会自动产生mipmaps。因为FBO不会调用glCopyTex{Sub}Image2D()来修改纹理。因此,要产生mipmap,glGenerateMipmapEXT()必须被显示调用。

 

原英文网址:http://www.songho.ca/opengl/gl_fbo.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值