帧缓冲

帧缓冲

什么是帧缓冲?

  • 简单的说,把颜色缓冲、深度缓冲和模板缓冲结合起来就是帧缓冲;帧缓冲被存储在内存中
  • OpenGL允许自定义帧缓冲,即自定义帧缓冲中的颜色缓冲、深度缓冲和模板缓冲

为什么需要帧缓冲?

  • 利用帧缓冲可以做出炫酷的后期处理效果
  • 这个“后期处理”是很值得细品的,稍后我们稍微品一下

帧缓冲的作用

  • 当创建并绑定了一个帧缓冲后,再进行的渲染操作都将会绘制到这个帧缓冲中,由于这不是默认的帧缓冲,所以不会对屏幕上的结果产生任何影响,这个过程也被称作离屏渲染
  • 我们拿离屏渲染的结果进行一系列“黑处理”,然后再到默认帧缓冲当中,让他显示到屏幕上,完成一次渲染操作。其中帧缓冲中的结果,即离屏渲染的结果,是一个半成品;一系列黑处理就是所谓的后期处理;

怎么使用帧缓冲?

一个完整帧缓冲要满足的条件

  • 附加至少一个缓冲(颜色缓冲、深度缓冲或者模板缓冲)
  • 至少有一个颜色附件(这说明要为帧缓冲创建附件,并将附件附加到帧缓冲上)
  • 所有的附件都需要分配内存空间(对于纹理附件使用glTexImage2D(…),对于渲染缓冲对象用glRenderbufferStorage(…))
  • 每个缓冲都应该有相同的样本数(某书上是这么写的,目前还不知道是啥意思)

附件

  • 附件:是一个内存位置,他能作为帧缓冲的一个缓冲,可以将他想象成一张图像
  • 附件有两个类型:纹理(Texture)、渲染缓冲对象(Renderbuffer Object)
纹理附件
  • 他就像一个普通的颜色缓冲或者深度缓冲或者模板缓冲
  • 他的作用:当把一个纹理附件附加到帧缓冲后,所有的渲染操作的结果都将会写到这个纹理中
  • 优点:因为所有渲染操作的结果存储在一个纹理图像中,之后就可以在着色器中方便的使用他
  • 与普通纹理的区别:纹理的分辨率根据实际情况设定,纹理的数据设为空
  • 需要注意的地方:如果想把屏幕上的内容渲染到一个更大或者更小的纹理上,在渲染到帧缓冲之前,需要再次调用glViewport(…),并使用新纹理的分辨率作为参数,否则只会有一部分的纹理有内容,或者只会有一部分的屏幕会被渲染到这个纹理上
渲染缓冲对象(rbo)
  • 与纹理附件一样,也是个内存位置
  • 优点:rbo直接将所有的渲染数据存储到他的缓冲中,不会做任何针对纹理格式的转换,从而让他变为一个更快的可写存储介质
  • 特色:rbo通常都是只写的,所以不能直接读取他们;但是任然可以使用glReadPixels(…)来读他,原因是:这么做是从当前绑定的帧缓冲中返回数据,而不是附件本身
  • 作用:由于rbo通常都是只写的,所以他经常被用于绑定深度缓冲或者模板缓冲,因为这两种缓冲大部分时间都只是进行深度测试或者模板测试,而不需要从中读取值,即采样
两种附件的选择
  • 通常的规则是:如果不需要从一个缓冲中采样数据,那么对这个缓冲使用渲染缓冲对象(rbo);如果需要从缓冲中采样颜色或者深度等数据,那么应该选择纹理

创建帧缓冲

控制函数
  • 创建帧缓冲的索引——glGenFramebuffer(…)
  • 绑定当前使用的帧缓冲——glBindFramebuffer(…)
  • 给纹理分配内存——glTexImage2D(…)
  • 将纹理附件绑定到帧缓冲——glFramebufferTexture2D(…)
  • 指定rbo的类型和分配内存——glRenderbufferStorage(…)
  • 将rbo附件绑定到帧缓冲——glFramebufferRenderbuffer(…)
  • 当使用不包含颜色附件的帧缓冲时,是不完整的帧缓冲,这时要明确的告诉OpenGL不使用任何颜色数据进行渲染,同时使用glDrawBuffer(GL_NONE)和glReadBuffer(GL_NONE)来说明
  • 在多目标渲染时使用glDrawBuffers(n, attachments)

一些例子

渲染到纹理

在这里插入图片描述

  • 在渲染前绑定帧缓冲,进行离屏渲染,然后绑定默认帧缓冲,将离屏渲染的结果当作一个普通的纹理,直接输出到屏幕上
  • 看到的效果与直接渲染到默认帧缓冲是一样的
添加简单的后期处理
反相

在这里插入图片描述

  • 用1.0减去离屏渲染得到的颜色值,就是反相的效果
灰度

在这里插入图片描述

  • 灰度效果就是移除场景中除了黑白灰以外所有的颜色
  • 简单的实现方式是:分别将rgb三个分量的颜色值相加再平均,然后rgb三个分量的值都用这一个平均值代替即可
	float color = (FragColor.r + FragColor.g + FragColor.b) / 3.0; 
	FragColor = vec4(color, color, color, 1.0);
  • 进阶的实现方式:由于人眼对绿色更敏感,而对蓝色不很敏感,为了有更好的效果给他们增加不同的权重:
	float color = 0.2126 * FragColor.r + 0.7152 * FragColor.g + 0.0722 * FragColor.b; 
	FragColor = vec4(color, color, color, 1.0);

在这里插入图片描述

核效果

在这里插入图片描述

  • 核效果的实现思路是:对当前纹理值周围的多个纹理值进行采样,然后再加权平均后得到的值作为最终的纹理值
  • ”核“是从英语kernel翻译过来的,他在这里指的是一个类似矩阵的数据,他的思想跟卷积的思想一致,也可以按卷积来理解
  • 怎么取当前纹理周围的纹理值?对当前纹理坐标添加一个偏移量即可
  • 大部分核将所有的权重加起来后都会等于1;如果不等于1,这就意味着最终的纹理颜色将会比原纹理值更亮或者更暗
  • 核是后期处理中很有用的工具,实现起来也很简单,上图是一个
    一个利用核实现锐化的效果
	 float kernel[9] = float[]( 
	     -1, -1, -1, 
	     -1, 9, -1, 
	     -1, -1, -1
	     );
模糊

在这里插入图片描述

  • 这个模糊效果也是用核实现的,比之前模板测试那一篇里用的高斯模糊更好理解
	float kernel[9] = float[](
	    1.0 / 16, 2.0 / 16, 1.0 / 16,
	    2.0 / 16, 4.0 / 16, 2.0 / 16,
	    1.0 / 16, 2.0 / 16, 1.0 / 16  
	);
边缘检测

在这里插入图片描述

  • 这也是一个核效果,他高亮了边缘,暗化了其他部分
	 float kernel[9] = float[]( 
	      1,  1, 1, 
	      1, -8, 1, 
	      1,  1, 1
	     );
  • 需要注意的地方:在实现核效果的时候会对纹理周围的值进行采样,这就很容易取到纹理之外的像素,因此需要将纹理的环绕方式设置为GL_CLAMP_TO_EDGE,避免取到纹理另一边的像素而出现错误的效果
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值