【Unity】【UI Shader】关于用Shader实现字体的描边和阴影

前言

描边和阴影,Unity本来是由自带的组件的(Outline和Shadow)。Unity自己的实现方式如下:

Outline:把原文字/图片以往的网格复制4份,然后上下左右各偏移一点距离(相当于多绘制了4遍)。

Shadow:把原文字/图片的网格复制1份,然后往某个方向偏移一点(相当于多绘制了1遍)。

我觉得是挺蛋疼的,所以就突发奇想干脆用Shader来实现会不会好一点。

 

正文:

由于本人水平有限,所以大部分代码都是参考网上的,代码放最后了。

代码里实现的效果如下所示:

其实发现本来想写描边的,结果搞成阴影了。不过问题不大,可以来分析一下:

这个Shader的思路就是写2个Pass,第一个Pass把原输入的网格扩大一点(注意调整扩大后的偏移),然后把他的颜色调整为黑色。然后再写1个Pass正常绘制,叠加在黑色的文字上面。但是这种实现方法有局限性:在设置阴影与原文字差别不大的时候可以 (_OutlineWidth的值大概是 0.01左右),一旦把阴影(或者说描边)的宽度扩大就会出现问题:

可以看到,这个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
                }
        }

}

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值