在UI设计时通常会遇到一种情况,就是需要对未解锁的功能在Icon上呈现一个置灰的效果。这时如果不是有特殊的要求的话,就可以通过Shader来进行进行去色处理。
置灰原理
其实比较简单,就是利用rgb来计算灰度值。研究表明,人眼对绿色的敏感度最高,对红色的敏感度次之,对蓝色的敏感度最低。使用不同的权重将得到比较合理的灰度图像。实验和理论推导出来的结果是(0.299, 0.587, 0.114)。也就是说:
1 | FinalColor = dot(OriginColor , (0.299, 0.587, 0.114)) |
用ShaderGraph连一个置灰的Shader
创建一个Texture2D变量:MainTex。如果你想把它用在UIImage或者2DObject的SpriteRenderer上,就要把这个变量的reference写为”_MainTex”,才能识别到组件上的Sprite。之后就是将Texture2D连上Sample Texture2D得到每个像素点上的颜色。有了颜色再根据之前的公式计算出灰度值。为了控制置灰程度将灰度与原本颜色值通过一个float变量进行lerp,再将lerp后的结果输出即可。连完如下:
我这里还加了一个置黑的lerp。
但是直接使用会发现如果将带着这个Shader材质的Image放在带Mask组件的物体下,会报如下警告,且Mask没有效果。
Material SG_UI_Gray_Mat doesn’t have _Stencil property
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
很显然是mask组件没找到Shader里对应的属性,没法实现相应的功能。
通过改造Unity自带的Shader实现置灰
mask无法使用很显然是Shader里缺了功能。由于置灰本身逻辑很简单,于是我们可以通过改造Unity原来自带的Shader来实现置灰,还能保证mask的功能。
1.下载内置Shader源码。
由于即使是URP管线下,UI默认用的材质还是BuiltIn的,所以我们要到Unity官方下载 找对应版本,点最后的ReleaseNote。再在打开的网页里找BuiltinShaders下载BuiltInShader的代码。
在其中找到DefaultResourcesExtra\UI\UI-Default.shader就是我们要改的Shader了。
2.修改Shader代码。
- Step1. 避免冲突修改ShaderName。
1
2//1.避免冲突修改ShaderName
Shader "UI/DefaultGray" - Step2. 增加灰度比例变量
1
_GrayRatio("GrayRatio", range(0 , 1)) = 1
- Step3. 找到片元着色器里计算颜色的地方计算灰度值,将灰度结果同原颜色值进行lerp,再赋值回原颜色变量。最后效果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//增加的变量要再定义一次
float _GrayRatio;
fixed4 frag(v2f IN) : SV_Target
{
fixed4 c = tex2D(_MainTex, IN.texcoord) * IN.color;
//计算灰度值,将灰度结果同原颜色值进行lerp
float gray = dot(c.xyz, float3(0.299,0.518,0.184));
c.xyz = lerp(c.xyz , gray,_GrayRatio);
//---Add--
half4 color = c + IN.color * _TextureSampleAdd;
#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color.a *= m.x * m.y;
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
写在最后
这个效果我放在了我的开源仓库里Art&EffectCollection_Unity,URP和Gray_Shader分支下。
P.S.
- Gray & Grey:Gray是美式拼写,Grey是英式拼写。但是在指姓氏时Grey不能写作Gray,专有名词里也不能互相替换。