【OpenGL】OpenGL幀快取物件(FBO:Frame Buffer Object)

smilestone322發表於2017-08-15

轉自:http://blog.csdn.net/xiajun07061225/article/details/7283929/


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

英文原文:

In OpenGL rendering pipeline, 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-createdframebuffer 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 theapplication-created framebuffer object (FBO) other than the traditional window-system-providedframebuffer. 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, renderbuffer object 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.

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 byGL_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.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.

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, andglFramebufferRenderbufferEXT() to switch renderbuffer objects.

Creating Frame Buffer Object (FBO)

Creating framebuffer objects is similar to generating vertex buffer objects (VBO).

glGenFramebuffersEXT()

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

glGenFramebuffersEXT() 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 glDeleteFramebuffersEXT() 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.

glGenRenderbuffersEXT()

void glGenRenderbuffersEXT(GLsizei n, GLuint* ids)void glDeleteRenderbuffersEXT(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_EXTGL_RENDERBUFFER_HEIGHT_EXTGL_RENDERBUFFER_INTERNAL_FORMAT_EXTGL_RENDERBUFFER_RED_SIZE_EXTGL_RENDERBUFFER_GREEN_SIZE_EXTGL_RENDERBUFFER_BLUE_SIZE_EXTGL_RENDERBUFFER_ALPHA_SIZE_EXTGL_RENDERBUFFER_DEPTH_SIZE_EXTGL_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 usingglCheckFramebufferStatusEXT(). If the FBO is not complete, then any drawing and reading command (glBegin(), glCopyTexImage2D(), etc) will be failed.

GLenum glCheckFramebufferStatusEXT(GLenum target)

glCheckFramebufferStatusEXT() 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 glCheckFramebufferStatusEXT() returns GL_FRAMEBUFFER_UNSUPPORTED_EXT.

Example: Render To Texture


Download the source and binary: fbo.zip 
Extras: 
- Rendering to the depth buffer only: fboDepth.zip 
- Rendering the outlines of an object using stencil buffer: fboStencil.zip

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 objectGLuint 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 mipmapglTexImage2D(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 infoGLuint rboId;glGenRenderbuffersEXT(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 objectGLuint fboId;glGenFramebuffersEXT(1, &fboId);glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);// attach the texture to FBO color attachment pointglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,                          GL_TEXTURE_2D, textureId, 0);// attach the renderbuffer to depth attachment pointglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT,                             GL_RENDERBUFFER_EXT, rboId);// check FBO statusGLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);if(status != GL_FRAMEBUFFER_COMPLETE_EXT)    fboUsed = false;// switch back to window-system-provided framebufferglBindFramebufferEXT(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 FBOglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);// clear buffersglClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// draw a scene to a texture directlydraw();// unbind FBOglBindFramebufferEXT(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 Pixel Buffer Object (PBO) to modify the texture efficiently.

5

相關文章