1. 程式人生 > >模型勾邊outline

模型勾邊outline

簡述
在卡通渲染中一般會涉及到模型勾邊。效率最高的方式是在shader中去做。render to texture的實現方式這裡不討論。

Shader勾邊實現流程大致為:對模型進行2遍(2個pass)繪製,第一遍(勾邊pass)在vertex shader中對模型沿頂點法線方向放大,fragment shader設定輸出顏色為勾邊顏色;第二遍正常繪製模型,除被放大的部分外,其餘被覆蓋,這樣就有了勾邊的效果。

實現
SubShader 
{
    Tags { "RenderType"="Opaque"}

    pass
    { 
        ZWrite Off

        CGPROGRAM
           #include "UnityCG.cginc" 

        struct v2f_outline {
            float4 pos : SV_POSITION;
        };          

        v2f_outline vert_outline(appdata_full v) {
            // vertex data scaled according to normal direction
            v2f_outline o;
            v.vertex.xyz += v.normal*0.01;

            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);        
            return o;
        }
        half4 frag_outline( v2f_outline i) :COLOR 
        {
            return half4(0, 1, 0, 1);
        }

        #pragma vertex vert_outline
        #pragma fragment frag_outline
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }

    Pass 
    {
        CGPROGRAM

        v2f_full vert (appdata_full v) 
        {
            v2f_full o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);           

            return o; 
        }

        fixed4 frag (v2f_full i) : COLOR0 
        {                       
            fixed4 tex = tex2D (_MainTex, i.uv.xy);         
            return tex;     
        }   

        #pragma vertex vert
        #pragma fragment frag
        #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }
}


其中需要注意的是勾邊pass的ZWrite設為關閉,保證接下來的正常繪製不會因為深度檢測被剔除。 


改進
1.如何保證勾邊在不同的攝像機距離下大小一致?

之前實現的放大繪製是在模型空間下做的,這使得勾邊跟模型一樣存在近大遠小的問題,遠處模型的勾邊會比近處模型的勾邊細,所以我們需要對勾邊根據離攝像機的遠近進行一個縮放。

v2f_outline vert_outline(appdata_full v) {
    // vertex data scaled according to normal direction
    v2f_outline o;
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    float3 norm   = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
    float3 offset = TransformViewToProjection(normalize(norm.xyz));     
    o.pos.xy += normalize(offset) * 0.01 * o.pos.w;         

    return o;
}

模型放大放到裁剪空間去做,o.pos存錯的是裁剪空間的頂點資訊,其中o.pos.w儲存了頂點與攝像機的距離,可做為一個模型放大係數。 


2.如何解決在與其他模型重疊的情況下出現不合理的勾邊?


某個攝像機距離下重疊的邊界出現勾邊,移動攝像機勾邊交替閃爍,時有時無,這是z-fighting現象,解決的辦法是設定勾邊pass的Offset語句。

pass
{ 
    Offset 3, 0
    ZWrite Off
    ...


3.如何在被遮擋的情況下顯示勾邊?
物體被遮擋,仍然需要顯示,需要設定勾邊pass的語句ZTest為Always,深度檢測一直通過,這樣勾邊pass的片段不會被深度剔除。

pass
{ 
    Offset 3, 0
    ZWrite Off
    ZTest Always
    ...



在這個基礎上如果只需顯示被遮擋的勾邊,就需要利用Stencil Buffer。繪製順序需要反過來:先執行正常繪製,寫入stencil值,然後執行勾邊pass,對stencil的值做比較,如果相等,則片段被stencil剔除,這樣,除了放大的部分外,其餘部分都被剔除了。

SubShader 
{
    Tags { "RenderType"="Opaque"}

    Pass 
    {           
        Stencil {
            Ref 2
            Comp always
            Pass replace
            ZFail replace
        }
        CGPROGRAM

        v2f_full vert (appdata_full v) 
        {
            v2f_full o;
            o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
            o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);           

            return o; 
        }

        fixed4 frag (v2f_full i) : COLOR0 
        {                       
            fixed4 tex = tex2D (_MainTex, i.uv.xy);         
            return tex;     
        }   

        #pragma vertex vert
        #pragma fragment frag
        #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }

    pass
    { 
         Stencil {
             Ref 2
             Comp NotEqual
         }

        Offset 3, 0
        ZWrite Off
        ZTest Always

        CGPROGRAM
        #include "UnityCG.cginc"    

        struct v2f_outline {
            float4 pos : SV_POSITION;
        };          

        v2f_outline vert_outline(appdata_full v) {
            // vertex data scaled according to normal direction
            v2f_outline o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            float3 norm   = mul ((float3x3)UNITY_MATRIX_IT_MV, v.normal);
            float3 offset = TransformViewToProjection(normalize(norm.xyz));     
            o.pos.xy += normalize(offset) * 0.01 * o.pos.w;         

            return o;
        }

        half4 frag_outline( v2f_outline i) :COLOR 
        {
            return half4(0, 1, 0, 1);
        }

        #pragma vertex vert_outline
        #pragma fragment frag_outline
        #pragma fragmentoption ARB_precision_hint_fastest 

        ENDCG
    }
}


--------------------- 
作者:bill2ccssddnn 
來源:CSDN 
原文:https://blog.csdn.net/bill2ccssddnn/article/details/52148647 
版權宣告:本文為博主原創文章,轉載請附上博文連結!