1. 程式人生 > 實用技巧 >Unity Shader模板測試-描邊

Unity Shader模板測試-描邊

Unity Shader模板測試描邊效果,常用於 rpg 專案中主角被遮擋的情況,將被遮擋的部分的輪廓描邊繪製出來,這樣可以在任何情況都能知道主角在哪裡。(還有另外一種就是使用X光效果,但這種效果不需要用到模板測試,所以這裡用 描邊效果 舉例子)

效果如下: 思路 實現這種效果需要兩個pass 1、正常繪製,即深度測試 LEqual,正常遮擋,同時往 模板緩衝區 寫入一個 參考值 Ref=1(即使深度測試失敗的值也要寫入,保證 角色所有畫素在模板緩衝區的值 都是 參考值 Ref=1) 2、只繪製被遮擋部分的描邊
  • 先把模型的 頂點 往 法線方向 偏移一個值(這個就是描邊的寬度值,可以理解為將模型放大了),可以在觀察空間 或者 世界空間、模型空間 偏移,只要和 法線 在同一空間下。
  • 對比 模板緩衝區 的參考值 Ref=1,因為第一個 pass 寫入的參考值是 1,所以這個pass中就要不等1才讓它通過,這樣就能得到一個 差值區域,即描邊的區域。
  • 深度測試,讓被遮擋部分才讓它通過,即 ZTest Greater,done!
shader程式碼:
Shader "Custom/Unlit-Texture-Outline" {
    Properties{
        _MainTex("Base (RGB)", 2D) = "white" {}
        _OutlineColor("Outline Color", Color) = (1,1,0,1) //描邊顏色
        _Outline("Outline width", Range(0.0, 0.5)) = 0.03 // 描邊寬度
    }
    CGINCLUDE
    #include "UnityCG.cginc"
    struct appdata_t {
        float4 vertex : POSITION;
        float2 texcoord : TEXCOORD0;
        float3 normal : NORMAL;
    };
    struct v2f {
        float4 vertex : SV_POSITION;
        half2 texcoord : TEXCOORD0;
    };
    sampler2D _MainTex;
    float4 _MainTex_ST;
    float _Outline;
    float4 _OutlineColor;
    v2f vert(appdata_t v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
        return o;
    }
    v2f vert_outline(appdata_t v)
    {
        v2f o;
        // 方式一,觀察空間 下往法線偏移頂點
        float4 viewPos = mul(UNITY_MATRIX_MV, v.vertex);
        //float3 viewNorm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
        float3 viewNorm = mul(v.normal, (float3x3)UNITY_MATRIX_T_MV);
        float3 offset = normalize(viewNorm) * _Outline;
        viewPos.xyz += offset;
        o.vertex = mul(UNITY_MATRIX_P, viewPos);
        //方式二,世界空間 下往法線偏移頂點
        //float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
        //float3 worldNormal = UnityObjectToWorldNormal(v.normal);
        //float3 offset = normalize(worldNormal) * _Outline;
        //worldPos.xyz += offset;
        //o.vertex = mul(UNITY_MATRIX_VP, worldPos);
        return o;
    }
    ENDCG
    SubShader{
        Tags{ "Queue" = "Transparent" "RenderType" = "Opaque" }
        Pass{ // 正常繪製
            Stencil
            {
                Ref 1
                Comp Always
                Pass Replace
                ZFail Replace
            }
            ZTest LEqual
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            fixed4 frag(v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.texcoord);
                return col;
            }
            ENDCG
        }
        Pass{ // 遮擋部分繪製描邊
            ZTest Greater
            ZWrite Off
            //Blend DstAlpha OneMinusDstAlpha
            Stencil{
                Ref 1
                Comp NotEqual
            }
            CGPROGRAM
            #pragma vertex vert_outline
            #pragma fragment frag
            half4 frag(v2f i) :COLOR
            {
                return _OutlineColor;
            }
            ENDCG
        }
    }
}
使用 剔除Cull 的方式 描邊 這種方式的描邊不適合做遮擋部分描邊,且不遮擋部分的效果也沒有 模板測試 那種方式好,他的原理也是使用兩個pass,一個pass正常渲染,剔除背面 (Cull Back),另外一個pass 也需要頂點外拉,然後 剔除正面(Cull Front),偏移深度。這種方式會在人體內也有描邊,不像 模板測試 那種方式在人體完全沒有描邊。 效果 shader程式碼
// 這種方式的描邊不適合做遮擋部分描邊,且不遮擋部分的效果也沒有 模板測試 那種方式好
Shader "ITS/test/testOutline_cull" {
Properties{
    _MainTex("Base (RGB)", 2D) = "white" {}
    _OutlineColor("Outline Color", Color) = (1,1,0,1)
    _Outline("Outline width", Range(0.0, 0.5)) = 0.03
}
CGINCLUDE
#include "UnityCG.cginc"
struct appdata_t {
    float4 vertex : POSITION;
    float2 texcoord : TEXCOORD0;
    float3 normal : NORMAL;
};
struct v2f {
    float4 vertex : SV_POSITION;
    half2 texcoord : TEXCOORD0;
};
sampler2D _MainTex;
float4 _MainTex_ST;
float _Outline;
float4 _OutlineColor;
v2f vert(appdata_t v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
    return o;
}
v2f vert_outline(appdata_t v)
{
    v2f o;
    // 方式一,觀察空間 下往法線偏移頂點
    float4 viewPos = mul(UNITY_MATRIX_MV, v.vertex);
    //float3 viewNorm = mul((float3x3)UNITY_MATRIX_IT_MV, v.normal);
    float3 viewNorm = mul(v.normal, (float3x3)UNITY_MATRIX_T_MV);
    float3 offset = normalize(viewNorm) * _Outline;
    viewPos.xyz += offset;
    o.vertex = mul(UNITY_MATRIX_P, viewPos);
    //方式二,世界空間 下往法線偏移頂點
    //float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
    //float3 worldNormal = UnityObjectToWorldNormal(v.normal);
    //float3 offset = normalize(worldNormal) * _Outline;
    //worldPos.xyz += offset;
    //o.vertex = mul(UNITY_MATRIX_VP, worldPos);
    return o;
}
ENDCG
SubShader{
    Tags{ "Queue" = "Transparent" "RenderType" = "Opaque" }
    Pass{
        ZTest LEqual
        Cull Back
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        fixed4 frag(v2f i) : SV_Target
        {
            fixed4 col = tex2D(_MainTex, i.texcoord);
            return col;
        }
        ENDCG
    }
    Pass{
        // ZTest Greater
        ZWrite Off
        Cull Front
        Offset 100,0
        CGPROGRAM
        #pragma vertex vert_outline
        #pragma fragment frag
        half4 frag(v2f i) : COLOR
        {
            return _OutlineColor;
        }
        ENDCG
    }
}
}
ps: 還有其他描邊處理的方式,比如 後處理 來自:https://blog.csdn.net/yangxuan0261/article/details/79686192