GameTech/URP 渲染实战
源码 / shaders/Ch04/C04_08_DomainWarp.glsl
shaders/Ch04/C04_08_DomainWarp.glsl
// 案例 4.8:域扭曲(Domain Warping)—— 用噪声去扭曲另一个噪声的输入坐标。(GLSL 版)
Shader "Cookbook/Ch04/DomainWarp"
{
    SubShader
    {
        Tags { "RenderType"="Opaque" "Queue"="Geometry" }

        Pass
        {
            Name "Unlit"
            Tags { "LightMode"="UniversalForward" }

            GLSLPROGRAM
            #include "Ch04/ProceduralCommon.glsl"

            uniform float _Levels, _Scale, _Strength, _Octaves, _Speed;
            uniform vec4  _Offset;
            uniform vec4  _Color1, _Color2, _Color3, _Color4;

            #ifdef FRAGMENT
            float F(vec2 p) { return Fbm(p, int(_Octaves), 2.0, 0.5); }   // 约 [-1,1]

            void main()
            {
                vec2 p = uv * _Scale + _Offset.xy;
                float t = _Time.y * _Speed;
                // 第 1 层:q 是一个二维“位移场”,两个分量用两个不同偏移的 fBm
                vec2 q = vec2(0.0);
                if (_Levels >= 1.0)
                    q = vec2(F(p + vec2(0.0, 0.0) + t), F(p + vec2(5.2, 1.3) - t));
                // 第 2 层:拿被 q 扭过的坐标再求一次位移场 r
                vec2 r = vec2(0.0);
                if (_Levels >= 2.0)
                    r = vec2(F(p + _Strength * q + vec2(1.7, 9.2)), F(p + _Strength * q + vec2(8.3, 2.8)));
                // 最终:在被扭曲的坐标上采样
                vec2 warped = p + _Strength * ((_Levels >= 2.0) ? r : q);
                float f = F(warped) * 0.5 + 0.5;

                // 用 f、|q|、r.x 三个量混色:位移场本身也是很好的“着色依据”
                vec3 col = mix(_Color1.rgb, _Color2.rgb, saturate(f * f * 2.2));
                col = mix(col, _Color4.rgb, saturate(length(q) * 0.9));
                col = mix(col, _Color3.rgb, saturate(abs(r.x) * 1.2) * step(1.5, _Levels));
                col *= 0.6 + 0.6 * f;
                fragColor = vec4(col, 1.0);
            }
            #endif
            ENDGLSL
        }
    }
}