Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1357112
  • 博文数量: 281
  • 博客积分: 8800
  • 博客等级: 中将
  • 技术积分: 3346
  • 用 户 组: 普通用户
  • 注册时间: 2006-05-17 22:31
文章分类

全部博文(281)

文章存档

2013年(1)

2012年(18)

2011年(16)

2010年(44)

2009年(86)

2008年(41)

2007年(10)

2006年(65)

我的朋友

分类: LINUX

2011-10-20 14:56:32

Related Topics:
Download: , ,

Overview

In , the geometry data and textures are transformed and passed several tests, and then finally rendered onto a screen as 2D pixels. The final rendering destination of the OpenGL pipeline is called framebuffer. Framebuffer is a collection of 2D arrays or storages utilized by OpenGL; colour buffers, depth buffer, stencil buffer and accumulation buffer. By default, OpenGL uses the framebuffer as a rendering destination that is created and managed entirely by the window system. This default framebuffer is called window-system-provided framebuffer.

The OpenGL extension, GL_EXT_framebuffer_object provides an interface to create additional non-displayable framebuffer objects (FBO). This framebuffer is called application-created framebuffer in order to distinguish from the default window-system-provided framebuffer. By using framebuffer object (FBO), an OpenGL application can redirect the rendering output to the application-created framebuffer object (FBO) other than the traditional window-system-provided framebuffer. And, it is fully controlled by OpenGL.

Similar to window-system-provided framebuffer, a FBO contains a collection of rendering destinations; color, depth and stencil buffer. (Note that accumulation buffer is not defined in FBO.) These logical buffers in a FBO are called framebuffer-attachable images, which are 2D arrays of pixels that can be attached to a framebuffer object.

There are two types of framebuffer-attachable images; texture images and renderbuffer images. If an image of a texture object is attached to a framebuffer, OpenGL performs "render to texture". And if an image of a renderbuffer object is attached to a framebuffer, then OpenGL performs "offscreen rendering".

By the way, is a new type of storage object defined in GL_EXT_framebuffer_object extension. It is used as a rendering destination for a single 2D image during rendering process.

OpenGL Frame Buffer Object (FBO)
Connectivity among FBO, texture and Renderbuffer

The following diagram shows the connectivity among the framebuffer object, texture object and renderbuffer object. Multiple texture objects or renderbuffer objects can be attached to a framebuffer object through the attachment points.

There are multiple color attachment points (GL_COLOR_ATTACHMENT0_EXT,..., GL_COLOR_ATTACHMENTn_EXT), one depth attachment point (GL_DEPTH_ATTACHMENT_EXT), and one stencil attachment point (GL_STENCIL_ATTACHMENT_EXT) in a framebuffer object. The number of color attachment points is implementation dependent, but each FBO must have at least one color attachement point. You can query the maximum number of color attachement points with GL_MAX_COLOR_ATTACHMENTS_EXT, which are supported by a graphics card. The reason that a FBO has multiple color attachement points is to allow to render the color buffer to multiple destinations at the same time. This "multiple render targets" (MRT) can be accomplished by GL_ARB_draw_buffers extension. Notice that the framebuffer object itself does not have any image storage(array) in it, but, it has only multiple attachment points.

Framebuffer object (FBO) provides an efficient switching mechanism; detach the previous framebuffer-attachable image from a FBO, and attach a new framebuffer-attachable image to the FBO. Switching framebuffer-attachable images is much faster than switching between FBOs. FBO provides glFramebufferTexture2DEXT() to switch 2D texture objects, and glFramebufferRenderbufferEXT() to switch renderbuffer objects.

Creating Frame Buffer Object (FBO)

Creating framebuffer objects is similar to generating .

glGenFramebuffer***T()
void glGenFramebuffer***T(GLsizei n, GLuint* ids) void glDeleteFramebuffer***T(GLsizei n, const GLuint* ids)

glGenFramebuffer***T() requires 2 parameters; the first one is the number of framebuffers to create, and the second parameter is the pointer to a GLuint variable or an array to store a single ID or multiple IDs. It returns the IDs of unused framebuffer objects. ID 0 means the default framebuffer, which is the window-system-provided framebuffer.
And, FBO may be deleted by calling glDeleteFramebuffer***T() when it is not used anymore.

