Files
Vision/Assets/Shaders/VFX.shader
T
2026-10-12 14:26:05 +08:00

326 lines
15 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
[HideInInspector] _ZWrite ("ZWrite", Float) = 0
[HDR]_TintColor ("着色", Color) = (0.5,0.5,0.5,0.5)
_GlowScale("发光强度", float) = 1
_AlphaScale("透明度", float) = 1
_SoftParticleFade ("软粒子过渡", Range(0, 5)) = 0
_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 [_ZWrite]
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;
// 软粒子:拿相机深度图和粒子自身深度比较,贴近其它几何体时淡出(距离=0 时完全不生效)
UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
float _SoftParticleFade;
#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 || _USEUVDISTORTION_ON
// xy = 遮罩 UV,zw = 噪声 UV(合占一个插值器,给软粒子腾位置)
fixed4 uvPack : TEXCOORD2;
#endif
#if _USEDISSOLVE_ON
fixed2 texcoordDissolve: TEXCOORD4;
#endif
#if _USEUIMASKCLIP_ON
fixed2 worldPosition : TEXCOORD5;
#endif
// 软粒子:xy/w = 屏幕 UV,w = 视空间深度
float4 screenPos : TEXCOORD3;
#if _USEVERTEXMASK_ON
fixed2 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;
o.screenPos = ComputeScreenPos(o.vertex);
// 把打包在各 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.uvPack.xy = TRANSFORM_TEX(v.texcoord,_Mask);
#endif
#if _USEUVDISTORTION_ON
o.uvPack.zw = 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).xy;
#endif
#if _USEVERTEXMASK_ON
o.vertex2 = v.vertex.xy;
#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.uvPack.zw += fixed2(_Time.g * noiseScrollXY);
i.texcoord.xy += tex2D(_UVNoiseTex, i.uvPack.zw).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.uvPack.xy += _Time.y * float2(_MaskSpeedU, _MaskSpeedV);
#endif
col.a = saturate(col.a * UIMaskSampleChannel(_Mask, i.uvPack.xy, _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
// 软粒子:与相机深度图比较,贴近其它几何体(如地面)时按深度差淡出,消除生硬交线。
// _SoftParticleFade = 0(默认)时被 step 门控掉,完全不生效,也不产生分支/变体。
// 注意:正交相机下 screenPos.w != 视空间深度,此效果不适用。
{
float2 screenUV = i.screenPos.xy / i.screenPos.w;
float sceneEyeZ = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, screenUV));
float partEyeZ = i.screenPos.w;
float softFade = saturate((sceneEyeZ - partEyeZ) / max(_SoftParticleFade, 0.0001));
col.a *= lerp(1.0, softFade, step(0.0001, _SoftParticleFade));
}
return col;
}
ENDCG
}
}
}
}