GameTech/URP 渲染实战
源码 / Ch12/Cloth.shader
Ch12/Cloth.shader
// 布料:Charlie sheen 法线分布 + Neubelt 可见性 + 包裹漫反射与次表面色(Filament 布料模型的思路)。
Shader "Cookbook/Ch12/Cloth"
{
    Properties
    {
        [MainColor] _BaseColor ("Base Color", Color) = (0.35, 0.03, 0.08, 1)
        [KeywordEnum(GGX, Charlie, Subsurface)] _Model ("Model", Float) = 2
        _Smoothness ("Smoothness (GGX only)", Range(0, 1)) = 0.35
        _SheenColor ("Sheen Color", Color) = (0.9, 0.55, 0.6, 1)
        _SheenRoughness ("Sheen Roughness", Range(0.05, 1)) = 0.45
        _SubsurfaceColor ("Subsurface Color", Color) = (0.45, 0.05, 0.1, 1)
        _Wrap ("Diffuse Wrap", Range(0, 1)) = 0.5
        _WeaveScale ("Weave Scale", Float) = 160
        _WeaveStrength ("Weave Strength", Range(0, 1)) = 0.25
    }

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

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
        CBUFFER_START(UnityPerMaterial)
            half4 _BaseColor, _SheenColor, _SubsurfaceColor;
            half  _Smoothness, _SheenRoughness, _Wrap;
            float _WeaveScale;
            half  _WeaveStrength;
        CBUFFER_END
        ENDHLSL

        Pass
        {
            Name "ForwardLit"
            Tags { "LightMode"="UniversalForwardOnly" }

            HLSLPROGRAM
            #pragma target 3.5
            #pragma vertex Ch12Vert
            #pragma fragment frag
            #pragma shader_feature_local_fragment _MODEL_GGX _MODEL_CHARLIE _MODEL_SUBSURFACE
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
            #pragma multi_compile _ _ADDITIONAL_LIGHTS
            #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
            #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
            #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING
            #pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION
            #pragma multi_compile_fragment _ _LIGHT_COOKIES
            #pragma multi_compile _ _FORWARD_PLUS
            #pragma multi_compile_fog
            #pragma multi_compile_instancing

            #include "Assets/Cookbook/Ch12/Ch12Common.hlsl"

            struct MaterialCtx
            {
                float3 N, V;
                float3 albedo, sheen;
                float  a;          // GGX 用的 α
                float  sheenA;     // Charlie 用的 α
            };

            half3 ShadeOneLight(MaterialCtx m, Light light)
            {
                float3 L = light.direction;
                float3 H = normalize(m.V + L);
                float NoLu = dot(m.N, L);
                float NoL = saturate(NoLu);
                float NoV = max(dot(m.N, m.V), 1e-4);
                float NoH = saturate(dot(m.N, H));
                float atten = light.distanceAttenuation * light.shadowAttenuation;

            #if defined(_MODEL_GGX)
                // 对照组:把布当成普通电介质
                float3 brdf = m.albedo * ADV_INV_PI + AdvSpecularGGX(m.N, m.V, L, m.a, 0.04);
                return brdf * PI * light.color * (NoL * atten);
            #else
                // 镜面:Charlie 分布 × Neubelt 可见性 × sheen 颜色(布料没有明显的菲涅尔变化,直接用颜色当 F)
                float3 Fr = AdvD_Charlie(NoH, m.sheenA) * AdvV_Neubelt(NoV, NoL) * m.sheen;
              #if defined(_MODEL_SUBSURFACE)
                // 漫反射:包裹 + 次表面色,光“渗”进暗部并带上颜色
                float3 Fd = m.albedo * ADV_INV_PI * AdvWrapDiffuse(NoLu, _Wrap);
                Fd *= saturate(_SubsurfaceColor.rgb + NoL);
                return (Fd + Fr * NoL) * PI * light.color * atten;
              #else
                float3 Fd = m.albedo * ADV_INV_PI;
                return (Fd + Fr) * PI * light.color * (NoL * atten);
              #endif
            #endif
            }

            #include "Assets/Cookbook/Ch12/LightLoop.hlsl"

            half4 frag(Varyings i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i);
                float3 T, B, N; Ch12BuildFrame(i, T, B, N);
                float3 V = GetWorldSpaceNormalizeViewDir(i.positionWS);
                float2 screenUV = GetNormalizedScreenSpaceUV(i.positionCS);
                float NoV = saturate(dot(N, V));

                // 程序化平纹:经纬线交替压在上面,用两组正弦的乘积近似线的明暗
                float2 w = i.uv * _WeaveScale;
                float warp = abs(sin(w.x * PI)) * step(0.5, frac(w.y * 0.5 + floor(w.x) * 0.5));
                float weft = abs(sin(w.y * PI)) * (1.0 - step(0.5, frac(w.y * 0.5 + floor(w.x) * 0.5)));
                float thread = saturate(warp + weft);
                half weave = lerp(1.0, lerp(0.7, 1.1, thread), _WeaveStrength);

                MaterialCtx m;
                m.N = N; m.V = V;
                m.albedo = _BaseColor.rgb * weave;
                m.sheen = _SheenColor.rgb * weave;
                float pr = 1.0 - _Smoothness;
                m.a = max(pr * pr, 0.002);
                m.sheenA = max(_SheenRoughness * _SheenRoughness, 0.01);

                half3 color = AccumulateDirectLighting(m, i.positionWS, screenUV);

                // 间接光
                color += SampleSH(N) * m.albedo;
            #if defined(_MODEL_GGX)
                float2 AB = AdvEnvBRDFApprox(pr, NoV);
                color += GlossyEnvironmentReflection(reflect(-V, N), i.positionWS, pr, 1.0, screenUV) * (0.04 * AB.x + AB.y);
            #else
                // 环境 sheen:严格做法是像 split-sum 那样把 D_Charlie·V_Neubelt 预积分成 LUT;
                // 这里用一个随掠射角增强的经验系数代替,够用且便宜
                float sheenEnv = lerp(0.12, 0.35, _SheenRoughness) * (0.4 + 0.6 * pow(1.0 - NoV, 2.0));
                color += GlossyEnvironmentReflection(reflect(-V, N), i.positionWS, _SheenRoughness, 1.0, screenUV) * m.sheen * sheenEnv;
            #endif

                color = MixFog(color, i.fogFactor);
                return half4(color, 1);
            }
            ENDHLSL
        }

        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On ZTest LEqual ColorMask 0 Cull Back
            HLSLPROGRAM
            #pragma vertex ShadowPassVertex
            #pragma fragment ShadowPassFragment
            #pragma multi_compile_instancing
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
            // URP 自带的阴影 Pass 只在 _ALPHATEST_ON 时才读 _BaseMap,其余部分与材质无关,直接复用
            #include "Packages/com.unity.render-pipelines.universal/Shaders/ShadowCasterPass.hlsl"
            ENDHLSL
        }
        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On ColorMask R Cull Back
            HLSLPROGRAM
            #pragma vertex DepthOnlyVertex
            #pragma fragment DepthOnlyFragment
            #pragma multi_compile_instancing
            #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthOnlyPass.hlsl"
            ENDHLSL
        }
        Pass
        {
            Name "DepthNormals"
            Tags { "LightMode"="DepthNormals" }
            ZWrite On Cull Back
            HLSLPROGRAM
            #pragma vertex DepthNormalsVertex
            #pragma fragment DepthNormalsFragment
            #pragma multi_compile_instancing
            #include "Packages/com.unity.render-pipelines.universal/Shaders/DepthNormalsPass.hlsl"
            ENDHLSL
        }
    }
}