glBindFramebufferEXT()

Once a FBO is created, it has to be bound before using it.

void glBindFramebufferEXT(GLenum target, GLuint id)

The first parameter, target, should be GL_FRAMEBUFFER_EXT, and the second parameter is the ID of a framebuffer object. Once a FBO is bound, all OpenGL operations affect onto the current bound framebuffer object. The object ID 0 is reserved for the default window-system provided framebuffer. Therefore, in order to unbind the current framebuffer (FBO), use ID 0 in glBindFramebufferEXT().

Renderbuffer Object

In addition, renderbuffer object is newly introduced for offscreen rendering. It allows to render a scene directly to a renderbuffer object, instead of rendering to a texture object. Renderbuffer is simply a data storage object containing a single image of a renderable internal format. It is used to store OpenGL logical buffers that do not have corresponding texture format, such as stencil or depth buffer.

glGenRenderbuffer***T()
void glGenRenderbuffer***T(GLsizei n, GLuint* ids) void glDeleteRenderbuffer***T(GLsizei n, const Gluint* ids)

Once a renderbuffer is created, it returns non-zero positive integer. ID 0 is reserved for OpenGL.

glBindRenderbufferEXT()
void glBindRenderbufferEXT(GLenum target, GLuint id)

Same as other OpenGL objects, you have to bind the current renderbuffer object before referencing it. The target parameter should be GL_RENDERBUFFER_EXT for renderbuffer object.

glRenderbufferStorageEXT()
void glRenderbufferStorageEXT(GLenum target, GLenum internalFormat, GLsizei width, GLsizei height)

When a renderbuffer object is created, it does not have any data storage, so we have to allocate a memory space for it. This can be done by using glRenderbufferStorageEXT(). The first parameter must be GL_RENDERBUFFER_EXT. The second parameter would be color-renderable (GL_RGB, GL_RGBA, etc.), depth-renderable (GL_DEPTH_COMPONENT), or stencil-renderable formats (GL_STENCIL_INDEX). The width and height are the dimension of the renderbuffer image in pixels.

The width and height should be less than GL_MAX_RENDERBUFFER_SIZE_EXT, otherwise, it generates GL_INVALID_VALUE error.

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

You also get various parameters of the currently bound renderbuffer object. target should be GL_RENDERBUFFER_EXT, and the second parameter is the name of parameter. The last is the pointer to an integer variable to store the returned value. The available names of the renderbuffer parameters are;

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
Attaching images to FBO

FBO itself does not have any image storage(buffer) in it. Instead, we must attach framebuffer-attachable images (texture or renderbuffer objects) to the FBO. This mechanism allows that FBO quickly switch (detach and attach) the framebuffer-attachable images in a FBO. It is much faster to switch framebuffer-attachable images than to switch between FBOs. And, it saves unnecessary data copies and memory consumption. For example, a texture can be attached to multiple FBOs, and its image storage can be shared by multiple FBOs.

Attaching a 2D texture image to FBO
glFramebufferTexture2DEXT(GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint textureId, GLint level)

glFramebufferTexture2DEXT() is to attach a 2D texture image to a FBO. The first parameter must be GL_FRAMEBUFFER_EXT, and the second parameter is the attachment point where to connect the texture image. A FBO has multiple color attachment points (GL_COLOR_ATTACHMENT0_EXT, ..., GL_COLOR_ATTACHMENTn_EXT), GL_DEPTH_ATTACHMENT_EXT, and GL_STENCIL_ATTACHMENT_EXT. The third parameter, "textureTarget" is GL_TEXTURE_2D in most cases. The fourth parameter is the identifier of the texture object. The last parameter is the mipmap level of the texture to be attached.

If the textureId parameter is set to 0, then, the texture image will be detached from the FBO. If a texture object is deleted while it is still attached to a FBO, then, the texture image will be automatically detached from the currently bound FBO. However, if it is attached to multiple FBOs and deleted, then it will be detached from only the bound FBO, but will not be detached from any other un-bound FBOs.

Attaching a Renderbuffer image to FBO
void glFramebufferRenderbufferEXT(GLenum target, GLenum attachmentPoint, GLenum renderbufferTarget, GLuint renderbufferId)

