Skip to content

Commit 6da6c51

Browse files
authored
WebGLRenderer: Add multi-scattering energy compensation for direct lighting (#32072)
* WebGLRenderer: Add multi-scattering energy compensation for direct lighting. * Updated builds. * Simplified BRDF_GGX_Multiscatter function.
1 parent 11751a0 commit 6da6c51

File tree

1 file changed

+43
-1
lines changed

1 file changed

+43
-1
lines changed

src/renderers/shaders/ShaderChunk/lights_physical_pars_fragment.glsl.js

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,48 @@ void computeMultiscattering( const in vec3 normal, const in vec3 viewDir, const
426426
427427
}
428428
429+
// GGX BRDF with multi-scattering energy compensation for direct lighting
430+
// Based on "Practical Multiple Scattering Compensation for Microfacet Models"
431+
// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
432+
vec3 BRDF_GGX_Multiscatter( const in vec3 lightDir, const in vec3 viewDir, const in vec3 normal, const in PhysicalMaterial material ) {
433+
434+
// Single-scattering BRDF (standard GGX)
435+
vec3 singleScatter = BRDF_GGX( lightDir, viewDir, normal, material );
436+
437+
// Multi-scattering compensation
438+
float dotNL = saturate( dot( normal, lightDir ) );
439+
float dotNV = saturate( dot( normal, viewDir ) );
440+
441+
// Precomputed DFG values for view and light directions
442+
vec2 dfgV = DFGApprox( vec3(0.0, 0.0, 1.0), vec3(sqrt(1.0 - dotNV * dotNV), 0.0, dotNV), material.roughness );
443+
vec2 dfgL = DFGApprox( vec3(0.0, 0.0, 1.0), vec3(sqrt(1.0 - dotNL * dotNL), 0.0, dotNL), material.roughness );
444+
445+
// Single-scattering energy for view and light
446+
vec3 FssEss_V = material.specularColor * dfgV.x + material.specularF90 * dfgV.y;
447+
vec3 FssEss_L = material.specularColor * dfgL.x + material.specularF90 * dfgL.y;
448+
449+
float Ess_V = dfgV.x + dfgV.y;
450+
float Ess_L = dfgL.x + dfgL.y;
451+
452+
// Energy lost to multiple scattering
453+
float Ems_V = 1.0 - Ess_V;
454+
float Ems_L = 1.0 - Ess_L;
455+
456+
// Average Fresnel reflectance
457+
vec3 Favg = material.specularColor + ( 1.0 - material.specularColor ) * 0.047619; // 1/21
458+
459+
// Multiple scattering contribution
460+
vec3 Fms = FssEss_V * FssEss_L * Favg / ( 1.0 - Ems_V * Ems_L * Favg * Favg + EPSILON );
461+
462+
// Energy compensation factor
463+
float compensationFactor = Ems_V * Ems_L;
464+
465+
vec3 multiScatter = Fms * compensationFactor;
466+
467+
return singleScatter + multiScatter;
468+
469+
}
470+
429471
#if NUM_RECT_AREA_LIGHTS > 0
430472
431473
void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in vec3 geometryPosition, const in vec3 geometryNormal, const in vec3 geometryViewDir, const in vec3 geometryClearcoatNormal, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
@@ -490,7 +532,7 @@ void RE_Direct_Physical( const in IncidentLight directLight, const in vec3 geome
490532
491533
#endif
492534
493-
reflectedLight.directSpecular += irradiance * BRDF_GGX( directLight.direction, geometryViewDir, geometryNormal, material );
535+
reflectedLight.directSpecular += irradiance * BRDF_GGX_Multiscatter( directLight.direction, geometryViewDir, geometryNormal, material );
494536
495537
reflectedLight.directDiffuse += irradiance * BRDF_Lambert( material.diffuseColor );
496538
}

0 commit comments

Comments
 (0)