前言
描邊和陰影,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
}
}
}