GameTech/URP 渲染实战
源码 / Ch05/RoughDiffuse.shader
Ch05/RoughDiffuse.shader
// 案例 5.7 粗糙漫反射:Oren-Nayar(定性模型)与 Minnaert
Shader "Cookbook/Ch05/RoughDiffuse"
{
    Properties
    {
        [MainColor] _BaseColor("Base Color", Color) = (0.75, 0.72, 0.68, 1)
        [KeywordEnum(Lambert, OrenNayar, Minnaert)] _Model("Model", Float) = 1
        _Roughness("Oren-Nayar Sigma (弧度)", Range(0, 1.5)) = 0.5
        _MinnaertK("Minnaert k", Range(0.3, 2)) = 0.8
        _AmbientStrength("Ambient Strength", Range(0, 1)) = 0.3
    }

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

        HLSLINCLUDE
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

        CBUFFER_START(UnityPerMaterial)
            half4 _BaseColor;
            float _Roughness;
            float _MinnaertK;
            half  _AmbientStrength;
        CBUFFER_END
        ENDHLSL

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

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma shader_feature_local_fragment _MODEL_LAMBERT _MODEL_ORENNAYAR _MODEL_MINNAERT
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS   : NORMAL;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float3 positionWS : TEXCOORD0;
                float3 normalWS   : TEXCOORD1;
            };

            Varyings vert(Attributes input)
            {
                Varyings output;
                VertexPositionInputs pos = GetVertexPositionInputs(input.positionOS.xyz);
                output.positionCS = pos.positionCS;
                output.positionWS = pos.positionWS;
                output.normalWS = TransformObjectToWorldNormal(input.normalOS);
                return output;
            }

            // Oren-Nayar 定性模型,返回值已乘 N·L(URP 约定,不含 1/π)。
            // 原式 A + B·max(0, cos(φi-φr))·sinα·tanβ 里的三角函数可以全部消掉:
            //   s = L·V - (N·L)(N·V) = sinθi·sinθr·cos(φi-φr)
            //   sinα·tanβ = sinθi·sinθr / max(cosθi, cosθr)
            // 于是 cos(φi-φr)·sinα·tanβ = s / max(N·L, N·V)
            float OrenNayar(float3 N, float3 L, float3 V, float sigma)
            {
                float NdotL = saturate(dot(N, L));
                float NdotV = saturate(dot(N, V));
                float s2 = sigma * sigma;
                float A = 1.0 - 0.5 * s2 / (s2 + 0.33);
                float B = 0.45 * s2 / (s2 + 0.09);
                float s = dot(L, V) - NdotL * NdotV;
                float t = max(max(NdotL, NdotV), 1e-4);
                return NdotL * (A + B * max(0.0, s) / t);
            }

            // Minnaert:k = 1 时就是 Lambert;k < 1 边缘变亮(天鹅绒、月面感),k > 1 边缘更暗
            float Minnaert(float3 N, float3 L, float3 V, float k)
            {
                float NdotL = saturate(dot(N, L));
                float NdotV = max(saturate(dot(N, V)), 1e-3);
                return pow(NdotL, k) * pow(NdotV, k - 1.0);
            }

            half4 frag(Varyings input) : SV_Target
            {
                float3 N = normalize(input.normalWS);
                float3 V = GetWorldSpaceNormalizeViewDir(input.positionWS);
                Light mainLight = GetMainLight();
                float3 L = mainLight.direction;
                half3 radiance = mainLight.color * mainLight.distanceAttenuation;

            #if defined(_MODEL_ORENNAYAR)
                float d = OrenNayar(N, L, V, _Roughness);
            #elif defined(_MODEL_MINNAERT)
                float d = Minnaert(N, L, V, _MinnaertK);
            #else
                float d = saturate(dot(N, L));
            #endif

                half3 color = _BaseColor.rgb * radiance * d;
                color += _BaseColor.rgb * SampleSH(N) * _AmbientStrength;
                return half4(color, 1);
            }
            ENDHLSL
        }

        // ---- 投影与深度:本章先照抄,原理见第 6 章 ----
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode"="ShadowCaster" }
            ZWrite On
            ZTest LEqual
            ColorMask 0

            HLSLPROGRAM
            #pragma vertex ShadowDepthVert
            #pragma fragment ShadowDepthFrag
            #pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
            #include "Assets/Cookbook/Ch05/Ch05ShadowDepth.hlsl"
            ENDHLSL
        }

        Pass
        {
            Name "DepthOnly"
            Tags { "LightMode"="DepthOnly" }
            ZWrite On
            ColorMask R

            HLSLPROGRAM
            #pragma vertex DepthOnlyVert
            #pragma fragment DepthOnlyFrag
            #include "Assets/Cookbook/Ch05/Ch05ShadowDepth.hlsl"
            ENDHLSL
        }
    }
}