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;
}
这个片段着色器很简单,只在“最简单的着色器”上多加了一步,即将从定点着色器中传出的顶点颜色属性乘到了纹理采样得到的像素上。
看到这个代码,就很容易理解为什么是变暗,而不是变成灰色了
顶点的颜色数据是从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得到的结果)
这才是我想要的灰色!
(PS:感谢GYB提供的思路!)