在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_ATTACHMENT
i
: 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的高度爲12D texture
2D texture是最好理解的了,包含2D image3D 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_FRAMEBUFFER和GL_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_RENDERBUFFERrenderbuffer
: 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_NEAREST 或 GL_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);
除了影響glBlitFramebuffer
, glReadBuffer
還影響所有的可從color buffer 讀數據的操作, 比如:glReadPixels
, glCopyTexImage1D
, glCopyTexImage2D
, glCopyTexSubImage1D
, glCopyTexSubImage2D
, glCopyTextSubImage3D
…