|
@@ -4,11 +4,11 @@ import DFGApprox from './BSDF/DFGApprox.js';
|
|
|
import EnvironmentBRDF from './BSDF/EnvironmentBRDF.js';
|
|
|
import F_Schlick from './BSDF/F_Schlick.js';
|
|
|
import BRDF_Sheen from './BSDF/BRDF_Sheen.js';
|
|
|
-import { lightingModel } from '../core/LightingModel.js';
|
|
|
+import LightingModel from '../core/LightingModel.js';
|
|
|
import { diffuseColor, specularColor, roughness, clearcoat, clearcoatRoughness, sheen, sheenRoughness } from '../core/PropertyNode.js';
|
|
|
import { transformedNormalView, transformedClearcoatNormalView } from '../accessors/NormalNode.js';
|
|
|
import { positionViewDirection } from '../accessors/PositionNode.js';
|
|
|
-import { tslFn, float, vec3 } from '../shadernode/ShaderNode.js';
|
|
|
+import { float, vec3 } from '../shadernode/ShaderNode.js';
|
|
|
import { cond } from '../math/CondNode.js';
|
|
|
|
|
|
const clearcoatF0 = vec3( 0.04 );
|
|
@@ -61,144 +61,152 @@ const computeMultiscattering = ( singleScatter, multiScatter, specularF90 = floa
|
|
|
|
|
|
};
|
|
|
|
|
|
-const LM_Init = tslFn( ( context, stack, builder ) => {
|
|
|
+//
|
|
|
|
|
|
- if ( builder.includes( clearcoat ) ) {
|
|
|
+class PhysicalLightingModel extends LightingModel {
|
|
|
|
|
|
- context.clearcoatRadiance = vec3().temp();
|
|
|
- context.reflectedLight.clearcoatSpecular = vec3().temp();
|
|
|
+ constructor( clearcoat = true, sheen = true ) {
|
|
|
|
|
|
- const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
|
|
|
+ super();
|
|
|
|
|
|
- const Fcc = F_Schlick( {
|
|
|
- dotVH: dotNVcc,
|
|
|
- f0: clearcoatF0,
|
|
|
- f90: clearcoatF90
|
|
|
- } );
|
|
|
+ this.clearcoat = clearcoat;
|
|
|
+ this.sheen = sheen;
|
|
|
|
|
|
- const outgoingLight = context.reflectedLight.total;
|
|
|
- const clearcoatLight = outgoingLight.mul( clearcoat.mul( Fcc ).oneMinus() ).add( context.reflectedLight.clearcoatSpecular.mul( clearcoat ) );
|
|
|
-
|
|
|
- outgoingLight.assign( clearcoatLight );
|
|
|
+ this.clearcoatRadiance = null;
|
|
|
+ this.clearcoatSpecular = null;
|
|
|
+ this.sheenSpecular = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( builder.includes( sheen ) ) {
|
|
|
+ init( { reflectedLight } ) {
|
|
|
|
|
|
- context.reflectedLight.sheenSpecular = vec3().temp();
|
|
|
+ if ( this.clearcoat === true ) {
|
|
|
|
|
|
- const outgoingLight = context.reflectedLight.total;
|
|
|
+ this.clearcoatRadiance = vec3().temp();
|
|
|
+ this.clearcoatSpecular = vec3().temp();
|
|
|
|
|
|
- const sheenEnergyComp = sheen.r.max( sheen.g ).max( sheen.b ).mul( 0.157 ).oneMinus();
|
|
|
- const sheenLight = outgoingLight.mul( sheenEnergyComp ).add( context.reflectedLight.sheenSpecular );
|
|
|
+ const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
|
|
|
|
|
|
- outgoingLight.assign( sheenLight );
|
|
|
+ const Fcc = F_Schlick( {
|
|
|
+ dotVH: dotNVcc,
|
|
|
+ f0: clearcoatF0,
|
|
|
+ f90: clearcoatF90
|
|
|
+ } );
|
|
|
|
|
|
- }
|
|
|
+ const outgoingLight = reflectedLight.total;
|
|
|
+ const clearcoatLight = outgoingLight.mul( clearcoat.mul( Fcc ).oneMinus() ).add( this.clearcoatSpecular.mul( clearcoat ) );
|
|
|
+
|
|
|
+ outgoingLight.assign( clearcoatLight );
|
|
|
|
|
|
-} );
|
|
|
+ }
|
|
|
|
|
|
-const RE_IndirectSpecular_Physical = tslFn( ( context ) => {
|
|
|
+ if ( this.sheen === true ) {
|
|
|
|
|
|
- const { radiance, iblIrradiance, reflectedLight } = context;
|
|
|
+ this.sheenSpecular = vec3().temp();
|
|
|
|
|
|
- if ( reflectedLight.sheenSpecular ) {
|
|
|
+ const outgoingLight = reflectedLight.total;
|
|
|
|
|
|
- reflectedLight.sheenSpecular.addAssign( iblIrradiance.mul(
|
|
|
- sheen,
|
|
|
- IBLSheenBRDF( transformedNormalView, positionViewDirection, sheenRoughness )
|
|
|
- ) );
|
|
|
+ const sheenEnergyComp = sheen.r.max( sheen.g ).max( sheen.b ).mul( 0.157 ).oneMinus();
|
|
|
+ const sheenLight = outgoingLight.mul( sheenEnergyComp ).add( this.sheenSpecular );
|
|
|
+
|
|
|
+ outgoingLight.assign( sheenLight );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
}
|
|
|
|
|
|
- if ( reflectedLight.clearcoatSpecular ) {
|
|
|
+ direct( { lightDirection, lightColor, reflectedLight } ) {
|
|
|
|
|
|
- const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
|
|
|
+ const dotNL = transformedNormalView.dot( lightDirection ).clamp();
|
|
|
+ const irradiance = dotNL.mul( lightColor );
|
|
|
|
|
|
- const clearcoatEnv = EnvironmentBRDF( {
|
|
|
- dotNV: dotNVcc,
|
|
|
- specularColor: clearcoatF0,
|
|
|
- specularF90: clearcoatF90,
|
|
|
- roughness: clearcoatRoughness
|
|
|
- } );
|
|
|
+ if ( this.sheen === true ) {
|
|
|
|
|
|
- reflectedLight.clearcoatSpecular.addAssign( context.clearcoatRadiance.mul( clearcoatEnv ) );
|
|
|
+ this.sheenSpecular.addAssign( irradiance.mul( BRDF_Sheen( { lightDirection } ) ) );
|
|
|
|
|
|
- }
|
|
|
+ }
|
|
|
|
|
|
- // Both indirect specular and indirect diffuse light accumulate here
|
|
|
+ if ( this.clearcoat === true ) {
|
|
|
|
|
|
- const singleScattering = vec3().temp();
|
|
|
- const multiScattering = vec3().temp();
|
|
|
- const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
|
|
|
+ const dotNLcc = transformedClearcoatNormalView.dot( lightDirection ).clamp();
|
|
|
+ const ccIrradiance = dotNLcc.mul( lightColor );
|
|
|
|
|
|
- computeMultiscattering( singleScattering, multiScattering );
|
|
|
+ this.clearcoatSpecular.addAssign( ccIrradiance.mul( BRDF_GGX( { lightDirection, f0: clearcoatF0, f90: clearcoatF90, roughness: clearcoatRoughness, normalView: transformedClearcoatNormalView } ) ) );
|
|
|
|
|
|
- const totalScattering = singleScattering.add( multiScattering );
|
|
|
+ }
|
|
|
|
|
|
- const diffuse = diffuseColor.mul( totalScattering.r.max( totalScattering.g ).max( totalScattering.b ).oneMinus() );
|
|
|
+ reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
|
|
|
|
|
|
- reflectedLight.indirectSpecular.addAssign( radiance.mul( singleScattering ) );
|
|
|
- reflectedLight.indirectSpecular.addAssign( multiScattering.mul( cosineWeightedIrradiance ) );
|
|
|
+ reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
|
|
|
|
|
|
- reflectedLight.indirectDiffuse.addAssign( diffuse.mul( cosineWeightedIrradiance ) );
|
|
|
+ }
|
|
|
|
|
|
-} );
|
|
|
+ indirectDiffuse( { irradiance, reflectedLight } ) {
|
|
|
|
|
|
-const RE_IndirectDiffuse_Physical = tslFn( ( context ) => {
|
|
|
+ reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
|
|
|
|
|
|
- const { irradiance, reflectedLight } = context;
|
|
|
+ }
|
|
|
|
|
|
- reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
|
|
|
+ indirectSpecular( { radiance, iblIrradiance, reflectedLight, } ) {
|
|
|
|
|
|
-} );
|
|
|
+ if ( this.sheen === true ) {
|
|
|
|
|
|
-const RE_Direct_Physical = tslFn( ( inputs ) => {
|
|
|
+ this.sheenSpecular.addAssign( iblIrradiance.mul(
|
|
|
+ sheen,
|
|
|
+ IBLSheenBRDF( transformedNormalView, positionViewDirection, sheenRoughness )
|
|
|
+ ) );
|
|
|
|
|
|
- const { lightDirection, lightColor, reflectedLight } = inputs;
|
|
|
+ }
|
|
|
|
|
|
- const dotNL = transformedNormalView.dot( lightDirection ).clamp();
|
|
|
- const irradiance = dotNL.mul( lightColor );
|
|
|
+ if ( this.clearcoat === true ) {
|
|
|
|
|
|
- if ( reflectedLight.sheenSpecular ) {
|
|
|
+ const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
|
|
|
|
|
|
- reflectedLight.sheenSpecular.addAssign( irradiance.mul( BRDF_Sheen( { lightDirection } ) ) );
|
|
|
+ const clearcoatEnv = EnvironmentBRDF( {
|
|
|
+ dotNV: dotNVcc,
|
|
|
+ specularColor: clearcoatF0,
|
|
|
+ specularF90: clearcoatF90,
|
|
|
+ roughness: clearcoatRoughness
|
|
|
+ } );
|
|
|
|
|
|
- }
|
|
|
+ this.clearcoatSpecular.addAssign( this.clearcoatRadiance.mul( clearcoatEnv ) );
|
|
|
|
|
|
- if ( reflectedLight.clearcoatSpecular ) {
|
|
|
+ }
|
|
|
|
|
|
- const dotNLcc = transformedClearcoatNormalView.dot( lightDirection ).clamp();
|
|
|
- const ccIrradiance = dotNLcc.mul( lightColor );
|
|
|
+ // Both indirect specular and indirect diffuse light accumulate here
|
|
|
|
|
|
- reflectedLight.clearcoatSpecular.addAssign( ccIrradiance.mul( BRDF_GGX( { lightDirection, f0: clearcoatF0, f90: clearcoatF90, roughness: clearcoatRoughness, normalView: transformedClearcoatNormalView } ) ) );
|
|
|
+ const singleScattering = vec3().temp();
|
|
|
+ const multiScattering = vec3().temp();
|
|
|
+ const cosineWeightedIrradiance = iblIrradiance.mul( 1 / Math.PI );
|
|
|
|
|
|
- }
|
|
|
+ computeMultiscattering( singleScattering, multiScattering );
|
|
|
|
|
|
- reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
|
|
|
+ const totalScattering = singleScattering.add( multiScattering );
|
|
|
|
|
|
- reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
|
|
|
+ const diffuse = diffuseColor.mul( totalScattering.r.max( totalScattering.g ).max( totalScattering.b ).oneMinus() );
|
|
|
|
|
|
-} );
|
|
|
+ reflectedLight.indirectSpecular.addAssign( radiance.mul( singleScattering ) );
|
|
|
+ reflectedLight.indirectSpecular.addAssign( multiScattering.mul( cosineWeightedIrradiance ) );
|
|
|
|
|
|
-const RE_AmbientOcclusion_Physical = tslFn( ( context ) => {
|
|
|
+ reflectedLight.indirectDiffuse.addAssign( diffuse.mul( cosineWeightedIrradiance ) );
|
|
|
|
|
|
- const { ambientOcclusion, reflectedLight } = context;
|
|
|
+ }
|
|
|
|
|
|
- const dotNV = transformedNormalView.dot( positionViewDirection ).clamp(); // @ TODO: Move to core dotNV
|
|
|
+ ambientOcclusion( { ambientOcclusion, reflectedLight } ) {
|
|
|
|
|
|
- const aoNV = dotNV.add( ambientOcclusion );
|
|
|
- const aoExp = roughness.mul( - 16.0 ).oneMinus().negate().exp2();
|
|
|
+ const dotNV = transformedNormalView.dot( positionViewDirection ).clamp(); // @ TODO: Move to core dotNV
|
|
|
|
|
|
- const aoNode = ambientOcclusion.sub( aoNV.pow( aoExp ).oneMinus() ).clamp();
|
|
|
+ const aoNV = dotNV.add( ambientOcclusion );
|
|
|
+ const aoExp = roughness.mul( - 16.0 ).oneMinus().negate().exp2();
|
|
|
|
|
|
- reflectedLight.indirectDiffuse.mulAssign( ambientOcclusion );
|
|
|
+ const aoNode = ambientOcclusion.sub( aoNV.pow( aoExp ).oneMinus() ).clamp();
|
|
|
|
|
|
- reflectedLight.indirectSpecular.mulAssign( aoNode );
|
|
|
+ reflectedLight.indirectDiffuse.mulAssign( ambientOcclusion );
|
|
|
|
|
|
+ reflectedLight.indirectSpecular.mulAssign( aoNode );
|
|
|
|
|
|
-} );
|
|
|
+ }
|
|
|
|
|
|
-const physicalLightingModel = lightingModel( LM_Init, RE_Direct_Physical, RE_IndirectDiffuse_Physical, RE_IndirectSpecular_Physical, RE_AmbientOcclusion_Physical );
|
|
|
+}
|
|
|
|
|
|
-export default physicalLightingModel;
|
|
|
+export default PhysicalLightingModel;
|