309 lines
13 KiB
GLSL
309 lines
13 KiB
GLSL
// 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
|
||
}
|
||
}
|
||
}
|
||
}
|