GameTech/URP 渲染实战
源码 / shaders/Ch06/ClassicLitForwardPass.glsl
shaders/Ch06/ClassicLitForwardPass.glsl
// 案例 6.9 ClassicLit 的前向 Pass:归一化 Blinn-Phong + 球谐环境光 + 主光阴影 + 附加光(Forward / Forward+)
// + 附加光阴影 + Light Layers + SSAO + 雾(GLSL 版,对应 Ch06/ClassicLitForwardPass.hlsl)
// 网页版没有光照 Cookie 和逐顶点阴影坐标插值(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR),这两段省略。

VARYING vec2 uv;
VARYING vec3 positionWS;
VARYING vec3 normalWS;
VARYING vec4 fogFactorAndVertexLight;   // x:雾;yzw:逐顶点附加光

#ifdef VERTEX
// URP 的 VertexLighting():只有 _ADDITIONAL_LIGHTS_VERTEX(逐顶点附加光)时才非零
vec3 VertexLighting(vec3 positionWS, vec3 normalWS)
{
    vec3 vertexLightColor = vec3(0.0);
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
    for (int i = 0; i < GetAdditionalLightsCount(); i++)
    {
        Light light = GetAdditionalPerObjectLight(i, positionWS);
        vertexLightColor += LightingLambert(light.color * light.distanceAttenuation, light.direction, normalWS);
    }
#endif
    return vertexLightColor;
}

void main()
{
    VertexPositionInputs vertexInput = GetVertexPositionInputs(positionOS.xyz);
    vec3 nWS = TransformObjectToWorldNormal(normalOS);

    gl_Position = vertexInput.positionCS;
    positionWS = vertexInput.positionWS;
    normalWS = nWS;
    uv = TransformTex(texcoord0.xy, _BaseMap_ST);

    float fogFactor = ComputeFogFactor(vertexInput.positionCS.z);
    vec3 vertexLight = VertexLighting(vertexInput.positionWS, nWS);   // 只有 _ADDITIONAL_LIGHTS_VERTEX 时非零
    fogFactorAndVertexLight = vec4(fogFactor, vertexLight);
}
#endif

#ifdef FRAGMENT
InputData BuildInputData(vec3 normalWS)
{
    InputData inputData = InitInputData();
    inputData.positionWS = positionWS;
    inputData.positionCS = gl_FragCoord;
    inputData.normalWS = normalWS;
    inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(positionWS);
#if defined(_RECEIVE_SHADOWS_OFF)
    inputData.shadowCoord = vec4(0, 0, 0, 0);                               // 不接收阴影:URP 不定义 MAIN_LIGHT_CALCULATE_SHADOWS
#else
    inputData.shadowCoord = TransformWorldToShadowCoord(positionWS);        // 级联:逐像素选级联
#endif
    // URP 14 强制逐像素算雾(_FOG_FRAGMENT),这里按像素位置重新求雾因子,顶点雾因子只作后备
    inputData.fogCoord = ComputeFogFactor(TransformWorldToHClip(positionWS).z);
    inputData.vertexLighting = fogFactorAndVertexLight.yzw;
    inputData.bakedGI = SampleSH(normalWS);                     // 本章只用球谐环境光,光照贴图见第 8 章
    inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(gl_FragCoord);
    inputData.shadowMask = vec4(1, 1, 1, 1);                    // 没有烘焙阴影时为 1
    return inputData;
}

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

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

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

void main()
{
    vec4 baseSample = texture(_BaseMap, uv) * _BaseColor;
#if defined(_ALPHATEST_ON)
    if (baseSample.a - _Cutoff < 0.0) discard;
#endif
    vec3 albedo = baseSample.rgb;
    vec3 specColor = _SpecColor.rgb;
    float shininess = exp2(10.0 * _Smoothness + 1.0);        // 与 URP SimpleLit 相同的映射

    vec3 N = NormalizeNormalPerPixel(normalWS);
    InputData inputData = BuildInputData(N);

    vec4 shadowMask = inputData.shadowMask;                   // URP:CalculateShadowMask(inputData)
    // URP:CreateAmbientOcclusionFactor(uv, 1.0)。没开 SSAO 时两项都是 1
    AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(inputData.normalizedScreenSpaceUV);
    uint meshRenderingLayers = GetMeshRenderingLayer();

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

    // ---- 主光源:带阴影、阴影淡出(URP 的 GetMainLight(inputData, shadowMask, aoFactor))----
    Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, shadowMask);
    mainLight.color *= aoFactor.directAmbientOcclusion;
    // HLSL 版的 #if defined(_LIGHT_LAYERS):GLSL 版里是 uniform _LightLayers(URP Asset 打开 Light Layers 时为 1)
    if (_LightLayers < 0.5 || IsMatchingLightLayer(GetMainLightLayerMask(), meshRenderingLayers))
        color += ClassicBlinnPhong(mainLight, N, inputData.viewDirectionWS, albedo, specColor, shininess);

    // ---- 附加光源 ----
#if defined(_ADDITIONAL_LIGHTS)
    // HLSL 版:Forward+ 先单独循环附加方向光,再用 LIGHT_LOOP_BEGIN 遍历分簇结构;
    // 网页版统一成普通 for 循环(Forward 下是本物体的灯,Forward+ 下是全部可见附加光,方向光在最前面)
    int pixelLightCount = GetAdditionalLightsCount();
    for (int lightIndex = 0; lightIndex < pixelLightCount; lightIndex++)
    {
        // 这个重载会顺带算出附加光阴影(_ADDITIONAL_LIGHT_SHADOWS)
        Light light = GetAdditionalLight(lightIndex, inputData.positionWS, shadowMask);
    #if defined(_RECEIVE_SHADOWS_OFF)
        light.shadowAttenuation = 1.0;
    #endif
        light.color *= aoFactor.directAmbientOcclusion;
        if (_LightLayers < 0.5 || IsMatchingLightLayer(GetAdditionalLightLayerMask(lightIndex), meshRenderingLayers))
            color += ClassicBlinnPhong(light, N, inputData.viewDirectionWS, albedo, specColor, shininess);
    }
#endif

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

    color += _EmissionColor.rgb;
    color = MixFog(color, inputData.fogCoord);
    fragColor = vec4(color, baseSample.a);
}
#endif