Files
Vision/Assets/Shaders/VFX.shader
T
2026-10-12 10:38:19 +08:00

284 lines
12 KiB
GLSL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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
// 默认黑色 = 不叠加任何颜色,保证新参数不影响原有溶解效果
[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;
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);
}
#if _USEUIMASKCLIP_ON
fixed4 _ClipRect;
#endif
#if _USEVERTEXMASK_ON
fixed4 _VertexOffset;
fixed _VertexFlipX;
#endif
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
fixed4 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
fixed4 texcoord : TEXCOORD0;
#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;
#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
fixed DissolveWithParticle =(_Dissolve*(1-i.texcoord.z)*(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
}
}
}
}