1、屏幕后期处理效果
屏幕后期处理效果( Screen Post-Processing Effects)是一种在渲染管线的最后阶段应用的视觉效果,允许在场景渲染完成后对最终图像进行各种调整和效果处理,从而增强视觉体验
常见的屏幕后期处理效果有:景深、模糊、色彩调整 等等
2、Unity中 屏幕后期处理效果的 基本实现原理
想要完成屏幕后期处理效果
最关键的问题在于
- 如何获取 游戏画面渲染完毕后的画面信息
- 如何为 获取到的画面信息添加自定义效果
只要搞清楚这两点,自然就明白了基本实现原理
(1)如何获取 游戏画面渲染完毕后的画面信息
在Unity中获取渲染纹理的常用方法有三种:RenderTexture、GrabPass、OnRenderImage
在处理屏幕后期处理效果时会使用OnRenderImage函数来获取 游戏画面渲染完毕后的画面信息
(2)如何为 获取到的画面信息添加自定义效果
主要思路是将获取到的游戏画面作为 自定义Shader的主纹理,通过自定义Shader利用捕获的画面来实现自定义效果
3、捕获画面的关键——OnRenderImage函数
OnRenderImage函数
它是在继承了MonoBehaviour的脚本中能够被自动调用的函数(类似生命周期函数),它会在图像的渲染操作完成后调用
它的固定写法是:
void OnRenderImage(RenderTexture source, RenderTexture destination)
第一个参数:源渲染纹理,当前渲染得到的屏幕图像存储在该参数当中
第二个参数:目标渲染纹理,将经过处理后的图像写入到目标纹理中用于最终的显示
通过该函数我们便可以得到当前渲染的游戏画面,并在该函数中对画面对应的渲染纹理进行处理后用于最终显示
注意:
该函数得到的源纹理默认是在所有的不透明和透明的Pass执行完毕后调用的,基于该源纹理进行修改会对游戏场景中所有游戏对象产生影响,如果你想要在不透明的Pass执行完毕后就调用该函数,只需要在该函数前加上特性 [ImageEffectOpaque] 这样就不会对透明物体产生影响
//加入该特性 就不会对透明物体产生影响
//[ImageEffectOpaque]
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{1.将源纹理直接复制到目标纹理//Graphics.Blit(source, destination);//把源纹理 通过 材质球当中的Shader进行效果处理 然后写入到目标纹理中 最终呈现在屏幕上Graphics.Blit(source, destination, material);
}
4、实现效果的关键——Graphics.Blit函数
Graphics.Blit函数用于将一个图像从一个纹理复制到另一个纹理,同时可以在这个过程中用着色器对图像进行处理,它有很多重载,主要讲解几个常用的:
(1)将源纹理直接复制到目标纹理
Graphics.Blit(Texture source, RenderTexture dest)
(2)将源纹理复制到目标纹理并应用一个材质
Graphics.Blit(Texture source, RenderTexture dest, Material mat, int pass = -1);source源纹理会被传递给mat材质中Shader中名为_MainTex的纹理属性用于进行处理
pass参数默认值为 - 1,表示会依次调用Shader内的所有Pass进行处理,否则,只会调用给定索引的Pass
5、屏幕后处理基类
补充:
(1)Shader.isSupported
通过获取Shader对象中的isSupported属性判断Shader在目标平台和硬件上是否能正确运行
(2)[ExecuteInEditMode]特性
用于使脚本在编辑器模式下也能执行
(3)[RequireComponent(typeof(组件名))]特性
指定某个脚本所依赖的组件,它确保当你将脚本附加到游戏对象时,所需的组件也会自动添加到该游戏对象中,如果这些组件已经存在,它们不会被重复添加,因为后处理脚本一般添加到摄像机上,因此我们用于依赖摄像机
(4)材质球中的 HideFlags 枚举
从材质球对象中可以点出 HideFlags 枚举
- HideFlags.None: 对象是完全可见和可编辑的。这是默认值。
- HideFlags.HideInHierarchy: 对象在层级视图中被隐藏,但仍然存在于场景中。
- HideFlags.HideInInspector: 对象在检查器中被隐藏,但仍然存在于层级视图中。
- HideFlags.DontSaveInEditor: 对象不会被保存到场景中。适用于编辑器模式,不会影响播放模式。
- HideFlags.NotEditable: 对象在检查器中是只读的,不能被修改。
- HideFlags.DontSaveInBuild: 对象不会被包含在构建中。
- HideFlags.DontUnloadUnusedAsset: 对象在资源清理时不会被卸载,即使它没有被引用。
- HideFlags.DontSave: 对象不会被保存到场景中,不会在构建中保存,也不会在编辑器中保存。这是 DontSaveInEditor | DontSaveInBuild | DontUnloadUnusedAsset 的组合。
如果想要设置枚举满足多个条件 直接多个枚举 进行位或运算即可 |
为什么要实现屏幕后处理基类
原因一:为了实现屏幕后期处理效果,每次都需要做的事情一定是
- 实现一个继承子MonoBehaviour的自定义C#脚本
- 关联对应的材质球或者Shader
- 实现OnRenderImage函数
- 在OnRenderImage函数中使用Graphics.Blit函数
那么这些共同点我们完全可以抽象到一个基类中去完成,以后只需要在子类中实现各自的基本逻辑即可
原因二:可以在基类中用代码动态创建材质球,不需要为每个后处理效果都手动创建材质球,只需要在Inspector窗口关联对应使用的Shader即可
原因三:在进行屏幕后处理之前,往往需要检查一系列条件是否满足,比如:
当前平台是否支持当前使用的Unity Shader,我们可以在基类中进行判断,避免每次书写相同逻辑
注意:
在一些老版本中,你可能还会在基类中判断目标平台是否支持屏幕后处理和渲染纹理,一般通过Unity中的SystemInfo类判断,该类可以用于确定底层平台和硬件相关的功能是否被支持
官方说明:https://docs.unity.cn/cn/2022.3/ScriptReference/SystemInfo.html
using System.Collections;
using System.Collections.Generic;
using UnityEngine;[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectBase : MonoBehaviour
{// 屏幕后处理效果会使用的Shaderpublic Shader shader;// 动态创建的材质球private Material _material;protected Material material {get {if (shader == null || !shader.isSupported)return null;else {if (_material != null && _material.shader == shader)return _material;// 用支持的 Shader 动态创建一个材质球用于渲染_material = new Material(shader);_material.hideFlags = HideFlags.DontSave;return _material;}}}protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination) {UpdateProperty();if (material != null)Graphics.Blit(source, destination, material);elseGraphics.Blit(source, destination);}protected virtual void UpdateProperty() {}
}