US3S10L3——屏幕后处理基本实现原理

Unity中屏幕后期处理效果的基本实现原理

从上文可以知道,想要完成屏幕后期处理效果,最关键的问题在于:

  1. 如何获取游戏画面渲染完毕后的画面信息
  2. 如何为获取到的画面信息添加自定义效果

只要搞清楚这两点,自然就明白了基本实现原理:

  1. 如何获取游戏画面渲染完毕后的画面信息

    之前在学习 渲染目标纹理 时学习过,在 Unity 中获取渲染纹理的常用方法有三种:
    ​RenderTexture​(渲染纹理)、GrabPass​(Shader 内使用)、OnRenderImage
    我们在处理屏幕后期处理效果时会使用 OnRenderImage​ 函数来获取 游戏画面渲染完毕后的画面信息

  2. 如何为获取到的画面信息添加自定义效果

    主要思路是将获取到的游戏画面作为 自定义Shader 的主纹理
    通过 自定义Shader 利用捕获的画面来实现自定义效果

捕获画面的关键 —— OnRenderImage函数

OnRenderImage()​ 是在继承了 MonoBehaviour​ 的脚本中能够被自动调用的函数(类似生命周期函数)
它会在图像的渲染操作完成后调用,它的固定写法是:

1
void OnRenderImage(RenderTexture source, RenderTexture destination) { }
  • 第一个参数 source​:源渲染纹理,当前渲染得到的屏幕图像存储在该参数当中
  • 第二个参数 destination​:目标渲染纹理,将经过处理后的图像写入到目标纹理中用于最终的显示

通过该函数我们便可以得到当前渲染的游戏画面,并在该函数中对画面对应的渲染纹理进行处理后用于最终显示

一般来说,实现屏幕后处理的脚本都是挂载在摄像机上的

注意!

该函数得到的源纹理默认是在所有的不透明和透明的 Pass​ 执行完毕后调用的,
基于该源纹理进行修改会对游戏场景中所有游戏对象产生影响,

如果你想要在不透明的 Pass​ 执行完毕后就调用该函数,只需要在该函数前加上特性 [ImageEffectOpaque]​,这样就不会对透明物体产生影响

1
2
[ImageEffectOpaque]
private void OnRenderImage(RenderTexture source, RenderTexture destination) { }

实现效果的关键 —— Graphics.Blit函数

Graphics.Blit()​ 用于将一个图像从一个纹理复制到另一个纹理,同时可以在这个过程中用着色器对图像进行处理
它有很多重载,我们主要讲解几个常用的:

  1. 将源纹理直接复制到目标纹理

    1
    Graphics.Blit(Texture source, RenderTexture dest)

    使用示例:

    1
    2
    3
    4
    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
    Graphics.Blit(source, destination); // 将原纹理直接赋值到目标纹理
    }
  2. 将源纹理复制到目标纹理并应用一个材质

    1
    Graphics.Blit(Texture source, RenderTexture dest, Material mat, int pass = -1);
    • source​ 源纹理参数会被传递给 mat​ 材质中 Shader​ 中名为 _MainTex​ 的纹理属性用于进行处理
    • pass​ 参数默认值为 -1​,表示会依次调用 Shader 内的所有 Pass​ 进行处理,否则,只会调用给定索引的 Pass​​

    source​ 源纹理在经过 mat​ 材质中 Shader​ 处理后,最终会将 Shader 输出的颜色渲染到 dest​ 上,改变屏幕显示内容

    使用示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    using UnityEngine;

    public class Lesson100 : MonoBehaviour
    {
    public Material material;

    private void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
    Graphics.Blit(source, destination, material);
    }
    }

    将此脚本挂载到主摄像机上,然后使用之前实现的棋盘格程序纹理 Shader 的材质,这个材质会直接渲染棋盘格颜色

    image

    Shader 代码详见:US3S8L11——Shader代码动态生成程序纹理

    显示效果:

    image

    可见,虽然这个材质内没有使用 source​ 屏幕内容纹理,
    但是因为材质会直接输出棋盘格颜色,并渲染到 destination 目标纹理内了,因此整个屏幕都变成了棋盘格样式的

    也就是说,如果能够合理的在 Shader 内传入的 source​ 屏幕内容纹理,就可以对屏幕显示内容作出修改