GameTech/URP 渲染实战
源码 / Ch07/Ch07Common.hlsl
Ch07/Ch07Common.hlsl
// 第 7 章公用代码:简化光照 + ShadowCaster/DepthOnly 两个辅助 Pass。
// 本章的重点是“法线从哪来”,光照模型沿用第 5、6 章的 Lambert + 归一化 Blinn-Phong + 球谐环境光,
// 这样不同案例之间的差别只来自法线和 UV,便于对比。
#ifndef COOKBOOK_CH07_COMMON_INCLUDED
#define COOKBOOK_CH07_COMMON_INCLUDED

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

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

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

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

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

// ShadowCaster 与 DepthOnly:只依赖顶点位置和法线,与表面着色无关。
// _LightDirection/_LightPosition 由 URP 在渲染阴影时设置(见 ShadowUtils.SetupShadowCasterConstantBuffer)
float3 _LightDirection;
float3 _LightPosition;

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

float4 Ch07ShadowVert(Ch07ShadowAttributes input) : SV_POSITION
{
    float3 positionWS = TransformObjectToWorld(input.positionOS.xyz);
    float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
    float3 lightDirectionWS = normalize(_LightPosition - positionWS);
#else
    float3 lightDirectionWS = _LightDirection;
#endif
    float4 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;
}

float4 Ch07DepthVert(Ch07ShadowAttributes input) : SV_POSITION
{
    return TransformObjectToHClip(input.positionOS.xyz);
}

half4 Ch07NullFrag() : SV_Target
{
    return 0;
}

#endif