OpenGL之FBO

一:概述

在OpenGL rendering pipeline中,原始数据和纹理经过处理过后最终会被渲染到屏幕上成为2D像素。
OpenGL pipeline的最终渲染目标就是framebuffer(帧缓存)
OpenGL 使用作为渲染目标的帧缓存是被window 系统生成管理的,这个默认的framebuffer被称为window-system-provided framebuffer.

作为OpenGL的扩展,GL_ARB_framebuffer_object提供了创建addtional nondisplayable framebuffer objects(FBO)。
这个framebuffer被称为application-created framebuffer,目的是用来和window-system-provided framebuffer区分开来。

通过FBO,一个OpenGL 应用可以将(redirect)渲染输出到application-created framebuffer object (FBO),而不是traditional window-system-provided framebuffer.
而且,它是完全被OpenGL控制的。

与window-system-provided framebuffer相似,一个FBO包含了一系列的渲染目标集合,color,depth,stencil buffer(accumulation buffer 没有定义在FBO)

这些FBO的逻辑缓存器被称为framebuffer-attachable images,这些都是可以被赋给一个framebuffer object 的2D像素数组

有两种framebuffer-attachable images,分别是texture images 以及 renderbuffer images

如果一个纹理图像被赋给一个framebuffer,OpenGL执行“render to texture”(纹理渲染);如果是一个渲染缓存对象(renderbuffer object)被赋值给一个framebuffer,那么OpenGL 执行“offscreen rendering”(离屏渲染)

本图展示了framebuffer 对象,texture对象,以及renderbuffer 对象的关系,多个纹理对象和渲染缓存对象都可以连接到一个帧缓存对象上通过attachment 指针。

一个帧缓存对象中有多个color attachment指针(GL_COLOR_ATTACHMENT0,…, GL_COLOR_ATTACHMENTn),一个depth attachment 指针,以及一个stencil attachment指针。

颜色指针实现时都是相互独立的,但是每个FBO都最少包含一个颜色指针。

一个FBO拥有多个颜色指针的原因是允许同时将颜色缓存渲染多个目标,这个“multiple render targets”(MRT)可以被GL_ARB_draw_buffers扩展实现。

#####*注意:framebuffer object 本身不拥有任何图像缓存(array),它拥有的只是多个附着指针。

FBO提供了一种有效的转换机制,将先前的framebuffer-attachable-image 从一个FBO上解绑,然后再附着一个新的framebuffer-attachable-image 。变换framebuffer-attachable-image比FBO之间的相互转换快的多。

FBO提供 glFramebufferTexture2D()去转换2D 纹理对象,并提供glFramebufferRenderbuffer() 去转换渲染缓存对象

二:生成FBO

生成FBO与生成VBO类似,先是glGenFramebuffers(),


glGenFramebuffers()
void glGenFramebuffers(GLsizei n, GLuint* ids)
void glDeleteFramebuffers(GLsizei n, const GLuint* ids)

glGenFramebuffers()中第一个参数是要生成的framebuffers数目,第二个参数是标志符。
返回的是还未使用的framebuffer 对象s的IDs,ID 0 是默认framebuffer ,即 window-system-provided framebuffer

当FBO不再使用时,利用 glDeleteFramebuffers()可以将其删除


glBindFramebuffer()

一旦一个FBO生成后,在使用之前必须先进行绑定
void glBindFramebuffer(GLenum target, GLuint id)
第一个参数,target必须是GL_FRAMEBUFFER,第二个参数是标志符
当不想使用当前FBO时可用默认 window-system-provided framebuffer 也就是在glBindFramebuffer()使用ID 0来解绑

三:Renderbuffer Object

renderbuffer object 是作为离屏渲染被引进的,它允许直接渲染一个场景到一个renderbuffer 对象上,用来替代渲染到一个纹理对象。

renderbuffer 简化了一个数据存储对象包含一个可渲染内部格式的单映像(containing a single image of a renderable internal format),

renderbuffer object 用来存储没有对应的纹理格式的OpenGL 逻辑缓存,如stencil 或depth buffer


glGenRenderbuffers()
void glGenRenderbuffers(GLsizei n, GLuint* ids)
void glDeleteRenderbuffers(GLsizei n, const Gluint* ids)

