- 什么是帧缓冲?
帧缓冲是颜色缓冲、深度缓冲、模板缓冲的结合,存储于内存中;其中,颜色缓冲用于写入颜色值,深度缓冲用于写入深度信息,模板缓冲用于基于一些条件丢弃指定片段。 - 帧缓冲的创建与使用
首先需要创建一个帧缓冲对象,并将其绑定到当前帧缓冲中;
在绑定后,所有的读/写帧缓冲的操作都会影响当前绑定的帧缓冲;
如GL_READ_FRAMEBUFFER: 可执行读取操作;
GL_DRAW_FRAMEBUFFER: 可执行写入操作;
gl_ReadPixels在绑定到GL_DRAW_FRAMEBUFFER上时可以进行渲染、清空和其他写入操作;
为帧缓冲添加至少一个附件(attachment)(颜色、深度、模板缓冲)且其中至少一个附件是颜色附件;附件应当是已经做好的(已经存储在内存中);每个缓冲都应该有同样数目的样本;
使用glCheckFramebufferStatus检查是否成功;
由于我们的帧缓冲不是默认的帧缓冲,渲染命令对窗口的视频输出不会产生任何影响。出于这个原因,它被称为离屏渲染(off-screen rendering),就是渲染到一个另外的缓冲中。为了让所有的渲染操作对主窗口产生影响我们必须通过绑定为0来使默认帧缓冲被激活;
做完所有帧缓冲操作,不要忘记删除帧缓冲对象;
GLuint fbo;
glGenFrameBuffers(1,&fbo);
glBindFrameBuffer(GL_FRAMEBUFFER,fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glReadBuffer(GL_COLOR_ATTACHMENT0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER)==GL_FRAMEBUFFER_COMPLETE)
{
return true;
}
glBindFrameBuffer(GL_FRAMEBUFFER,0);
glDeleteFramebuffers(1,&fbo);
- 纹理
在执行完成检测前,我们需要把一个或更多的附件附加到帧缓冲上。一个附件就是一个内存地址,这个内存地址里面包含一个为帧缓冲准备的缓冲,它可以是个图像。当创建一个附件的时候我们有两种方式可以采用:纹理或渲染缓冲(renderbuffer)对象。
当把一个纹理附加到帧缓冲上的时候,所有渲染命令会写入到纹理上,就像它是一个普通的颜色/深度或者模板缓冲一样。使用纹理的好处是,所有渲染操作的结果都会被储存为一个纹理图像,这样我们就可以简单的在着色器中使用了。
创建一个帧缓冲的纹理和创建普通纹理差不多:我们只分配内存,而不去填充它。纹理填充会在渲染到帧缓冲的时候去做。
如果打算把整个屏幕渲染到一个或大或小的纹理上,你需要用新的纹理的尺寸作为参数再次调用glViewport(要在渲染到你的帧缓冲之前做好),否则只有一小部分纹理或屏幕能够绘制到纹理上。现在我们已经创建了一个纹理,最后一件要做的事情是把它附加到帧缓冲上:
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 800, 600, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, texture, 0);
- glReadPixels函数
GPU渲染完数据在显存,回传内存的唯一方式。
共有七个参数:
前四个可以得到一个矩形区域,该区域所包含的所有像素都会被读取出来;
x,y :指定从帧缓冲区读取的第一个像素的窗口坐标。 此位置是矩形像素块的左下角;
width, height: 指定像素矩形的尺寸。 一个宽度和高度对应于单个像素;
format: 指定像素数据的格式。 接受以下符号值:GL_ALPHA,GL_RGB和GL_RGBA; 例如:GL_RGB就会依次读取像素的红、绿、蓝三种数据,GL_RGBA则会依次读取像素的红、绿、蓝、alpha四种数据,GL_RED则只读取像素的红色数据(类似的还有GL_GREEN,GL_BLUE,以及GL_ALPHA)。如果采用的不是RGBA颜色模式,而是采用颜色索引模式,则也可以使用GL_COLOR_INDEX来读取像素的颜色索引。除了颜色之外,还可以读取其它内容,例如深度缓冲区的深度数据等;
type: 指定像素数据的数据类型。 必须是GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4或GL_UNSIGNED_SHORT_5_5_5_1之一;
data: 返回像素数据,像素数据被读取后,将被保存到这个指针所表示的地址。
注意:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓冲区的。因此,如果需要读取已经绘制好的像素,往往需要先交换前后缓冲。
void WINAPI glReadPixels(
GLint x,
GLint y,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
GLvoid *pixels
);
- glBlitFramebuffer函数
帧缓冲区位块传送(Blit)也是 OpenGL ES 3.0 的新特性,主要用于帧缓冲区之间的像素拷贝,性能高且使用方便,可以指定缓冲区任意矩形区域的像素拷贝。
进行帧缓冲区间位块传之前,需要指定好源帧缓冲区 GL_READ_FRAMEBUFFER 和目标帧缓冲区 GL_DRAW_FRAMEBUFFER;
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo1);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,fbo2);
glReadBUffer(GL_COLOR_ATTACHMENT0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glBlitFrameBuffer(0,0,RenderImage.width, RenderImage.height,0, 0, m_SurfaceWidth, m_SurfaceHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);