Unity Shader 屏幕后处理效果

Unity Shader 屏幕后处理效果:边缘检测,高斯模糊与Bloom

基本的屏幕后处理脚本系统:

基本原理:

想要实现屏幕后处理的基础在于得到渲染后的屏幕图像,即抓取屏幕,Unity为我们提供了一个方便的接口OnRenderImage函数:

1
MonoBehaviour.OnRenderImage(RenderTexture src,RenderTexture dest)

当我们在脚本中声明此函数之后,Unity会把当前渲染得到的图像存储在第一个参数对应的源渲染纹理中(src),通过函数中的一系列操作后,再把目标渲染纹理(dest)显示在屏幕上。

在OnRenderImage函数中,我们通常是利用Graphics.Blit函数来完成对渲染纹理的处理。

1
2
3
4
public static void Blit(Texture source, RenderTexture dest);
public static void Blit(Texture source, RenderTexture dest, Material mat, int pass = -1);
public static void Blit(Texture source, Material mat, int pass = -1);
public static void Blit(Texture source, RenderTexture dest, Vector2 scale, Vector2 offset);
  • source对应原纹理,在屏幕后处理技术中,该纹理通常就是当前屏幕的渲染纹理或者是上一步处理后得到的渲染纹理。
  • dest对应目标渲染纹理,如果它的值为null,就会直接将结果渲染在屏幕上。
  • mat是我们使用的材质,这个材质使用的Unity Shader将会进行各种屏幕后处理操作,而source纹理将会被传递给Shader中名为_MainTex的纹理属性。
  • pass的默认值为-1,表示将会依次调用Shader中的所有Pass,否则只会调用给定索引的Pass。

处理过程:

在摄像机中添加一个用于屏幕后处理的脚本,在这个脚本中,我们会实现使用OnRenderImage获取当前屏幕的渲染纹理。再调用Graphics.Blit函数使用特定的Unity Shader来对当前图像进行处理,再把返回的渲染纹理显示在屏幕上。对于复杂的屏幕特效,我们可能需要多次调用Graphics.Blit函数来对上一步的输出结果进行下一步处理。

在开始屏幕后处理之前,我们需要检查一系列条件是否满足,例如当前平台是否支持渲染纹理和屏幕特效,是否支持当前使用的Unity Shader等。

调整屏幕的亮度、饱和度与对比度

每当OnRenderImage函数被调用时,它会检查材质是否可用。如果可用,就把参数传递给材质,再调用Graphics.Blit进行处理;否则,直接把原图像显示在屏幕上,不作任何处理。

屏幕后处理实际上上是在场景绘制了一个与屏幕同宽同高的四边形面片,为了防止它对其他物体产生影响,我们需要设置相关的渲染状态。

边缘检测

边缘检测的原理是利用一些边缘检测算子来对图像进行卷积操作

  1. 卷积:卷积操作指的是使用一个卷积核,对一张图像中的每个像素进行一系列操作。卷积核通常是一个四方形网格结构,每个方格都有一个权重,进行卷积时,我们将卷积核的中心放在目标像素上,翻转核之后再依次计算核中每个元素和其覆盖的图像像素值的乘积并求和,得到的结果就是该位置的新像素值。

  2. 梯度:边的形成的核心性质就是在边的两侧其差值较大,这种差值的绝对值叫做梯度。基于这个内容,我们使用几种不同的边缘检测算子来计算梯度。

    • Roberts
    • Prewitt
    • Sobel

    每个算子都包含x,y两个方向上的卷积核,每次我们需要对一个像素分别计算两个方向上的梯度,再求出总梯度G。

    出于性能考虑一般使用G=Gx+Gy来代替平方根。

高斯模糊

高斯模糊利用卷积计算,它的卷积核名为高斯核。

Bloom效果

根据一个阈值提取出图像中的较亮区域,把它们存储在一张渲染纹理中,再利用高斯模糊对这张渲染纹理进行模糊处理,模拟光线的扩散,最后再将其与原图像进行混合,得到最终的效果。

运动模糊

原理:

  1. 累积缓存:当物体快速移动产生多张图像后,我们取它们之间的平均值作为最后的运动模糊图像。这种暴力的方法对性能消耗很大,想要获取多张帧图像,我们需要在同一帧里渲染多次场景。
  2. 速度缓存:这个缓存中存储了各个像素当前的运动速度,根据速度来决定模糊的方向和大小。
  3. 类似累积缓存:保存之前的渲染结果,不断把当前的渲染图像叠加到之前的渲染图像中,产生一种运动轨迹的视觉效果,这种方法与原始的累积缓存相比性能更好,但是模糊效果可能略有影响。