GameTech/URP 渲染实战
源码 / shaders/Ch11/MyPBR_ForwardPass.glsl
shaders/Ch11/MyPBR_ForwardPass.glsl
// MyPBR 的 UniversalForward Pass:顶点变换 + 准备 InputData + 调用 MyComputeLighting(GLSL 版,对应 Ch11/MyPBR_ForwardPass.hlsl)
#ifndef COOKBOOK_MYPBR_FORWARD_PASS_INCLUDED
#define COOKBOOK_MYPBR_FORWARD_PASS_INCLUDED

#include "Ch11/MyPBR_Lighting.glsl"

// WebGL 不允许结构体做 varying:HLSL 的 Varyings 拆成单独的变量(顶点输入 Attributes 由引擎固定命名:positionOS、normalOS……)
VARYING vec2  uv;
VARYING vec3  positionWS;
VARYING vec3  normalWS;
#if defined(_NORMALMAP)
VARYING vec4  tangentWS;      // xyz 切线,w 副切线符号
#endif
VARYING float fogFactor;
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
VARYING vec3  vertexLight;
#endif
// 光照贴图 UV / 逐顶点 SH:网页版没有光照贴图,SH 在片元里逐像素求

#ifdef VERTEX
void MyPBRVert()
{
    VertexPositionInputs pos = GetVertexPositionInputs(positionOS.xyz);
    VertexNormalInputs   nrm = GetVertexNormalInputs(normalOS, tangentOS);

    gl_Position = pos.positionCS;
    positionWS  = pos.positionWS;
    uv          = TransformTex(texcoord0.xy, _BaseMap_ST);
    normalWS    = nrm.normalWS;
#if defined(_NORMALMAP)
    // 镜像缩放(负缩放)会翻转副切线方向,GetOddNegativeScale 负责修正
    tangentWS   = vec4(nrm.tangentWS, tangentOS.w * GetOddNegativeScale());
#endif
    fogFactor   = ComputeFogFactor(pos.positionCS.z);

#if defined(_ADDITIONAL_LIGHTS_VERTEX)
    // 逐顶点附加光:只累加 color * atten * NoL(漫反射的 E/π),镜面无法逐顶点
    vec3 vl = vec3(0.0);
    int n = GetAdditionalLightsCount();
    for (int i = 0; i < n; ++i)
    {
        Light l = GetAdditionalLight(i, pos.positionWS);
        vl += l.color * l.distanceAttenuation * saturate(dot(nrm.normalWS, l.direction));
    }
    vertexLight = vl;
#endif
}
#endif

#ifdef FRAGMENT
// 填 URP 的 InputData:它只是“像素的几何与环境数据”的容器,
// 填好它,阴影、SSAO、附加光循环都能直接用
InputData MyInitInputData(vec3 normalTS)
{
    InputData d = InitInputData();
    d.positionWS = positionWS;
    d.positionCS = gl_FragCoord;
#if defined(_NORMALMAP)
    float sgn = tangentWS.w;
    vec3 bitangent = sgn * cross(normalWS, tangentWS.xyz);
    mat3 tbn = mat3(tangentWS.xyz, bitangent, normalWS);   // GLSL 的 mat3 按列构造:列就是 T/B/N
    d.tangentToWorld = tbn;
    d.normalWS = TransformTangentToWorld(normalTS, tbn);
#else
    d.normalWS = normalWS;
#endif
    d.normalWS = NormalizeNormalPerPixel(d.normalWS);   // 插值后长度不为 1,必须重新归一化
    d.viewDirectionWS = GetWorldSpaceNormalizeViewDir(positionWS);
    d.shadowCoord = TransformWorldToShadowCoord(positionWS);
    d.fogCoord = fogFactor;
#if defined(_ADDITIONAL_LIGHTS_VERTEX)
    d.vertexLighting = vertexLight;
#endif
    d.bakedGI = SampleSH(d.normalWS);                     // 网页版没有光照贴图:逐像素 SH
    d.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(gl_FragCoord);
    d.shadowMask = vec4(1.0);
    return d;
}

vec4 MyPBRFrag()
{
    MySurface s  = MySampleSurface(uv);
    InputData id = MyInitInputData(s.normalTS);
    MyMaterial m = MyInitMaterial(s.albedo, s.metallic, s.perceptualRoughness);

    // HLSL 版在 DEBUG_DISPLAY 下把表面参数交给 Rendering Debugger;网页版的调试视图见 Ch11/MyPBRDebug.glsl
    MyLightingTerms t = MyComputeLighting(s, m, id);
    vec3 color = t.directDiffuse + t.directSpecular + t.indirectDiffuse + t.indirectSpecular + t.emission;
    color = MixFog(color, id.fogCoord);
    return vec4(color, 1.0);
}
#endif

#endif