GameTech/URP 渲染实战
源码 / Ch06/ClassicLitForwardPass.hlsl
Ch06/ClassicLitForwardPass.hlsl
// 案例 6.9 ClassicLit 的前向 Pass:归一化 Blinn-Phong + 球谐环境光 + 主光阴影 + 附加光(Forward / Forward+)
// + 附加光阴影 + Light Layers + 光照 Cookie + SSAO + 雾
#ifndef COOKBOOK_CH06_CLASSIC_LIT_FORWARD_PASS_INCLUDED
#define COOKBOOK_CH06_CLASSIC_LIT_FORWARD_PASS_INCLUDED

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

struct Attributes
{
    float4 positionOS : POSITION;
    float3 normalOS   : NORMAL;
    float2 texcoord   : TEXCOORD0;
    UNITY_VERTEX_INPUT_INSTANCE_ID
};

struct Varyings
{
    float4 positionCS : SV_POSITION;
    float2 uv         : TEXCOORD0;
    float3 positionWS : TEXCOORD1;
    float3 normalWS   : TEXCOORD2;
    half4  fogFactorAndVertexLight : TEXCOORD3;   // x:雾;yzw:逐顶点附加光
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    float4 shadowCoord : TEXCOORD4;               // 不分级联时,阴影坐标可在顶点里算好再插值
#endif
    UNITY_VERTEX_INPUT_INSTANCE_ID
    UNITY_VERTEX_OUTPUT_STEREO
};

Varyings ClassicLitVertex(Attributes input)
{
    Varyings output = (Varyings)0;
    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_TRANSFER_INSTANCE_ID(input, output);
    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

    VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
    half3 normalWS = TransformObjectToWorldNormal(input.normalOS);

    output.positionCS = vertexInput.positionCS;
    output.positionWS = vertexInput.positionWS;
    output.normalWS = normalWS;
    output.uv = TRANSFORM_TEX(input.texcoord, _BaseMap);

    half fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
    half3 vertexLight = VertexLighting(vertexInput.positionWS, normalWS);   // 只有 _ADDITIONAL_LIGHTS_VERTEX 时非零
    output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);

#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    output.shadowCoord = GetShadowCoord(vertexInput);
#endif
    return output;
}

InputData BuildInputData(Varyings input, half3 normalWS)
{
    InputData inputData = (InputData)0;
    inputData.positionWS = input.positionWS;
    inputData.positionCS = input.positionCS;
    inputData.normalWS = normalWS;
    inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(input.positionWS);
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR)
    inputData.shadowCoord = input.shadowCoord;
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS)
    inputData.shadowCoord = TransformWorldToShadowCoord(input.positionWS);   // 级联:逐像素选级联
#else
    inputData.shadowCoord = float4(0, 0, 0, 0);
#endif
    // URP 14 强制逐像素算雾(ShaderVariablesFunctions.hlsl 里 #define _FOG_FRAGMENT 1),顶点雾因子只作后备
    inputData.fogCoord = InitializeInputDataFog(float4(input.positionWS, 1.0), input.fogFactorAndVertexLight.x);
    inputData.vertexLighting = input.fogFactorAndVertexLight.yzw;
    inputData.bakedGI = SampleSH(normalWS);                     // 本章只用球谐环境光,光照贴图见第 8 章
    inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
    inputData.shadowMask = half4(1, 1, 1, 1);                   // 没有烘焙阴影时为 1
    return inputData;
}

// 一盏灯的贡献:归一化 Blinn-Phong(URP 约定,漫反射不除 π,高光系数为 (n+8)/8)
half3 ClassicBlinnPhong(Light light, half3 normalWS, half3 viewDirWS, half3 albedo, half3 specColor, float shininess)
{
    half NdotL = saturate(dot(normalWS, light.direction));
    half3 radiance = light.color * (light.distanceAttenuation * light.shadowAttenuation * NdotL);

    float3 H = SafeNormalize(float3(light.direction) + float3(viewDirWS));
    float NdotH = saturate(dot(float3(normalWS), H));
    float spec = (shininess + 8.0) * 0.125 * pow(NdotH, shininess);

    return (albedo + specColor * spec) * radiance;
}

half4 ClassicLitFragment(Varyings input) : SV_Target
{
    UNITY_SETUP_INSTANCE_ID(input);
    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

    half4 baseSample = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv) * _BaseColor;
#if defined(_ALPHATEST_ON)
    clip(baseSample.a - _Cutoff);
#endif
    half3 albedo = baseSample.rgb;
    half3 specColor = _SpecColor.rgb;
    float shininess = exp2(10.0 * _Smoothness + 1.0);        // 与 URP SimpleLit 相同的映射

    half3 normalWS = NormalizeNormalPerPixel(input.normalWS);
    InputData inputData = BuildInputData(input, normalWS);    // 名字必须叫 inputData,LIGHT_LOOP_BEGIN 会用到

    half4 shadowMask = CalculateShadowMask(inputData);
    AmbientOcclusionFactor aoFactor = CreateAmbientOcclusionFactor(inputData.normalizedScreenSpaceUV, 1.0);
    uint meshRenderingLayers = GetMeshRenderingLayer();

    // ---- 环境光(SSAO 只压暗间接光,直接光按 Direct Lighting Strength 衰减)----
    half3 color = inputData.bakedGI * albedo * aoFactor.indirectAmbientOcclusion;

    // ---- 主光源:带阴影、阴影淡出和 Cookie ----
    Light mainLight = GetMainLight(inputData, shadowMask, aoFactor);
#if defined(_LIGHT_LAYERS)
    if (IsMatchingLightLayer(mainLight.layerMask, meshRenderingLayers))
#endif
        color += ClassicBlinnPhong(mainLight, normalWS, inputData.viewDirectionWS, albedo, specColor, shininess);

    // ---- 附加光源 ----
#if defined(_ADDITIONAL_LIGHTS)
    uint pixelLightCount = GetAdditionalLightsCount();

    #if USE_FORWARD_PLUS
    for (uint lightIndex = 0; lightIndex < min(URP_FP_DIRECTIONAL_LIGHTS_COUNT, MAX_VISIBLE_LIGHTS); lightIndex++)
    {
        FORWARD_PLUS_SUBTRACTIVE_LIGHT_CHECK
        Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
        #if defined(_LIGHT_LAYERS)
        if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
        #endif
            color += ClassicBlinnPhong(light, normalWS, inputData.viewDirectionWS, albedo, specColor, shininess);
    }
    #endif

    LIGHT_LOOP_BEGIN(pixelLightCount)
        // 这个重载会顺带算出附加光阴影(_ADDITIONAL_LIGHT_SHADOWS)与 Cookie
        Light light = GetAdditionalLight(lightIndex, inputData, shadowMask, aoFactor);
        #if defined(_LIGHT_LAYERS)
        if (IsMatchingLightLayer(light.layerMask, meshRenderingLayers))
        #endif
            color += ClassicBlinnPhong(light, normalWS, inputData.viewDirectionWS, albedo, specColor, shininess);
    LIGHT_LOOP_END
#endif

#if defined(_ADDITIONAL_LIGHTS_VERTEX)
    color += inputData.vertexLighting * albedo;               // 逐顶点附加光:只有漫反射
#endif

    color += _EmissionColor.rgb;
    color = MixFog(color, inputData.fogCoord);
    return half4(color, baseSample.a);
}

#endif