1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
| Shader "Custom/URP/FireEffect" { Properties { // 火焰基础颜色(从底部到顶部的渐变) _ColorBottom ("Flame Color Bottom", Color) = (1.0, 0.3, 0.0, 1.0) _ColorMiddle ("Flame Color Middle", Color) = (1.0, 0.8, 0.1, 1.0) _ColorTop ("Flame Color Top", Color) = (0.8, 0.9, 1.0, 0.0) // FBM 参数 _NoiseScale ("Noise Scale", Range(1.0, 10.0)) = 3.0 _NoiseSpeed ("Noise Speed", Range(0.0, 5.0)) = 1.5 _NoiseStrength ("Distortion Strength", Range(0.0, 1.0)) = 0.3 // 火焰形状(底部宽,顶部收窄) _ShapeSharpness ("Shape Sharpness", Range(1.0, 8.0)) = 3.0 // 粒子软裁剪(配合 URP Depth Texture) _SoftParticleRange ("Soft Particle Range", Range(0.1, 5.0)) = 1.0 }
SubShader { Tags { "Queue" = "Transparent" "RenderType" = "Transparent" "RenderPipeline" = "UniversalPipeline" "IgnoreProjector" = "True" }
Blend SrcAlpha One // 加法混合(适合火焰发光) ZWrite Off Cull Off
Pass { Name "FireForward" Tags { "LightMode" = "UniversalForward" }
HLSLPROGRAM #pragma vertex vert #pragma fragment frag // 粒子软裁剪需要深度纹理 #pragma multi_compile _ SOFTPARTICLES_ON #pragma multi_compile_fog
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
CBUFFER_START(UnityPerMaterial) float4 _ColorBottom; float4 _ColorMiddle; float4 _ColorTop; float _NoiseScale; float _NoiseSpeed; float _NoiseStrength; float _ShapeSharpness; float _SoftParticleRange; CBUFFER_END
struct Attributes { float4 positionOS : POSITION; float2 uv : TEXCOORD0; float4 color : COLOR; // 粒子系统颜色/透明度 UNITY_VERTEX_INPUT_INSTANCE_ID };
struct Varyings { float4 positionHCS : SV_POSITION; float2 uv : TEXCOORD0; float4 color : COLOR; // 软粒子需要屏幕坐标 float4 screenPos : TEXCOORD1; UNITY_VERTEX_OUTPUT_STEREO };
// ======== 噪声函数 ========
float hash12(float2 p) { float3 p3 = frac(float3(p.xyx) * 0.1031); p3 += dot(p3, p3.yzx + 33.33); return frac((p3.x + p3.y) * p3.z); }
float valueNoise(float2 x) { float2 p = floor(x); float2 f = frac(x); f = f * f * (3.0 - 2.0 * f); float a = hash12(p + float2(0, 0)); float b = hash12(p + float2(1, 0)); float c = hash12(p + float2(0, 1)); float d = hash12(p + float2(1, 1)); return lerp(lerp(a, b, f.x), lerp(c, d, f.x), f.y); }
static const float2x2 fbmRot = float2x2(1.6, 1.2, -1.2, 1.6);
// 2D FBM(4 层,适合移动端性能) float fbm4(float2 p) { float v = 0.0, a = 0.5; [unroll] // 固定循环次数时展开 for (int i = 0; i < 4; i++) { v += a * valueNoise(p); p = mul(fbmRot, p); a *= 0.5; } return v; }
// ======== 颜色梯度(三色渐变) ======== float3 flameGradient(float t, float3 bot, float3 mid, float3 top) { // t: 0 = 底部,1 = 顶部 float3 lower = lerp(bot, mid, saturate(t * 2.0)); float3 upper = lerp(mid, top, saturate(t * 2.0 - 1.0)); return lerp(lower, upper, step(0.5, t)); }
Varyings vert(Attributes IN) { Varyings OUT; UNITY_SETUP_INSTANCE_ID(IN); UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT); OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz); OUT.uv = IN.uv; OUT.color = IN.color; // 计算屏幕坐标(软粒子使用) OUT.screenPos = ComputeScreenPos(OUT.positionHCS); return OUT; }
half4 frag(Varyings IN) : SV_Target { float2 uv = IN.uv; float time = _Time.y * _NoiseSpeed; // Unity 内置时间变量
// === 第一步:UV 扰动(FBM 域扭曲)=== // 使用两层 FBM 进行 UV 扰动,模拟火焰的湍流 float2 distortUV = uv * _NoiseScale + float2(0.0, -time); // 向上流动 float distortX = fbm4(distortUV + float2(1.7, 9.2)) - 0.5; float distortY = fbm4(distortUV + float2(8.3, 2.8)) - 0.5; float2 distortedUV = uv + float2(distortX, distortY) * _NoiseStrength;
// === 第二步:火焰形状遮罩 === // 从底部(宽)到顶部(窄)的形状,用 UV.y 控制宽度 float distFromCenter = abs(distortedUV.x - 0.5) * 2.0; // [0,1] float shapeWidth = 1.0 - pow(uv.y, 1.0 / _ShapeSharpness); float shapeMask = saturate(1.0 - distFromCenter / max(shapeWidth, 0.001));
// === 第三步:FBM 密度场 === // 主火焰密度(沿 Y 轴向上流动的 FBM) float density = fbm4(distortedUV * _NoiseScale + float2(0.0, -time * 1.2)); density = saturate(density * 2.0 - 0.3); // 增加对比度,消除低密度底噪
// === 第四步:顶部衰减 === // 越靠近顶部越透明(fire tip) float topFade = 1.0 - smoothstep(0.5, 1.0, uv.y); float finalAlpha = density * shapeMask * topFade;
// === 第五步:颜色计算 === // 用 uv.y 和密度混合三色梯度 float colorT = uv.y + (1.0 - density) * 0.3; float3 flameColor = flameGradient( colorT, _ColorBottom.rgb, _ColorMiddle.rgb, _ColorTop.rgb );
// === 第六步:软粒子(可选) === #if defined(SOFTPARTICLES_ON) // 比较粒子深度与场景深度,避免粒子切割地面 float sceneDepth = LinearEyeDepth( SampleSceneDepth(IN.screenPos.xy / IN.screenPos.w), _ZBufferParams ); float particleDepth = IN.screenPos.w; float softFactor = saturate((sceneDepth - particleDepth) / _SoftParticleRange); finalAlpha *= softFactor; #endif
// 粒子系统传入的颜色/透明度(用于粒子生命周期控制) finalAlpha *= IN.color.a; flameColor *= IN.color.rgb;
return half4(flameColor, finalAlpha); } ENDHLSL } } }
|