OpenGL Framebuffer Object (FBO)

在OpenGL中, OpenGL Context有一個默認的FBO用來繪製圖像, 但是我們也可以創建新的用戶定義的Framebuffers, 這樣我們可以繪製到自定義的framebuffer, 而不會影響到窗口系統。


關鍵詞定義

  • Image: 本文中, Image是包含像素的二維數組,這些像素具有特定的存儲格式。
  • Layered Image: 具有某一特定尺寸和格式的一套images, 對應於texture的某一個mipmap level. (注意 layered image不是一個人在戰鬥,而是一整套的images!)
  • Texture: 表示一系列的images的集合。所有的image具有相同的格式, 但是尺寸不一樣(不同的mipmap level). Texture 可以綁定(bind)到 shaders.
  • Renderbuffer: 包含一個image, 不可以綁定(bind)到shaders, 只能附着(attach)到FBO.
  • Framebuffer-attachable image: 可附着到FBO的image
  • Framebuffer-attachable layered image: 可附着到FBO的layered image.
  • Attachment point: FBO中一個署名的附着點,可用來附着(attach) framebuffer-attachable image 和 framebuffer-attachable layered image. 對於附着(attach)的image,要求具有特定的格式。
  • Attach: 附着是表示將兩個物體連繫起來, 和綁定(bind) 是不一樣的。 一個物體被綁定(bind)到context, 但是物體與物體則是相互附着(attach).

綁定FBO

和其它 OpenGL object一樣,FBO有以下的相關函數:

void glGenFramebuffers(GLsizei n, GLuint* framebuffers);
void glDeleteFramebuffers(GLsizei n, GLuint* framebuffers);
void glBindFramebuffer(GLenum target, GLuint framebuffer);
  • 其中的target參數可以是:

    • GL_FRAMEBUFFER
    • GL_READ_FRAMEBUFFER
    • GL_DRAW_FRAMEBUFFER

    GL_READ_FRAMEBUFFER:當target設置爲GL_READ_FRAMEBUFFER時, framebuffer參數所指代的FBO變成讀操作的目標,如glReadPixels, glCopyTexImage2D, glCopyTexSubImage2D.
    GL_DRAW_FRAMEBUFFER: 當target 設置爲 GL_DRAW_FRAMEBUFFER是, framebuffer指代的FBO變成繪製操作的目標, 如glDrawArrays, glDrawElements等等。
    GL_FRAMEBUFFER: 此時讀操作和繪製操作都在此FBO上進行。

  • 參數 framebuffer
    framebuffer的值是一個非負整數,0預留給由窗口系統提供的默認FBO. 當將參數設置爲0時,目標還原到初始狀態,即窗口系統提供的默認FBO.

一個FBO綁定到OpenGL context會一直有效,直到另一個FBO被綁定或者glDeleteFramebuffers被調用。


FBO attachment point

FBO有下列attachment points:

  • GL_COLOR_ATTACHMENTi: attachment points的數量跟實現相關, 最少需要8個, 所以i 至少可以取0-7範圍以內的值, 附着的image必須具有可繪製的格式(color-renderable formats). 所有壓縮格式都是不可繪製的,因此不可以附着到FBO.
  • GL_DEPTH_ATTACHMENT:只能附着具有depth格式的image, 附着的image被稱爲FBO的depth buffer.
  • GL_STENCIL_ATTACHMENT:只能附着具有stencil格式的image, 附着的image被稱爲FBO的stencil buffer.
  • GL_DEPTH_STENCIL_ATTACHMENT:附着的image既是FBO的depth buffer 又是 stencil buffer. 被附着的image格式應該是a packed depth-stencil internal format.

texture 類型簡要回顧

  • 1D texture
    1D texture可以看成包含的image的高度爲1

  • 2D texture
    2D texture是最好理解的了,包含2D image

  • 3D texture
    3D texture的每一個mipmap level就包含一套2D image. 如果用xy來表示image 平面,那麼z 軸就是表示image的數量的維度,每一個整數就表示一個layered image. 因此,3D texture 中的每一張image都可以通過一個mipmap level 和一個 layer定位。

  • cubemap
    包含一個立方體的六個平面,因此cubemap包含6張image, 因此每一張image可以通過mipmap level和平面定位。

  • 1D array texture
    每一個mipmap level包含一套2D image(高度爲1),數量等於數組的維度, 每一張image由一個mipmap level 和數組索性(array index) 定位。

  • 2D array texture
    和3D texture類似, 只要將z的值換成數組索引就可以。 每一張image 可以通過一個 mipmap level 和一個數組索引定位。 不同的是,數組大小不會隨着mipmap層次的下降而改變。

  • Buffer texture
    類似於1D texture, 只是它只包含一張image, 可以通過mipmap level 0 表示。


Attaching texture image

當我們需要將texture內部的某一張image 附着到FBO時,我們可以使用以下方法:

void glFramebufferTexture1D(GLenum target,
                            GLenum attachment,
                            GLenum textarget, 
                            GLuint texture,
                            GLint level);

