Browse Source

LightingModel: New class struct (#26486)

* LightingModel: New class struct

* cleanup

* cleanup
sunag 2 years ago
parent
commit
93996d78f5

+ 3 - 4
examples/jsm/nodes/Nodes.js

@@ -12,7 +12,7 @@ export { default as CacheNode, cache } from './core/CacheNode.js';
 export { default as ConstNode } from './core/ConstNode.js';
 export { default as ContextNode, context, label } from './core/ContextNode.js';
 export { default as IndexNode, vertexIndex, instanceIndex } from './core/IndexNode.js';
-export { default as LightingModel, lightingModel } from './core/LightingModel.js';
+export { default as LightingModel } from './core/LightingModel.js';
 export { default as Node, addNodeClass, createNodeFromType } from './core/Node.js';
 export { default as NodeAttribute } from './core/NodeAttribute.js';
 export { default as NodeBuilder } from './core/NodeBuilder.js';
@@ -155,7 +155,6 @@ export * from './materials/Materials.js';
 export * from './materialx/MaterialXNodes.js';
 
 // functions
-export { default as BRDF_BlinnPhong } from './functions/BSDF/BRDF_BlinnPhong.js';
 export { default as BRDF_GGX } from './functions/BSDF/BRDF_GGX.js';
 export { default as BRDF_Lambert } from './functions/BSDF/BRDF_Lambert.js';
 export { default as D_GGX } from './functions/BSDF/D_GGX.js';
@@ -168,5 +167,5 @@ export { getDistanceAttenuation } from './lighting/LightUtils.js';
 export { default as getGeometryRoughness } from './functions/material/getGeometryRoughness.js';
 export { default as getRoughness } from './functions/material/getRoughness.js';
 
-export { default as phongLightingModel } from './functions/PhongLightingModel.js';
-export { default as physicalLightingModel } from './functions/PhysicalLightingModel.js';
+export { default as PhongLightingModel } from './functions/PhongLightingModel.js';
+export { default as PhysicalLightingModel } from './functions/PhysicalLightingModel.js';

+ 7 - 9
examples/jsm/nodes/core/LightingModel.js

@@ -1,17 +1,15 @@
 class LightingModel {
 
-	constructor( init = null, direct = null, indirectDiffuse = null, indirectSpecular = null, ambientOcclusion = null ) {
+	init( /*input, stack, builder*/ ) { }
 
-		this.init = init;
-		this.direct = direct;
-		this.indirectDiffuse = indirectDiffuse;
-		this.indirectSpecular = indirectSpecular;
-		this.ambientOcclusion = ambientOcclusion;
+	direct( /*input, stack, builder*/ ) { }
 
-	}
+	indirectDiffuse( /*input, stack, builder*/ ) { }
+
+	indirectSpecular( /*input, stack, builder*/ ) { }
+
+	ambientOcclusion( /*input, stack, builder*/ ) { }
 
 }
 
 export default LightingModel;
-
-export const lightingModel = ( ...params ) => new LightingModel( ...params );

+ 0 - 30
examples/jsm/nodes/functions/BSDF/BRDF_BlinnPhong.js

@@ -1,30 +0,0 @@
-import F_Schlick from './F_Schlick.js';
-import { shininess, specularColor } from '../../core/PropertyNode.js';
-import { transformedNormalView } from '../../accessors/NormalNode.js';
-import { positionViewDirection } from '../../accessors/PositionNode.js';
-import { tslFn, float } from '../../shadernode/ShaderNode.js';
-
-const G_BlinnPhong_Implicit = () => float( 0.25 );
-
-const D_BlinnPhong = tslFn( ( { dotNH } ) => {
-
-	return shininess.mul( 0.5 / Math.PI ).add( 1.0 ).mul( dotNH.pow( shininess ) );
-
-} );
-
-const BRDF_BlinnPhong = tslFn( ( { lightDirection } ) => {
-
-	const halfDir = lightDirection.add( positionViewDirection ).normalize();
-
-	const dotNH = transformedNormalView.dot( halfDir ).clamp();
-	const dotVH = positionViewDirection.dot( halfDir ).clamp();
-
-	const F = F_Schlick( { f0: specularColor, f90: 1.0, dotVH } );
-	const G = G_BlinnPhong_Implicit();
-	const D = D_BlinnPhong( { dotNH } );
-
-	return F.mul( G ).mul( D );
-
-} );
-
-export default BRDF_BlinnPhong;

+ 0 - 24
examples/jsm/nodes/functions/LambertLightingModel.js

@@ -1,24 +0,0 @@
-import BRDF_Lambert from './BSDF/BRDF_Lambert.js';
-import { lightingModel } from '../core/LightingModel.js';
-import { diffuseColor } from '../core/PropertyNode.js';
-import { transformedNormalView } from '../accessors/NormalNode.js';
-import { tslFn } from '../shadernode/ShaderNode.js';
-
-const RE_Direct_Lambert = tslFn( ( { lightDirection, lightColor, reflectedLight } ) => {
-
-	const dotNL = transformedNormalView.dot( lightDirection ).clamp();
-	const irradiance = dotNL.mul( lightColor );
-
-	reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
-
-} );
-
-const RE_IndirectDiffuse_Lambert = tslFn( ( { irradiance, reflectedLight } ) => {
-
-	reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
-
-} );
-
-const lambertLightingModel = lightingModel( null, RE_Direct_Lambert, RE_IndirectDiffuse_Lambert );
-
-export default lambertLightingModel;

+ 53 - 14
examples/jsm/nodes/functions/PhongLightingModel.js

@@ -1,28 +1,67 @@
+import LightingModel from '../core/LightingModel.js';
+import F_Schlick from './BSDF/F_Schlick.js';
 import BRDF_Lambert from './BSDF/BRDF_Lambert.js';
-import BRDF_BlinnPhong from './BSDF/BRDF_BlinnPhong.js';
-import { lightingModel } from '../core/LightingModel.js';
 import { diffuseColor } from '../core/PropertyNode.js';
-import { materialSpecularStrength } from '../accessors/MaterialNode.js';
 import { transformedNormalView } from '../accessors/NormalNode.js';
-import { tslFn } from '../shadernode/ShaderNode.js';
-
-const RE_Direct_BlinnPhong = tslFn( ( { lightDirection, lightColor, reflectedLight } ) => {
+import { materialSpecularStrength } from '../accessors/MaterialNode.js';
+import { shininess, specularColor } from '../core/PropertyNode.js';
+import { positionViewDirection } from '../accessors/PositionNode.js';
+import { tslFn, float } from '../shadernode/ShaderNode.js';
 
-	const dotNL = transformedNormalView.dot( lightDirection ).clamp();
-	const irradiance = dotNL.mul( lightColor );
+const G_BlinnPhong_Implicit = () => float( 0.25 );
 
-	reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
+const D_BlinnPhong = tslFn( ( { dotNH } ) => {
 
-	reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_BlinnPhong( { lightDirection } ) ).mul( materialSpecularStrength ) );
+	return shininess.mul( 0.5 / Math.PI ).add( 1.0 ).mul( dotNH.pow( shininess ) );
 
 } );
 
-const RE_IndirectDiffuse_BlinnPhong = tslFn( ( { irradiance, reflectedLight } ) => {
+const BRDF_BlinnPhong = tslFn( ( { lightDirection } ) => {
+
+	const halfDir = lightDirection.add( positionViewDirection ).normalize();
+
+	const dotNH = transformedNormalView.dot( halfDir ).clamp();
+	const dotVH = positionViewDirection.dot( halfDir ).clamp();
+
+	const F = F_Schlick( { f0: specularColor, f90: 1.0, dotVH } );
+	const G = G_BlinnPhong_Implicit();
+	const D = D_BlinnPhong( { dotNH } );
 
-	reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
+	return F.mul( G ).mul( D );
 
 } );
 
-const phongLightingModel = lightingModel( null, RE_Direct_BlinnPhong, RE_IndirectDiffuse_BlinnPhong );
+class PhongLightingModel extends LightingModel {
+
+	constructor( specular = true ) {
+
+		super();
+
+		this.specular = specular;
+
+	}
+
+	direct( { lightDirection, lightColor, reflectedLight } ) {
+
+		const dotNL = transformedNormalView.dot( lightDirection ).clamp();
+		const irradiance = dotNL.mul( lightColor );
+
+		reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
+
+		if ( this.specular === true ) {
+
+			reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_BlinnPhong( { lightDirection } ) ).mul( materialSpecularStrength ) );
+
+		}
+
+	}
+
+	indirectDiffuse( { irradiance, reflectedLight } ) {
+
+		reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
+
+	}
+
+}
 
-export default phongLightingModel;
+export default PhongLightingModel;

+ 90 - 82
examples/jsm/nodes/functions/PhysicalLightingModel.js

@@ -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;

+ 7 - 10
examples/jsm/nodes/lighting/DirectionalLightNode.js

@@ -17,20 +17,17 @@ class DirectionalLightNode extends AnalyticLightNode {
 
 		super.construct( builder );
 
+		const lightingModel = builder.context.lightingModel;
+
 		const lightColor = this.colorNode;
 		const lightDirection = lightTargetDirection( this.light );
-		const lightingModelFunctionNode = builder.context.lightingModelNode;
 		const reflectedLight = builder.context.reflectedLight;
 
-		if ( lightingModelFunctionNode && lightingModelFunctionNode.direct ) {
-
-			lightingModelFunctionNode.direct( {
-				lightDirection,
-				lightColor,
-				reflectedLight
-			} );
-
-		}
+		lightingModel.direct( {
+			lightDirection,
+			lightColor,
+			reflectedLight
+		} );
 
 	}
 

+ 5 - 7
examples/jsm/nodes/lighting/EnvironmentNode.js

@@ -56,22 +56,20 @@ class EnvironmentNode extends LightingNode {
 
 		//
 
-		let isolateClearcoatRadiance = null;
+		const clearcoatRadiance = builder.context.lightingModel.clearcoatRadiance;
 
-		if ( builder.context.clearcoatRadiance  ) {
+		if ( clearcoatRadiance ) {
 
-			const clearcoatRadiance = context( envNode, createRadianceContext( clearcoatRoughness, transformedClearcoatNormalView ) ).mul( intensity );
+			const clearcoatRadianceContext = context( envNode, createRadianceContext( clearcoatRoughness, transformedClearcoatNormalView ) ).mul( intensity );
+			const isolateClearcoatRadiance = cache( clearcoatRadianceContext );
 
-			isolateClearcoatRadiance = cache( clearcoatRadiance );
-
-			builder.context.clearcoatRadiance.addAssign( isolateClearcoatRadiance );
+			clearcoatRadiance.addAssign( isolateClearcoatRadiance );
 
 		}
 
 		//
 
 		properties.radiance = isolateRadiance;
-		properties.clearcoatRadiance = isolateClearcoatRadiance;
 		properties.irradiance = irradiance;
 
 	}

+ 12 - 9
examples/jsm/nodes/lighting/LightingContextNode.js

@@ -6,11 +6,11 @@ import { addNodeElement, nodeProxy, float, vec3 } from '../shadernode/ShaderNode
 
 class LightingContextNode extends ContextNode {
 
-	constructor( node, lightingModelNode = null, backdropNode = null, backdropAlphaNode = null ) {
+	constructor( node, lightingModel = null, backdropNode = null, backdropAlphaNode = null ) {
 
 		super( node );
 
-		this.lightingModelNode = lightingModelNode;
+		this.lightingModel = lightingModel;
 		this.backdropNode = backdropNode;
 		this.backdropAlphaNode = backdropAlphaNode;
 
@@ -24,7 +24,7 @@ class LightingContextNode extends ContextNode {
 
 	construct( builder ) {
 
-		const { lightingModelNode, backdropNode, backdropAlphaNode } = this;
+		const { lightingModel, backdropNode, backdropAlphaNode } = this;
 
 		const context = this.context = {}; // reset context
 		const properties = builder.getNodeProperties( this );
@@ -61,17 +61,20 @@ class LightingContextNode extends ContextNode {
 		};
 
 		context.reflectedLight = reflectedLight;
-		context.lightingModelNode = lightingModelNode || context.lightingModelNode;
+		context.lightingModel = lightingModel || context.lightingModel;
 
 		Object.assign( properties, reflectedLight, lighting );
 		Object.assign( context, lighting );
 
-		// @TODO: Call needed return a new node ( or rename the ShaderNodeInternal.call() function ), it's not moment to run
-		if ( lightingModelNode && lightingModelNode.init ) lightingModelNode.init( context, builder.stack, builder );
+		if ( lightingModel ) {
 
-		if ( lightingModelNode && lightingModelNode.indirectDiffuse ) lightingModelNode.indirectDiffuse( context, builder.stack, builder );
-		if ( lightingModelNode && lightingModelNode.indirectSpecular ) lightingModelNode.indirectSpecular( context, builder.stack, builder );
-		if ( lightingModelNode && lightingModelNode.ambientOcclusion ) lightingModelNode.ambientOcclusion( context, builder.stack, builder );
+			lightingModel.init( context, builder.stack, builder );
+
+			lightingModel.indirectDiffuse( context, builder.stack, builder );
+			lightingModel.indirectSpecular( context, builder.stack, builder );
+			lightingModel.ambientOcclusion( context, builder.stack, builder );
+
+		}
 
 		return super.construct( builder );
 

+ 7 - 10
examples/jsm/nodes/lighting/PointLightNode.js

@@ -34,6 +34,8 @@ class PointLightNode extends AnalyticLightNode {
 
 		const { colorNode, cutoffDistanceNode, decayExponentNode, light } = this;
 
+		const lightingModel = builder.context.lightingModel;
+
 		const lVector = objectViewPosition( light ).sub( positionView ); // @TODO: Add it into LightNode
 
 		const lightDirection = lVector.normalize();
@@ -47,18 +49,13 @@ class PointLightNode extends AnalyticLightNode {
 
 		const lightColor = colorNode.mul( lightAttenuation );
 
-		const lightingModelFunctionNode = builder.context.lightingModelNode;
 		const reflectedLight = builder.context.reflectedLight;
 
-		if ( lightingModelFunctionNode && lightingModelFunctionNode.direct ) {
-
-			lightingModelFunctionNode.direct( {
-				lightDirection,
-				lightColor,
-				reflectedLight
-			} );
-
-		}
+		lightingModel.direct( {
+			lightDirection,
+			lightColor,
+			reflectedLight
+		} );
 
 	}
 

+ 7 - 10
examples/jsm/nodes/lighting/SpotLightNode.js

@@ -50,6 +50,8 @@ class SpotLightNode extends AnalyticLightNode {
 
 		super.construct( builder );
 
+		const lightingModel = builder.context.lightingModel;
+
 		const { colorNode, cutoffDistanceNode, decayExponentNode, light } = this;
 
 		const lVector = objectViewPosition( light ).sub( positionView ); // @TODO: Add it into LightNode
@@ -68,18 +70,13 @@ class SpotLightNode extends AnalyticLightNode {
 
 		const lightColor = colorNode.mul( spotAttenuation ).mul( lightAttenuation );
 
-		const lightingModelFunctionNode = builder.context.lightingModelNode;
 		const reflectedLight = builder.context.reflectedLight;
 
-		if ( lightingModelFunctionNode && lightingModelFunctionNode.direct ) {
-
-			lightingModelFunctionNode.direct( {
-				lightDirection,
-				lightColor,
-				reflectedLight
-			} );
-
-		}
+		lightingModel.direct( {
+			lightDirection,
+			lightColor,
+			reflectedLight
+		} );
 
 	}
 

+ 2 - 2
examples/jsm/nodes/materials/MeshLambertNodeMaterial.js

@@ -1,5 +1,5 @@
 import NodeMaterial, { addNodeMaterial } from './NodeMaterial.js';
-import lambertLightingModel from '../functions/LambertLightingModel.js';
+import PhongLightingModel from '../functions/PhongLightingModel.js';
 
 import { MeshLambertMaterial } from 'three';
 
@@ -23,7 +23,7 @@ class MeshLambertNodeMaterial extends NodeMaterial {
 
 	constructLightingModel( /*builder*/ ) {
 
-		return lambertLightingModel;
+		return new PhongLightingModel( false ); // ( specular ) -> force lambert
 
 	}
 

+ 2 - 2
examples/jsm/nodes/materials/MeshPhongNodeMaterial.js

@@ -1,8 +1,8 @@
 import NodeMaterial, { addNodeMaterial } from './NodeMaterial.js';
 import { shininess, specularColor } from '../core/PropertyNode.js';
 import { materialShininess, materialSpecularColor } from '../accessors/MaterialNode.js';
-import phongLightingModel from '../functions/PhongLightingModel.js';
 import { float } from '../shadernode/ShaderNode.js';
+import PhongLightingModel from '../functions/PhongLightingModel.js';
 
 import { MeshPhongMaterial } from 'three';
 
@@ -29,7 +29,7 @@ class MeshPhongNodeMaterial extends NodeMaterial {
 
 	constructLightingModel( /*builder*/ ) {
 
-		return phongLightingModel;
+		return new PhongLightingModel();
 
 	}
 

+ 7 - 0
examples/jsm/nodes/materials/MeshPhysicalNodeMaterial.js

@@ -4,6 +4,7 @@ import { clearcoat, clearcoatRoughness, sheen, sheenRoughness } from '../core/Pr
 import { materialClearcoatNormal } from '../accessors/ExtendedMaterialNode.js';
 import { materialClearcoat, materialClearcoatRoughness, materialSheen, materialSheenRoughness } from '../accessors/MaterialNode.js';
 import { float, vec3 } from '../shadernode/ShaderNode.js';
+import PhysicalLightingModel from '../functions/PhysicalLightingModel.js';
 import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js';
 
 import { MeshPhysicalMaterial } from 'three';
@@ -43,6 +44,12 @@ class MeshPhysicalNodeMaterial extends MeshStandardNodeMaterial {
 
 	}
 
+	constructLightingModel( /*builder*/ ) {
+
+		return new PhysicalLightingModel();
+
+	}
+
 	constructVariants( builder ) {
 
 		super.constructVariants( builder );

+ 2 - 2
examples/jsm/nodes/materials/MeshStandardNodeMaterial.js

@@ -3,7 +3,7 @@ import { diffuseColor, metalness, roughness, specularColor } from '../core/Prope
 import { mix } from '../math/MathNode.js';
 import { materialRoughness, materialMetalness } from '../accessors/MaterialNode.js';
 import getRoughness from '../functions/material/getRoughness.js';
-import physicalLightingModel from '../functions/PhysicalLightingModel.js';
+import PhysicalLightingModel from '../functions/PhysicalLightingModel.js';
 import { float, vec3, vec4 } from '../shadernode/ShaderNode.js';
 
 import { MeshStandardMaterial } from 'three';
@@ -31,7 +31,7 @@ class MeshStandardNodeMaterial extends NodeMaterial {
 
 	constructLightingModel( /*builder*/ ) {
 
-		return physicalLightingModel;
+		return new PhysicalLightingModel( false, false ); // ( clearcoat, sheen ) -> standard
 
 	}
 

+ 13 - 8
examples/webgpu_lights_custom.html

@@ -26,13 +26,23 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { tslFn, color, lights, toneMapping, MeshStandardNodeMaterial, PointsNodeMaterial } from 'three/nodes';
+			import { color, lights, toneMapping, MeshStandardNodeMaterial, PointsNodeMaterial, LightingModel } from 'three/nodes';
 
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 
+			class CustomLightingModel extends LightingModel {
+
+				direct( { lightColor, reflectedLight } ) {
+
+					reflectedLight.directDiffuse.addAssign( lightColor );
+
+				}
+
+			}
+
 			let camera, scene, renderer;
 
 			let light1, light2, light3;
@@ -100,13 +110,8 @@
 
 				// custom lighting model
 
-				const customLightingModel = tslFn( ( { lightColor, reflectedLight } ) => {
-
-					reflectedLight.directDiffuse.addAssign( lightColor );
-
-				} );
-
-				const lightingModelContext = allLightsNode.context( { lightingModelNode: { direct: customLightingModel } } );
+				const lightingModel = new CustomLightingModel();
+				const lightingModelContext = allLightsNode.context( { lightingModel } );
 
 				materialPoints.lightsNode = lightingModelContext;