A renderbuffer image can be attached by calling glFramebufferRenderbufferEXT(). The first and second parameters are same as glFramebufferTexture2DEXT(). The third parameter must be GL_RENDERBUFFER_EXT, and the last parameter is the ID of the renderbuffer object.

If renderbufferId parameter is set to 0, the renderbuffer image will be detached from the attachment point in the FBO. If a renderbuffer object is deleted while it is still attached in a FBO, then it will be automatically detached from the bound FBO. However, it will not be detached from any other non-bound FBOs.

Checking FBO Status

Once attachable images (textures and renderbuffers) are attached to a FBO and before performing FBO operation, you must validate if the FBO status is complete or incomplete by using glCheckFramebufferStatu***T(). If the FBO is not complete, then any drawing and reading command (glBegin(), glCopyTexImage2D(), etc) will be failed.

GLenum glCheckFramebufferStatu***T(GLenum target)

glCheckFramebufferStatu***T() validates all its attached images and framebuffer parameters on the currently bound FBO. And, this function cannot be called within glBegin()/glEnd() pair. The target parameter should be GL_FRAMEBUFFER_EXT. It returns non-zero value after checking the FBO. If all requirements and rules are satisfied, then it returns GL_FRAMEBUFFER_COMPLETE_EXT. Otherwise, it returns a relevant error value, which tells what rule is violated.

The rules of FBO completeness are:

  • The width and height of framebuffer-attachable image must be not zero.
  • If an image is attached to a color attachment point, then the image must have a color-renderable internal format. (GL_RGBA, GL_DEPTH_COMPONENT, GL_LUMINANCE, etc)
  • If an image is attached to GL_DEPTH_ATTACHMENT_EXT, then the image must have a depth-renderable internal format. (GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT24_EXT, etc)
  • If an image is attached to GL_STENCIL_ATTACHMENT_EXT, then the image must have a stencil-renderable internal format. (GL_STENCIL_INDEX, GL_STENCIL_INDEX8_EXT, etc)
  • FBO must have at least one image attached.
  • All images attached a FBO must have the same width and height.
  • All images attached the color attachment points must have the same internal format.

Note that even though all of the above conditions are satisfied, your OpenGL driver may not support some combinations of internal formats and parameters. If a particular implementation is not supported by OpenGL driver, then glCheckFramebufferStatu***T() returns GL_FRAMEBUFFER_UNSUPPORTED_EXT.

Example: Render To Texture

OpenGL FBO Example: Render To Texture

Download the source and binary:
Extras:
- Rendering to the depth buffer only:
- Rendering the outlines of an object using stencil buffer:

Sometimes, you need to generate dynamic textures on the fly. The most common examples are generating mirroring/reflection effects, dynamic cube/environment maps and shadow maps. Dynamic texturing can be accomplished by rendering the scene to a texture. A traditional way of render-to-texture is to draw a scene to the framebuffer as normal, and then copy the framebuffer image to a texture by using glCopyTexSubImage2D().

Using FBO, we can render a scene directly onto a texture, so we don't have to use the window-system-provided framebuffer at all. Further more, we can eliminate an additional data copy (from framebuffer to texture).

This demo program performs render to texture operation with/without FBO, and compares the performance difference. Other than performance gain, there is another advantage of using FBO. If the texture resolution is larger than the size of the rendering window in traditional render-to-texture mode (without FBO), then the area out of the window region will be clipped. However, FBO does not suffer from this clipping problem. You can create a framebuffer-renderable image larger than the display window.

The following codes is to setup a FBO and framebuffer-attachable images before the rendering loop is started. Note that not only a texture image is attached to the FBO, but also, a renderbuffer image is attached to the depth attachment point of the FBO. We do not actually use this depth buffer, however, the FBO itself needs it for depth test. If we don't attach this depth renderable image to the FBO, then the rendering output will be corrupted because of missing depth test. If stencil test is also required during FBO rendering, then additional renderbuffer image should be attached to GL_STENCIL_ATTACHMENT_EXT.

