UnityShade入门精要(8·屏幕后期处理效果)
屏幕后期处理
要实现屏幕特效,首先要获取屏幕图像,可以使用GrabPass 命令或 OnRenderimage 函数来获取当前屏幕图像,OnRenderimage是在cs脚本使用的,
函数声明:MonoBehaviour.OnRenderimage (RenderTexture src, RenderTexture dest),src对应当前渲染的图像,dest存储操作后的目标图像
通常是利用 Grapbics.Bllt (位块传输)函数来完成对渲染纹理的处理,(如果dest的值为null就会直接将结果显示在屏幕上),参数mat是使用的材质,而src纹理将会被传递给Shader 中名为_MainTex 的纹理属性,会进行各种屏幕后处理操作,参数pass 的默认值为-1, 表示将会依次调用 Shader 内的所有Pass。否则,只会调用给定索引的Pass
在默认情况下, OnRenderlmage 函数会在所有的不透明和透明的Pass执行完毕后被调用, 以,但有时希望在执行某queue后立即调用,我们可以在OnRenderlmage函数前添加ImageEffectOpaque 属性来实现这样的目的便对场景中所有游戏对象都产生影响
但是在进行屏幕后处理之前,我们需要检查一系列条件是否满足,后期处理脚本都应挂载在摄像机上,在实现各种屏幕特效时,我们只需要继承自该基类
cs中检查了各种资源和条件是否满足,由于每个屏幕后处理效果通常都需要指定一个Shader来创建一个用于处理渲染纹理的材质,定义了check的新函数,首先检查shader是否支持,返回根据shader创建的material
调整屏幕的亮度、饱和度和对比度
新建cs继承基类,使用OnRenderlmage和blit,新建shader用于处理屏幕的图像,亮度系数直接*rgb,饱和度系数通过lerp控制rgb的趋近于0,对比度系数通过lerp控制rgb趋近于0.5均值
边缘检测
很多屏幕效果都是利用卷积实现的,不同的卷积核可以实现不同的效果
边缘检测的卷积核应该是什么样的,首先看如何定义边的,如果相邻像素之间存在差别明显的颜色、亮度、纹理等屈性,我们就会认为它们之间应该有一条边界,
也就是想要对边界进行处理,就首先要找到边界,可以通过卷积核求得像素的梯度值(两侧颜色变换量),梯度值越高,越有可能是边缘点
根据卷积后获得像素的梯度值lerp,即越趋近原本颜色,还是边界指定的颜色
高斯模糊
模糊的实现有很多方法,例如均值模糊(其邻域内各个像素值的平均值)和中值模糊(所有像素排序后的中值替换掉原颜色)
高斯模糊同样利用了卷积计算,它使用的卷积核名为高斯核,每个元素的计算都是基于下面的高斯方程:
X和y分别对应了当前位置到卷积核中心的整数距离,G(x,y)即当前位置的权重,O是标准方差(一般取值为 1),
高斯方程很好地模拟了邻域每个像素对当前处理像素的影响程度一距离越近,影响越大,高斯核的维数越高,模糊程度越大,高斯模糊迭代次数越多,模糊程度越大
总的采样次数是NxNxWxH次,因此当N的大小不断增加时,采样次数会变得非常巨大,我们可以把这个二维高斯函数拆分成两个一维的高斯核,优化性能为2xNxWxH(对每个像素仅计算2n次)
由于要这次使用两个pass,需要2次blit,建立中间的buffer区,其中这个buffer可以通过缩放为更小的图像获得更好的性能,这时由于分辨率不同了,因此需要使用双线性滤波模式(2维,对于线性(函数多项式最高项的次数为1)的属于一维)
还可以控制高斯模糊的迭代次数,通过多次blit实现,仅使用两个中间缓冲交换数据就可以实现,
shader中由于两个pass会共用部分shader,将函数写在外面,shader内部只需要名字声明,
NAMEyuyi可以在其他Shader 中直接通过它们的名字来使用该Pass, 而不需要再重复编写代码
Bloom效果
泛光效果的实现原理,先根据一个阙值提取出图像中的较亮区域, 把它们存储在一张渲染纹理中,再利用高斯模糊对这张渲染纹理进行模糊处理,模拟光线扩散的效果, 最后再将其和原图像进行混合, 得到最终的效果。
一共有4个pass:提取亮部,高斯模糊_垂直,高斯模糊_水平,混合
在cs中,首先通过0pass,提取出亮部,原理是将原图像每个颜色同时减去指定值,剩余下来的就是非暗部区域
for次迭代模糊,通过1和2pass,直接使用通过NAME公开的shader,对传入的buffer0高斯模糊
调用3pass混合颜色,前两部已经将原图像和亮部图像分别存储为了纹理,将两个纹理中获得的颜色混合
运动模糊
在摄像机曝光时,物体发生运动就会产生模糊的画面,
运动模糊的实现有多种方法,累积缓存 ,当物体快速移动产生多张图像后,我们取它们之间的平均值作为最后的运动模糊图像,然而我们需要在同一帧里渲染多次场景,性能消耗很大
另一种方法是速度缓存,存储了各个像素当前的运动速度,然后利用该值来决定模糊的方向和大小。
我们将使用类似上述第一种方法的实现来模拟运动模糊的效果。我们不需要在一帧中把场景渲染多次,但需要保存之前的渲染结果,不断把当前的渲染图像叠加到之前的渲染图像中,从而产生一种运动轨迹的视觉效果