2
0
Эх сурвалжийг харах

NodeMaterial: updates and revision (#23872)

sunag 3 жил өмнө
parent
commit
a31c66ef73
36 өөрчлөгдсөн 478 нэмэгдсэн , 246 устгасан
  1. 3 0
      examples/jsm/nodes/Nodes.js
  2. 1 1
      examples/jsm/nodes/accessors/SkinningNode.js
  3. 1 1
      examples/jsm/nodes/core/ContextNode.js
  4. 39 0
      examples/jsm/nodes/core/VarNode.js
  5. 1 1
      examples/jsm/nodes/display/ColorSpaceNode.js
  6. 1 1
      examples/jsm/nodes/display/NormalMapNode.js
  7. 50 0
      examples/jsm/nodes/display/ToneMappingNode.js
  8. 7 0
      examples/jsm/nodes/fog/FogNode.js
  9. 34 0
      examples/jsm/nodes/functions/BSDF/BRDF_GGX.js
  10. 10 0
      examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js
  11. 19 0
      examples/jsm/nodes/functions/BSDF/D_GGX.js
  12. 19 0
      examples/jsm/nodes/functions/BSDF/F_Schlick.js
  13. 19 0
      examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js
  14. 0 123
      examples/jsm/nodes/functions/BSDFs.js
  15. 28 0
      examples/jsm/nodes/functions/PhysicalLightingModel.js
  16. 0 25
      examples/jsm/nodes/functions/PhysicalMaterialFunctions.js
  17. 23 0
      examples/jsm/nodes/functions/light/getDistanceAttenuation.js
  18. 13 0
      examples/jsm/nodes/functions/material/getGeometryRoughness.js
  19. 19 0
      examples/jsm/nodes/functions/material/getRoughness.js
  20. 9 27
      examples/jsm/nodes/lights/LightContextNode.js
  21. 7 9
      examples/jsm/nodes/lights/LightNode.js
  22. 32 0
      examples/jsm/nodes/lights/ReflectedLightNode.js
  23. 17 3
      examples/jsm/nodes/materials/MeshStandardNodeMaterial.js
  24. 26 7
      examples/jsm/nodes/materials/NodeMaterial.js
  25. 1 1
      examples/jsm/nodes/procedural/CheckerNode.js
  26. 7 8
      examples/jsm/nodes/shadernode/ShaderNodeElements.js
  27. 4 4
      examples/jsm/nodes/shadernode/ShaderNodeUtils.js
  28. 32 0
      examples/jsm/nodes/utils/MaxMipLevelNode.js
  29. 30 0
      examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js
  30. 5 2
      examples/webgl_nodes_playground.html
  31. 3 6
      examples/webgpu_lights_custom.html
  32. 7 4
      examples/webgpu_lights_selective.html
  33. 1 1
      examples/webgpu_materials.html
  34. 3 18
      examples/webgpu_nodes_playground.html
  35. 3 3
      examples/webgpu_skinning.html
  36. 4 1
      examples/webgpu_skinning_instancing.html

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

@@ -46,6 +46,7 @@ import UVNode from './accessors/UVNode.js';
 // display
 import ColorSpaceNode from './display/ColorSpaceNode.js';
 import NormalMapNode from './display/NormalMapNode.js';
+import ToneMappingNode from './display/ToneMappingNode.js';
 
 // math
 import MathNode from './math/MathNode.js';
@@ -137,6 +138,7 @@ const nodeLib = {
 	// display
 	ColorSpaceNode,
 	NormalMapNode,
+	ToneMappingNode,
 
 	// math
 	MathNode,
@@ -227,6 +229,7 @@ export {
 	// display
 	ColorSpaceNode,
 	NormalMapNode,
+	ToneMappingNode,
 
 	// math
 	MathNode,

+ 1 - 1
examples/jsm/nodes/accessors/SkinningNode.js

@@ -81,7 +81,7 @@ class SkinningNode extends Node {
 
 	generate( builder ) {
 
-		Skinning( {
+		Skinning.call( {
 			index: this.skinIndexNode,
 			weight: this.skinWeightNode,
 			bindMatrix: this.bindMatrixNode,

+ 1 - 1
examples/jsm/nodes/core/ContextNode.js

@@ -21,7 +21,7 @@ class ContextNode extends Node {
 
 		const previousContext = builder.getContext();
 
-		builder.setContext( Object.assign( {}, builder.context, this.context ) );
+		builder.setContext( { ...builder.context, ...this.context } );
 
 		const snippet = this.node.build( builder, output );
 

+ 39 - 0
examples/jsm/nodes/core/VarNode.js

@@ -1,4 +1,5 @@
 import Node from './Node.js';
+import OperatorNode from '../math/OperatorNode.js';
 
 class VarNode extends Node {
 
@@ -11,6 +12,44 @@ class VarNode extends Node {
 
 	}
 
+	op( op, ...params ) {
+
+		this.node = new OperatorNode( op, this.node, ...params );
+
+		return this;
+
+	}
+
+	assign( ...params ) {
+
+		return this.op( '=', ...params );
+
+	}
+
+	add( ...params ) {
+
+		return this.op( '+', ...params );
+
+	}
+
+	sub( ...params ) {
+
+		return this.op( '-', ...params );
+
+	}
+
+	mul( ...params ) {
+
+		return this.op( '*', ...params );
+
+	}
+
+	div( ...params ) {
+
+		return this.op( '/', ...params );
+
+	}
+
 	getHash( builder ) {
 
 		return this.name || super.getHash( builder );

+ 1 - 1
examples/jsm/nodes/display/ColorSpaceNode.js

@@ -77,7 +77,7 @@ class ColorSpaceNode extends TempNode {
 
 			const encodingFunctionNode = EncodingLib[ method ];
 
-			return encodingFunctionNode( {
+			return encodingFunctionNode.call( {
 				value: node
 			} ).build( builder, type );
 

+ 1 - 1
examples/jsm/nodes/display/NormalMapNode.js

@@ -71,7 +71,7 @@ class NormalMapNode extends TempNode {
 
 		} else if ( normalMapType === TangentSpaceNormalMap ) {
 
-			const perturbNormal2ArbCall = perturbNormal2ArbNode( {
+			const perturbNormal2ArbCall = perturbNormal2ArbNode.call( {
 				eye_pos: positionView,
 				surf_norm: normalView,
 				mapN: normalMap,

+ 50 - 0
examples/jsm/nodes/display/ToneMappingNode.js

@@ -0,0 +1,50 @@
+import TempNode from '../core/Node.js';
+import ShaderNode from '../shadernode/ShaderNode.js';
+import { mul, float } from '../shadernode/ShaderNodeElements.js';
+
+import { LinearToneMapping } from 'three';
+
+// exposure only
+export const LinearToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+
+	return mul( color, exposure );
+
+} );
+
+class ToneMappingNode extends TempNode {
+
+	constructor( toneMapping, exposureNode = float( 1 ), colorNode = null ) {
+
+		super( 'vec3' );
+
+		this.toneMapping = toneMapping;
+
+		this.exposureNode = exposureNode;
+		this.colorNode = colorNode;
+
+	}
+
+	generate( builder ) {
+
+		const type = this.getNodeType( builder );
+
+		const colorNode = this.color || builder.context.color;
+
+		const toneMapping = this.toneMapping;
+		const toneMappingParams = { exposure: this.exposureNode, color: colorNode };
+
+		if ( toneMapping === LinearToneMapping ) {
+
+			return LinearToneMappingNode.call( toneMappingParams ).build( builder, type );
+
+		} else {
+
+			return this.colorNode.build( builder, type );
+
+		}
+
+	}
+
+}
+
+export default ToneMappingNode;

+ 7 - 0
examples/jsm/nodes/fog/FogNode.js

@@ -1,4 +1,5 @@
 import Node from '../core/Node.js';
+import MathNode from '../math/MathNode.js';
 
 class FogNode extends Node {
 
@@ -11,6 +12,12 @@ class FogNode extends Node {
 
 	}
 
+	mix( outputNode ) {
+
+		return new MathNode( MathNode.MIX, outputNode, this.colorNode, this );
+
+	}
+
 	generate( builder ) {
 
 		return this.factorNode.build( builder, 'float' );

+ 34 - 0
examples/jsm/nodes/functions/BSDF/BRDF_GGX.js

@@ -0,0 +1,34 @@
+import F_Schlick from './F_Schlick.js';
+import V_GGX_SmithCorrelated from './V_GGX_SmithCorrelated.js';
+import D_GGX from './D_GGX.js';
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import {
+	dotNV, add, mul, saturate, dot, pow2, normalize,
+	transformedNormalView, positionViewDirection
+} from '../../shadernode/ShaderNodeElements.js';
+
+// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
+const BRDF_GGX = new ShaderNode( ( inputs ) => {
+
+	const { lightDirection, f0, f90, roughness } = inputs;
+
+	const alpha = pow2( roughness ); // UE4's roughness
+
+	const halfDir = normalize( add( lightDirection, positionViewDirection ) );
+
+	const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
+	//const dotNV = saturate( dot( transformedNormalView, positionViewDirection ) );
+	const dotNH = saturate( dot( transformedNormalView, halfDir ) );
+	const dotVH = saturate( dot( positionViewDirection, halfDir ) );
+
+	const F = F_Schlick.call( { f0, f90, dotVH } );
+
+	const V = V_GGX_SmithCorrelated.call( { alpha, dotNL, dotNV } );
+
+	const D = D_GGX.call( { alpha, dotNH } );
+
+	return mul( F, mul( V, D ) );
+
+} ); // validated
+
+export default BRDF_GGX;

+ 10 - 0
examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js

@@ -0,0 +1,10 @@
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import { mul } from '../../shadernode/ShaderNodeElements.js';
+
+const BRDF_Lambert = new ShaderNode( ( inputs ) => {
+
+	return mul( 1 / Math.PI, inputs.diffuseColor ); // punctual light
+
+} ); // validated
+
+export default BRDF_Lambert;

+ 19 - 0
examples/jsm/nodes/functions/BSDF/D_GGX.js

@@ -0,0 +1,19 @@
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import { add, sub, mul, div, pow2 } from '../../shadernode/ShaderNodeElements.js';
+
+// Microfacet Models for Refraction through Rough Surfaces - equation (33)
+// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
+// alpha is "roughness squared" in Disney’s reparameterization
+const D_GGX = new ShaderNode( ( inputs ) => {
+
+	const { alpha, dotNH } = inputs;
+
+	const a2 = pow2( alpha );
+
+	const denom = add( mul( pow2( dotNH ), sub( a2, 1.0 ) ), 1.0 ); // avoid alpha = 0 with dotNH = 1
+
+	return mul( 1 / Math.PI, div( a2, pow2( denom ) ) );
+
+} ); // validated
+
+export default D_GGX;

+ 19 - 0
examples/jsm/nodes/functions/BSDF/F_Schlick.js

@@ -0,0 +1,19 @@
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import { add, sub, mul, exp2 } from '../../shadernode/ShaderNodeElements.js';
+
+const F_Schlick = new ShaderNode( ( inputs ) => {
+
+	const { f0, f90, dotVH } = inputs;
+
+	// Original approximation by Christophe Schlick '94
+	// float fresnel = pow( 1.0 - dotVH, 5.0 );
+
+	// Optimized variant (presented by Epic at SIGGRAPH '13)
+	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
+	const fresnel = exp2( mul( sub( mul( - 5.55473, dotVH ), 6.98316 ), dotVH ) );
+
+	return add( mul( f0, sub( 1.0, fresnel ) ), mul( f90, fresnel ) );
+
+} ); // validated
+
+export default F_Schlick;

+ 19 - 0
examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js

@@ -0,0 +1,19 @@
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import { add, sub, mul, div, pow2, max, sqrt, EPSILON } from '../../shadernode/ShaderNodeElements.js';
+
+// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
+// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
+const V_GGX_SmithCorrelated = new ShaderNode( ( inputs ) => {
+
+	const { alpha, dotNL, dotNV } = inputs;
+
+	const a2 = pow2( alpha );
+
+	const gv = mul( dotNL, sqrt( add( a2, mul( sub( 1.0, a2 ), pow2( dotNV ) ) ) ) );
+	const gl = mul( dotNV, sqrt( add( a2, mul( sub( 1.0, a2 ), pow2( dotNL ) ) ) ) );
+
+	return div( 0.5, max( add( gv, gl ), EPSILON ) );
+
+} ); // validated
+
+export default V_GGX_SmithCorrelated;

+ 0 - 123
examples/jsm/nodes/functions/BSDFs.js

@@ -1,123 +0,0 @@
-import ShaderNode from '../shadernode/ShaderNode.js';
-import {
-	add, addTo, sub, mul, div, saturate, dot, pow, pow2, exp2, normalize, max, sqrt, negate,
-	cond, greaterThan, and,
-	transformedNormalView, positionViewDirection,
-	diffuseColor, specularColor, roughness,
-	EPSILON
-} from '../shadernode/ShaderNodeElements.js';
-
-export const F_Schlick = new ShaderNode( ( inputs ) => {
-
-	const { f0, f90, dotVH } = inputs;
-
-	// Original approximation by Christophe Schlick '94
-	// float fresnel = pow( 1.0 - dotVH, 5.0 );
-
-	// Optimized variant (presented by Epic at SIGGRAPH '13)
-	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
-	const fresnel = exp2( mul( sub( mul( - 5.55473, dotVH ), 6.98316 ), dotVH ) );
-
-	return add( mul( f0, sub( 1.0, fresnel ) ), mul( f90, fresnel ) );
-
-} ); // validated
-
-export const BRDF_Lambert = new ShaderNode( ( inputs ) => {
-
-	return mul( 1 / Math.PI, inputs.diffuseColor ); // punctual light
-
-} ); // validated
-
-export const getDistanceAttenuation = new ShaderNode( ( inputs ) => {
-
-	const { lightDistance, cutoffDistance, decayExponent } = inputs;
-
-	return cond(
-		and( greaterThan( cutoffDistance, 0 ), greaterThan( decayExponent, 0 ) ),
-		pow( saturate( add( div( negate( lightDistance ), cutoffDistance ), 1.0 ) ), decayExponent ),
-		1.0
-	);
-
-} ); // validated
-
-//
-// STANDARD
-//
-
-// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
-// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
-export const V_GGX_SmithCorrelated = new ShaderNode( ( inputs ) => {
-
-	const { alpha, dotNL, dotNV } = inputs;
-
-	const a2 = pow2( alpha );
-
-	const gv = mul( dotNL, sqrt( add( a2, mul( sub( 1.0, a2 ), pow2( dotNV ) ) ) ) );
-	const gl = mul( dotNV, sqrt( add( a2, mul( sub( 1.0, a2 ), pow2( dotNL ) ) ) ) );
-
-	return div( 0.5, max( add( gv, gl ), EPSILON ) );
-
-} ); // validated
-
-// Microfacet Models for Refraction through Rough Surfaces - equation (33)
-// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
-// alpha is "roughness squared" in Disney’s reparameterization
-export const D_GGX = new ShaderNode( ( inputs ) => {
-
-	const { alpha, dotNH } = inputs;
-
-	const a2 = pow2( alpha );
-
-	const denom = add( mul( pow2( dotNH ), sub( a2, 1.0 ) ), 1.0 ); // avoid alpha = 0 with dotNH = 1
-
-	return mul( 1 / Math.PI, div( a2, pow2( denom ) ) );
-
-} ); // validated
-
-
-// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
-export const BRDF_GGX = new ShaderNode( ( inputs ) => {
-
-	const { lightDirection, f0, f90, roughness } = inputs;
-
-	const alpha = pow2( roughness ); // UE4's roughness
-
-	const halfDir = normalize( add( lightDirection, positionViewDirection ) );
-
-	const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
-	const dotNV = saturate( dot( transformedNormalView, positionViewDirection ) );
-	const dotNH = saturate( dot( transformedNormalView, halfDir ) );
-	const dotVH = saturate( dot( positionViewDirection, halfDir ) );
-
-	const F = F_Schlick( { f0, f90, dotVH } );
-
-	const V = V_GGX_SmithCorrelated( { alpha, dotNL, dotNV } );
-
-	const D = D_GGX( { alpha, dotNH } );
-
-	return mul( F, mul( V, D ) );
-
-} ); // validated
-
-export const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
-
-	const { lightDirection, lightColor, directDiffuse, directSpecular } = inputs;
-
-	const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
-	let irradiance = mul( dotNL, lightColor );
-
-	irradiance = mul( irradiance, Math.PI ); // punctual light
-
-	addTo( directDiffuse, mul( irradiance, BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
-
-	addTo( directSpecular, mul( irradiance, BRDF_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
-
-} );
-
-export const PhysicalLightingModel = new ShaderNode( ( inputs/*, builder*/ ) => {
-
-	// PHYSICALLY_CORRECT_LIGHTS <-> builder.renderer.physicallyCorrectLights === true
-
-	RE_Direct_Physical( inputs );
-
-} );

+ 28 - 0
examples/jsm/nodes/functions/PhysicalLightingModel.js

@@ -0,0 +1,28 @@
+import BRDF_Lambert from './BSDF/BRDF_Lambert.js';
+import BRDF_GGX from './BSDF/BRDF_GGX.js';
+import ShaderNode from '../shadernode/ShaderNode.js';
+import {
+	mul, saturate, dot, transformedNormalView,
+	diffuseColor, specularColor, roughness
+} from '../shadernode/ShaderNodeElements.js';
+
+const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
+
+	const { lightDirection, lightColor, reflectedLight } = inputs;
+
+	const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
+	const irradiance = mul( dotNL, lightColor );
+
+	reflectedLight.directSpecular.add( mul( irradiance, BRDF_GGX.call( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
+
+	reflectedLight.directDiffuse.add( mul( irradiance, BRDF_Lambert.call( { diffuseColor: diffuseColor.rgb } ) ) );
+
+} );
+
+const PhysicalLightingModel = new ShaderNode( ( inputs/*, builder*/ ) => {
+
+	RE_Direct_Physical.call( inputs );
+
+} );
+
+export default PhysicalLightingModel;

+ 0 - 25
examples/jsm/nodes/functions/PhysicalMaterialFunctions.js

@@ -1,25 +0,0 @@
-import ShaderNode from '../shadernode/ShaderNode.js';
-import { add, max, min, abs, dFdx, dFdy, normalGeometry } from '../shadernode/ShaderNodeElements.js';
-
-export const getGeometryRoughness = new ShaderNode( () => {
-
-	const dxy = max( abs( dFdx( normalGeometry ) ), abs( dFdy( normalGeometry ) ) );
-	const geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );
-
-	return geometryRoughness;
-
-} );
-
-export const getRoughness = new ShaderNode( ( inputs ) => {
-
-	const { roughness } = inputs;
-
-	const geometryRoughness = getGeometryRoughness();
-
-	let roughnessFactor = max( roughness, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.
-	roughnessFactor = add( roughnessFactor, geometryRoughness );
-	roughnessFactor = min( roughnessFactor, 1.0 );
-
-	return roughnessFactor;
-
-} );

+ 23 - 0
examples/jsm/nodes/functions/light/getDistanceAttenuation.js

@@ -0,0 +1,23 @@
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import {
+	div, max, sub, mul, saturate, pow, pow2, pow4, cond, greaterThan
+} from '../../shadernode/ShaderNodeElements.js';
+
+const getDistanceAttenuation = new ShaderNode( ( inputs ) => {
+
+	const { lightDistance, cutoffDistance, decayExponent } = inputs;
+
+	// based upon Frostbite 3 Moving to Physically-based Rendering
+	// page 32, equation 26: E[window1]
+	// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
+	const distanceFalloff = div( 1.0, max( pow( lightDistance, decayExponent ), 0.01 ) );
+
+	return cond(
+		greaterThan( cutoffDistance, 0 ),
+		mul( distanceFalloff, pow2( saturate( sub( 1.0, pow4( div( lightDistance, cutoffDistance ) ) ) ) ) ),
+		distanceFalloff
+	);
+
+} ); // validated
+
+export default getDistanceAttenuation;

+ 13 - 0
examples/jsm/nodes/functions/material/getGeometryRoughness.js

@@ -0,0 +1,13 @@
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import { max, abs, dFdx, dFdy, normalGeometry } from '../../shadernode/ShaderNodeElements.js';
+
+const getGeometryRoughness = new ShaderNode( () => {
+
+	const dxy = max( abs( dFdx( normalGeometry ) ), abs( dFdy( normalGeometry ) ) );
+	const geometryRoughness = max( max( dxy.x, dxy.y ), dxy.z );
+
+	return geometryRoughness;
+
+} );
+
+export default getGeometryRoughness;

+ 19 - 0
examples/jsm/nodes/functions/material/getRoughness.js

@@ -0,0 +1,19 @@
+import getGeometryRoughness from './getGeometryRoughness.js';
+import ShaderNode from '../../shadernode/ShaderNode.js';
+import { add, max, min } from '../../shadernode/ShaderNodeElements.js';
+
+const getRoughness = new ShaderNode( ( inputs ) => {
+
+	const { roughness } = inputs;
+
+	const geometryRoughness = getGeometryRoughness.call();
+
+	let roughnessFactor = max( roughness, 0.0525 ); // 0.0525 corresponds to the base mip of a 256 cubemap.
+	roughnessFactor = add( roughnessFactor, geometryRoughness );
+	roughnessFactor = min( roughnessFactor, 1.0 );
+
+	return roughnessFactor;
+
+} );
+
+export default getRoughness;

+ 9 - 27
examples/jsm/nodes/lights/LightContextNode.js

@@ -1,16 +1,14 @@
 import ContextNode from '../core/ContextNode.js';
-import VarNode from '../core/VarNode.js';
-import UniformNode from '../core/UniformNode.js';
-import OperatorNode from '../math/OperatorNode.js';
-import { PhysicalLightingModel } from '../functions/BSDFs.js';
-import { Vector3 } from 'three';
+import { reflectedLight } from '../shadernode/ShaderNodeElements.js';
 
 class LightContextNode extends ContextNode {
 
-	constructor( node ) {
+	constructor( node, lightingModelNode = null ) {
 
 		super( node );
 
+		this.lightingModelNode = lightingModelNode;
+
 	}
 
 	getNodeType( /*builder*/ ) {
@@ -21,37 +19,21 @@ class LightContextNode extends ContextNode {
 
 	generate( builder ) {
 
-		const material = builder.material;
+		const { lightingModelNode } = this;
 
-		let lightingModel = null;
+		this.context.reflectedLight = reflectedLight();
 
-		if ( material.isMeshStandardMaterial === true ) {
+		if ( lightingModelNode !== null ) {
 
-			lightingModel = PhysicalLightingModel;
+			this.context.lightingModelNode = lightingModelNode;
 
 		}
 
-		const directDiffuse = new VarNode( new UniformNode( new Vector3() ), 'DirectDiffuse', 'vec3' );
-		const directSpecular = new VarNode( new UniformNode( new Vector3() ), 'DirectSpecular', 'vec3' );
-
-		this.context.directDiffuse = directDiffuse;
-		this.context.directSpecular = directSpecular;
-
-		if ( lightingModel !== null ) {
-
-			this.context.lightingModel = lightingModel;
-
-		}
-
-		// add code
-
 		const type = this.getNodeType( builder );
 
 		super.generate( builder, type );
 
-		const totalLight = new OperatorNode( '+', directDiffuse, directSpecular );
-
-		return totalLight.build( builder, type );
+		return this.context.reflectedLight.build( builder, type );
 
 	}
 

+ 7 - 9
examples/jsm/nodes/lights/LightNode.js

@@ -5,7 +5,7 @@ import UniformNode from '../core/UniformNode.js';
 import OperatorNode from '../math/OperatorNode.js';
 import MathNode from '../math/MathNode.js';
 import { NodeUpdateType } from '../core/constants.js';
-import { getDistanceAttenuation } from '../functions/BSDFs.js';
+import getDistanceAttenuation from '../functions/light/getDistanceAttenuation.js';
 
 import { Color } from 'three';
 
@@ -51,7 +51,7 @@ class LightNode extends Node {
 
 		const lightDistance = new MathNode( MathNode.LENGTH, lVector );
 
-		const lightAttenuation = getDistanceAttenuation( {
+		const lightAttenuation = getDistanceAttenuation.call( {
 			lightDistance,
 			cutoffDistance: this._lightCutoffDistanceNode,
 			decayExponent: this._lightDecayExponentNode
@@ -61,18 +61,16 @@ class LightNode extends Node {
 
 		lightPositionView.object3d = this.light;
 
-		const lightingModelFunction = builder.context.lightingModel;
+		const lightingModelFunctionNode = builder.context.lightingModelNode;
 
-		if ( lightingModelFunction !== undefined ) {
+		if ( lightingModelFunctionNode !== undefined ) {
 
-			const directDiffuse = builder.context.directDiffuse;
-			const directSpecular = builder.context.directSpecular;
+			const reflectedLight = builder.context.reflectedLight;
 
-			lightingModelFunction( {
+			lightingModelFunctionNode.call( {
 				lightDirection,
 				lightColor,
-				directDiffuse,
-				directSpecular
+				reflectedLight
 			}, builder );
 
 		}

+ 32 - 0
examples/jsm/nodes/lights/ReflectedLightNode.js

@@ -0,0 +1,32 @@
+import TempNode from '../core/Node.js';
+import VarNode from '../core/VarNode.js';
+import ConstNode from '../core/UniformNode.js';
+import OperatorNode from '../math/OperatorNode.js';
+import { Vector3 } from 'three';
+
+class ReflectedLightNode extends TempNode {
+
+	constructor() {
+
+		super( 'vec3' );
+
+		this.directDiffuse = new VarNode( new ConstNode( new Vector3() ), 'DirectDiffuse' );
+		this.directSpecular = new VarNode( new ConstNode( new Vector3() ), 'DirectSpecular' );
+		this.indirectDiffuse = new VarNode( new ConstNode( new Vector3() ), 'IndirectDiffuse' );
+		this.indirectSpecular = new VarNode( new ConstNode( new Vector3() ), 'IndirectSpecular' );
+
+	}
+
+	generate( builder ) {
+
+		const { directDiffuse, directSpecular, indirectDiffuse, indirectSpecular } = this;
+
+		const totalLight = new OperatorNode( '+', directDiffuse, directSpecular, indirectDiffuse, indirectSpecular );
+
+		return totalLight.build( builder );
+
+	}
+
+}
+
+export default ReflectedLightNode;

+ 17 - 3
examples/jsm/nodes/materials/MeshStandardNodeMaterial.js

@@ -5,7 +5,9 @@ import {
 	normalView,
 	materialRoughness, materialMetalness
 } from '../shadernode/ShaderNodeElements.js';
-import { getRoughness } from '../functions/PhysicalMaterialFunctions.js';
+import getRoughness from '../functions/material/getRoughness.js';
+import PhysicalLightingModel from '../functions/PhysicalLightingModel.js';
+
 import { MeshStandardMaterial } from 'three';
 
 const defaultValues = new MeshStandardMaterial();
@@ -51,7 +53,19 @@ export default class MeshStandardNodeMaterial extends NodeMaterial {
 
 		diffuseColorNode = this.generateStandardMaterial( builder, { colorNode, diffuseColorNode } );
 
-		this.generateLight( builder, diffuseColorNode, lightNode );
+		const outgoingLightNode = this.generateLight( builder, { diffuseColorNode, lightNode } );
+
+		this.generateOutput( builder, { diffuseColorNode, outgoingLightNode } );
+
+	}
+
+	generateLight( builder, { diffuseColorNode, lightNode } ) {
+
+		const outgoingLightNode = super.generateLight( builder, { diffuseColorNode, lightNode, lightingModelNode: PhysicalLightingModel } );
+
+		// @TODO: add IBL code here
+
+		return outgoingLightNode;
 
 	}
 
@@ -67,7 +81,7 @@ export default class MeshStandardNodeMaterial extends NodeMaterial {
 		// ROUGHNESS
 
 		let roughnessNode = this.roughnessNode ? float( this.roughnessNode ) : materialRoughness;
-		roughnessNode = getRoughness( { roughness: roughnessNode } );
+		roughnessNode = getRoughness.call( { roughness: roughnessNode } );
 
 		builder.addFlow( 'fragment', label( roughnessNode, 'Roughness' ) );
 

+ 26 - 7
examples/jsm/nodes/materials/NodeMaterial.js

@@ -3,8 +3,8 @@ import { getNodesKeys } from '../core/NodeUtils.js';
 import ExpressionNode from '../core/ExpressionNode.js';
 import {
 	float, vec3, vec4,
-	assign, label, mul, add, mix, bypass,
-	positionLocal, skinning, instance, modelViewProjection, lightContext, colorSpace,
+	assign, label, mul, add, bypass,
+	positionLocal, skinning, instance, modelViewProjection, context, lightContext, colorSpace,
 	materialAlphaTest, materialColor, materialOpacity
 } from '../shadernode/ShaderNodeElements.js';
 
@@ -22,9 +22,12 @@ class NodeMaterial extends ShaderMaterial {
 
 	build( builder ) {
 
+		const { lightNode } = this;
 		const { diffuseColorNode } = this.generateMain( builder );
 
-		this.generateLight( builder, diffuseColorNode, this.lightNode );
+		const outgoingLightNode = this.generateLight( builder, { diffuseColorNode, lightNode } );
+
+		this.generateOutput( builder, { diffuseColorNode, outgoingLightNode } );
 
 	}
 
@@ -90,33 +93,49 @@ class NodeMaterial extends ShaderMaterial {
 
 	}
 
-	generateLight( builder, diffuseColorNode, lightNode ) {
+	generateLight( builder, { diffuseColorNode, lightNode, lightingModelNode } ) {
+
+		// < ANALYTIC LIGHTS >
 
 		// OUTGOING LIGHT
 
 		let outgoingLightNode = diffuseColorNode.xyz;
-		if ( lightNode && lightNode.hasLight !== false ) outgoingLightNode = builder.addFlow( 'fragment', label( lightContext( lightNode ), 'Light' ) );
+		if ( lightNode && lightNode.hasLight !== false ) outgoingLightNode = builder.addFlow( 'fragment', label( lightContext( lightNode, lightingModelNode ), 'Light' ) );
 
 		// EMISSIVE
 
 		if ( this.emissiveNode ) outgoingLightNode = add( vec3( this.emissiveNode ), outgoingLightNode );
 
+		return outgoingLightNode;
+
+	}
+
+	generateOutput( builder, { diffuseColorNode, outgoingLightNode } ) {
+
+		const { renderer } = builder;
+
 		// OUTPUT
 
 		let outputNode = vec4( outgoingLightNode, diffuseColorNode.a );
 
+		// TONE MAPPING
+
+		if ( renderer.toneMappingNode ) outputNode = context( renderer.toneMappingNode, { color: outputNode } );
+
 		// ENCODING
 
 		outputNode = colorSpace( outputNode, builder.renderer.outputEncoding );
 
 		// FOG
 
-		if ( builder.fogNode ) outputNode = mix( outputNode, builder.fogNode.colorNode, builder.fogNode );
+		if ( builder.fogNode ) outputNode = builder.fogNode.mix( outputNode );
 
 		// RESULT
 
 		builder.addFlow( 'fragment', label( outputNode, 'Output' ) );
 
+		return outputNode;
+
 	}
 
 	setDefaultValues( values ) {
@@ -199,7 +218,7 @@ class NodeMaterial extends ShaderMaterial {
 
 	}
 
-	static fromMaterial( material ) { }
+	static fromMaterial( /*material*/ ) { }
 
 }
 

+ 1 - 1
examples/jsm/nodes/procedural/CheckerNode.js

@@ -26,7 +26,7 @@ class CheckerNode extends Node {
 
 	generate( builder ) {
 
-		return checkerShaderNode( { uv: this.uvNode } ).build( builder );
+		return checkerShaderNode.call( { uv: this.uvNode } ).build( builder );
 
 	}
 

+ 7 - 8
examples/jsm/nodes/shadernode/ShaderNodeElements.js

@@ -5,6 +5,7 @@ import AttributeNode from '../core/AttributeNode.js';
 import UniformNode from '../core/UniformNode.js';
 import BypassNode from '../core/BypassNode.js';
 import InstanceIndexNode from '../core/InstanceIndexNode.js';
+import ContextNode from '../core/ContextNode.js';
 
 // accessor nodes
 import BufferNode from '../accessors/BufferNode.js';
@@ -32,6 +33,7 @@ import JoinNode from '../utils/JoinNode.js';
 // other nodes
 import ColorSpaceNode from '../display/ColorSpaceNode.js';
 import LightContextNode from '../lights/LightContextNode.js';
+import ReflectedLightNode from '../lights/ReflectedLightNode.js';
 
 // utils
 import ShaderNode from './ShaderNode.js';
@@ -110,14 +112,6 @@ export const sampler = ( texture ) => nodeObject( new ConvertNode( texture.isNod
 
 export const cond = nodeProxy( CondNode );
 
-export const addTo = ( varNode, ...params ) => {
-
-	varNode.node = add( varNode.node, ...nodeArray( params ) );
-
-	return nodeObject( varNode );
-
-};
-
 export const add = nodeProxy( OperatorNode, '+' );
 export const sub = nodeProxy( OperatorNode, '-' );
 export const mul = nodeProxy( OperatorNode, '*' );
@@ -175,8 +169,11 @@ export const materialMetalness = nodeObject( new MaterialNode( MaterialNode.META
 export const skinning = nodeProxy( SkinningNode );
 export const instance = nodeProxy( InstanceNode );
 
+export const context = nodeProxy( ContextNode );
 export const lightContext = nodeProxy( LightContextNode );
 
+export const reflectedLight = nodeProxy( ReflectedLightNode );
+
 export const colorSpace = ( node, encoding ) => nodeObject( new ColorSpaceNode( null, nodeObject( node ) ).fromEncoding( encoding ) );
 
 export const bypass = nodeProxy( BypassNode );
@@ -229,3 +226,5 @@ export const transformDirection = nodeProxy( MathNode, 'transformDirection' );
 
 export const EPSILON = float( 1e-6 );
 export const INFINITY = float( 1e6 );
+
+export const dotNV = saturate( dot( transformedNormalView, positionViewDirection ) );

+ 4 - 4
examples/jsm/nodes/shadernode/ShaderNodeUtils.js

@@ -141,13 +141,13 @@ const ShaderNodeProxy = function ( NodeClass, scope = null, factor = null ) {
 
 export const ShaderNodeScript = function ( jsFunc ) {
 
-	return ( inputs, builder ) => {
+	return { call: ( inputs, builder ) => {
 
-		new ShaderNodeObjects( inputs );
+		inputs = new ShaderNodeObjects( inputs );
 
 		return new ShaderNodeObject( jsFunc( inputs, builder ) );
 
-	};
+	} };
 
 };
 
@@ -158,7 +158,7 @@ export const nodeProxy = ( ...val ) => new ShaderNodeProxy( ...val );
 
 const bools = [ false, true ];
 const uints = [ 0, 1, 2, 3 ];
-const ints = [ -1, -2 ];
+const ints = [ - 1, - 2 ];
 const floats = [ 0.5, 1.5, 1 / 3, 1e-6, 1e6, Math.PI, Math.PI * 2, 1 / Math.PI, 2 / Math.PI, 1 / ( Math.PI * 2 ), Math.PI / 2 ];
 
 const boolsCacheMap = new Map();

+ 32 - 0
examples/jsm/nodes/utils/MaxMipLevelNode.js

@@ -0,0 +1,32 @@
+import UniformNode from '../core/UniformNode.js';
+import { NodeUpdateType } from '../core/constants.js';
+
+class MaxMipLevelNode extends UniformNode {
+
+	constructor( texture ) {
+
+		super( 0 );
+
+		this.texture = texture;
+
+		this.updateType = NodeUpdateType.Frame;
+
+	}
+
+	update() {
+
+		const { width, height } = this.texture.images ? this.texture.images[ 0 ] : this.texture.image;
+
+		this.value = Math.log( Math.max( width, height ) ) * Math.LOG2E;
+
+		if ( this.value > 0 ) {
+
+			this.updateType = NodeUpdateType.None;
+
+		}
+
+	}
+
+}
+
+export default MaxMipLevelNode;

+ 30 - 0
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -140,18 +140,48 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	}
 
+	getSamplerBias( textureProperty, uvSnippet, biasSnippet, shaderStage = this.shaderStage ) {
+
+		if ( shaderStage === 'fragment' ) {
+
+			return `textureSampleBias( ${textureProperty}, ${textureProperty}_sampler, ${uvSnippet}, ${biasSnippet} )`;
+
+		} else {
+
+			this._include( 'repeatWrapping' );
+
+			const dimension = `textureDimensions( ${textureProperty}, 0 )`;
+
+			return `textureLoad( ${textureProperty}, repeatWrapping( ${uvSnippet}, ${dimension} ), i32( ${biasSnippet} ) )`;
+
+		}
+
+	}
+
 	getTexture( textureProperty, uvSnippet, shaderStage = this.shaderStage ) {
 
 		return this.getSampler( textureProperty, uvSnippet, shaderStage );
 
 	}
 
+	getTextureBias( textureProperty, uvSnippet, biasSnippet, shaderStage = this.shaderStage ) {
+
+		return this.getSamplerBias( textureProperty, uvSnippet, biasSnippet, shaderStage );
+
+	}
+
 	getCubeTexture( textureProperty, uvSnippet, shaderStage = this.shaderStage ) {
 
 		return this.getSampler( textureProperty, uvSnippet, shaderStage );
 
 	}
 
+	getCubeTextureBias( textureProperty, uvSnippet, biasSnippet, shaderStage = this.shaderStage ) {
+
+		return this.getSamplerBias( textureProperty, uvSnippet, biasSnippet, shaderStage );
+
+	}
+
 	getPropertyName( node, shaderStage = this.shaderStage ) {
 
 		if ( node.isNodeVary === true ) {

+ 5 - 2
examples/webgl_nodes_playground.html

@@ -82,16 +82,19 @@
 			// Lights
 
 			const topLight = new THREE.PointLight( 0xF4F6F0, 1 );
-			topLight.position.set( 0, 100000, 100000 );
+			topLight.position.set( 0, 1000, 1000 );
 			scene.add( topLight );
 
-			const backLight = new THREE.PointLight( 0x0c1445, 1.4 );
+			const backLight = new THREE.PointLight( 0x0c1445, 1 );
 			backLight.position.set( - 100, 20, - 260 );
 			scene.add( backLight );
 
 			renderer = new THREE.WebGLRenderer( { antialias: true } );
 			document.body.appendChild( renderer.domElement );
 			renderer.outputEncoding = THREE.sRGBEncoding;
+			renderer.toneMapping = THREE.LinearToneMapping;
+			renderer.toneMappingExposure = 4000;
+			renderer.physicallyCorrectLights = true;
 
 			renderer.domElement.className = 'renderer';
 

+ 3 - 6
examples/webgpu_lights_custom.html

@@ -33,8 +33,6 @@
 
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 
-			import { addTo } from 'three-nodes/Nodes.js';
-
 			let camera, scene, renderer;
 
 			let light1, light2, light3;
@@ -95,14 +93,13 @@
 
 				const customLightingModel = new Nodes.ShaderNode( ( inputs ) => {
 
-					const { lightColor, directDiffuse } = inputs;
+					const { lightColor, reflectedLight } = inputs;
 
-					addTo( directDiffuse, lightColor );
+					reflectedLight.directDiffuse.add( lightColor );
 
 				} );
 
-				const lightingModelContext = new Nodes.ContextNode( allLightsNode );
-				lightingModelContext.context.lightingModel = customLightingModel;
+				const lightingModelContext = new Nodes.LightContextNode( allLightsNode, customLightingModel );
 
 				materialPoints.lightNode = lightingModelContext;
 

+ 7 - 4
examples/webgpu_lights_selective.html

@@ -81,19 +81,19 @@
 
 				//lights
 
-				light1 = new THREE.PointLight( 0xff0040, 2, 100 );
+				light1 = new THREE.PointLight( 0xff0040, 2, 1000 );
 				light1.add( new THREE.Mesh( sphere, new THREE.MeshBasicMaterial( { color: 0xff0040 } ) ) );
 				scene.add( light1 );
 
-				light2 = new THREE.PointLight( 0x0040ff, 2, 100 );
+				light2 = new THREE.PointLight( 0x0040ff, 2, 1000 );
 				light2.add( new THREE.Mesh( sphere, new THREE.MeshBasicMaterial( { color: 0x0040ff } ) ) );
 				scene.add( light2 );
 
-				light3 = new THREE.PointLight( 0x80ff80, 2, 100 );
+				light3 = new THREE.PointLight( 0x80ff80, 2, 1000 );
 				light3.add( new THREE.Mesh( sphere, new THREE.MeshBasicMaterial( { color: 0x80ff80 } ) ) );
 				scene.add( light3 );
 
-				light4 = new THREE.PointLight( 0xffaa00, 2, 100 );
+				light4 = new THREE.PointLight( 0xffaa00, 2, 1000 );
 				light4.add( new THREE.Mesh( sphere, new THREE.MeshBasicMaterial( { color: 0xffaa00 } ) ) );
 				scene.add( light4 );
 
@@ -115,6 +115,7 @@
 
 				const centerObject = new THREE.Mesh( geometryTeapot, new Nodes.MeshStandardNodeMaterial( { color: 0x555555 } ) );
 				centerObject.material.normalNode = new Nodes.NormalMapNode( new Nodes.TextureNode( normalMapTexture ) );
+				centerObject.material.metalness = .5;
 				centerObject.material.roughness = .5;
 				scene.add( centerObject );
 
@@ -133,6 +134,8 @@
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				document.body.appendChild( renderer.domElement );
+				renderer.outputEncoding = THREE.sRGBEncoding;
+				renderer.toneMappingNode = new Nodes.ToneMappingNode( THREE.LinearToneMapping, 100 );
 
 				//controls
 

+ 1 - 1
examples/webgpu_materials.html

@@ -141,7 +141,7 @@
 				} );
 
 				material = new Nodes.MeshBasicNodeMaterial();
-				material.colorNode = desaturateShaderNode( { color: new Nodes.TextureNode( texture ) } );
+				material.colorNode = desaturateShaderNode.call( { color: new Nodes.TextureNode( texture ) } );
 				materials.push( material );
 
 				// Custom WGSL ( desaturate filter )

+ 3 - 18
examples/webgpu_nodes_playground.html

@@ -96,20 +96,19 @@
 				// Lights
 
 				const topLight = new THREE.PointLight( 0xF4F6F0, 1 );
-				topLight.position.set( 0, 100000, 100000 );
+				topLight.position.set( 0, 1000, 1000 );
 				scene.add( topLight );
 
-				const backLight = new THREE.PointLight( 0x0c1445, 1.4 );
+				const backLight = new THREE.PointLight( 0x0c1445, 1 );
 				backLight.position.set( - 100, 20, - 260 );
 				scene.add( backLight );
 
-				nodeLights = new Nodes.LightsNode().fromLights( [ topLight, backLight ] );
-
 				renderer = new WebGPURenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
 				document.body.appendChild( renderer.domElement );
 				renderer.outputEncoding = THREE.sRGBEncoding;
+				renderer.toneMappingNode = new Nodes.ToneMappingNode( THREE.LinearToneMapping, 4000 );
 
 				renderer.domElement.className = 'renderer';
 
@@ -145,20 +144,6 @@
 
 				} );
 
-				nodeEditor.addEventListener( 'add', ( e ) => {
-
-					const node = e.node;
-
-					if ( node.value !== null && node.value.isMeshStandardNodeMaterial === true ) {
-
-						const material = node.value;
-
-						material.lightNode = nodeLights;
-
-					}
-
-				} );
-
 				document.body.appendChild( nodeEditor.domElement );
 
 				const loaderFBX = new FBXLoader();

+ 3 - 3
examples/webgpu_skinning.html

@@ -67,8 +67,6 @@
 				camera.add( light );
 				scene.add( camera );
 
-				const lightNode = new LightsNode().fromLights( [ light ] );
-
 				const loader = new FBXLoader();
 				loader.load( 'models/fbx/Samba Dancing.fbx', function ( object ) {
 
@@ -82,7 +80,7 @@
 						if ( child.isMesh ) {
 
 							child.material = new Nodes.MeshStandardNodeMaterial();
-							child.material.lightNode = lightNode;
+							child.material.roughness = .1;
 
 						}
 
@@ -97,6 +95,8 @@
 				renderer = new WebGPURenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.outputEncoding = THREE.sRGBEncoding;
+				renderer.toneMappingNode = new Nodes.ToneMappingNode( THREE.LinearToneMapping, 800 );
 				document.body.appendChild( renderer.domElement );
 
 				window.addEventListener( 'resize', onWindowResize );

+ 4 - 1
examples/webgpu_skinning_instancing.html

@@ -61,7 +61,7 @@
 
 				//lights
 
-				const centerLight = new THREE.PointLight( 0xffffff, .8, 7000 );
+				const centerLight = new THREE.PointLight( 0xff9900, .8, 7000 );
 				centerLight.position.y = 450;
 				centerLight.position.z = - 200;
 				scene.add( centerLight );
@@ -86,6 +86,7 @@
 						if ( child.isMesh ) {
 
 							child.material = new Nodes.MeshStandardNodeMaterial();
+							child.material.roughness = .1;
 
 							child.isInstancedMesh = true;
 							child.instanceMatrix = new THREE.InstancedBufferAttribute( new Float32Array( instanceCount * 16 ), 16 );
@@ -115,6 +116,8 @@
 				renderer = new WebGPURenderer();
 				renderer.setPixelRatio( window.devicePixelRatio );
 				renderer.setSize( window.innerWidth, window.innerHeight );
+				renderer.outputEncoding = THREE.sRGBEncoding;
+				renderer.toneMappingNode = new Nodes.ToneMappingNode( THREE.LinearToneMapping, 800 );
 				document.body.appendChild( renderer.domElement );
 
 				window.addEventListener( 'resize', onWindowResize );