... // create a texture object 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); // automatic mipmap glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); glBindTexture(GL_TEXTURE_2D, 0); // create a renderbuffer object to store depth info GLuint rboId; glGenRenderbuffer***T(1, &rboId); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId); glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT); glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); // create a framebuffer object GLuint fboId; glGenFramebuffer***T(1, &fboId); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); // attach the texture to FBO color attachment point glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0); // attach the renderbuffer to depth attachment point glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboId); // check FBO status GLenum status = glCheckFramebufferStatu***T(GL_FRAMEBUFFER_EXT); if(status != GL_FRAMEBUFFER_COMPLETE_EXT) fboUsed = false; // switch back to window-system-provided framebuffer glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); ...

The rendering procedure of render-to-texture is almost same as normal drawing. We only need to switch the rendering destination from the window-system-provided to the non-displayable, application-created framebuffer (FBO).

... // set rendering destination to FBO glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId); // clear buffers glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // draw a scene to a texture directly draw(); // unbind FBO glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); // trigger mipmaps generation explicitly // NOTE: If GL_GENERATE_MIPMAP is set to GL_TRUE, then glCopyTexSubImage2D() // triggers mipmap generation automatically. However, the texture attached // onto a FBO should generate mipmaps manually via glGenerateMipmapEXT(). glBindTexture(GL_TEXTURE_2D, textureId); glGenerateMipmapEXT(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); ...

Note that glGenerateMipmapEXT() is also included as part of FBO extension in order to generate mipmaps explicitly after modifying the base level texture image. If GL_GENERATE_MIPMAP is set to GL_TRUE, then glTex{Sub}Image2D() and glCopyTex{Sub}Image2D() trigger automatic mipmap generation (in OpenGL version 1.4 or greater). However, FBO operation does not generate its mipmaps automatically when the base level texture is modified because FBO does not call glCopyTex{Sub}Image2D() to modify the texture. Therefore, glGenerateMipmapEXT() must be explicitly called for mipmap generation.

If you need to a post processing of the texture, it is possible to combine with to modify the texture efficiently.

 

 


