【UnityShader】模板Stencil测试

效果


原理
使用后处理,

在后处理阶段先渲染产生一张RenderTexture,包含要被描边的物体,使用描边色渲染。
高斯模糊RenderTexture,会产生边缘
用高斯模糊的图片反向剔除未模糊的图,这样只保留模糊多出的部分。
此时RenderTexture上为只有边缘的图,将此边缘图与渲染结果图进行混合并输出屏幕,即得到结果。
实现
OutLineCameraComponent.cs 挂载在摄像机下,赋值两个shader。

using UnityEngine;  
using System.Collections.Generic;
using UnityEngine.Rendering;  


[ExecuteInEditMode]
public class OutLineCameraComponent
{  
    private RenderTexture renderTexture = null;  
    private CommandBuffer commandBuffer = null;  

    [Header("Outline/OutLineEffect.shader")]
    public Shader preoutlineShader = null;  
   [Header("Outline/OutlinePrePass.shader")]
     public Shader shader = null;

    private Material _material = null;

    public Material mMaterial
    {
        get
        {
            if (_material == null)
                _material = GenerateMaterial(shader);
            return _material;
        }
    }

    [Header("采样范围")]
    public float samplerArea = 1;  


    [Header("降分辨率")]
    public int downSample = 1;  


    [Header("迭代次数")]
    public int iteration = 2;

    [Header("描边强度")]
    [Range(0.0f, 10.0f)]  
    public float outLineStrength = 3.0f;  

    //根据shader创建用于屏幕特效的材质
        protected Material GenerateMaterial(Shader shader)
        {
            if (shader == null)
                return null;

            if (shader.isSupported == false)
                return null;
            Material material = new Material(shader);
            material.hideFlags = HideFlags.DontSave;

            if (material)
                return material;

            return null;
        }


    //目标对象  
    private List targetObjects = new List();

    public void AddTarget(OutLineTargetComponent target)
    {
        if(target.material==null)
            target.material = new Material(preoutlineShader);
        targetObjects.Add(target);

        RefreshCommandBuff();

    }
    public void RemoveTarget(OutLineTargetComponent target)
    {
        bool found = false;

        for (int i = 0; i < targetObjects.Count; i++)
        {
            if (targetObjects[i] == target)
            {
                targetObjects.Remove(target);
                DestroyImmediate(target.material);
                target.material = null;
                found = true;
                break;
            }
        }

        if(found)
            RefreshCommandBuff();
    }

    public void RefreshCommandBuff()
    {
        if (renderTexture)  
            RenderTexture.ReleaseTemporary(renderTexture);  
        renderTexture = RenderTexture.GetTemporary(Screen.width >> downSample, Screen.height >> downSample, 0);  

        commandBuffer = new CommandBuffer();  
        commandBuffer.SetRenderTarget(renderTexture);  
        commandBuffer.ClearRenderTarget(true, true, Color.black);
        for (int i = 0; i < targetObjects.Count; i++)
        {
            Renderer[] renderers = targetObjects[i].GetComponentsInChildren();  
            foreach (Renderer r in renderers)
                commandBuffer.DrawRenderer(r, targetObjects[i].material);
        }
    }


   void OnEnable()  
    {  
        if (preoutlineShader == null)  
            return;  
        RefreshCommandBuff();
    }  

    void OnDisable()  
    {  
        if (renderTexture)  
        {  
            RenderTexture.ReleaseTemporary(renderTexture);  
            renderTexture = null;  
        }  

        if (commandBuffer != null)  
        {  
            commandBuffer.Release();  
            commandBuffer = null;  
        }  

    }  

