Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import BRDF_GGX from './BRDF_GGX.js';
import DFGApprox from './DFGApprox.js';
import { specularColor, specularF90, roughness } from '../../core/PropertyNode.js';

Check warning on line 3 in src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js

View workflow job for this annotation

GitHub Actions / Lint, Unit, Unit addons, Circular dependencies & Examples testing

'roughness' is defined but never used

Check warning on line 3 in src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js

View workflow job for this annotation

GitHub Actions / Lint, Unit, Unit addons, Circular dependencies & Examples testing

'specularF90' is defined but never used

Check warning on line 3 in src/nodes/functions/BSDF/BRDF_GGX_Multiscatter.js

View workflow job for this annotation

GitHub Actions / Lint, Unit, Unit addons, Circular dependencies & Examples testing

'specularColor' is defined but never used
import { normalView } from '../../accessors/Normal.js';
import { positionViewDirection } from '../../accessors/Position.js';
import { Fn, float } from '../../tsl/TSLBase.js';

// GGX BRDF with multi-scattering energy compensation for direct lighting
// This provides more accurate energy conservation, especially for rough materials
// Based on "Practical Multiple Scattering Compensation for Microfacet Models"
// https://blog.selfshadow.com/publications/turquin/ms_comp_final.pdf
const BRDF_GGX_Multiscatter = /*@__PURE__*/ Fn( ( { lightDirection, f0, f90, roughness: _roughness, f, USE_IRIDESCENCE, USE_ANISOTROPY } ) => {

// Single-scattering BRDF (standard GGX)
const singleScatter = BRDF_GGX( { lightDirection, f0, f90, roughness: _roughness, f, USE_IRIDESCENCE, USE_ANISOTROPY } );

// Multi-scattering compensation
const dotNL = normalView.dot( lightDirection ).clamp();
const dotNV = normalView.dot( positionViewDirection ).clamp();

// Precomputed DFG values for view and light directions
const dfgV = DFGApprox( { roughness: _roughness, dotNV } );
const dfgL = DFGApprox( { roughness: _roughness, dotNV: dotNL } );

// Single-scattering energy for view and light
const FssEss_V = f0.mul( dfgV.x ).add( f90.mul( dfgV.y ) );
const FssEss_L = f0.mul( dfgL.x ).add( f90.mul( dfgL.y ) );

const Ess_V = dfgV.x.add( dfgV.y );
const Ess_L = dfgL.x.add( dfgL.y );

// Energy lost to multiple scattering
const Ems_V = float( 1.0 ).sub( Ess_V );
const Ems_L = float( 1.0 ).sub( Ess_L );

// Average Fresnel reflectance
const Favg = f0.add( f0.oneMinus().mul( 0.047619 ) ); // 1/21

// Multiple scattering contribution
// Uses geometric mean of view and light contributions for better energy distribution
const Fms = FssEss_V.mul( FssEss_L ).mul( Favg ).div( float( 1.0 ).sub( Ems_V.mul( Ems_L ).mul( Favg ).mul( Favg ) ).add( 1e-5 ) );

// Energy compensation factor
const compensationFactor = Ems_V.mul( Ems_L );

const multiScatter = Fms.mul( compensationFactor );

return singleScatter.add( multiScatter );

} );

export default BRDF_GGX_Multiscatter;
3 changes: 2 additions & 1 deletion src/nodes/functions/PhysicalLightingModel.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import BRDF_Lambert from './BSDF/BRDF_Lambert.js';
import BRDF_GGX from './BSDF/BRDF_GGX.js';
import BRDF_GGX_Multiscatter from './BSDF/BRDF_GGX_Multiscatter.js';
import DFGApprox from './BSDF/DFGApprox.js';
import EnvironmentBRDF from './BSDF/EnvironmentBRDF.js';
import F_Schlick from './BSDF/F_Schlick.js';
Expand Down Expand Up @@ -597,7 +598,7 @@ class PhysicalLightingModel extends LightingModel {

reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );

reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX( { lightDirection, f0: specularColor, f90: 1, roughness, iridescence: this.iridescence, f: this.iridescenceFresnel, USE_IRIDESCENCE: this.iridescence, USE_ANISOTROPY: this.anisotropy } ) ) );
reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX_Multiscatter( { lightDirection, f0: specularColor, f90: 1, roughness, f: this.iridescenceFresnel, USE_IRIDESCENCE: this.iridescence, USE_ANISOTROPY: this.anisotropy } ) ) );

}

Expand Down
Loading