void glFramebufferTexture2D(GLenum target, 
                            GLenum attachment,
                            GLenum textarget, 
                            GLuint texture, 
                            GLint level);

 void glFramebufferTextureLayer(GLenum target, 
                            GLeunm attachment,
                            GLuint texture,
                            GLint level, 
                            GLint layer);                                                 
  • target 參數和glBindFramebuffer一樣,但是這裏GL_FRAMEBUFFERGL_DRAW_FRAMEBUFFER等同。
  • attachment 參數則是上面提到的attachment point.
  • textarget 當attach 非cubemap時, textarget 可以是 GL_TEXTURE_1D, GL_TEXTURE_2D_MULTISAMPLE 等等, 當attach的是cubemap, 必須使用glFramebufferTexture2D函數, textarget 必須是GL_TEXTURE_CUBE_MAP_POSITIVE_X/Y/Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_X/Y/Z.
  • texture 參數是需要附着的texture 名稱, 當texture 爲 0時, 表示解除該attachment point的附着物。

注意,由於texture具有多個image, 因此必須明確指定需要attach的image.


Attaching renderbuffers

在創建好renderbuffer之後,你就可以將它attach到FBO:

void glFramebufferRenderbuffer(GLenum target, 
                               GLenum attachment,
                               GLenum renderbuffertarget,
                               GLuint renderbuffer);
  • renderbuffertarget 必須是 GL_RENDERBUFFER
  • renderbuffer: renderbuffer object name

Attaching layered image

上面講了layered image對應於texture的某個mipmap level上的所有images, 當然像1D texture, 2D texture, 它們一個mipmap level 上只有一張image, 但是像 3D texture, 2D array texture, cubemap等等,一個mipmap level上面可是有很多的images的。 我們可以將整個mipmap level附着到某一個attachment point:

void glFramebufferTexture(GLenum target, 
                          GLenum attachment,
                          GLuint texture,
                          GLint level);

Framebuffer completeness

每一個attachment point都要求附着到它上面的image具有特定的格式, 但是當你將一個不符合要求的image 附着上來,並不會馬上產生錯誤信息,而是要等到你使用FBO的時候錯誤纔會顯現出來。 當然除了image格式不符合要求, image的尺寸也有可能不符合條件, 因此, 我們需要一些手段來幫助我們檢查FBO的完整度。

一個有效的FBO被稱爲”完整的Framebuffer” (“framebuffer complete”),檢查FBO完整度,可以調用:

GLenum glCheckFramebufferStatus(GLenum target);

調用它並不是必需的,但是使用一個非完整的FBO會產生錯誤,因此在使用前檢查FBO的完整度是一個好的習慣。當FBO完整時返回值是 GL_FRAMEBUFFER_COMPLETE, 否則就有問題。


Framebuffer blits

Framebuffer blits 可以高效的將一個framebuffer(GL_READ_FRAMEBUFFER)的某塊矩形區域拷貝到另外一個framebuffer(GL_DRAW_FRAMEBUFFER).

void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, 
                       GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, 
                       GLbitfield mask, GLenum filter);
  • srcX0, srcY0, srcX1, srcY1: 指定GL_READ_FRAMEBUFFER的源矩形區域。
  • dstX0, dstY0, dstX1, dstY1: 指定GL_DRAW_FRAMEBUFFER的目標矩形區域。
  • mask: 指定拷貝操作的按位‘或’掩碼, GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT, GL_DEPTH_STENCIL_ATTACHMENT.
  • filter: 當拷貝需要縮放時, 指定插值算法,GL_NEARESTGL_LINEAR.

我們注意到mask掩碼有一個GL_COLOR_BUFFER_BIT,但是我們FBO的color buffer attachment point卻是多個, 到底拷貝哪個buffer,抑或是拷貝所有的color buffers? 答案是隻拷貝一個buffer, 默認值在single-buffer configuration時 是 GL_FRONT, 在double-buffer configuration時 是 GL_BACK.

那麼假如我們的GL_READ_FRAMEBUFFER是一個自定義FBO,顯然我們必須要指定目標color buffer,這時我們使用函數:

void glReadBuffer(GLenum mode);
  • mode: GL_FRONT_LEFT, GL_FRONT_RIGHT, GL_BACK_LEFT, GL_BACK_RIGHT, GL_FRONT, GL_BACK, GL_LEFT, GL_RIGHT, GL_COLOR_ATTACHMENTi.

glReadBuffer隱式的指定GL_READ_FRAMEBUFFER的color buffer, OpenGL還提供另外一個函數來顯示的指定FBO:

void glNamedFramebufferReadBuffer( GLuint framebuffer, GLenum mode);

除了影響glBlitFramebufferglReadBuffer還影響所有的可從color buffer 讀數據的操作, 比如:glReadPixels, glCopyTexImage1D, glCopyTexImage2D, glCopyTexSubImage1D, glCopyTexSubImage2D, glCopyTextSubImage3D

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章