    void OnRenderImage(RenderTexture source, RenderTexture destination)  
    {  
        if (mMaterial && renderTexture && commandBuffer != null)  
        {  
            Graphics.ExecuteCommandBuffer(commandBuffer);  

            //对RT进行Blur处理  
            RenderTexture temp1 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0);  
            RenderTexture temp2 = RenderTexture.GetTemporary(source.width >> downSample, source.height >> downSample, 0);  

            //高斯模糊,两次模糊,横向纵向,使用pass0进行高斯模糊  
            mMaterial.SetVector("_offsets", new Vector4(0, samplerArea, 0, 0));  
            Graphics.Blit(renderTexture, temp1, mMaterial, 0);  
            mMaterial.SetVector("_offsets", new Vector4(samplerArea, 0, 0, 0));  
            Graphics.Blit(temp1, temp2, mMaterial, 0);  

            //如果有叠加再进行迭代模糊处理  
            for(int i = 0; i < iteration; i++)  
            {  
                mMaterial.SetVector("_offsets", new Vector4(0, samplerArea, 0, 0));  
                Graphics.Blit(temp2, temp1, mMaterial, 0);  
                mMaterial.SetVector("_offsets", new Vector4(samplerArea, 0, 0, 0));  
                Graphics.Blit(temp1, temp2, mMaterial, 0);  
            }  

            //用模糊图和原始图计算出轮廓图
            mMaterial.SetTexture("_BlurTex", temp2);  
            Graphics.Blit(renderTexture, temp1, mMaterial, 1);  

            //轮廓图和场景图叠加  
            mMaterial.SetTexture("_BlurTex", temp1);  
            mMaterial.SetFloat("_OutlineStrength", outLineStrength);  
            Graphics.Blit(source, destination, mMaterial, 2);  

            RenderTexture.ReleaseTemporary(temp1);  
            RenderTexture.ReleaseTemporary(temp2);  
        }  
        else  
        {  
            Graphics.Blit(source, destination);  
        }  
    }  
}  

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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
OutLineTargetComponent.cs 挂载在要使用描边的物体上

using UnityEngine;

[ExecuteInEditMode]
public class OutLineTargetComponent : MonoBehaviour
{
    public Color color = Color.green;

    public Material material { set; get; }

    void OnEnable()
    {
        Camera[] allCameras = Camera.allCameras;
        for (int i = 0; i < allCameras.Length; i++)
        {
            if (allCameras[i].GetComponent()!=null)
            {
                allCameras[i].GetComponent().AddTarget(this);
            }
        }
    }

    private void Update()
    {
        if(material!=null)
            material.SetColor("_OutLineColor", color);  
    }

    void OnDisable()
    {
        Camera[] allCameras = Camera.allCameras;
        for (int i = 0; i < allCameras.Length; i++)
        {
            if (allCameras[i].GetComponent()!=null)
            {
                allCameras[i].GetComponent().RemoveTarget(this);
            }
        }
    }
}
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
OutlinePrePass.shader

Shader "OutLine/OutlinePrePass"  
{
    SubShader  
    {
        Pass  
        {
            CGPROGRAM
            #include "UnityCG.cginc"  
            fixed4 _OutLineColor;

            struct v2f  
            {  
                float4 pos : SV_POSITION;  
            };  

            v2f vert(appdata_full v)  
            {  
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
                return o;  
            }

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


            #pragma vertex vert  
            #pragma fragment frag  
            ENDCG  
        }  
    }  
}  
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
OutLineEffect.shader

