在OpenGL中, OpenGL Context有一个默认的FBO用来绘制图像, 但是我们也可以创建新的用户定义的Framebuffers, 这样我们可以绘制到自定义的framebuffer, 而不会影响到窗口系统。
和其它 OpenGL object一样,FBO有以下的相关函数:
void glGenFramebuffers(GLsizei n, GLuint* framebuffers);
void glDeleteFramebuffers(GLsizei n, GLuint* framebuffers);
void glBindFramebuffer(GLenum target, GLuint framebuffer);
其中的target
参数可以是:
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 points:
i
: attachment points的数量跟实现相关, 最少需要8个, 所以i
至少可以取0-7范围以内的值, 附着的image必须具有可绘制的格式(color-renderable formats). 所有压缩格式都是不可绘制的,因此不可以附着到FBO.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 表示。
当我们需要将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.
在创建好renderbuffer之后,你就可以将它attach到FBO:
void glFramebufferRenderbuffer(GLenum target,
GLenum attachment,
GLenum renderbuffertarget,
GLuint renderbuffer);
renderbuffertarget
必须是 GL_RENDERBUFFERrenderbuffer
: renderbuffer object name上面讲了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);
每一个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(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
…