OpenGL基础33:帧缓冲(上)之离屏渲染

 

在之前的章节,所有的物体都是中规中矩的显示的,只考虑了光照对物体的影响,那假设想要显示特殊的效果该怎么操作呢?例如马赛克风、将所有的物体都显示为黑白色,就像上世纪80年代的灰白电视一样,又或者说将整个场景渲染到一张泛黄的纸上以体现出年代感……

当然是修改着色器,事实上,很多地方都是这么做的,不过有些情况下,场景中的物体和对应的着色器都不少,若是想要整个场景(视口)体现出某个效果,就需要借助别的方法了

一、帧缓冲

为了解决这个问题,来学习帧缓冲吧,当有了目的之后才能更好的去理解和掌握。这一章相对之前要复杂一些,但是用一句简单的话来表达这个过程就是:想办法把当前的场景作为一张纹理存起来,然后再去全屏渲染这张“纹理”,在这种情况下,我们只需要给这张“纹理”编写一个独一无二的着色器就可以了

到目前为止,已经了解并使用了几种不同类型的屏幕缓冲:用于写入颜色值的颜色缓冲,用于写入深度信息的深度缓冲,以及允许我们基于一些条件丢弃指定片段的模板缓冲,这几种缓冲结合起来叫做帧缓冲(Framebuffer),它被储存于内存中

我们目前所做的渲染操作都是是在默认的帧缓冲之上进行的,当你创建了你的窗口的时候默认帧缓冲就被创建和配置好了(GLFW为我们做了这件事)

是的,GLFW已经为我们把这份代码写了,只不过我们是要自己再“客制化”一个想要的结果,OpenGL给了我们自己定义帧缓冲的自由,我们可以选择性的定义自己的颜色缓冲、深度和模板缓冲

 

和VBO、EBO一样,也可以用同样的方法创建和绑定一个FBO(帧缓冲)变量:

GLuint FBO;
glGenFramebuffers(1, &FBO);
glBindFramebuffer(GL_FRAMEBUFFER, FBO);

绑定的目标有以下3种,一般都是第一种:

  • GL_FRAMEBUFFER:绑定到目标后,接下来所有的读、写帧缓冲的操作都会影响到当前绑定的帧缓冲
  • GL_READ_FRAMEBUFFER:允许读取操作
  • GL_DRAW_FRAMEBUFFER:允许进行渲染、清空和其他的写入操作

构建一个完整的帧缓冲必须满足以下条件:如果不满足条件,则属于无效构建

  • 必须往里面加入至少一个附件(颜色、深度、模板缓冲)
  • 必须要有至少一个为颜色附件
  • 所有的附件都应该已经存储在内存中的
  • 每个缓冲都应该有同样数目的样本

样本是什么暂时不管,后面会有讲到,总而言之,因为帧缓冲的构建需要条件,所以我们在后面需要用 glCheckFramebufferStatus 函数来检查是否真的满足所有的条件,如果返回的值是 GL_FRAMEBUFFER_COMPLETE 就说明创建成功了

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;

 

二、离屏渲染

再回到帧缓冲区,这确实是一个很重要的概念,它在 OpenGL基础1:最简单的OpenGL例子 这一章中就被第一次提到了,里面介绍了 glfwSwapBuffers(window) 这一个函数

现在的显示器都是液晶显示器,而在以前主流的显示器都是显像管显示器(CRT)。不知道你还记不记得小学时的电脑课,那种大屁股显示器就是了,对于CRT显示器,显示的原理是这样的:要显示的图像经过CRT电子枪以极快的速度一行一行的扫描,扫描出来就呈现了一帧画面,随后电子枪又会回到初始位置循环扫描,形成了我们看到的图片或视频

而不管是CRT,还是液晶,它们都是从缓冲区中读取数据,其中缓冲区的数据就是通过 CPU 和 GPU 的计算给予的,如果在一帧时间内,CPU 或者 GPU 没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变,这也是界面卡顿的原因

如果理解了双缓冲区,那么像理解离屏渲染就容易一些,对于双缓冲区,需要另外的缓冲区是为了一个用于显示的同时,另一个用于后台绘制,并且相互切换,这样我们就感觉不到绘制的这一过程。而对于离屏渲染,需要另外的缓冲区是为了二次处理,拿这次缓冲区的数据来为真正要显示的图像服务,当然啦,这个缓冲区大部分情况下由我们自己创建

在OpenGL中,GPU有2种渲染方式:

  • On-Screen Rendering:当前屏幕渲染,在当前用于显示的屏幕缓冲区进行渲染操作
  • Off-Screen Rendering:离屏渲染,在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作

其实,离屏渲染是比较耗得,不止需要创建新的缓冲区,而且还需要多次切换上下文环境,先是从当前屏幕(On-Screen)切换到离屏(Off-Screen);等到离屏渲染结束以后,为了将离屏缓冲区的渲染结果显示到屏幕上还需要将上下文环境从离屏切换到当前屏幕,除了接下来我们手动创建FBO,以下几种情况也会触发离屏渲染:

  • shouldRasterize(光栅化)
  • masks(遮罩)
  • shadows(阴影)
  • edge antialiasing(抗锯齿)
  • group opacity(不透明)

可能有几个没有了解过,这些有机会的话都在后面讲,毕竟这也才开始理解离屏渲染

回到上面的帧缓冲代码,通过glBindFramebuffer方法,就相当于切换了当时的缓冲区,后续所有渲染操作将渲染到当前绑定的帧缓冲的附加缓冲中(即离屏渲染),如果当前激活的帧缓冲不是默认的帧缓冲,渲染命令对窗口的视频输出就不会产生任何影响,因此后面若是要渲染到主窗口,就必须要通过 glBindFramebuffer(GL_FRAMEBUFFER, 0) 来使默认帧缓冲被激活

当我们做完所有帧缓冲操作后,不要忘记删除帧缓冲对象:

glDeleteFramebuffers(1, &fbo);

 

好了,现在回到最初的目的:

想办法把当前的场景作为一张纹理存起来,然后再去全屏渲染这张“纹理”,在这种情况下,我们只需要给这张“纹理”编写一个独一无二的着色器就可以了

在此,目的就变成了:定义一个帧缓冲并且启用,然后走之前正常的渲染逻辑,这个时候,本应该在屏幕中展示的图象就被渲染到了自己定义的帧缓冲中,接下来,我们再想办法将帧缓冲中的数据转变为纹理,并且对这张纹理在默认帧缓冲中进行渲染显示

 

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值