|
| 1 | +import BRDF_GGX from './BRDF_GGX.js'; |
| 2 | +import DFGApprox from './DFGApprox.js'; |
| 3 | +import { normalView } from '../../accessors/Normal.js'; |
| 4 | +import { positionViewDirection } from '../../accessors/Position.js'; |
| 5 | +import { EPSILON } from '../../math/MathNode.js'; |
| 6 | +import { Fn, float } from '../../tsl/TSLBase.js'; |
| 7 | + |
| 8 | +// GGX BRDF with multi-scattering energy compensation for direct lighting |
| 9 | +// This provides more accurate energy conservation, especially for rough materials |
| 10 | +// Based on "Practical Multiple Scattering Compensation for Microfacet Models" |
| 11 | +// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf |
| 12 | +const BRDF_GGX_Multiscatter = /*@__PURE__*/ Fn( ( { lightDirection, f0, f90, roughness: _roughness, f, USE_IRIDESCENCE, USE_ANISOTROPY } ) => { |
| 13 | + |
| 14 | + // Single-scattering BRDF (standard GGX) |
| 15 | + const singleScatter = BRDF_GGX( { lightDirection, f0, f90, roughness: _roughness, f, USE_IRIDESCENCE, USE_ANISOTROPY } ); |
| 16 | + |
| 17 | + // Multi-scattering compensation |
| 18 | + const dotNL = normalView.dot( lightDirection ).clamp(); |
| 19 | + const dotNV = normalView.dot( positionViewDirection ).clamp(); |
| 20 | + |
| 21 | + // Precomputed DFG values for view and light directions |
| 22 | + const dfgV = DFGApprox( { roughness: _roughness, dotNV } ); |
| 23 | + const dfgL = DFGApprox( { roughness: _roughness, dotNV: dotNL } ); |
| 24 | + |
| 25 | + // Single-scattering energy for view and light |
| 26 | + const FssEss_V = f0.mul( dfgV.x ).add( f90.mul( dfgV.y ) ); |
| 27 | + const FssEss_L = f0.mul( dfgL.x ).add( f90.mul( dfgL.y ) ); |
| 28 | + |
| 29 | + const Ess_V = dfgV.x.add( dfgV.y ); |
| 30 | + const Ess_L = dfgL.x.add( dfgL.y ); |
| 31 | + |
| 32 | + // Energy lost to multiple scattering |
| 33 | + const Ems_V = float( 1.0 ).sub( Ess_V ); |
| 34 | + const Ems_L = float( 1.0 ).sub( Ess_L ); |
| 35 | + |
| 36 | + // Average Fresnel reflectance |
| 37 | + const Favg = f0.add( f0.oneMinus().mul( 0.047619 ) ); // 1/21 |
| 38 | + |
| 39 | + // Multiple scattering contribution |
| 40 | + // Uses geometric mean of view and light contributions for better energy distribution |
| 41 | + const Fms = FssEss_V.mul( FssEss_L ).mul( Favg ).div( float( 1.0 ).sub( Ems_V.mul( Ems_L ).mul( Favg ).mul( Favg ) ).add( EPSILON ) ); |
| 42 | + |
| 43 | + // Energy compensation factor |
| 44 | + const compensationFactor = Ems_V.mul( Ems_L ); |
| 45 | + |
| 46 | + const multiScatter = Fms.mul( compensationFactor ); |
| 47 | + |
| 48 | + return singleScatter.add( multiScatter ); |
| 49 | + |
| 50 | +} ); |
| 51 | + |
| 52 | +export default BRDF_GGX_Multiscatter; |
0 commit comments