GameTech/URP 渲染实战
源码 / Ch04/C04_07_FbmClouds.shader
Ch04/C04_07_FbmClouds.shader
// 案例 4.7:fBm 云纹 —— 叠加多层噪声,加上覆盖率和软边,就是一片天空。
Shader "Cookbook/Ch04/FbmClouds"
{
    Properties
    {
        [IntRange] _Octaves ("Octaves", Range(1, 10)) = 6
        _Scale ("Scale", Float) = 3
        _Lacunarity ("Lacunarity", Float) = 2
        _Gain ("Gain", Range(0, 1)) = 0.5
        _Cover ("Cloud Cover", Range(0, 1)) = 0.5
        _Softness ("Edge Softness", Range(0.01, 1)) = 0.35
        _Wind ("Wind (xy) units/s", Vector) = (0.05, 0.01, 0, 0)
        _Offset ("Offset (for stills)", Vector) = (0, 0, 0, 0)
        _SkyTop ("Sky Top", Color) = (0.16, 0.38, 0.72, 1)
        _SkyBottom ("Sky Bottom", Color) = (0.55, 0.72, 0.90, 1)
        _CloudLit ("Cloud Lit", Color) = (1, 1, 1, 1)
        _CloudShade ("Cloud Shade", Color) = (0.62, 0.66, 0.74, 1)
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" "RenderPipeline"="UniversalPipeline" "Queue"="Geometry" }

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

            HLSLPROGRAM
            #pragma vertex ProceduralVert
            #pragma fragment frag
            #include "Assets/Cookbook/Ch04/ProceduralCommon.hlsl"

            CBUFFER_START(UnityPerMaterial)
                float  _Octaves, _Scale, _Lacunarity, _Gain, _Cover, _Softness;
                float4 _Wind, _Offset;
                half4  _SkyTop, _SkyBottom, _CloudLit, _CloudShade;
            CBUFFER_END

            half4 frag (Varyings input) : SV_Target
            {
                float2 p = input.uv * _Scale + _Offset.xy + _Wind.xy * _Time.y;
                int oct = (int)_Octaves;
                float n = Fbm(p, oct, _Lacunarity, _Gain) * 0.5 + 0.5;          // → [0,1]
                // 覆盖率:把阈值往下推,云就变多;smoothstep 的宽度决定边缘软硬
                float density = smoothstep(1.0 - _Cover, 1.0 - _Cover + _Softness, n);
                // 简单的“自阴影”:朝光源方向(左上)偏移一点再采一次,差值当作明暗
                float n2 = Fbm(p + float2(-0.06, 0.06), oct, _Lacunarity, _Gain) * 0.5 + 0.5;
                float lit = saturate(0.5 + (n - n2) * 6.0);
                half3 sky = lerp(_SkyBottom.rgb, _SkyTop.rgb, input.uv.y);
                half3 cloud = lerp(_CloudShade.rgb, _CloudLit.rgb, lit);
                return half4(lerp(sky, cloud, density), 1);
            }
            ENDHLSL
        }
    }
}