模板测试是渲染管线中逐片元操作的一环,它的作用是筛选出指定模板的片元,而不符合模板的片元会被舍弃,从而做到一个遮罩的效果。

以下是Unity中实践的一个效果:

场景中可以看出,熊模型和茶壶模型都在差不多的位置,但是正面的框中只能看到熊模型,侧面的框中只能看到茶壶模型,利用的就是模板测试的原理。

以下是shader代码

遮罩的shader代码:

Shader "Test/StencilMask"
{
    Properties
    {
        _ID ("Mask Id", Int) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry+1" }
        ColorMask 0
        ZWrite Off

        //模板遮罩关键代码
        Stencil
        {
            Ref[_ID]
            Comp Always
            Pass Replace
        }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return half4(1, 1, 1, 1);
            }
            ENDCG
        }
    }
}

游戏物体的shader代码:

Shader "Test/StencilObject"
{
    Properties
    {
        _ColorTint ("Color Tint", Color) = (1,1,1,1)
        _MainTex ("Base (RGB)", 2D) = "white" {}
        _BumpMap ("Normalmap", 2D) = "bump" {}
        _Amount ("Extrusion Amount", Range(-0.5, 0.5)) = 0.1
        _ID ("Mask Id", Int) = 0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry+2" }
        LOD 300

        //被遮罩的游戏物体
        Stencil
        {
            Ref[_ID]
            Comp Equal
        }

        CGPROGRAM
        #pragma surface surf CustomLambert vertex:myvert finalColor:mycolor addshadow exclude_path:deferred exclude_path:prepass nometa
        #pragma target 3.0

        fixed4 _ColorTint;
        sampler2D _MainTex;
        sampler2D _BumpMap;
        half _Amount;

        struct Input
        {
            float2 uv_MainTex;
            float2 uv_BumpMap;
        };

        void myvert (inout appdata_full v)
        {
            v.vertex.xyz += v.normal * _Amount;    
        }

        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 tex = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = tex.rgb;
            o.Alpha = tex.a;
            o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
        }

        half4 LightingCustomLambert (SurfaceOutput s, half3 lightDir, half atten)
        {
            half NdotL = dot(s.Normal, lightDir);
            half4 c;
            c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten);
            c.a = s.Alpha;
            return c;
        }

        void mycolor(Input IN, SurfaceOutput o, inout fixed color)
        {
            color *= _ColorTint;    
        }

        ENDCG
    }
    FallBack "Legacy Shaders/Diffuse"
}

之后只要把对应模型的材质球的Mask Id调整成一致就好。

Logo

分享前沿Unity技术干货和开发经验,精彩的Unity活动和社区相关信息

更多推荐