URP 渲染实战 / 第 10 章 逐行读懂 URP Lit / 案例 10.2
案例 10.2URP 高光 vs 标准 GGX:并排看进阶
目标:在同一个 Shader 里,用 URP 的 InitializeBRDFData + DirectBRDFSpecular 和 core 包的 D_GGX、V_SmithJointGGX、F_Schlick 分别计算主光的直接高光,用两排粗糙度递增的球对比。
Assets/Cookbook/Ch10/SpecCompare.shader(节选:frag)
half4 frag(Varyings i) : SV_Target { float3 N = normalize(i.normalWS); float3 V = GetWorldSpaceNormalizeViewDir(i.positionWS); Light light = GetMainLight(); float3 L = light.direction; float NoL = saturate(dot(N, L)); // 用 URP 自己的初始化:金属度 1、albedo = F0,于是 brdf.specular = F0、brdf.diffuse = 0 half alpha = 1; BRDFData brdf; InitializeBRDFData(_F0.rgb, 1.0, 0, _Smoothness, alpha, brdf); float3 spec; // 已经乘了 π 的“Unity 约定”镜面项,乘 lightColor·NoL 即出射辐射率 if (_Mode < 0.5) { spec = brdf.specular * DirectBRDFSpecular(brdf, N, L, V); } else { float3 H = SafeNormalize(L + V); float NoV = max(dot(N, V), 1e-4), NoH = saturate(dot(N, H)), VoH = saturate(dot(V, H)); float a = brdf.roughness; // α,与 URP 用同一个值 float DV = D_GGX(NoH, a) * V_SmithJointGGX(NoL, NoV, a); // core/BSDF.hlsl spec = PI * DV * F_Schlick(_F0.rgb, VoH); } float3 c = spec * light.color * NoL; return half4(1.0 - exp(-c * _Exposure), 1); }
void main() { vec3 N = normalize(normalWS); vec3 V = GetWorldSpaceNormalizeViewDir(positionWS); Light light = GetMainLight(); vec3 L = light.direction; float NoL = saturate(dot(N, L)); // 用 URP 自己的初始化:金属度 1、albedo = F0,于是 brdf.specular = F0、brdf.diffuse = 0 float alpha = 1.0; BRDFData brdf; InitializeBRDFData(_F0.rgb, 1.0, vec3(0.0), _Smoothness, alpha, brdf); vec3 spec; // 已经乘了 π 的“Unity 约定”镜面项,乘 lightColor·NoL 即出射辐射率 if (_Mode < 0.5) { spec = brdf.specular * DirectBRDFSpecular(brdf, N, L, V); } else { vec3 H = SafeNormalize(L + V); float NoV = max(dot(N, V), 1e-4), NoH = saturate(dot(N, H)), VoH = saturate(dot(V, H)); float a = brdf.roughness; // α,与 URP 用同一个值 float DV = D_GGX(NoH, a) * V_SmithJointGGX(NoL, NoV, a); // core/BSDF.hlsl spec = PI * DV * F_Schlick(_F0.rgb, VoH); } vec3 c = spec * light.color * NoL; fragColor = vec4(1.0 - exp(-c * _Exposure), 1.0); }
参照组完全使用 core 包现成的标准函数:D_GGX 带 ,所以结果再乘 与 URP 的约定对齐;V_SmithJointGGX 是 9.7 节的高度相关可见性项。两组用同一个 brdf.roughness,保证 相同。为了让暗淡的粗糙高光也看得见,F0 取 0.9,输出用 映射。

与图 10-5 的数字一致:前两列 URP 的亮核更大更亮;第四、五列下排更亮,而且在右下方明暗交界线附近有一圈明显的亮边——那是标准公式里 在掠射时增大、Schlick 的 F 增大共同造成的,而 URP 的 被钳在 10 倍以内,没有这圈亮边。哪一个“更对”?从能量上看是下排(下一个案例验证);从观感上看,粗糙物体边缘这圈亮边在实际场景中常被认为“不干净”,URP 的近似反而更讨美术喜欢。这就是工程上的取舍。