1. 程式人生 > >Unity3D開發(二):NGUI之UIButton"禁用"狀態時置灰

Unity3D開發(二):NGUI之UIButton"禁用"狀態時置灰

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;
}
這個片段著色器很簡單,只在“最簡單的著色器”上多加了一步,即將從定點著色器中傳出的頂點顏色屬性乘到了紋理取樣得到的畫素上。

看到這個程式碼,就很容易理解為什麼是變暗,而不是變成灰色了

頂點的顏色資料是從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;
}
其中(0.299,0.587,0.114)為灰度公式的引數

我複製了一份NGUI例子的紋理和材質,將此Shader設定到材質中,渲染效果如圖


(最上面兩個是原始狀態下的效果,中間兩個是NGUI提供的禁用狀態效果,最下面兩個分別是修改後Shader渲染同一個Atlas得到的結果)

這才是我想要的灰色!

(PS:感謝GYB提供的思路!)