// UIMask:UI Mask 粒子/特效 shader —— 加色与普通混合共用一个 shader // // 混合模式在材质面板上是一个下拉框 "_BlendMode"(加色 Additive / 普通混合 AlphaBlend)。 // 它由 Assets/Shaders/Editor/UIMaskBlendMode.cs([UIMaskBlendMode] 绘制器)映射到真正的 Blend 状态: // 加色(Additive): _SrcBlend = 5 (SrcAlpha) _DstBlend = 1 (One) // 普通混合(AlphaBlend):_SrcBlend = 5 (SrcAlpha) _DstBlend = 10 (OneMinusSrcAlpha) // 渲染面同理:[UIMaskRenderFace] 下拉映射到隐藏属性 _Cull(双面=0,只渲染后面=1,只渲染前面=2)。 // _SrcBlend / _DstBlend / _Cull 是隐藏属性,由下拉框自动写入,不要手动改。 // Blend 是固定功能状态,不参与 shader 变体表,所以切换混合模式不产生额外变体。 // 面向美术:程序/技术类参数一律用 [HideInInspector] 隐藏(混合模式内部值、Stencil 组、 // 顶点 Mask 翻转),需要时由程序控制,材质面板只留艺术参数。 Shader "VFX/Yeomai_VFX" { Properties { [UIMaskBlendMode] _BlendMode ("混合模式", Float) = 0 [HideInInspector] _SrcBlend ("Src Blend", Float) = 5 [HideInInspector] _DstBlend ("Dst Blend", Float) = 1 [UIMaskRenderFace] _RenderFace ("渲染面", Float) = 2 [HideInInspector] _Cull ("Cull", Float) = 0 [HDR]_TintColor ("着色", Color) = (0.5,0.5,0.5,0.5) _GlowScale("发光强度", float) = 1 _AlphaScale("透明度", float) = 1 _MainTex ("主贴图", 2D) = "white" {} [Toggle] _UseUvAni("启用", Float) = 0 _SpeedU("U 速度", Float) = 0 _SpeedV("V 速度", Float) = 0 [Toggle] _UseUvDistortion("启用", Float) = 0 _UVNoiseTex("噪声贴图", 2D) = "black" {} _Distortion ("扰动量", Float) = 0 _NoiseScroll ("噪声滚动 (XY 速度 / ZW 强度)", Vector) = (0,0,1,1) [Toggle] _UseMask("启用", Float) = 0 [Toggle] _UseMaskUvAni("UV 滚动", Float) = 0 _Mask("遮罩贴图", 2D) = "white" {} [UIMaskChannel] _MaskChannel("通道", Float) = 3 _MaskSpeedU("U 速度", Float) = 0 _MaskSpeedV("V 速度", Float) = 0 [Toggle] _UseDissolve("启用", Float) = 0 _DissolveTex ("溶解贴图", 2D) = "white" {} [UIMaskChannel] _DissolveChannel("通道", Float) = 3 _Dissolve ("溶解进度", Range(0, 1.01)) = 0 _DissolveWidth ("溶解宽度", Range(0, 1)) = 0 [CustomDataChannel] _DissolveCustomChannel ("自定义数据通道", Float) = 0 // 默认黑色 = 不叠加任何颜色,保证新参数不影响原有溶解效果 [HDR]_DissolveEdgeColor ("溶解边缘颜色", Color) = (0, 0, 0, 1) _DissolveEdgeSharpness ("边缘锐度 (越大越细)", Range(0.001, 8)) = 2 // 第二层:贴边界的细亮圈(烧纸的燃烧边),同样默认黑色不影响原效果 [HDR]_DissolveHighlightColor ("溶解边缘高亮", Color) = (0, 0, 0, 1) _DissolveHighlightWidth ("高亮宽度 (越小越细)", Range(0.001, 0.5)) = 0.08 [Toggle] _UseUIMaskClip("启用", Float) = 0 _ClipRect("裁切矩形 (由 Mask / RectMask2D 控制)", Vector) = (-1, -1, 1, 1) [Toggle] _UseVertexMask("启用", Float) = 0 _VertexOffset ("方向与范围", Vector) = (0,0,1,1) // 程序专用:顶点 Mask 的翻转,由程序设置 [HideInInspector] _VertexFlipX ("程序专用", Float) = 1 // required for UI.Mask —— 这几个由 Unity 的 Mask / RectMask2D 在运行时写入,不需要美术看到 [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 } CustomEditor "UIMaskShaderGUI" Category { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" } Blend [_SrcBlend][_DstBlend] Cull [_Cull] Lighting Off ZWrite Off SubShader { Stencil { Ref[_Stencil] Comp[_StencilComp] Pass[_StencilOp] ReadMask[_StencilReadMask] WriteMask[_StencilWriteMask] } ColorMask[_ColorMask] Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma target 2.0 // 全部使用 shader_feature(按材质用到的组合裁剪),不要改成 multi_compile #pragma shader_feature _USEMASK_ON #pragma shader_feature _USEMASKUVANI_ON #pragma shader_feature _USEUVANI_ON #pragma shader_feature _USEUVDISTORTION_ON #pragma shader_feature _USEDISSOLVE_ON #pragma shader_feature _USEUIMASKCLIP_ON #pragma shader_feature _USEVERTEXMASK_ON // 不声明 multi_compile_fog:UNITY_FOG_* 宏在无雾关键字时展开为空, // 原来的"_UseReceiveFog"开关勾了也不会有任何效果,故已移除。 #include "UnityCG.cginc" #include "UnityUI.cginc" sampler2D _MainTex; fixed4 _TintColor; #if _USEMASK_ON sampler2D _Mask; fixed4 _Mask_ST; float _MaskChannel; #if _USEMASKUVANI_ON float _MaskSpeedU; float _MaskSpeedV; #endif #endif #if _USEUVANI_ON float _SpeedU; float _SpeedV; #endif #if _USEUVDISTORTION_ON sampler2D _UVNoiseTex; fixed4 _UVNoiseTex_ST; fixed _Distortion; fixed4 _NoiseScroll; #endif #if _USEDISSOLVE_ON sampler2D _DissolveTex; fixed4 _DissolveTex_ST; float _DissolveChannel; fixed _Dissolve; fixed _DissolveWidth; float _DissolveCustomChannel; fixed4 _DissolveEdgeColor; fixed _DissolveEdgeSharpness; fixed4 _DissolveHighlightColor; fixed _DissolveHighlightWidth; #endif // 通道选择:0=R 1=G 2=B 3=A。用点乘取通道,避免动态分量索引(SM2.0 不支持) inline float UIMaskSampleChannel(sampler2D tex, float2 uv, float channel) { float4 c = tex2D(tex, uv); float4 w = 1.0 - saturate(abs(channel - float4(0.0, 1.0, 2.0, 3.0))); return dot(c, w); } // 通用模块:按通道号取粒子自定义数据(供"自定义数据通道"下拉框使用) // channel: 0 = null(返回 0),1~4 = Custom1.xyzw,5~8 = Custom2.xyzw inline float CustomDataChannelValue(float4 custom1, float4 custom2, float channel) { float4 w1 = 1.0 - saturate(abs(channel - float4(1.0, 2.0, 3.0, 4.0))); float4 w2 = 1.0 - saturate(abs(channel - float4(5.0, 6.0, 7.0, 8.0))); return dot(custom1, w1) + dot(custom2, w2); } #if _USEUIMASKCLIP_ON fixed4 _ClipRect; #endif #if _USEVERTEXMASK_ON fixed4 _VertexOffset; fixed _VertexFlipX; #endif struct appdata_t { float4 vertex : POSITION; fixed4 color : COLOR; // 顶点流约定(渲染器 → 自定义顶点流,按此顺序):UV, Custom1.xyzw, Custom2.xyzw float4 texcoord : TEXCOORD0; // UV.xy + Custom1.xy float4 texcoord1 : TEXCOORD1; // Custom1.zw + Custom2.xy float4 texcoord2 : TEXCOORD2; // Custom2.zw UNITY_VERTEX_INPUT_INSTANCE_ID }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; fixed4 texcoord : TEXCOORD0; float4 customData : TEXCOORD1; // Custom1.xyzw float4 customData2 : TEXCOORD6; // Custom2.xyzw #if _USEMASK_ON fixed2 texcoordMask : TEXCOORD2; #endif #if _USEUVDISTORTION_ON fixed2 texcoordNoise : TEXCOORD3; #endif #if _USEDISSOLVE_ON fixed2 texcoordDissolve: TEXCOORD4; #endif #if _USEUIMASKCLIP_ON fixed4 worldPosition : TEXCOORD5; #endif #if _USEVERTEXMASK_ON fixed4 vertex2 : TEXCOORD7; #endif UNITY_VERTEX_OUTPUT_STEREO }; fixed4 _MainTex_ST; fixed _GlowScale; fixed _AlphaScale; v2f vert (appdata_t v) { v2f o; UNITY_SETUP_INSTANCE_ID(v); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); o.vertex = UnityObjectToClipPos(v.vertex); o.color = v.color * _TintColor; o.texcoord.xy = TRANSFORM_TEX(v.texcoord,_MainTex); o.texcoord.zw = v.texcoord.zw; // 把打包在各 texcoord 里的粒子自定义数据还原成 Custom1.xyzw / Custom2.xyzw o.customData = float4(v.texcoord.z, v.texcoord.w, v.texcoord1.x, v.texcoord1.y); o.customData2 = float4(v.texcoord1.z, v.texcoord1.w, v.texcoord2.x, v.texcoord2.y); #if _USEMASK_ON o.texcoordMask = TRANSFORM_TEX(v.texcoord,_Mask); #endif #if _USEUVDISTORTION_ON o.texcoordNoise = TRANSFORM_TEX(v.texcoord,_UVNoiseTex); #endif #if _USEDISSOLVE_ON o.texcoordDissolve = TRANSFORM_TEX(v.texcoord,_DissolveTex); #endif #if _USEUIMASKCLIP_ON o.worldPosition = mul(unity_ObjectToWorld, v.vertex); #endif #if _USEVERTEXMASK_ON o.vertex2 = v.vertex; #endif return o; } fixed4 frag (v2f i) : SV_Target { #if _USEUVANI_ON // 用 _Time.y(float)而不是 _Time.g(fixed):运行久了 fixed 精度不足会出现像素块/卡顿 i.texcoord.xy += _Time.y * float2(_SpeedU, _SpeedV); #endif #if _USEUVDISTORTION_ON float2 noiseScrollXY = _NoiseScroll.xy; i.texcoordNoise += fixed2(_Time.g * noiseScrollXY); i.texcoord.xy += tex2D(_UVNoiseTex, i.texcoordNoise).xy*_Distortion*_NoiseScroll.zw; #endif fixed4 col = 2.0f * i.color * tex2D(_MainTex, i.texcoord.xy); col.rgb *= _GlowScale; col.a = saturate(col.a) * _AlphaScale; #if _USEMASK_ON #if _USEMASKUVANI_ON i.texcoordMask += _Time.y * float2(_MaskSpeedU, _MaskSpeedV); #endif col.a = saturate(col.a * UIMaskSampleChannel(_Mask, i.texcoordMask, _MaskChannel)); #endif #if _USEDISSOLVE_ON // 溶解进度 = 滑条 × 选中的自定义数据通道;通道 = null 时完全用滑条(保持原行为) fixed customProgress = CustomDataChannelValue(i.customData, i.customData2, _DissolveCustomChannel); fixed useCustom = step(0.5, _DissolveCustomChannel); fixed dissolveProgress = _Dissolve * lerp(1.0, customProgress, useCustom); fixed DissolveWithParticle =(dissolveProgress*(1+_DissolveWidth)-_DissolveWidth) ; fixed dissolveMap = UIMaskSampleChannel(_DissolveTex, i.texcoordDissolve, _DissolveChannel); fixed dissolveAlpha = saturate(smoothstep( DissolveWithParticle, (DissolveWithParticle+_DissolveWidth), dissolveMap)); col.a *= dissolveAlpha; // 溶解边缘色:越靠近溶解边界越亮。已溶解区域 alpha≈0 不可见,所以视觉上就是沿着溶解轮廓的一圈颜色。 // 做火焰时把颜色设成 HDR 橙红(用 HDR 拾色器的强度调亮),边缘就会像在燃烧。 fixed dissolveEdge = pow(1.0 - dissolveAlpha, _DissolveEdgeSharpness); col.rgb += _DissolveEdgeColor.rgb * dissolveEdge; // 细亮圈:只在溶解边界(dissolveAlpha ≈ 0.5)附近加亮,越靠近边界越亮, // 宽度由 _DissolveHighlightWidth 控制;颜色用 HDR 拉高就是"烧得发白"的亮边。 fixed highlightBand = saturate(1.0 - abs(dissolveAlpha - 0.5) / max(_DissolveHighlightWidth, 0.0001)); col.rgb += _DissolveHighlightColor.rgb * smoothstep(0.0, 1.0, highlightBand); #endif #if _USEUIMASKCLIP_ON col.a *= UnityGet2DClipping(i.worldPosition.xy, _ClipRect); #endif #if _USEVERTEXMASK_ON col.a *= smoothstep(1, 0, ((i.vertex2.x - _VertexOffset.x) * _VertexOffset.z + (i.vertex2.y - _VertexOffset.y) * _VertexOffset.w) * _VertexFlipX); #endif return col; } ENDCG } } } }