一旦一个renderbuffer 被创建,函数会返回一个非零正整数。ID 0被OpenGL 预先保留


glBindRenderbuffers()
void glBindRenderbuffer(GLenum target, GLuint id)

对于renderbuffer object 而言第一个参数应该是GL_RENDERBUFFER


glRenderbufferStorage()
void glRenderbufferStorage(GLenum  target,
                           GLenum  internalFormat,
                           GLsizei width,
                           GLsizei height)

当一个renderbuffer object 创建后,它并没有任何数据存储空间,所以我们必须先为它开创内存空间,这可以用glRenderbufferStorage()来实现。

第一个参数必须是GL_RENDERBUFFER,第二个参数应该是color-renderable (GL_RGB, GL_RGBA, etc.), depth-renderable (GL_DEPTH_COMPONENT), 或则 stencil-renderable formats (GL_STENCIL_INDEX)

其中width和height的粒度是像素,而且不能超过GL_MAX_RENDERBUFFER_SIZE,否则会报错


glGetRenderbufferParameteriv()
void glGetRenderbufferParameteriv(GLenum target,
                                  GLenum param,
                                  GLint* value)

获取相应的参数,第一个是GL_RENDERBUFFER,第二个是参数名,第三个是存储返回数据的指针

四:Attaching images to FBO

FBO本身没有任何的映象存储(buffer),我们必须连接framebuffer-attachable images (texture or renderbuffer objects)到该FBO。

它可快速变换framebuffer-attachable images ,比在FBOs中相互变换快的多,而且还节省了不必要的数据拷贝和内存消耗。

如:一个纹理可以被连接到多个FBO对象之上,而且它的映像存储可以被多个FBO分享。


Attaching a 2D texture image to FBO

glFramebufferTexture2D(GLenum target,
                       GLenum attachmentPoint,
                       GLenum textureTarget,
                       GLuint textureId,
                       GLint  level)

glFramebufferTexture2D()将一个2D纹理映像连接到一个FBO对象上。

第一个参数必须是GL_FRAMEBUFFER,第二个参数是一个连接到纹理映像的attachment 指针。一个FBO有多个 color attachment points (GL_COLOR_ATTACHMENT0, …, GL_COLOR_ATTACHMENTn), GL_DEPTH_ATTACHMENT, 和 GL_STENCIL_ATTACHMENT.
第三个参数大多数情况下是GL_TEXTURE_2D,第四个参数是纹理对象的ID,最后一个参数是等待连接的纹理的mipmap等级。

如果textureId参数设为0,纹理映像会从该FBO上解除绑定。如果一个纹理对象在还与一个FBO对象连接期间被删除,那么该纹理映像会自动与当前绑定的FBO解除绑定。

但是如果它连接了多个FBO还被删除了,那么只会与当前使用的FBO解除联系,不会从其他未与当前上下文绑定的FBO解除联系


Attaching a Renderbuffer image to FBO

void glFramebufferRenderbuffer(GLenum target,
                               GLenum attachmentPoint,
                               GLenum renderbufferTarget,
                               GLuint renderbufferId)

第三个参数是GL_RENDERBUFFER,其余与上一个近乎相同

五:FBO WITH MSAA(Multi Sample Anti Aliasing) FBO多样本抗混叠

当你渲染一个FBO时,抗混叠性能不会自动生效,即使你已经在创建OpenGL rendering context时配置了相关属性(with the multisampling attribute (SAMPLEBUFFERS_ARB) for window-system-provided framebuffer.)

为了激活多样本抗混叠模式,需要准备和联系多样本映像到一个FBO的color 或/和 depth attachement points。

glRenderbufferStorageMultisample() 可用来生成一个Multi Sample Anti Aliasing 模式的renderbuffer image

void glRenderbufferStorageMultisample(GLenum  target,
                                      GLsizei samples,
                                      GLenum  internalFormat,
                                      GLsizei width,
                                      GLsizei height)

与glRenderbufferStorage()相比,新增了一个参数samples,这是多样本的数目。

如果是0的话,就不再是MSAA模式,会转而调用glRenderbufferStorage() 。

*注意:如果多个images被联系到一个FBO上,那么所有的images必须有相同数目的multisamples。