Shader "OutLine/OutLineEffect" {  

    Properties{  
        _MainTex("Base (RGB)", 2D) = "white" {}  
        _BlurTex("Blur", 2D) = "white"{}  
    }  

    CGINCLUDE  
    #include "UnityCG.cginc"  

    //用于剔除中心留下轮廓  
    struct v2f_cull  
    {  
        float4 pos : SV_POSITION;  
        float2 uv : TEXCOORD0;  
    };  

    //用于模糊  
    struct v2f_blur  
    {  
        float4 pos : SV_POSITION;  
        float2 uv  : TEXCOORD0;  
        float4 uv01 : TEXCOORD1;  
        float4 uv23 : TEXCOORD2;  
        float4 uv45 : TEXCOORD3;  
    };  

    //用于最后叠加  
    struct v2f_add  
    {  
        float4 pos : SV_POSITION;  
        float2 uv  : TEXCOORD0;  
        float2 uv1 : TEXCOORD1;  
    };  

    sampler2D _MainTex;  
    float4 _MainTex_TexelSize;  
    sampler2D _BlurTex;  
    float4 _BlurTex_TexelSize;  
    float4 _offsets;  
    float _OutlineStrength;  

    //获得轮廓 pass 1
    v2f_cull vert_cull(appdata_img v)  
    {  
        v2f_cull o;  
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
        o.uv = v.texcoord.xy;  
        //dx中纹理从左上角为初始坐标,需要反向  
#if UNITY_UV_STARTS_AT_TOP  
        if (_MainTex_TexelSize.y < 0)  
            o.uv.y = 1 - o.uv.y;  
#endif    
        return o;  
    }  

    fixed4 frag_cull(v2f_cull i) : SV_Target  
    {  
        fixed4 colorMain = tex2D(_MainTex, i.uv);  
        fixed4 colorBlur = tex2D(_BlurTex, i.uv);  
        return colorBlur - colorMain;  
    }  

    //高斯模糊 pass 0
    v2f_blur vert_blur(appdata_img v)  
    {  
        v2f_blur o;  
        _offsets *= _MainTex_TexelSize.xyxy;  
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
        o.uv = v.texcoord.xy;  

        o.uv01 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1);  
        o.uv23 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 2.0;  
        o.uv45 = v.texcoord.xyxy + _offsets.xyxy * float4(1, 1, -1, -1) * 3.0;  

        return o;  
    }  
    fixed4 frag_blur(v2f_blur i) : SV_Target  
    {  
        fixed4 color = fixed4(0,0,0,0);  
        color += 0.40 * tex2D(_MainTex, i.uv);  
        color += 0.15 * tex2D(_MainTex, i.uv01.xy);  
        color += 0.15 * tex2D(_MainTex, i.uv01.zw);  
        color += 0.10 * tex2D(_MainTex, i.uv23.xy);  
        color += 0.10 * tex2D(_MainTex, i.uv23.zw);  
        color += 0.05 * tex2D(_MainTex, i.uv45.xy);  
        color += 0.05 * tex2D(_MainTex, i.uv45.zw);  
        return color;  
    }  

    //最终叠加 pass 2
    v2f_add vert_final(appdata_img v)  
    {  
        v2f_add o;  
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);  
        o.uv.xy = v.texcoord.xy;  
        o.uv1.xy = o.uv.xy;  
#if UNITY_UV_STARTS_AT_TOP  
        if (_MainTex_TexelSize.y < 0)  
            o.uv.y = 1 - o.uv.y;  
#endif    
        return o;  
    }  

    fixed4 frag_final(v2f_add i) : SV_Target  
    {  
        fixed4 ori = tex2D(_MainTex, i.uv1);  
        fixed4 blur = tex2D(_BlurTex, i.uv);  
        fixed4 final = ori + blur * _OutlineStrength;  
        return final;  
    }  

        ENDCG  

    SubShader  
    {  
        //pass 0: 高斯模糊  
        Pass  
        {  
            ZTest Off  
            Cull Off  
            ZWrite Off  
            Fog{ Mode Off }  

            CGPROGRAM  
            #pragma vertex vert_blur  
            #pragma fragment frag_blur  
            ENDCG  
        }  

        //pass 1: 剔除中心部分   
        Pass  
        {  
            ZTest Off  
            Cull Off  
            ZWrite Off  
            Fog{ Mode Off }  

            CGPROGRAM  
            #pragma vertex vert_cull
            #pragma fragment frag_cull
            ENDCG  
        }  


        //pass 2: 最终叠加  
        Pass  
        {  

            ZTest Off  
            Cull Off  
            ZWrite Off  
            Fog{ Mode Off }  

            CGPROGRAM  
            #pragma vertex vert_final
            #pragma fragment frag_final  
            ENDCG  
        }  

    }  
}
--------------------- 
作者:叫我上上 
来源:CSDN 
原文:https://blog.csdn.net/vikingsc2007_1/article/details/76570931 
版权声明:本文为博主原创文章,转载请附上博文链接!

你可能感兴趣的:(StencilBuffer,Shader)