http://www.tuicool.com/articles/Vruuqme
NGUI中的Button差點兒是最經常使用到的控件之中的一個,而且能夠組合各種組件(比方UIButtonColor,UIButtonOffset,UITweenxx),方便設置Button的各種狀態下的屬性。差點兒能夠滿足我們的全部需求。
可是對於當Button的isEnabled屬性設置為false時,依據設置的disableColor屬性設置不可點擊時的顏色時,盡管我們設置的灰色,但並非我們想象中的樣子!
設置的是灰色,實際執行結果卻還是彩色的,僅僅是暗了一點。並不可以非常好地表現出其“禁用”的狀態!
1.原理
Unity3d中全部的渲染都是基於Shader的,而Shader綁定在Material上。打開一個NGUI樣例中自帶的Material,得到其使用Shader的文件
NGUI中大部分材質都使用的Unlit/Transparent Colored(PS:盡管在Unlit下,但並非Unity3d內置的,而是NGUI擴展的)
找到其片段着色器,代碼例如以下:
- fixed4 frag (v2f i) : COLOR
- {
- fixed4 col = tex2D(_MainTex, i.texcoord) * i.color;
- return col;
- }
fixed4 frag (v2f i) : COLOR { fixed4 col = tex2D(_MainTex, i.texcoord) * i.color; return col; }
這個片段着色器非常easy,僅僅在“最簡單的着色器”上多加了一步,即將從定點着色器中傳出的頂點顏色屬性乘到了紋理採樣得到的像素上。
看到這個代碼。就非常easy理解為什么是變暗,而不是變成灰色了
頂點的顏色數據是從UISprite之類的面板中傳遞進來的,其最大值是白色(255,255,255,255),而這里是正交化的,最大值白色相應(1.0,1.0,1.0,1.0)。這也是默認值,當採樣得到的像素值x1.0,相當於採樣得到的紋理值;假設設置一個其它的顏色。正交化后肯定會小於1.0,當採樣得到的像素值乘以這個值后,像素值會比之前小,而最小值是(0,0。 0,0)即黑色,也就是說假設設置一個不是白色的顏色。就會使像素值更接近於黑色。這就是變暗的原因!
2.置灰
NGUI僅僅提供了這樣一種變暗的功能,用來表現其“禁用”的狀態,可是這並非最好的結果,假設須要介於黑白之間的灰色紋理,難道非要美術對每個可能會被置灰的紋理又一次制作一張紋理嗎?
這就更糟了。游戲中紋理是非常占空間的。這樣做相當於將UI資源翻了一倍!
還是從Shader方面入手吧。
想象一下。假設在着色器處理之前。傳遞一個bool值,當這個bool值為true時。正常繪制紋理;當這個bool值為false時。繪制灰色紋理。
(Unity3d的Shader中並不支持傳遞bool值,這里僅僅是舉個栗子)
這樣看似非常合理,也確實能夠實現,可是會有一個問題,這個bool值肯定要在頂點着色器階段傳過去,而NGUI提供的“紋理打包”功能(即非常多紋理合並成一個Atlas,即節省空間,還能夠有一些其它信息。比方九宮格拉伸的參數。。。),當這個bool值為false時,這個Atlas中所有的繪制即所有變為灰色,這是不符合邏輯的,當然能夠每張小圖單獨處理,即相當於損失掉NGUI的“紋理打包”功能
3.解決方式
損失一個顏色值吧,作為“約定”!
選取一個顏色值,作為約定為置灰的標記,當片段着色器檢測到這個顏色值之后,運行渲染灰色的shader!
這個顏色值能夠隨意選擇。我這里選取純黑色作為“約定顏色”,片段着色器代碼例如以下:
- fixed4 frag (v2f i) : COLOR
- {
- fixed4 col;
- if (i.color.r < 0.001)
- {
- col = tex2D(_MainTex, i.texcoord);
- float grey = dot(col.rgb, float3(0.299, 0.587, 0.114));
- col.rgb = float3(grey, grey, grey);
- }
- else
- {
- col = tex2D(_MainTex, i.texcoord) * i.color;
- }
- return col;
- }
fixed4 frag (v2f i) : COLOR { fixed4 col; if (i.color.r < 0.001) { col = tex2D(_MainTex, i.texcoord); float grey = dot(col.rgb, float3(0.299, 0.587, 0.114)); col.rgb = float3(grey, grey, grey); } else { col = tex2D(_MainTex, i.texcoord) * i.color; } return col; }
當中(0.299,0.587,0.114)為灰度公式的參數
我復制了一份NGUI樣例的紋理和材質,將此Shader設置到材質中,渲染效果如圖
(最上面兩個是原始狀態下的效果。中間兩個是NGUI提供的禁用狀態效果。最以下兩個各自是改動后Shader渲染同一個Atlas得到的結果)
這才是我想要的灰色!