天天看點

【Unity】【UI Shader】關于用Shader實作字型的描邊和陰影前言正文:結論:代碼:

前言

描邊和陰影,Unity本來是由自帶的元件的(Outline和Shadow)。Unity自己的實作方式如下:

Outline:把原文字/圖檔以往的網格複制4份,然後上下左右各偏移一點距離(相當于多繪制了4遍)。

Shadow:把原文字/圖檔的網格複制1份,然後往某個方向偏移一點(相當于多繪制了1遍)。

我覺得是挺蛋疼的,是以就突發奇想幹脆用Shader來實作會不會好一點。

正文:

由于本人水準有限,是以大部分代碼都是參考網上的,代碼放最後了。

代碼裡實作的效果如下所示:

【Unity】【UI Shader】關于用Shader實作字型的描邊和陰影前言正文:結論:代碼:

其實發現本來想寫描邊的,結果搞成陰影了。不過問題不大,可以來分析一下:

這個Shader的思路就是寫2個Pass,第一個Pass把原輸入的網格擴大一點(注意調整擴大後的偏移),然後把他的顔色調整為黑色。然後再寫1個Pass正常繪制,疊加在黑色的文字上面。但是這種實作方法有局限性:在設定陰影與原文字差别不大的時候可以 (_OutlineWidth的值大概是 0.01左右),一旦把陰影(或者說描邊)的寬度擴大就會出現問題:

【Unity】【UI Shader】關于用Shader實作字型的描邊和陰影前言正文:結論:代碼:

可以看到,這個Shader會導緻文字的整體放大(而且還有網格漂移的問題),最後沒法和原有文本很好地疊加在一起。我覺得是不行的,這種和我預想的描邊/陰影效果差别挺大的。如果繼續按照這個思路來搞,按實作秒表就再加4個Pass,前後左右各偏移一丢丢就可以了。Pass裡面的内容倒是都大同小異。

結論:

我這個方法能做個參考吧,但是我覺得這個Shader不行,沒有達到我想要的效果。

照這個思路的話,無論是描邊還是陰影都隻能描一條很細的邊。目前我實作的效果更像是陰影,如果要寫描邊其實也比較容易,最笨的就是再寫4個Pass。思路和Unity原有的是一樣的,都是上下左右各偏移一點點。但是如果這麼搞的話,那描邊+陰影+原本的文字繪制就有6個Pass了。你說和原有的Unity方法性能相差幾何?我覺得其實優勢不大了……

而且這個思路最後做完,其實效果和Unity自帶的元件效果是差不多的,或者說沒什麼大的差別。

我覺得是無用功了~

不過是不是有能用1個Pass就把描邊+陰影效果都實作,而且還能解決描邊不重合的方法呢?我覺得是有的,不過我目前沒找到什麼好的思路。

代碼:

// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "UI/UI_ShadowOutline"
{
    Properties
    {
        [PerRendererData] _MainTex("Sprite Texture", 2D) = "white" {}
        _Color("Tint", Color) = (1,1,1,1)

        _StencilComp("Stencil Comparison", Float) = 8
        _Stencil("Stencil ID", Float) = 0
        _StencilOp("Stencil Operation", Float) = 0
        _StencilWriteMask("Stencil Write Mask", Float) = 255
        _StencilReadMask("Stencil Read Mask", Float) = 255

        _ColorMask("Color Mask", Float) = 15
        _OutlineWidth("描邊寬度",range(0,1)) = 1
        _Offset("描邊偏移",Vector) = (200,0,100,0)

        [Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip("Use Alpha Clip", Float) = 0
    }

        SubShader
        {
            Tags
            {
                "Queue" = "Transparent"
                "IgnoreProjector" = "True"
                "RenderType" = "Transparent"
                "PreviewType" = "Plane"
                "CanUseSpriteAtlas" = "True"
            }

            Stencil
            {
                Ref[_Stencil]
                Comp[_StencilComp]
                Pass[_StencilOp]
                ReadMask[_StencilReadMask]
                WriteMask[_StencilWriteMask]
            }

            Cull Off
            Lighting Off
            ZWrite Off
            ZTest[unity_GUIZTestMode]
            Blend SrcAlpha OneMinusSrcAlpha
            ColorMask[_ColorMask]

            Pass
            {
                Name "Default"
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma target 2.0

                #include "UnityCG.cginc"
                #include "UnityUI.cginc"

                #pragma multi_compile __ UNITY_UI_CLIP_RECT
                #pragma multi_compile __ UNITY_UI_ALPHACLIP

                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord  : TEXCOORD0;
                    float4 worldPosition : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };

                sampler2D _MainTex;
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
                float4 _MainTex_ST;
                float _OutlineWidth;
                float4 _Offset;

                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    OUT.color = v.color * _Color;
                    OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

                    //描邊;
                    _OutlineWidth += 1;
                    float3 targetPosition = OUT.worldPosition * _OutlineWidth;
                    targetPosition.z = v.vertex.z;
                    targetPosition.x -= (_OutlineWidth - 1) * _Offset.x + _Offset.y;
                    targetPosition.y -= (_OutlineWidth - 1) * _Offset.z + _Offset.w;
                    OUT.vertex = UnityObjectToClipPos(targetPosition);
                    //OUT.vertex.xy *= _OutlineWidth;

                    return OUT;
                }

                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 targetColor = half4(0,0,0,1);
                    half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * targetColor;

                    #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                    #endif

                    #ifdef UNITY_UI_ALPHACLIP
                    clip(color.a - 0.001);
                    #endif

                    return color;
                }
                    ENDCG
                }

                Pass
                {
                Name "Default"
            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma target 2.0

                #include "UnityCG.cginc"
                #include "UnityUI.cginc"

                #pragma multi_compile __ UNITY_UI_CLIP_RECT
                #pragma multi_compile __ UNITY_UI_ALPHACLIP

                struct appdata_t
                {
                    float4 vertex   : POSITION;
                    float4 color    : COLOR;
                    float2 texcoord : TEXCOORD0;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct v2f
                {
                    float4 vertex   : SV_POSITION;
                    fixed4 color : COLOR;
                    float2 texcoord  : TEXCOORD0;
                    float4 worldPosition : TEXCOORD1;
                    UNITY_VERTEX_OUTPUT_STEREO
                };

                sampler2D _MainTex;
                fixed4 _Color;
                fixed4 _TextureSampleAdd;
                float4 _ClipRect;
                float4 _MainTex_ST;
                float _OutlineWidth;

                v2f vert(appdata_t v)
                {
                    v2f OUT;
                    UNITY_SETUP_INSTANCE_ID(v);
                    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                    OUT.worldPosition = v.vertex;
                    OUT.color = v.color * _Color;
                    OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
                    OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);

                    return OUT;
                }

                fixed4 frag(v2f IN) : SV_Target
                {
                    half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;

                    #ifdef UNITY_UI_CLIP_RECT
                    color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
                    #endif

                    #ifdef UNITY_UI_ALPHACLIP
                    clip(color.a - 0.001);
                    #endif

                    return color;
                }
            ENDCG
                }
        }

}
           

繼續閱讀