From cb101b9bd1f0e9dbfcfe45d114d1a63af3e85d82 Mon Sep 17 00:00:00 2001 From: yum Date: Tue, 11 Nov 2025 17:26:35 -0800 Subject: fix energy conservation issues; notably smooth=0 metallic=1 matches filamented now --- brdf.cginc | 22 ++++++++++------------ filamented.cginc | 4 ++++ lighting.cginc | 5 ++++- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/brdf.cginc b/brdf.cginc index 3a0db4c..804cfe9 100644 --- a/brdf.cginc +++ b/brdf.cginc @@ -136,7 +136,7 @@ float4 brdf(Pbr pbr, LightData data) { data.direct.LoH = 1; #endif - float2 dfg_uv = float2(pbr.roughness, data.common.NoV); + float2 dfg_uv = float2(data.common.NoV, pbr.roughness_perceptual); float3 dfg; [branch] if (textureExists(_DFG_LUT)) { @@ -147,7 +147,8 @@ float4 brdf(Pbr pbr, LightData data) { #if defined(_CLEARCOAT) const float cc_f0 = 0.04f; - float2 cc_dfg_uv = float2(pbr.cc_roughness, data.common.NoV_cc); + float cc_perceptual_roughness = saturate(sqrt(pbr.cc_roughness)); + float2 cc_dfg_uv = float2(data.common.NoV_cc, cc_perceptual_roughness); float3 cc_dfg; [branch] if (textureExists(_DFG_LUT)) { @@ -159,7 +160,9 @@ float4 brdf(Pbr pbr, LightData data) { float cc_energy_comp = 1.0f + cc_f0 * (rcp(cc_Ess) - 1.0f); #endif - float3 F0_color = lerp(float3(f0, f0, f0), pbr.albedo.xyz, pbr.metallic); + float3 f0_color = lerp(f0, pbr.albedo.xyz, pbr.metallic); + float Ess = max(dfg.y, 1e-4f); + float3 energy_comp = 1.0f + f0_color * (rcp(Ess) - 1.0f); // Direct { @@ -197,9 +200,6 @@ float4 brdf(Pbr pbr, LightData data) { FDG = FDG * dfg.x + dfg.y; float3 direct_specular = FDG * remainder * data.direct.color * data.direct.NoL * lerp(1.0f, pbr.albedo.xyz, pbr.metallic); - float Ess = max(dfg.y, 1e-4f); - float invEss = rcp(Ess); - float3 energy_comp = 1.0f + F0_color * (invEss - 1.0f); direct_specular *= energy_comp; direct_specular = max(0, direct_specular); specular += direct_specular; @@ -237,13 +237,11 @@ float4 brdf(Pbr pbr, LightData data) { // Standard PBR IBL using split-sum approximation // Specular lobe float3 f0_spec = lerp(f0, pbr.albedo.xyz, pbr.metallic); - float3 ibl_specular_reflectance = f0_spec * dfg.x + dfg.y; - float3 indirect_specular = data.indirect.specular * ibl_specular_reflectance; - // surface reduction term lifted from here: - // UnityStandardBRDF.cginc :: BRDF1_Unity_PBS - float surface_reduction = 1.0f / (pbr.roughness * pbr.roughness + 1.0f); + float3 ibl_specular_reflectance = lerp(dfg.xxx, dfg.yyy, f0_spec); + float3 indirect_specular = data.indirect.specular * ibl_specular_reflectance * energy_comp; const float F = F_Schlick(data.indirect.NoL, f0_spec, f90); - const float3 is_conserved = surface_reduction * indirect_specular * F; + const float3 is_conserved = indirect_specular * F; + specular += is_conserved; remainder = saturate(remainder - is_conserved); diff --git a/filamented.cginc b/filamented.cginc index f6bd67a..62ec4c3 100644 --- a/filamented.cginc +++ b/filamented.cginc @@ -254,6 +254,10 @@ UnityGIInput InitialiseUnityGIInput(float3 worldPos, float3 view_dir) return d; } +float3 getSpecularDominantDirection(const float3 n, const float3 r, float roughness) { + return lerp(r, n, roughness * roughness); +} + inline half3 UnityGI_prefilteredRadiance(const UnityGIInput data, const float perceptualRoughness, const float3 r) { half3 specular; diff --git a/lighting.cginc b/lighting.cginc index 4e5a61b..5f66b80 100644 --- a/lighting.cginc +++ b/lighting.cginc @@ -188,7 +188,10 @@ void GetLighting(v2f i, Pbr pbr, out LightData data) { data.direct.color = lightColorIntensity.rgb * lightColorIntensity.w; // Indirect lighting - data.indirect.dir = -reflect(data.common.V, pbr.normal); + float3 reflect_dir = -reflect(data.common.V, pbr.normal); + float3 dominant_dir = getSpecularDominantDirection(pbr.normal, reflect_dir, pbr.roughness); + + data.indirect.dir = normalize(dominant_dir); data.indirect.H = normalize(data.common.V + data.indirect.dir); data.indirect.NoL = saturate(dot(pbr.normal, data.indirect.dir)); data.indirect.NoH = saturate(dot(pbr.normal, data.indirect.H)); -- cgit v1.2.3