FBO这个名字应该记住,同时还得记住VBO,PBO——这些算得上OpenGL的高级技术了,但是可以说,用处很广。从拓展到即将的核心,证明了它们的价值。这里我主要讲讲FBO(因为最近只用到FBO嘛嘿),全名Frame Buffer Object,目前主要用于离屏渲染技术。——ZwqXin.com
不知道是否有人曾经对D3D的RenderTarget技术垂延三尺呢?我可没有哦(明明是因为不了解,汗~),不过OpenGL其实也有类似的技术,名为FBO(帧缓存对象)。还记得以前模仿例子做渲染到纹理,用glCopyTexImage2D,心里总是替opengl捏捏汗,空白纹理,对拷,每帧用一次,哇,FPS!(没那么夸张吧...)现在有了FBO知识,就不想再用glCopyTexImage2D来拷屏幕了,FBO有那么好吗?
谈起缓存,你或许想起OpenGL五大缓存(who? ),但是这5种是象素格式中本来包含的。FBO里面的东西其实跟它们本质一样,都是一块特定的内存,你可以把一些屏幕信息给它保管(更厉害的在于,象素格式针对屏幕象素,所以位于屏幕视野外部分的信息你不会得到,但是FBO里面的东西不受这种限制)。这里提到“FBO里面的东西”,为什么不直接说FBO呢?不一样的。FBO本身不是一块内存,没有空间,真正存储东西,可实际读写的是依附于FBO的东西:纹理(texture)和渲染缓存(renderbuffer)。橙书上说纹理是一种复杂组织的数据格式,但是你只要了解它本质也是一块内存区域好了(确切一点:缓存)。至于renderbuffer,有depth-renderbuffer和stencil-renderbuffer等等。FBO就好象一个管理这些资源的管理设备那样(情况如同纹理对象管理纹理,所以都叫Object),这个设备有好多个用来标识上述资源的标签(GL_COLOR_ATTACHMENTi_EXT,GL_DEPTH_ATTACHMENT_EXT等等)。至于这个设备怎么工作呢?
GameDev上有篇精彩的文章,OpenGL FrameBuffer Object,分为两部分,讲述了FBO中基础的两个用法,分次渲染和一次性渲染(MRT)。建议想了解FBO的同学看看这篇文章,认真看完了,你就入门一半了;看完后把作者给的例子代码下载下来调试,观摩,完后你就入门4分之3了,在自己的框架上再按着上面的敲一次代码,自己实现一次,改改参数改改代码位置实验一下,然后你就入门99%了。(当然你可以搜搜中文相关的。)本Blog不打算像其他博客那样给你来次用法详解(大哥,你还是看上面的文章吧),我说说其他的。
FBO出现之前,我们是怎么离屏渲染的呢?1.前面提到的glCopyTexImage2D;2.glDrawBuffers(size, *p)。譬如你想实现这样一个功能:汽车倒后镜。或许你首先把相机放在倒后镜那位置,往车后方向一摄,把相片作为纹理贴到这个倒后镜上。当然了,车后情况随时变化,所以你得每帧更新。以前的话,渲染过程一开始,把视觉(相机)放在倒后镜位置,渲染一次(不显示出来),然后把结果从屏幕帧缓存区读出,通过glCopyTexImage2D拷贝到一张空纹理上,接下来恢复驾驶者视觉(相机回放),把那纹理贴倒后镜,正常渲染场景——然后马上又开始下一帧。不得不说的是拷贝过程(glReadPixels)很浪费时间。FBO,你做的是同样的步骤,不过用一个glDrawBuffer()命令把倒后镜所看到的场景直接映射到一张由FBO绑定的纹理上,速度就上来了。
屏幕信息呢?譬如GLSL的fragment shader处理每个象素,每个象素都有一个特别的信息,但又不仅仅颜色。熟悉GLSL的话知道,要使用gl_data[i]来把这样的信息输出。譬如gl_data[0]输出颜色,gl_data[1]输出特别的信息,但是输出到哪里呢?传统是用一个辅助缓存(Auxiliary Buffer,OPENGL五大缓存种类之一,有好几个,AUX0,AUX1...),渲染时用把p[]={GL_BACK_LEFT,GL_AUX0}数组放入glDrawBuffers(2,p)就能一边渲染原图象(按gl_data[0]的输出)到屏幕后缓冲,一边输出特定信息(按gl_data[1]的输出)到辅助缓存0号了。貌似这样很好,因为连FBO都不支持同时屏幕输出和输出到FBO,但是你要怎样使用辅助缓存0号(GL_AUX0)里的内容?.....还不是得glCopyTexImage2D到一张专门存储这种信息的纹理!FBO则不一样,首先,与前面一样,用绑定到FBO的一个纹理存储这种数据,接下来就可以直接用此纹理了;其次,即使不支持同时屏幕输出和输出到FBO,也还是有办法近似的,这个我找了整晚英文资料可以总结出一两个办法,详见我后面的文章。
整个过程是这样的:在预处理中,新建一个FBO对象,用Bind绑定到当前(这些BIND之类函数一般是表示“你接下来要处理这个对象啦”的意思),给FBO输入渲染缓存或纹理,检查FBO状态是否正确,再脱离绑定。渲染过程,在需要它时再一次绑定,指定把接下来的内容渲染到它里面的哪一个渲染缓存或纹理......脱离绑定,使用之。
连我都觉得表意一塌糊涂,所以还是看那文章吧。注意,这里检查状态(glCheckFramebufferStatu***T)很有必要,不然你连FBO起不起作用都不知道。在渲染到FBO后开始真正的屏幕渲染时,记得先脱离绑定glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0),否则还是继续渲染到FBO的,你可以把这个0看作代表“屏幕”这个主缓冲的代号;因为你渲染到FBO期间改变的东西是会保留下来的(OPENGL是个状态机嘛),你得去除这些影响,包括使用glPushAttrib之类函数;FBO绑定后开始渲染时还要记得glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT|.....);因为上面所说的这些改变包括象素格式,我们需要隔绝屏幕渲染时候的像素信息影响FBO渲染,因此你不想受渲染到FBO前的象素信息影响的话,像每帧开始时那样用glClear吧。
写得够长了,接下来谈谈一些应用吧。

本文来源于ZwqXin , 转载请注明
原文地址:


阅读(5543) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~