// create a 4x MSAA renderbuffer object for colorbuffer
int msaa = 4;
GLuint rboColorId;
glGenRenderbuffers(1, &rboColorId);
glBindRenderbuffer(GL_RENDERBUFFER, rboColorId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, GL_RGB8, width, height);

// create a 4x MSAA renderbuffer object for depthbuffer
GLuint rboDepthId;
glGenRenderbuffers(1, &rboDepthId);
glBindRenderbuffer(GL_RENDERBUFFER, rboDepthId);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa, GL_DEPTH_COMPONENT, width, height);

// create a 4x MSAA framebuffer object
GLuint fboMsaaId;
glGenFramebuffers(1, &fboMsaaId);
glBindFramebuffer(GL_FRAMEBUFFER, fboMsaaId);

// attach colorbuffer image to FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER,       // 1. fbo target: GL_FRAMEBUFFER
                          GL_COLOR_ATTACHMENT0, // 2. color attachment point
                          GL_RENDERBUFFER,      // 3. rbo target: GL_RENDERBUFFER
                          rboColorId);          // 4. rbo ID

// attach depthbuffer image to FBO
glFramebufferRenderbuffer(GL_FRAMEBUFFER,       // 1. fbo target: GL_FRAMEBUFFER
                          GL_DEPTH_ATTACHMENT,  // 2. depth attachment point
                          GL_RENDERBUFFER,      // 3. rbo target: GL_RENDERBUFFER
                          rboDepthId);          // 4. rbo ID

// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)
    fboUsed = false;

当然,glRenderbufferStorageMultisample()只是让MSAA 渲染到FBO,但是你并不能直接从MSAA FBO上使用结果。
如果需要转换结果到一个纹理或者其他non-multisampled framebuffer,必须还要用glBlitFramebuffer()转换成single-sample image .

void glBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, // source rectangle
                       GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, // destination rect
                       GLbitfield mask,
                       GLenum filter)

相应的例子如下,

// copy rendered image from MSAA (multi-sample) to normal (single-sample)
// NOTE: The multi samples at a pixel in read buffer will be converted
// to a single sample at the target pixel in draw buffer.
glBindFramebuffer(GL_READ_FRAMEBUFFER, fboMsaaId); // src FBO (multi-sample)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fboId);     // dst FBO (single-sample)

glBlitFramebuffer(0, 0, width, height,             // src rect
                  0, 0, width, height,             // dst rect
                  GL_COLOR_BUFFER_BIT,             // buffer mask
                  GL_LINEAR);                      // scale filter

六:Checking FBO Status

在使用FBO执行操作之前,需要先验证FBO的状态是否完成,这可以通过glCheckFramebufferStatus()实现。

// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)
    fboUsed = false;

下面是一个使用了纹理和renderbuffer的例子,

// create a texture object
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
glTexImage2D(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 info
GLuint rboId;
glGenRenderbuffers(1, &rboId);
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT,
                      TEXTURE_WIDTH, TEXTURE_HEIGHT);
glBindRenderbuffer(GL_RENDERBUFFER, 0);

// create a framebuffer object
GLuint fboId;
glGenFramebuffers(1, &fboId);
glBindFramebuffer(GL_FRAMEBUFFER, fboId);

// attach the texture to FBO color attachment point
glFramebufferTexture2D(GL_FRAMEBUFFER,        // 1. fbo target: GL_FRAMEBUFFER 
                       GL_COLOR_ATTACHMENT0,  // 2. attachment point
                       GL_TEXTURE_2D,         // 3. tex target: GL_TEXTURE_2D
                       textureId,             // 4. tex ID
                       0);                    // 5. mipmap level: 0(base)

// attach the renderbuffer to depth attachment point
glFramebufferRenderbuffer(GL_FRAMEBUFFER,      // 1. fbo target: GL_FRAMEBUFFER
                          GL_DEPTH_ATTACHMENT, // 2. attachment point
                          GL_RENDERBUFFER,     // 3. rbo target: GL_RENDERBUFFER
                          rboId);              // 4. rbo ID

// check FBO status
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if(status != GL_FRAMEBUFFER_COMPLETE)
    fboUsed = false;

// switch back to window-system-provided framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
...

你可能感兴趣的:(OpenGL,ES)