OpenGL Framebuffer Object (FBO)

来源:互联网 发布:火狐javascript void 0 编辑:程序博客网 时间:2024/05/22 14:11

在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

0 0