GameTech/URP 渲染实战
源码 / Ch09/BRDFLib.hlsl
Ch09/BRDFLib.hlsl
#ifndef COOKBOOK_CH09_BRDFLIB_INCLUDED
#define COOKBOOK_CH09_BRDFLIB_INCLUDED

// 第 9 章用到的 BRDF 各项。
// 全部按“教科书形式”书写:保留 π、不合并常数、不做移动端近似,方便和正文公式逐项对照。
// 所有函数加 C9_ 前缀,避免与 core 包 BSDF.hlsl 里的 D_GGX / F_Schlick 等同名函数冲突。
// 约定:参数 a 一律是 α = perceptualRoughness²(见 9.11 节),不是材质面板上的 Smoothness。

// ---------- 法线分布函数 D(h) ----------
// GGX / Trowbridge-Reitz:D = α² / (π · ((n·h)²(α²-1) + 1)²)
float C9_D_GGX(float NoH, float a)
{
    float a2 = a * a;
    float d = NoH * NoH * (a2 - 1.0) + 1.0;
    return a2 / (PI * d * d);
}

// Beckmann:D = exp(-tan²θh / α²) / (π α² cos⁴θh)
float C9_D_Beckmann(float NoH, float a)
{
    float a2 = a * a;
    float c2 = max(NoH * NoH, 1e-6);
    float t2 = (1.0 - c2) / c2;                 // tan²θh
    return exp(-t2 / a2) / (PI * a2 * c2 * c2);
}

// 归一化的 Blinn-Phong NDF:D = (n+2)/(2π) · cosⁿθh,指数 n 由 α 换算:n = 2/α² - 2
float C9_D_BlinnPhong(float NoH, float a)
{
    float n = 2.0 / max(a * a, 1e-4) - 2.0;
    return (n + 2.0) / (2.0 * PI) * pow(max(NoH, 0.0), n);
}

// ---------- 几何遮蔽 G ----------
// GGX 的 Smith Λ 函数:Λ(θ) = (-1 + sqrt(1 + α² tan²θ)) / 2
float C9_Lambda_GGX(float NoX, float a)
{
    float c2 = max(NoX * NoX, 1e-6);
    float t2 = (1.0 - c2) / c2;
    return 0.5 * (-1.0 + sqrt(1.0 + a * a * t2));
}

// 单方向遮蔽 G1 = 1 / (1 + Λ)
float C9_G1_GGX(float NoX, float a) { return 1.0 / (1.0 + C9_Lambda_GGX(NoX, a)); }

// Smith 分离形式:G = G1(l) · G1(v),假设“遮光”和“遮挡视线”互不相关
float C9_G_SmithSeparable(float NoL, float NoV, float a)
{
    return C9_G1_GGX(NoL, a) * C9_G1_GGX(NoV, a);
}

// Smith 高度相关形式(Height-Correlated):G = 1 / (1 + Λ(l) + Λ(v))
float C9_G_SmithHeightCorrelated(float NoL, float NoV, float a)
{
    return 1.0 / (1.0 + C9_Lambda_GGX(NoL, a) + C9_Lambda_GGX(NoV, a));
}

// Schlick-GGX 近似:G1 ≈ n·x / (n·x (1-k) + k),k = α/2
float C9_G_SchlickGGX(float NoL, float NoV, float a)
{
    float k = a * 0.5;
    float gl = NoL / (NoL * (1.0 - k) + k);
    float gv = NoV / (NoV * (1.0 - k) + k);
    return gl * gv;
}

// 可见性项 V = G / (4 (n·l)(n·v)),用高度相关 Smith 化简后的形式(Heitz 2014)
// V = 0.5 / ( n·l · sqrt((n·v)²(1-α²) + α²) + n·v · sqrt((n·l)²(1-α²) + α²) )
float C9_V_SmithHeightCorrelated(float NoL, float NoV, float a)
{
    float a2 = a * a;
    float lv = NoL * sqrt(NoV * NoV * (1.0 - a2) + a2);
    float ll = NoV * sqrt(NoL * NoL * (1.0 - a2) + a2);
    return 0.5 / max(lv + ll, 1e-5);
}

// ---------- 菲涅尔 F ----------
// Schlick 近似:F = F0 + (1 - F0)(1 - cosθ)⁵
float3 C9_F_Schlick(float3 F0, float cosTheta)
{
    float x = 1.0 - saturate(cosTheta);
    float x2 = x * x;
    return F0 + (1.0 - F0) * (x2 * x2 * x);
}

// ---------- 漫反射 ----------
// Lambert:f = ρ / π
float3 C9_Lambert(float3 albedo) { return albedo * INV_PI; }

// Disney(Burley 2012)漫反射:在 Lambert 基础上按粗糙度调整掠射角的明暗
// fd90 = 0.5 + 2 · (l·h)² · perceptualRoughness
float3 C9_DisneyDiffuse(float3 albedo, float NoL, float NoV, float LoH, float perceptualRoughness)
{
    float fd90 = 0.5 + 2.0 * LoH * LoH * perceptualRoughness;
    float lightScatter = 1.0 + (fd90 - 1.0) * pow(1.0 - NoL, 5.0);
    float viewScatter  = 1.0 + (fd90 - 1.0) * pow(1.0 - NoV, 5.0);
    return albedo * INV_PI * lightScatter * viewScatter;
}

#endif