Shader中ColorMask与ScrollView Mask冲突致材质无法更新的解决方法咨询
解决Unlit/AlphaCutoffAA_UI Shader中_ColorMask导致的材质更新异常问题
我之前碰到过完全类似的UI Shader与ScrollRect遮罩兼容的坑,核心问题出在**_ColorMask作为渲染状态参数在SubShader层级直接引用时,Unity UI系统动态修改它后,材质没有正确触发重渲染**——尤其是被ScrollRect遮罩的区域,因为遮罩本质是通过临时把_ColorMask设为0来实现像素裁剪的,参数变化没有同步到渲染管线。
下面是具体的修复方案,分两步走:
1. 调整Shader结构,适配Unity UI的参数更新机制
我们需要引入Unity UI的内置库,使用标准的UI顶点输入结构,同时把ColorMask的控制逻辑从SubShader移到Pass内部,确保参数变化能被正确捕获并生效:
修改后的完整Shader代码:
Shader "Unlit/AlphaCutoffAA_UI" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} _Color("Color Multiplier", Color) = (1, 1, 1, 1) _Cutoff ("Cutoff", Range(0,1)) = 0.5 _Blend ("Blend Range", Range(0,.2)) = 0.1 [HideInInspector]_StencilComp("Stencil Comparison", Float) = 8 [HideInInspector]_Stencil("Stencil ID", Float) = 0 [HideInInspector]_StencilOp("Stencil Operation", Float) = 0 [HideInInspector]_StencilWriteMask("Stencil Write Mask", Float) = 255 [HideInInspector]_StencilReadMask("Stencil Read Mask", Float) = 255 [HideInInspector]_ColorMask("Color Mask", Float) = 15 [HideInInspector]_UseUIAlphaClip("Use Alpha Clip", Float) = 0 } SubShader { Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="TransparentCutout" "PreviewType"="Plane" "CanUseSpriteAtlas"="True"} LOD 100 Lighting Off ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Stencil { Ref[_Stencil] Comp[_StencilComp] Pass[_StencilOp] ReadMask[_StencilReadMask] WriteMask[_StencilWriteMask] } Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 #include "UnityCG.cginc" #include "UnityUI.cginc" // 引入UI内置工具库 struct appdata_t { float4 vertex : POSITION; float2 texcoord : TEXCOORD0; float4 color : COLOR; // 增加UI顶点颜色输入,符合Unity UI渲染逻辑 }; struct v2f { float4 vertex : SV_POSITION; half2 texcoord : TEXCOORD0; fixed4 color : TEXCOORD1; float4 worldPosition : TEXCOORD2; }; sampler2D _MainTex; float4 _MainTex_ST; fixed _Cutoff; fixed _Blend; float4 _Color; fixed4 _TextureSampleAdd; float _ClipRect[4]; float _ColorMask; // 在Pass内声明参数,确保能实时获取最新值 v2f vert (appdata_t v) { v2f o; o.worldPosition = v.vertex; o.vertex = UnityObjectToClipPos(o.worldPosition); o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex); o.color = v.color * _Color; // 合并UI顶点颜色与自定义Color参数 // 处理UI矩形裁剪逻辑 #ifdef UNITY_UI_CLIP_RECT o.worldPosition.xy -= _ClipRect.xy; o.worldPosition.xy /= _ClipRect.zw; #endif return o; } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.texcoord) * i.color; col.rgb += _TextureSampleAdd.rgb; // 保留你的Alpha Cutoff AA逻辑 if(col.a < _Cutoff) { col.a = 0; } else if (col.a < _Cutoff + _Blend) { col.a = saturate((col.a - _Cutoff) / _Blend); } else { col.a = 1.0; } // 应用UI裁剪 #ifdef UNITY_UI_CLIP_RECT col.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect); #endif // 用Fragment逻辑替代静态ColorMask渲染状态,确保参数变化即时生效 if (_ColorMask == 0) { discard; // 完全裁剪像素 } else { // 根据_ColorMask的值屏蔽对应通道 col.r *= (_ColorMask & 1) ? 1 : 0; col.g *= (_ColorMask & 2) ? 1 : 0; col.b *= (_ColorMask & 4) ? 1 : 0; col.a *= (_ColorMask & 8) ? 1 : 0; } return col; } ENDCG } } FallBack "UnityUI/Default" }
2. 关键修改点说明
- 引入UnityUI.cginc:这个库包含了UI系统必备的裁剪、顶点颜色处理宏,让Shader完美适配Unity的UI组件(包括ScrollRect的遮罩逻辑)。
- 将ColorMask逻辑移到Fragment Shader:原来在SubShader层级的
ColorMask[_ColorMask]是静态渲染状态,参数变化时可能不会触发材质重绘。现在在Fragment中通过动态判断_ColorMask的值来控制像素输出,确保参数修改能即时反映到渲染结果。 - 添加UI裁剪处理:通过
UNITY_UI_CLIP_RECT宏处理ScrollRect的矩形裁剪,与Stencil遮罩逻辑互补,同时兼容原有功能。 - 使用标准UI顶点结构:加入顶点颜色输入,合并自定义_Color和UI的顶点颜色,完全贴合Unity UI的渲染流程。
额外验证步骤
- 替换Shader后,给UI Image绑定该材质,调整_Color、_Cutoff等参数,观察被ScrollRect遮罩的区域是否能正常更新。
- 如果仍有偶发的更新延迟,可以在代码中修改材质参数后,调用
material.SetDirty()强制标记材质为脏,触发立即重渲染。
内容的提问来源于stack exchange,提问作者Draco18s no longer trusts SE
相关产品推荐
相关产品推荐

