屏幕特效也被成为后期特效,通过抓取整个屏幕的画面,并对其进行渲染,可以实现诸如Bloom特效、运动模糊、HDR效果、景深效果等。
由于需要对Unity摄像机的画面进行渲染,所以我们在制作屏幕特效时都需要将一个特制的脚本挂在摄像机上,通过这个脚本将渲染纹理传递给着色器。
通用脚本
首先记录一下脚本的基础结构。
[ExecuteInEditMode] //添加此特性后,这个脚本就可以在编辑状态下运行
public class MyTestRenderImageBase : MonoBehaviour
{
//当前的着色器
public Shader curShader = null;
//当前的材质球
private Material curMaterial = null;
private Material CurMaterial
{
get
{
if(curMaterial == null)
{
//如果没有材质球,则创建一个使用curShader的材质球
curMaterial = new Material(curShader);
curMaterial.hideFlags = HideFlags.HideAndDontSave;
}
return curMaterial;
}
}
private void Start()
{
//容错处理
if(!SystemInfo.supportsImageEffects)
{
enabled = false;
return;
}
if(!curShader && !curShader.isSupported)
{
enabled = false;
}
}
/// <summary>
/// Unity提供的屏幕特效处理接口
/// </summary>
/// <param name="source">源纹理,通常就是当前屏幕的渲染纹理,或者上一步处理后得到的渲染纹理</param>
/// <param name="destination">目标渲染纹理</param>
private void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if(curShader)
{
//如果着色器存在,就是用着色器渲染当前纹理
Graphics.Blit(source, destination, CurMaterial);
}
else
{
//着色器不存在时,就直接将当前纹理覆盖传递给目标纹理
Graphics.Blit(source, destination);
}
}
private void OnDisable()
{
if(curMaterial)
{
//由于使用了HideFlags.HideAndDontSave标签,所以必须手动销毁此对象
DestroyImmediate(curMaterial);
}
}
}
通过上面的代码可以看出来,大体结构就是获取一个Shade→获取材质球→使用这个材质球渲染当前的屏幕画面。
Start()
中的代码基本都是容错用的。
比如通过使用SystemInfo.supportsImageEffects
来检测系统是否支持屏幕特效处理。
通过!curShader && !curShader.isSupported
来判断当前Shader是否可用。
Graphics.Blit
这个函数负责使用特定的Shader来渲染当前图像。
可能用到的有一下几种:
public static void Blit(Texture source, RenderTexture dest);
public static void Blit(Texture source, RenderTexture dest, Material mat);
public static void Blit(Texture source, Material mat, int pass = -1);
其中pass
默认值为-1表示将会依次调用Shader内的所有Pass,否则只会调用指定索引的Pass。
需要注意的是对于一些复杂的屏幕特效,我们可能需要多次调用Graphics.Blit
函数来对上一步的输出结果进行下一步处理。
其他的屏幕特效处理脚本也基本都是以此脚本为根基进行修改。