帧缓冲(Framebuffer),由颜色缓冲,深度缓冲,模板缓冲结合,被存储于内存中。
通过帧缓冲可以将你的场景渲染到一个不同的帧缓冲中,可以使我们能够在场景中创建镜子这样的效果,或者做出一些炫酷的特效。
创建帧缓冲
GLuint fbo;
glGenFramebuffers(1, &fbo);
绑定帧缓冲
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
可以分开绑定到读或写目标上,GL_READ_FRAMEBUFFER,GL_DRAW_FRAMEBUFFER
构建完整的缓冲
1,我们必须往里面加入至少一个附件(颜色、深度、模板缓冲)。
2,其中至少有一个是颜色附件。
3,所有的附件都应该是已经完全做好的(已经存储在内存之中)。
4,每个缓冲都应该有同样数目的样本。
在执行完成检测前,需要把一个或更多的附件附加到帧缓冲上。一个附件就是一个内存地址,这个内存地址里面包含一个为帧缓冲准备的缓冲,它可以是个图像。当创建一个附件的时候我们有两种方式可以采用:纹理或渲染缓冲(renderbuffer)对象。
纹理附件
当把一个纹理附加到帧缓冲上的时候,所有渲染命令会写入到纹理上,就像它是一个普通的颜色/深度或者模板缓冲一样。使用纹理的好处是,所有渲染操作的结果都会被储存为一个纹理图像,这样我们就可以简单的在着色器中使用了。
创建一个帧缓冲的纹理类似于创建普通的纹理,可以自定义宽高,不指定纹理数据(即只分配内存,不去填充),之后可以渲染到其中。
如果把整个屏幕渲染到一个或大或小的纹理上,需要用新的纹理的尺寸作为参数再次调用glViewport(要在渲染到你的帧缓冲之前做好),否则只有一小部分纹理或屏幕能够绘制到纹理上。
附加到帧缓冲上
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D, texture,0);
参数:
1,target:帧缓冲类型的目标(绘制、读取或两者都有)。
2,attachment:附件的类型。
3,textarget:附加的纹理类型。
4,texture:附加的实际纹理。
5,level:Mipmap level。
除颜色附件以外,我们还可以附加一个深度和一个模板纹理到帧缓冲对象上。
附加一个深度缓冲,用GL_DEPTH_ATTACHMENT作为附件类型。这时纹理格式和内部格式类型(internalformat)就成了GL_DEPTH_COMPONENT去反应深度缓冲的存储格式。
附加一个模板缓冲,用GL_STENCIL_ATTACHMENT作为第二个参数,把纹理格式指定为GL_STENCIL_INDEX。
也可以同时附加一个深度缓冲和一个模板缓冲为一个单独的纹理。这样纹理的每32位数值就包含了24位的深度信息和8位的模板信息。为了把一个深度和模板缓冲附加到一个单独纹理上,我们使用GL_DEPTH_STENCIL_ATTACHMENT类型配置纹理格式以包含深度值和模板值的结合物。下面是一个附加了深度和模板缓冲为单一纹理的例子:
glTexImage2D( GL_TEXTURE_2D,0, GL_DEPTH24_STENCIL8,800,600,0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL );
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, texture,0);
渲染缓冲对象附件
Renderbuffer objects,以OpenGL原生渲染格式储存它的数据,因此在离屏渲染到帧缓冲的时候,这些数据就相当于被优化过的了。
渲染缓冲对象将所有渲染数据直接储存到它们的缓冲里,而不会进行针对特定纹理格式的任何转换,这样它们就成了一种快速可写的存储介质了。然而,渲染缓冲对象通常是只写的,不能修改它们(就像获取纹理,不能写入纹理一样)。可以用glReadPixels函数去读取,函数返回一个当前绑定的帧缓冲的特定像素区域,而不是直接返回附件本身。
创建渲染缓冲对象
GLuint rbo;
glGenRenderbuffers(1, &rbo);
绑定渲染缓冲对象
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
调用glRenderbufferStorage函数可以创建一个深度和模板渲染缓冲对象:
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,800,600);
创建一个渲染缓冲对象与创建纹理对象相似,不同之处在于这个对象是专门被设计用于图像的,而不是通用目的的数据缓冲,比如纹理。这里我们选择GL_DEPTH24_STENCIL8作为内部格式,它同时代表24位的深度和8位的模板缓冲。
附加到帧缓冲上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
检查是否成功构建缓冲
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE) { //do something }
后续所有渲染操作将渲染到当前绑定的帧缓冲的附加缓冲中,由于我们的帧缓冲不是默认的帧缓冲,渲染命令对窗口的视频输出不会产生任何影响。出于这个原因,它被称为离屏渲染(off-screen rendering),就是渲染到一个另外的缓冲中。为了让所有的渲染操作对主窗口产生影响我们必须通过绑定为0来使默认帧缓冲被激活:
glBindFramebuffer(GL_FRAMEBUFFER,0);
渲染到纹理
帧缓冲做好了,我们要做的全部就是渲染到帧缓冲上,而不是绑定到帧缓冲对象的默认缓冲。余下所有命令会影响到当前绑定的帧缓冲上。所有深度和模板操作同样会从当前绑定的帧缓冲的深度和模板附件中读取,当然,得是在它们可用的情况下。如果遗漏了比如深度缓冲,所有深度测试就不会工作,因为当前绑定的帧缓冲里没有深度缓冲。
1,使用当前激活的帧缓冲,像往常那样渲染场景。
2,绑定到默认帧缓冲。
3,绘制一个四边形,让它平铺到整个屏幕上,用新的帧缓冲的颜色缓冲作为他的纹理。
删除帧缓冲
glDeleteFramebuffers(1, &fbo);
后期处理
Post-processing,渲染到纹理后,可以针对纹理像素处理,实现一些特效
反向
在片段着色器中用1减去采样的纹理颜色。
灰度
color.r = color.g = color.b = (color.r + color.g + color.b) / 3.0;
Kernel effects
从当前纹理值的周围采样多个纹理值
锐化
这个kernel表示一个像素周围八个像素乘以2,它自己乘以-15。这个例子基本上就是把周围像素乘上2,中间像素去乘以一个比较大的负数来进行平衡。
模糊
边检测
使得物体边缘提高亮度,其他部分进行暗化处理。
图片和一些文字来源于https://learnopengl-cn.github.io,本人收集作为笔记,如有侵权,望告知