URP 渲染实战 / 第 21 章 UV 动画与粒子 / 案例 21.1
案例 21.1UV 滚动:流动的熔岩入门
目标:只用一张灰度噪声图,做出缓慢流动、亮暗翻滚的熔岩河。
单层滚动就是 uv + speed × t:整张图匀速平移,像传送带。它的问题是“太刚体”——所有细节以同样的速度、同样的方向移动,眼睛一下就能看出是一张图在平移。改进分三步:
- 双层叠加:同一张噪声以不同的平铺、不同的速度和方向再滚一层,两层相乘。两层的相对运动让图案不断变化,再也找不到“那一张图”。相乘会让平均亮度下降(0.5 × 0.5 = 0.25),所以乘以 2 补回来。
- UV 扰动:用第二层的值去偏移第一层的 UV。原本直线运动的图案开始扭曲、汇聚、分叉,像有了流体的速度场。
- 渐变映射(Gradient Mapping):把灰度当作“温度”,查一张一维的颜色渐变(公共贴图
ramp_fire):暗处是冷却凝固的黑色岩壳,亮处是黄白色的高温熔岩,并乘上k²让高温处成为 HDR 发光。
Assets/Cookbook/Ch21/UVScroll.shader(节选:frag)
half4 frag(Varyings i) : SV_Target { // 时间:运行时用 _Time.y,配图时 _Time 不可控,用 _Phase 指定“第几秒” // frac 把偏移量限制在 0..1:贴图是 Repeat 寻址,结果不变,但避免长时间运行后浮点精度下降 float t = _Time.y + _Phase; float2 off1 = frac(_Layer1.zw * t); float2 off2 = frac(_Layer2.zw * t); // 第二层:既参与叠加,也用来扰动第一层的 UV half n2 = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, i.uv * _Layer2.xy + off2).r; float2 distort = (n2 - 0.5) * _Distort * _SecondLayer; half n1 = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, i.uv * _Layer1.xy + off1 + distort).r; half v = lerp(n1, n1 * n2 * 2.0, _SecondLayer); // 两层相乘再 ×2,保持平均亮度 v = saturate(v); half3 col = v.xxx; if (_UseRamp > 0.5) { // 渐变映射:灰度 → 颜色。暗处是冷却的黑色岩壳,亮处是高温的黄白色 half k = smoothstep(0.3, 1.0, v); col = SAMPLE_TEXTURE2D(_RampTex, sampler_RampTex, float2(k, 0.5)).rgb * (k * k * _Intensity); } col = MixFog(col, i.fog); return half4(col, 1); }
vec4 frag() { // 时间:运行时用 _Time.y,配图时 _Time 不可控,用 _Phase 指定“第几秒” // fract 把偏移量限制在 0..1:贴图是 Repeat 寻址,结果不变,但避免长时间运行后浮点精度下降 float t = _Time.y + _Phase; vec2 off1 = fract(_Layer1.zw * t); vec2 off2 = fract(_Layer2.zw * t); // 第二层:既参与叠加,也用来扰动第一层的 UV float n2 = texture(_NoiseTex, uv * _Layer2.xy + off2).r; vec2 distort = vec2((n2 - 0.5) * _Distort * _SecondLayer); float n1 = texture(_NoiseTex, uv * _Layer1.xy + off1 + distort).r; float v = mix(n1, n1 * n2 * 2.0, _SecondLayer); // 两层相乘再 ×2,保持平均亮度 v = saturate(v); vec3 col = vec3(v); if (_UseRamp > 0.5) { // 渐变映射:灰度 → 颜色。暗处是冷却的黑色岩壳,亮处是高温的黄白色 float k = smoothstep(0.3, 1.0, v); col = texture(_RampTex, vec2(k, 0.5)).rgb * (k * k * _Intensity); } col = MixFog(col, fog); return vec4(col, 1.0); }

调参建议两层的速度方向不要相同也不要正好相反,夹角 30°–90° 最自然;平铺比例避免整数倍(例如 1 与 1.7),否则两层图案会周期性地重合,出现“节拍”。渐变图的采样坐标先过一次
smoothstep 拉开对比,熔岩才会有清晰的“岩壳/裂缝”分界。坑:贴图的 Wrap ModeUV 滚动依赖 Repeat 寻址。如果噪声图被设成 Clamp,滚出 0..1 之后就是一片拉长的边缘像素。渐变图则相反,必须是 Clamp——否则采样坐标为 1 时会因为双线性过滤混进另一端的颜色,最亮处突然出现一圈暗色。本书公共贴图里
ramp_* 已设为 Clamp。