修正Shader文字描邊的跨字採樣錯誤

之前的擴展Text文章里,我就提到了UGUI內置描邊方案的性能問題,而最理想的解決方案就是用Shader的方式多次偏移採樣來實現描邊。但當初寫的Shader存在這樣的問題:

雖然是個概率事件,但總會有文字因為紋理貼圖太接近,採樣互相「串場」,導致文字邊緣出現黑邊。這個錯誤導致採樣偏移不能設太高,邊框必須很窄,使用範圍受限很大。直到今天 @胡宸碩 在評論里提醒我可以將文字範圍放到UV2里來限制採樣範圍。

但是,UV2隻有兩個數,而採樣範圍需要4個數(x,y,w,h),所以我就懷疑他的意思是不是讓我把(0,0),(0,1),(1,1),(1,0)放在文字的四個頂點上。實際做了後,發現這樣做雖然我能知道邊緣在哪裡,但是不知道到底在這個0-1區間內,描邊寬度的值是多少,數據還是不夠。

就只能回歸最開始的想法,將紋理範圍(x,y,w,h)放到文字的四個頂點上,這樣插值後的結果還是原數值,frag部分就能直接獲得了。

怎麼把4個浮點數放進只有2個數字的UV2里呢?

Pack:

Vector2 bottomLeft = m_TempVerts[0].uv0;Vector2 size = (m_TempVerts[2].uv0 - m_TempVerts[0].uv0) * (2 << 12);Vector2 packUVBounds = new Vector2(Mathf.Floor(size.x) + bottomLeft.x, Mathf.Floor(size.y) + bottomLeft.y);m_TempVerts[0].uv1 = packUVBounds;m_TempVerts[1].uv1 = packUVBounds;m_TempVerts[2].uv1 = packUVBounds;m_TempVerts[3].uv1 = packUVBounds;

將原本處於(0,1)範圍的w,h升至整數部分,和原來就是小數的xy相加,就合在一起了。

UnPack:

float2 xy = frac(v.texcoord2.xy);float2 wh = (v.texcoord2.xy - xy) / (2 << 12);

Shader部分再拆分整數和小數即可。

著色看看結果:

正常。

預先和邊框減一下:

half2 borderWidth = _BorderWidth / _MainTex_TexelSize.zw;OUT.clipRect = half4(xy + borderWidth,xy + wh - borderWidth);OUT.borderWidth = borderWidth;

再根據這個結果做過濾:

half4 border1 = tex2D(_MainTex, IN.texcoord + IN.borderWidth);half4 border2 = tex2D(_MainTex, IN.texcoord - IN.borderWidth);IN.borderWidth.x = -IN.borderWidth.x;half4 border3 = tex2D(_MainTex, IN.texcoord + IN.borderWidth);half4 border4 = tex2D(_MainTex, IN.texcoord - IN.borderWidth);half2 insideXY = step(IN.clipRect.xy,IN.texcoord.xy);half2 insideZW = step(IN.texcoord.xy,IN.clipRect.zw);border1 *= insideZW.x * insideZW.y;border2 *= insideXY.x * insideXY.y;border3 *= insideXY.x * insideZW.y;border4 *= insideZW.x * insideXY.y;half4 border = (saturate(border1 + border2 + border3 + border4) + _TextureSampleAdd) * _BorderColor;

最終效果如下:

不過,後來胡宸碩給了我回復,把原文發了過來我才發現,人家的這個文字範圍是直接通過tangent傳過來的。

……

……

……

……

然後我就改成那樣了。

這個東西需要Shader和C#的代碼做配合,我已經把他更新到之前的AdvancedText里去了。

pan.baidu.com/s/1gfw7mR

需要選擇Effect Type為MATERIAL,

然後把UI-DefaultBorder.shader創建的材質放到文本框的material里。

也可以用裡面的這個Text掛件代替AdvancedText實現這個功能。

對了,最後要記得把Canvas的AdditionShaderChannel打開,把Tangent數據引入。


推薦閱讀:

兩個平均年齡十一歲的小男孩,做了兩款遊戲
KBEngine遊戲伺服器(二)——運行Unity的Demo
基礎1——遊戲物體和腳本——造一個鍾
神谷英樹和他弟弟的遊戲回憶錄

TAG:Unity遊戲引擎 | 遊戲開發 |