Shader4_增加屏幕后效(上)

前言

在开始的时候说到过,我们是对 Unity 的默认管线进行 Shader 的编写,并通过提取 Unity 默认的 UI Shader 编写了 UIDefault.cginc,这样就可以在原有的 UI默认Shader 基础上进行修改。

屏幕后效的效果也特别简单,大部分情况下可以理解为上是将屏幕最后得到的渲染图当作一个 2D 的图片进行处理,所以对于 UI 的各种方法也都适用,也可以建立一个 PostEffcet.cginc 以及 其对应的 默认Shader模版。而对于 2D Sprite Shader 来说就比较复杂了,可以看到其背后的 .cginc 有很多东西,我们可以对其进行分析,然后总结出一套适合自己的 Unity默认管线 的 Shader 流程。

这里我们先添加屏幕后效的实现方法,这样之前写的 UI 效果也都适用。

参考文献:

屏幕后效

屏幕后效是针对相机来操作的,屏幕显示的最后是根据相机渲染的图片。而屏幕后效就是我们就是在处理完所有物体渲染后(UI可以不处理),对得到的本该直接渲染到屏幕上的一张 2D图片 进行操作而已。

PostEffectsBase脚本

需要挂在相机一个处理脚本,用来处理传入的 Shader ,使用 Shader 生成材质,并做一些判断,实际上我们也可以直接传入材质,这样在材质上就可以做参数输入了。

主要的 API 有两个:

void OnRenderImage(RenderTexture src, RenderTexture dest)

当相机渲染完毕后会自动调用

public static void Blit(Texture source, RenderTexture dest, Material mat);

在 Graphics 类中,相当于使用 mat材质 处理 source 并传给 dest,配合 OnRenderImage 方法使用,可以使用 mat材质 渲染屏幕。

PosetEffectsBase.cs 改自 入门精要

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
namespace Render.PostEffect
{
using UnityEngine;
using System.Collections;


[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class PostEffectsBase : MonoBehaviour
{
public Shader PEShader;
private Material m_PEMat;
protected Material PEMat { get
{ return m_PEMat = CheckShaderAndCreateMaterial(PEShader, m_PEMat);
} }

// Called when start
protected void CheckResources()
{
bool isSupported = CheckSupport();

if (isSupported == false)
{
NotSupported();
}
}

// Called in CheckResources to check support on this platform
protected bool CheckSupport()
{
if (SystemInfo.supportsImageEffects == false || SystemInfo.supportsRenderTextures == false)
{
Debug.LogWarning("This platform does not support image effects or render textures.");
return false;
}

return true;
}

// Called when the platform doesn't support this effect
protected void NotSupported()
{
enabled = false;
}

protected void Start()
{
CheckResources();
}

// Called when need to create the material used by this effect
protected Material CheckShaderAndCreateMaterial(Shader shader, Material material)
{
if (shader == null)
{
return null;
}

if (shader.isSupported && material && material.shader == shader)
return material;

if (!shader.isSupported)
{
return null;
}
else
{
material = new Material(shader);
material.hideFlags = HideFlags.DontSave;
if (material)
return material;
else
return null;
}
}
}
}

具体实现

可以看到其实并没有实现 OnRenderImage 也没有调用 Blit,这是因为对应的材质不同需要输入的一些参数也不同,我们通过继承这个 Base 脚本根据不同的效果传入不同的参数。

例如: PencilPE.cs 配合 PencilPE.shader 实现铅笔画风格

PencilPE.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


namespace Render.PostEffect
{
public class PencilPE : PostEffectsBase
{
[Range(0, 0.5f)]
public float BlurOffset;

void OnRenderImage(RenderTexture src, RenderTexture dest)
{
if (PEMat == null)
return;

PEMat.SetFloat("_BlurOffset", BlurOffset);

Graphics.Blit(src, dest, PEMat);
}
}

}

PencilPE.shader 其中的主要方法和 UI 的相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
Shader "PostEffect/Sharp"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_BlurOffset("Blur Offset", Range(0, 0.5)) = 0.05
}
SubShader
{
// No culling or depth
Cull Off ZWrite Off ZTest Always

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

#include "Assets/XiaoxiaoShaderLab/PostEffect.cginc"
#include "Assets/XiaoxiaoShaderLab/Method.cginc"

float _BlurOffset;
fixed4 ProcessColor(v2f IN)
{
float3x3 pencilFilter =
{
-0.5, -1.0, 0.0,
-1.0, 0.0, 1.0,
0.0, 1.0, 0.5
};

float4 filterCol = filterWithoutA(pencilFilter,_BlurOffset, _MainTex, IN.uv);
float gray = 0.3 * filterCol.x + 0.59 * filterCol.y + 0.11 * filterCol.z;
gray = 1.0 - gray;
return float4(gray, gray, gray, filterCol.a);
}

v2f vert (appdata v)
{
return vert_default(v);
}

fixed4 frag (v2f i) : SV_Target
{
return frag_default(i);
}
ENDCG
}
}
}

可以看到 PencilPE.cs 只是实现了参数的改变并调用了核心方法,如果我们不想多写脚本可以试着,直接在 Base 类中 把 mat 作为面板上的参数,直接传入。

而具体 PencilPE.shader 以及调用的 .cginc 我们放到下篇来讲。

0%