GameTech/URP 渲染实战
源码 / shaders/Ch07/Ch07Common.glsl
shaders/Ch07/Ch07Common.glsl
// 第 7 章公用代码:简化光照 + ShadowCaster/DepthOnly 两个辅助 Pass。(GLSL 版,对应 Ch07/Ch07Common.hlsl)
// 本章的重点是“法线从哪来”,光照模型沿用第 5、6 章的 Lambert + 归一化 Blinn-Phong + 球谐环境光,
// 这样不同案例之间的差别只来自法线和 UV,便于对比。
// 用法:颜色 Pass 直接 include,调用 Ch07Shade;
//       投影 / 深度 Pass 先 #define CH07_SHADOW_PASS 或 CH07_DEPTH_PASS 再 include(对应 HLSL 的 #pragma vertex Ch07ShadowVert 等)。

// positionWS/normalWS:世界空间位置与(已经过法线贴图扰动的)法线
// gloss:0~1 映射到高光指数 2~2048;specStrength:高光强度
// extraShadow:额外的阴影系数(视差自阴影会用到),没有就传 1
vec3 Ch07Shade(vec3 positionWS, vec3 normalWS, vec3 albedo, float gloss, float specStrength, float extraShadow)
{
    vec4 shadowCoord = TransformWorldToShadowCoord(positionWS);
    Light mainLight = GetMainLight(shadowCoord);

    vec3 V = GetWorldSpaceNormalizeViewDir(positionWS);
    vec3 L = mainLight.direction;
    vec3 H = SafeNormalize(L + V);
    float NdotL = saturate(dot(normalWS, L));
    float NdotH = saturate(dot(normalWS, H));

    // URP 的灯光强度已经“乘过 π”,所以 Lambert 不再除 π,
    // 归一化 Blinn-Phong 的 (n+8)/(8π) 同理写成 (n+8)/8
    float n = exp2(gloss * 10.0 + 1.0);
    float spec = specStrength * (n + 8.0) / 8.0 * pow(NdotH, n);

    float atten = mainLight.shadowAttenuation * mainLight.distanceAttenuation * extraShadow;
    vec3 direct = (albedo + spec) * NdotL * atten * mainLight.color;
    vec3 ambient = SampleSH(normalWS) * albedo;     // 环境光:球谐(第 8 章细讲)
    return direct + ambient;
}

// ShadowCaster 与 DepthOnly:只依赖顶点位置和法线,与表面着色无关。
// _LightDirection/_LightPosition 由 URP 在渲染阴影时设置(见 ShadowUtils.SetupShadowCasterConstantBuffer),
// GLSL 版里它们已在 Lighting.glsl 中声明;_CASTING_PUNCTUAL_LIGHT_SHADOW 是 uniform _CastingPunctualLightShadow
#ifdef VERTEX
vec4 Ch07ShadowVert()
{
    vec3 positionWS = TransformObjectToWorld(positionOS.xyz);
    vec3 normalWS = TransformObjectToWorldNormal(normalOS);
    vec3 lightDirectionWS = _CastingPunctualLightShadow > 0.5 ? normalize(_LightPosition - positionWS) : _LightDirection;
    vec4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
#if UNITY_REVERSED_Z
    positionCS.z = min(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#else
    positionCS.z = max(positionCS.z, UNITY_NEAR_CLIP_VALUE);
#endif
    return positionCS;
}

vec4 Ch07DepthVert()
{
    return TransformObjectToHClip(positionOS.xyz);
}

#if defined(CH07_SHADOW_PASS)
void main() { gl_Position = Ch07ShadowVert(); }
#elif defined(CH07_DEPTH_PASS)
void main() { gl_Position = Ch07DepthVert(); }
#endif
#endif

#ifdef FRAGMENT
vec4 Ch07NullFrag()
{
    return vec4(0.0);
}
#if defined(CH07_SHADOW_PASS) || defined(CH07_DEPTH_PASS)
void main() { fragColor = Ch07NullFrag(); }
#endif
#endif