57.屏幕后期处理效果-屏幕后处理基类
57.1 知识点
知识回顾:屏幕后期处理效果的基本实现
屏幕后期处理效果的基本实现原理是利用 OnRenderImage
函数和 Graphics.Blit
函数,获取当前屏幕画面并通过 Shader 对该纹理进行自定义处理。
捕获画面的关键void OnRenderImage(RenderTexture source, RenderTexture destination)
实现效果的关键Graphics.Blit (Texture source, RenderTexture dest, Material mat, int pass= -1);
知识点补充
Shader.isSupported
用于判断 Shader 在目标平台和硬件上是否能正确运行。通过获取 Shader 对象中的 isSupported
属性可以判断:
- 返回
false
:不支持 - 返回
true
:支持
[ExecuteInEditMode] 特性
允许脚本在编辑器模式下也能执行。
[RequireComponent(typeof(组件名))] 特性
指定某个脚本所依赖的组件,确保当脚本附加到游戏对象时,所需组件也会自动添加。如果组件已存在,则不会重复添加。
一般用于后处理脚本,通常依赖摄像机。
材质球中的 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
类)。
但是随着时代发展,目前几乎所有的现代图形硬件都是支持屏幕后处理和渲染纹理了,因此只需判断 Shader 是否被支持。
官方文档:SystemInfo 类
实现基类功能
主要步骤
声明基类
让基类依赖 Camera,并允许其在编辑模式下运行,确保可以随时查看效果。声明公共 Shader
在基类中声明一个公共 Shader,用于在 Inspector 窗口进行关联。声明私有 Material
声明一个私有的材质球(Material),用于动态创建。实现 Shader 判断与动态创建
在基类中实现逻辑,判断 Shader 是否可用,并动态创建材质球。实现 OnRenderImage 虚方法
在基类中实现OnRenderImage
的虚方法,完成屏幕后处理效果的基本逻辑。
声明基类,让其依赖Camera,并且让其在编辑模式下可运行,保证我们可以随时看到效果
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectBase : MonoBehaviour
{
}
声明 公共 Shader,用于在Inspector窗口关联,声明 私有 Material,用于动态创建
//屏幕后处理效果会使用的Shader
public Shader shader;
//一个用于动态创建出来的材质球 就不用再工程中手动创建了
private Material _material;
实现保护Material属性,判断Shader是否可用,并且动态创建Material的方法
protected Material material
{
get
{
//如果shader 没有 或者有但是不支持当前平台
if (shader == null || !shader.isSupported)
return null;
//避免每次调用属性都去new材质球
//如果之前new过了,并且shader也没有变化
//那就不用new了 直接返回使用即可
if (_material != null && _material.shader == shader)
return _material;
//除非材质球是空的 或者shader变化了 才会走下面的逻辑
//用支持的shader动态创建一个材质球 用于渲染
_material = new Material(shader);
//不希望材质球被保存下来 因此我们家一个标识
_material.hideFlags = HideFlags.DontSave;
return _material;
}
}
实现OnRenderImage的虚方法,完成基本逻辑
protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination)
{
//判断这个材质球是否为空 如果不为空 就证明这个shader能用来处理屏幕后处理效果
if (material != null)
{
Graphics.Blit(source, destination, material);
}
//如果为空 就不用处理后处理效果了 直接显示原画面就可以了
else
{
Graphics.Blit(source, destination);
}
}
赋值动态生成的国际象棋棋盘shader,可以看到效果
57.2 知识点代码
Lesson57_屏幕后期处理效果_屏幕后处理基类.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson57_屏幕后期处理效果_屏幕后处理基类 : MonoBehaviour
{
void Start()
{
#region 知识回顾
//屏幕后期处理效果的基本实现原理
//就是利用 OnRenderImage函数 和 Graphics.Blit函数
//来获取当前屏幕画面并利用Shader对该纹理进行自定义处理
//捕获画面的关键 —— void OnRenderImage(RenderTexture source, RenderTexture destination)
//实现效果的关键 —— Graphics.Blit (Texture source, RenderTexture dest, Material mat, int pass= -1);
#endregion
#region 知识点补充
//1.Shader.isSupported
// 如何判断Shader在目标平台和硬件上是否能正确运行
// 我们可以通过获取Shader对象中的isSupported属性判断
// 如果返回false,不支持
// 如果返回true,支持
//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 的组合。
// 如果想要设置枚举满足多个条件 直接多个枚举 进行位或运算即可 |
#endregion
#region 知识点一 为什么要实现屏幕后处理基类
//原因一:
//为了实现屏幕后期处理效果
//我们每次都需要做的事情一定是
//1.实现一个继承子MonoBehaviour的自定义C#脚本
//2.关联对应的材质球或者Shader
//3.实现OnRenderImage函数
//4.在OnRenderImage函数中使用Graphics.Blit函数
//那么这些共同点我们完全可以抽象到一个基类中去完成
//以后只需要在子类中实现各自的基本逻辑即可
//原因二:
//我们可以在基类中用代码动态创建材质球
//不需要为每个后处理效果都手动创建材质球
//只需要在Inspector窗口关联对应使用的Shader即可
//原因三:
//在进行屏幕后处理之前,我们往往需要检查一系列条件是否满足
//比如:
//当前平台是否支持当前使用的Unity Shader
//我们可以在基类中进行判断,避免每次书写相同逻辑
//注意:
//在一些老版本中,你可能还会在基类中判断目标平台是否支持屏幕后处理和渲染纹理
//一般通过Unity中的SystemInfo类判断
//该类可以用于确定底层平台和硬件相关的功能是否被支持
//官方说明:https://docs.unity.cn/cn/2022.3/ScriptReference/SystemInfo.html
//但是随着时代发展,目前几乎所有的现代图形硬件都是支持屏幕后处理和渲染纹理了
//因此我们无需再进行类似的判断的
//只需要判断Shader是否被支持即可
#endregion
#region 知识点二 实现基类功能
//主要目标
//1.声明基类,让其依赖Camera,并且让其在编辑模式下可运行,保证我们可以随时看到效果
//2.基类中声明 公共 Shader,用于在Inspector窗口关联
//3.基类中声明 私有 Material,用于动态创建
//4.基类中实现判断Shader是否可用,并且动态创建Material的方法
//5.基类中实现OnRenderImage的虚方法,完成基本逻辑
#endregion
}
}
PostEffectBase.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectBase : MonoBehaviour
{
//屏幕后处理效果会使用的Shader
public Shader shader;
//一个用于动态创建出来的材质球 就不用再工程中手动创建了
private Material _material;
protected virtual void OnRenderImage(RenderTexture source, RenderTexture destination)
{
//在进行渲染之前 先更新属性 在子类中重写即可
UpdateProperty();
//判断这个材质球是否为空 如果不为空 就证明这个shader能用来处理屏幕后处理效果
if (material != null)
{
Graphics.Blit(source, destination, material);
}
//如果为空 就不用处理后处理效果了 直接显示原画面就可以了
else
{
Graphics.Blit(source, destination);
}
}
/// <summary>
/// 更新材质球属性
/// </summary>
protected virtual void UpdateProperty()
{
}
protected Material material
{
get
{
//如果shader 没有 或者有但是不支持当前平台
if (shader == null || !shader.isSupported)
return null;
//避免每次调用属性都去new材质球
//如果之前new过了,并且shader也没有变化
//那就不用new了 直接返回使用即可
if (_material != null && _material.shader == shader)
return _material;
//除非材质球是空的 或者shader变化了 才会走下面的逻辑
//用支持的shader动态创建一个材质球 用于渲染
_material = new Material(shader);
//不希望材质球被保存下来 因此我们家一个标识
_material.hideFlags = HideFlags.DontSave;
return _material;
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com