瀏覽代碼

NodeMaterial: BSDFs, SkinningNode updated to ShaderNode and ArrayElement support (#22662)

* ShaderNode: updates

* use base values

* unused function

* fix multiply vector x matrix

* fix cache

* Improve ShaderNode

* BSDFs to ShaderNode

* Related: https://github.com/mrdoob/three.js/pull/21356

* Support to different NodeBuilder

* fix setvar

* get array element support

* fix void node

* .getInputType() to buffer array

* use getNodeType() as bufferType

* new nodes

* improvements to multiply matrices and assign

* Rewrite: GLSL to ShaderNode

* cleanup

* fix reference

* check if isNode
sunag 3 年之前
父節點
當前提交
96afd8db2b
共有 30 個文件被更改,包括 512 次插入491 次删除
  1. 7 5
      examples/jsm/renderers/nodes/Nodes.js
  2. 105 11
      examples/jsm/renderers/nodes/ShaderNode.js
  3. 2 2
      examples/jsm/renderers/nodes/accessors/NormalNode.js
  4. 1 2
      examples/jsm/renderers/nodes/accessors/PositionNode.js
  5. 39 36
      examples/jsm/renderers/nodes/accessors/SkinningNode.js
  6. 0 7
      examples/jsm/renderers/nodes/consts/MathConsts.js
  7. 9 4
      examples/jsm/renderers/nodes/core/FunctionNode.js
  8. 13 3
      examples/jsm/renderers/nodes/core/InputNode.js
  9. 2 2
      examples/jsm/renderers/nodes/core/Node.js
  10. 47 30
      examples/jsm/renderers/nodes/core/NodeBuilder.js
  11. 0 47
      examples/jsm/renderers/nodes/core/NodeKeywords.js
  12. 6 0
      examples/jsm/renderers/nodes/core/PropertyNode.js
  13. 6 0
      examples/jsm/renderers/nodes/core/VarNode.js
  14. 2 0
      examples/jsm/renderers/nodes/core/constants.js
  15. 25 18
      examples/jsm/renderers/nodes/display/ColorSpaceNode.js
  16. 2 2
      examples/jsm/renderers/nodes/display/NormalMapNode.js
  17. 64 101
      examples/jsm/renderers/nodes/functions/BSDFs.js
  18. 0 99
      examples/jsm/renderers/nodes/functions/EncodingFunctions.js
  19. 0 47
      examples/jsm/renderers/nodes/functions/MathFunctions.js
  20. 6 0
      examples/jsm/renderers/nodes/inputs/BufferNode.js
  21. 17 0
      examples/jsm/renderers/nodes/inputs/IntNode.js
  22. 1 1
      examples/jsm/renderers/nodes/inputs/TextureNode.js
  23. 18 12
      examples/jsm/renderers/nodes/lights/LightContextNode.js
  24. 27 27
      examples/jsm/renderers/nodes/lights/LightNode.js
  25. 55 20
      examples/jsm/renderers/nodes/math/MathNode.js
  26. 19 7
      examples/jsm/renderers/nodes/math/OperatorNode.js
  27. 1 1
      examples/jsm/renderers/nodes/parsers/GLSLNodeFunction.js
  28. 2 2
      examples/jsm/renderers/nodes/procedural/CheckerNode.js
  29. 31 0
      examples/jsm/renderers/nodes/utils/ArrayElementNode.js
  30. 5 5
      examples/webgpu_lights_custom.html

+ 7 - 5
examples/jsm/renderers/nodes/Nodes.js

@@ -42,6 +42,7 @@ import UVNode from './accessors/UVNode.js';
 // inputs
 import ColorNode from './inputs/ColorNode.js';
 import FloatNode from './inputs/FloatNode.js';
+import IntNode from './inputs/IntNode.js';
 import Matrix3Node from './inputs/Matrix3Node.js';
 import Matrix4Node from './inputs/Matrix3Node.js';
 import TextureNode from './inputs/TextureNode.js';
@@ -63,6 +64,7 @@ import LightNode from './lights/LightNode.js';
 import LightsNode from './lights/LightsNode.js';
 
 // utils
+import ArrayElementNode from './utils/ArrayElementNode.js';
 import JoinNode from './utils/JoinNode.js';
 import SplitNode from './utils/SplitNode.js';
 import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
@@ -76,15 +78,13 @@ export * from './core/constants.js';
 
 // functions
 export * from './functions/BSDFs.js';
-export * from './functions/EncodingFunctions.js';
-export * from './functions/MathFunctions.js';
-
-// consts
-export * from './consts/MathConsts.js';
 
 // materials
 export * from './materials/Materials.js';
 
+// shader node
+export * from './ShaderNode.js';
+
 export {
 	// core
 	ArrayInputNode,
@@ -130,6 +130,7 @@ export {
 	// inputs
 	ColorNode,
 	FloatNode,
+	IntNode,
 	Matrix3Node,
 	Matrix4Node,
 	TextureNode,
@@ -151,6 +152,7 @@ export {
 	LightsNode,
 
 	// utils
+	ArrayElementNode,
 	JoinNode,
 	SplitNode,
 	SpriteSheetUVNode,

+ 105 - 11
examples/jsm/renderers/nodes/ShaderNode.js

@@ -1,3 +1,7 @@
+// core
+import PropertyNode from './core/PropertyNode.js';
+import VarNode from './core/VarNode.js';
+
 // inputs
 import ColorNode from './inputs/ColorNode.js';
 import FloatNode from './inputs/FloatNode.js';
@@ -5,12 +9,17 @@ import Vector2Node from './inputs/Vector2Node.js';
 import Vector3Node from './inputs/Vector3Node.js';
 import Vector4Node from './inputs/Vector4Node.js';
 
+// accessors
+import PositionNode from './accessors/PositionNode.js';
+import NormalNode from './accessors/NormalNode.js';
+
 // math
 import OperatorNode from './math/OperatorNode.js';
 import CondNode from './math/CondNode.js';
 import MathNode from './math/MathNode.js';
 
 // utils
+import ArrayElementNode from './utils/ArrayElementNode.js';
 import JoinNode from './utils/JoinNode.js';
 import SplitNode from './utils/SplitNode.js';
 
@@ -21,22 +30,30 @@ const NodeHandler = {
 
 	construct( NodeClosure, params ) {
 
-		return NodeClosure( ShaderNodeObjects( params[ 0 ] ) );
+		const inputs = params.shift();
+
+		return NodeClosure( ShaderNodeObjects( inputs ), ...params );
 
 	},
 
 	get: function ( node, prop ) {
 
-		// Split Properties Pass
-
 		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
 
 			const splitProps = prop.match( /^[xyzwst]{1,4}$/ );
 
 			if ( splitProps !== null ) {
 
+				// accessing properties ( swizzle )
+
 				return ShaderNodeObject( new SplitNode( node, splitProps[ 0 ] ) );
 
+			} else if ( /^\d+$/.test( prop ) === true ) {
+
+				// accessing array
+
+				return ShaderNodeObject( new ArrayElementNode( node, new FloatNode( Number( prop ) ).setConst( true ) ) );
+
 			}
 
 		}
@@ -103,24 +120,45 @@ const ShaderNodeArray = ( array ) => {
 
 };
 
-const ShaderNodeProxy = ( NodeClass, scope ) => {
+const ShaderNodeProxy = ( NodeClass, scope = null, factor = null ) => {
 
-	return ( ...params ) => {
+	if ( scope === null ) {
 
-		return ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ) ) );
+		return ( ...params ) => {
 
-	};
+			return ShaderNodeObject( new NodeClass( ...ShaderNodeArray( params ) ) );
 
-};
+		};
 
+	} else if ( factor === null ) {
+
+		return ( ...params ) => {
+
+			return ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ) ) );
+
+		};
+
+	} else {
+
+		factor = ShaderNodeObject( factor );
+
+		return ( ...params ) => {
+
+			return ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ), factor ) );
+
+		};
+
+	}
+
+};
 
 const ShaderNodeScript = function ( jsFunc ) {
 
-	return ( inputs ) => {
+	return ( inputs, builder ) => {
 
 		ShaderNodeObjects( inputs );
 
-		return ShaderNodeObject( jsFunc( inputs ) );
+		return ShaderNodeObject( jsFunc( inputs, builder ) );
 
 	};
 
@@ -166,18 +204,42 @@ export const cond = ( ...params ) => {
 
 export const vec2 = ( ...params ) => {
 
+	// Providing one scalar value: This value is used for all components
+
+	if ( params.length === 1 ) {
+
+		params[ 1 ] = params[ 0 ];
+
+	}
+
 	return ShaderNodeObject( new Vector2Node( new Vector2( ...params ) ).setConst( true ) );
 
 };
 
 export const vec3 = ( ...params ) => {
 
+	// Providing one scalar value: This value is used for all components
+
+	if ( params.length === 1 ) {
+
+		params[ 1 ] = params[ 2 ] = params[ 0 ];
+
+	}
+
 	return ShaderNodeObject( new Vector3Node( new Vector3( ...params ) ).setConst( true ) );
 
 };
 
 export const vec4 = ( ...params ) => {
 
+	// Providing one scalar value: This value is used for all components
+
+	if ( params.length === 1 ) {
+
+		params[ 1 ] = params[ 2 ] = params[ 3 ] = params[ 0 ];
+
+	}
+
 	return ShaderNodeObject( new Vector4Node( new Vector4( ...params ) ).setConst( true ) );
 
 };
@@ -186,8 +248,32 @@ export const add = ShaderNodeProxy( OperatorNode, '+' );
 export const sub = ShaderNodeProxy( OperatorNode, '-' );
 export const mul = ShaderNodeProxy( OperatorNode, '*' );
 export const div = ShaderNodeProxy( OperatorNode, '/' );
-export const equals = ShaderNodeProxy( OperatorNode, '==' );
+export const equal = ShaderNodeProxy( OperatorNode, '==' );
+export const assign = ShaderNodeProxy( OperatorNode, '=' );
+export const greaterThan = ShaderNodeProxy( OperatorNode, '>' );
+export const and = ShaderNodeProxy( OperatorNode, '&&' );
+
+export const element = ShaderNodeProxy( ArrayElementNode );
+
+export const normalLocal = new NormalNode( NormalNode.LOCAL );
+export const normalWorld = new NormalNode( NormalNode.WORLD );
+export const normalView = new NormalNode( NormalNode.VIEW );
+export const transformedNormalView = new VarNode( new NormalNode( NormalNode.VIEW ), 'TransformedNormalView', 'vec3' );
+
+export const positionLocal = new PositionNode( PositionNode.LOCAL );
+export const positionWorld = new PositionNode( PositionNode.WORLD );
+export const positionView = new PositionNode( PositionNode.VIEW );
+export const positionViewDirection = new PositionNode( PositionNode.VIEW_DIRECTION );
+
+export const PI = float( 3.141592653589793 );
+export const RECIPROCAL_PI = float( 0.3183098861837907 );
+export const EPSILON = float( 1e-6 );
+
+export const materialDiffuseColor = new PropertyNode( 'MaterialDiffuseColor', 'vec4' );
+export const materialRoughness = new PropertyNode( 'MaterialRoughness', 'float' );
+export const materialSpecularTint = new PropertyNode( 'MaterialSpecularTint', 'vec4' );
 
+export const negate = ShaderNodeProxy( MathNode, 'negate' );
 export const floor = ShaderNodeProxy( MathNode, 'floor' );
 export const mod = ShaderNodeProxy( MathNode, 'mod' );
 export const cross = ShaderNodeProxy( MathNode, 'cross' );
@@ -195,7 +281,15 @@ export const max = ShaderNodeProxy( MathNode, 'max' );
 export const min = ShaderNodeProxy( MathNode, 'min' );
 export const dot = ShaderNodeProxy( MathNode, 'dot' );
 export const normalize = ShaderNodeProxy( MathNode, 'normalize' );
+export const sqrt = ShaderNodeProxy( MathNode, 'sqrt' );
 export const inversesqrt = ShaderNodeProxy( MathNode, 'inversesqrt' );
 export const sign = ShaderNodeProxy( MathNode, 'sign' );
 export const dFdx = ShaderNodeProxy( MathNode, 'dFdx' );
 export const dFdy = ShaderNodeProxy( MathNode, 'dFdy' );
+export const pow = ShaderNodeProxy( MathNode, 'pow' );
+export const pow2 = ShaderNodeProxy( MathNode, 'pow', 2 );
+export const pow3 = ShaderNodeProxy( MathNode, 'pow', 3 );
+export const pow4 = ShaderNodeProxy( MathNode, 'pow', 4 );
+export const exp2 = ShaderNodeProxy( MathNode, 'exp2' );
+export const saturate = ShaderNodeProxy( MathNode, 'saturate' );
+export const transformDirection = ShaderNodeProxy( MathNode, 'transformDirection' );

+ 2 - 2
examples/jsm/renderers/nodes/accessors/NormalNode.js

@@ -6,7 +6,6 @@ import ModelNode from '../accessors/ModelNode.js';
 import CameraNode from '../accessors/CameraNode.js';
 import OperatorNode from '../math/OperatorNode.js';
 import MathNode from '../math/MathNode.js';
-import { inverseTransformDirection } from '../functions/MathFunctions.js';
 
 class NormalNode extends Node {
 
@@ -50,7 +49,8 @@ class NormalNode extends Node {
 
 		} else if ( scope === NormalNode.WORLD ) {
 
-			const vertexNormalNode = inverseTransformDirection.call( { dir: new NormalNode( NormalNode.VIEW ), matrix: new CameraNode( CameraNode.VIEW_MATRIX ) } );
+			// To use INVERSE_TRANSFORM_DIRECTION only inverse the param order like this: MathNode( ..., Vector, Matrix );
+			const vertexNormalNode = new MathNode( MathNode.TRANSFORM_DIRECTION, new NormalNode( NormalNode.VIEW ), new CameraNode( CameraNode.VIEW_MATRIX ) );
 			outputNode = new MathNode( MathNode.NORMALIZE, new VaryNode( vertexNormalNode ) );
 
 		}

+ 1 - 2
examples/jsm/renderers/nodes/accessors/PositionNode.js

@@ -5,7 +5,6 @@ import VaryNode from '../core/VaryNode.js';
 import ModelNode from '../accessors/ModelNode.js';
 import MathNode from '../math/MathNode.js';
 import OperatorNode from '../math/OperatorNode.js';
-import { transformDirection } from '../functions/MathFunctions.js';
 
 class PositionNode extends Node {
 
@@ -45,7 +44,7 @@ class PositionNode extends Node {
 
 		} else if ( scope === PositionNode.WORLD ) {
 
-			const vertexPositionNode = transformDirection.call( { dir: new PositionNode( PositionNode.LOCAL ), matrix: new ModelNode( ModelNode.WORLD_MATRIX ) } );
+			const vertexPositionNode = new MathNode( MathNode.TRANSFORM_DIRECTION, new ModelNode( ModelNode.WORLD_MATRIX ), new PositionNode( PositionNode.LOCAL ) );
 			outputNode = new VaryNode( vertexPositionNode );
 
 		} else if ( scope === PositionNode.VIEW ) {

+ 39 - 36
examples/jsm/renderers/nodes/accessors/SkinningNode.js

@@ -3,45 +3,54 @@ import AttributeNode from '../core/AttributeNode.js';
 import ConstNode from '../core/ConstNode.js';
 import PositionNode from '../accessors/PositionNode.js';
 import NormalNode from '../accessors/NormalNode.js';
-import FunctionNode from '../core/FunctionNode.js';
 import Matrix4Node from '../inputs/Matrix4Node.js';
 import BufferNode from '../inputs/BufferNode.js';
 
+import { ShaderNode, assign, element, add, mul, transformDirection } from '../ShaderNode.js';
+
 import { NodeUpdateType } from '../core/constants.js';
 
-const Skinning = new FunctionNode( `
-	void ( inout vec3 position, inout vec3 normal, const in vec4 index, const in vec4 weight, const in mat4 bindMatrix, const in mat4 bindMatrixInverse ) {
+const Skinning = new ShaderNode( ( inputs, builder ) => {
+
+	const { position, normal, index, weight, bindMatrix, bindMatrixInverse, boneMatrices } = inputs;
+
+	const boneMatX = element( boneMatrices, index.x );
+	const boneMatY = element( boneMatrices, index.y );
+	const boneMatZ = element( boneMatrices, index.z );
+	const boneMatW = element( boneMatrices, index.w );
+
+	// POSITION
+
+	const skinVertex = mul( bindMatrix, position );
 
-		mat4 boneMatX = BoneMatrices[ int( index.x ) ];
-		mat4 boneMatY = BoneMatrices[ int( index.y ) ];
-		mat4 boneMatZ = BoneMatrices[ int( index.z ) ];
-		mat4 boneMatW = BoneMatrices[ int( index.w ) ];
+	const skinned = add(
+		mul( mul( boneMatX, skinVertex ), weight.x ),
+		mul( mul( boneMatY, skinVertex ), weight.y ),
+		mul( mul( boneMatZ, skinVertex ), weight.z ),
+		mul( mul( boneMatW, skinVertex ), weight.w )
+	);
 
-		// POSITION
+	const skinPosition = mul( bindMatrixInverse, skinned ).xyz;
 
-		vec4 skinVertex = bindMatrix * vec4( position, 1.0 );
+	// NORMAL
 
-		vec4 skinned = vec4( 0.0 );
-		skinned += boneMatX * skinVertex * weight.x;
-		skinned += boneMatY * skinVertex * weight.y;
-		skinned += boneMatZ * skinVertex * weight.z;
-		skinned += boneMatW * skinVertex * weight.w;
+	let skinMatrix = add(
+		mul( weight.x, boneMatX ),
+		mul( weight.y, boneMatY ),
+		mul( weight.z, boneMatZ ),
+		mul( weight.w, boneMatW )
+	);
 
-		position = ( bindMatrixInverse * skinned ).xyz;
+	skinMatrix = mul( mul( bindMatrixInverse, skinMatrix ), bindMatrix );
 
-		// NORMAL
+	const skinNormal = transformDirection( skinMatrix, normal ).xyz;
 
-		mat4 skinMatrix = mat4( 0.0 );
-		skinMatrix += skinWeight.x * boneMatX;
-		skinMatrix += skinWeight.y * boneMatY;
-		skinMatrix += skinWeight.z * boneMatZ;
-		skinMatrix += skinWeight.w * boneMatW;
-		skinMatrix = bindMatrixInverse * skinMatrix * bindMatrix;
+	// ASSIGNS
 
-		normal = vec4( skinMatrix * vec4( normal, 0.0 ) ).xyz;
+	assign( position, skinPosition ).build( builder );
+	assign( normal, skinNormal ).build( builder );
 
-	}`
-);
+} );
 
 class SkinningNode extends Node {
 
@@ -66,14 +75,6 @@ class SkinningNode extends Node {
 
 	generate( builder ) {
 
-		const keywords = builder.getContextValue( 'keywords' );
-
-		keywords.addKeyword( 'BoneMatrices', () => {
-
-			return new ConstNode( this.boneMatricesNode.build( builder ), 'mat4', 'BoneMatrices' );
-
-		} );
-
 		// inout nodes
 		const position = new PositionNode( PositionNode.LOCAL );
 		const normal = new NormalNode( NormalNode.LOCAL );
@@ -82,15 +83,17 @@ class SkinningNode extends Node {
 		const weight = this.skinWeightNode;
 		const bindMatrix = this.bindMatrixNode;
 		const bindMatrixInverse = this.bindMatrixInverseNode;
+		const boneMatrices = this.boneMatricesNode;
 
-		return Skinning.call( {
+		Skinning( {
 			position,
 			normal,
 			index,
 			weight,
 			bindMatrix,
-			bindMatrixInverse
-		} ).build( builder );
+			bindMatrixInverse,
+			boneMatrices
+		}, builder );
 
 	}
 

+ 0 - 7
examples/jsm/renderers/nodes/consts/MathConsts.js

@@ -1,7 +0,0 @@
-import ConstNode from '../core/ConstNode.js';
-
-export const PI = new ConstNode( '3.141592653589793', 'float', 'PI' );
-export const RECIPROCAL_PI = new ConstNode( '0.3183098861837907', 'float', 'RECIPROCAL_PI' );
-export const EPSILON = new ConstNode( '1e-6', 'float', 'EPSILON' );
-
-export const DEFAULT_SPECULAR_COEFFICIENT = new ConstNode( '0.04', 'float', 'DEFAULT_SPECULAR_COEFFICIENT' );

+ 9 - 4
examples/jsm/renderers/nodes/core/FunctionNode.js

@@ -8,7 +8,6 @@ class FunctionNode extends CodeNode {
 		super( code );
 
 		this.inputs = [];
-		this.nodeFunction = null;
 
 		this.useKeywords = true;
 
@@ -28,13 +27,19 @@ class FunctionNode extends CodeNode {
 
 	getNodeFunction( builder ) {
 
-		if ( this.nodeFunction === null ) {
+		const nodeData = builder.getDataFromNode( this );
 
-			this.nodeFunction = builder.parser.parseFunction( this.code );
+		let nodeFunction = nodeData.nodeFunction;
+
+		if ( nodeFunction === undefined ) {
+
+			nodeFunction = builder.parser.parseFunction( this.code );
+
+			nodeData.nodeFunction = nodeFunction;
 
 		}
 
-		return this.nodeFunction;
+		return nodeFunction;
 
 	}
 

+ 13 - 3
examples/jsm/renderers/nodes/core/InputNode.js

@@ -2,9 +2,11 @@ import Node from './Node.js';
 
 class InputNode extends Node {
 
-	constructor( nodeType ) {
+	constructor( inputType ) {
 
-		super( nodeType );
+		super( inputType );
+
+		this.inputType = inputType;
 
 		this.constant = false;
 
@@ -24,6 +26,12 @@ class InputNode extends Node {
 
 	}
 
+	getInputType( builder ) {
+
+		return this.inputType;
+
+	}
+
 	generateConst( builder ) {
 
 		return builder.getConst( this.getNodeType( builder ), this.value );
@@ -40,7 +48,9 @@ class InputNode extends Node {
 
 		} else {
 
-			const nodeUniform = builder.getUniformFromNode( this, builder.shaderStage, type );
+			const inputType = this.getInputType( builder );
+
+			const nodeUniform = builder.getUniformFromNode( this, builder.shaderStage, inputType );
 			const propertyName = builder.getPropertyName( nodeUniform );
 
 			return builder.format( propertyName, type, output );

+ 2 - 2
examples/jsm/renderers/nodes/core/Node.js

@@ -83,7 +83,7 @@ class Node {
 
 			if ( snippet === undefined ) {
 
-				snippet = this.generate( builder );
+				snippet = this.generate( builder ) || '';
 
 				nodeData.snippet = snippet;
 
@@ -93,7 +93,7 @@ class Node {
 
 		} else {
 
-			snippet = this.generate( builder, output );
+			snippet = this.generate( builder, output ) || '';
 
 		}
 

+ 47 - 30
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -141,15 +141,21 @@ class NodeBuilder {
 	getConst( type, value ) {
 
 		if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
-		if ( type === 'vec2' ) return `vec2( ${value.x}, ${value.y} )`;
-		if ( type === 'vec3' ) return `vec3( ${value.x}, ${value.y}, ${value.z} )`;
-		if ( type === 'vec4' ) return `vec4( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
-		if ( type === 'color' ) return `vec3( ${value.r}, ${value.g}, ${value.b} )`;
+		if ( type === 'vec2' ) return `${ this.getType( 'vec2' ) }( ${value.x}, ${value.y} )`;
+		if ( type === 'vec3' ) return `${ this.getType( 'vec3' ) }( ${value.x}, ${value.y}, ${value.z} )`;
+		if ( type === 'vec4' ) return `${ this.getType( 'vec4' ) }( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
+		if ( type === 'color' ) return `${ this.getType( 'vec3' ) }( ${value.r}, ${value.g}, ${value.b} )`;
 
 		throw new Error( `NodeBuilder: Type '${type}' not found in generate constant attempt.` );
 
 	}
 
+	getType( type ) {
+
+		return type;
+
+	}
+
 	getAttribute( name, type ) {
 
 		const attributes = this.attributes;
@@ -548,7 +554,7 @@ class NodeBuilder {
 
 		}
 
-		if ( this.context.vertex !== undefined && this.context.vertex !== null ) {
+		if ( this.context.vertex && this.context.vertex.isNode === true ) {
 
 			this.flowNodeFromShaderStage( 'vertex', this.context.vertex );
 
@@ -623,31 +629,42 @@ class NodeBuilder {
 
 		switch ( typeToType ) {
 
-			case 'float to vec2' : return `vec2( ${snippet} )`;
-			case 'float to vec3' : return `vec3( ${snippet} )`;
-			case 'float to vec4' : return `vec4( vec3( ${snippet} ), 1.0 )`;
-
-			case 'vec2 to float' : return `${snippet}.x`;
-			case 'vec2 to vec3' : return `vec3( ${snippet}, 0.0 )`;
-			case 'vec2 to vec4' : return `vec4( ${snippet}.xy, 0.0, 1.0 )`;
-
-			case 'vec3 to float' : return `${snippet}.x`;
-			case 'vec3 to vec2' : return `${snippet}.xy`;
-			case 'vec3 to vec4' : return `vec4( ${snippet}, 1.0 )`;
-
-			case 'vec4 to float' : return `${snippet}.x`;
-			case 'vec4 to vec2' : return `${snippet}.xy`;
-			case 'vec4 to vec3' : return `${snippet}.xyz`;
-
-			case 'mat3 to float' : return `( ${snippet} * vec3( 1.0 ) ).x`;
-			case 'mat3 to vec2' : return `( ${snippet} * vec3( 1.0 ) ).xy`;
-			case 'mat3 to vec3' : return `( ${snippet} * vec3( 1.0 ) ).xyz`;
-			case 'mat3 to vec4' : return `vec4( ${snippet} * vec3( 1.0 ), 1.0 )`;
-
-			case 'mat4 to float' : return `( ${snippet} * vec4( 1.0 ) ).x`;
-			case 'mat4 to vec2' : return `( ${snippet} * vec4( 1.0 ) ).xy`;
-			case 'mat4 to vec3' : return `( ${snippet} * vec4( 1.0 ) ).xyz`;
-			case 'mat4 to vec4' : return `( ${snippet} * vec4( 1.0 ) )`;
+			case 'int to float' : return `${ this.getType( 'float' ) }( ${ snippet } )`;
+			case 'int to vec2' : return `${ this.getType( 'vec2' ) }( ${ this.getType( 'float' ) }( ${ snippet } ) )`;
+			case 'int to vec3' : return `${ this.getType( 'vec3' ) }( ${ this.getType( 'float' ) }( ${ snippet } ) )`;
+			case 'int to vec4' : return `${ this.getType( 'vec4' ) }( ${ this.getType( 'vec3' ) }( ${ this.getType( 'float' ) }( ${ snippet } ) ), 1.0 )`;
+
+			case 'float to int' : return `${ this.getType( 'int' ) }( ${ snippet } )`;
+			case 'float to vec2' : return `${ this.getType( 'vec2' ) }( ${ snippet } )`;
+			case 'float to vec3' : return `${ this.getType( 'vec3' ) }( ${ snippet } )`;
+			case 'float to vec4' : return `${ this.getType( 'vec4' ) }( ${ this.getType( 'vec3' ) }( ${ snippet } ), 1.0 )`;
+
+			case 'vec2 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
+			case 'vec2 to float' : return `${ snippet }.x`;
+			case 'vec2 to vec3'  : return `${ this.getType( 'vec3' ) }( ${ snippet }, 0.0 )`;
+			case 'vec2 to vec4'  : return `${ this.getType( 'vec4' ) }( ${ snippet }.xy, 0.0, 1.0 )`;
+
+			case 'vec3 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
+			case 'vec3 to float' : return `${ snippet }.x`;
+			case 'vec3 to vec2'  : return `${ snippet }.xy`;
+			case 'vec3 to vec4'  : return `${ this.getType( 'vec4' ) }( ${ snippet }, 1.0 )`;
+
+			case 'vec4 to int' : return `${ this.getType( 'int' ) }( ${ snippet }.x )`;
+			case 'vec4 to float' : return `${ snippet }.x`;
+			case 'vec4 to vec2'  : return `${ snippet }.xy`;
+			case 'vec4 to vec3'  : return `${ snippet }.xyz`;
+
+			case 'mat3 to int' : return `${ this.getType( 'int' ) }( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).x`;
+			case 'mat3 to float' : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).x`;
+			case 'mat3 to vec2'  : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xy`;
+			case 'mat3 to vec3'  : return `( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ) ).xyz`;
+			case 'mat3 to vec4'  : return `${ this.getType( 'vec4' ) }( ${ snippet } * ${ this.getType( 'vec3' ) }( 1.0 ), 1.0 )`;
+
+			case 'mat4 to int' : return `${ this.getType( 'int' ) }( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).x`;
+			case 'mat4 to float' : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).x`;
+			case 'mat4 to vec2'  : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xy`;
+			case 'mat4 to vec3'  : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) ).xyz`;
+			case 'mat4 to vec4'  : return `( ${ snippet } * ${ this.getType( 'vec4' ) }( 1.0 ) )`;
 
 		}
 

+ 0 - 47
examples/jsm/renderers/nodes/core/NodeKeywords.js

@@ -3,18 +3,8 @@ import PropertyNode from './PropertyNode.js';
 import PositionNode from '../accessors/PositionNode.js';
 import NormalNode from '../accessors/NormalNode.js';
 
-import { PI, RECIPROCAL_PI, EPSILON } from '../consts/MathConsts.js';
-import { saturateMacro, whiteComplementMacro } from '../functions/MathFunctions.js';
-
 class NodeKeywords {
 
-	static PI = 'PI';
-	static RECIPROCAL_PI = 'RECIPROCAL_PI';
-	static EPSILON = 'EPSILON';
-
-	static Saturate = 'saturate';
-	static WhiteComplement = 'whiteComplement';
-
 	static PositionLocal = 'PositionLocal';
 	static PositionWorld = 'PositionWorld';
 	static PositionView = 'PositionView';
@@ -40,13 +30,6 @@ class NodeKeywords {
 	constructor() {
 
 		this.keywords = [
-			// consts
-			NodeKeywords.PI,
-			NodeKeywords.RECIPROCAL_PI,
-			NodeKeywords.EPSILON,
-			// variadic macros
-			NodeKeywords.Saturate,
-			NodeKeywords.WhiteComplement,
 			// nodes
 			NodeKeywords.PositionLocal,
 			NodeKeywords.PositionWorld,
@@ -93,36 +76,6 @@ class NodeKeywords {
 
 			switch ( name ) {
 
-				case NodeKeywords.PI:
-
-					node = PI;
-
-					break;
-
-				case NodeKeywords.RECIPROCAL_PI:
-
-					node = RECIPROCAL_PI;
-
-					break;
-
-				case NodeKeywords.EPSILON:
-
-					node = EPSILON;
-
-					break;
-
-				case NodeKeywords.Saturate:
-
-					node = saturateMacro;
-
-					break;
-
-				case NodeKeywords.WhiteComplement:
-
-					node = whiteComplementMacro;
-
-					break;
-
 				case NodeKeywords.PositionLocal:
 
 					node = new VarNode( new PositionNode( PositionNode.GEOMETRY ), name );

+ 6 - 0
examples/jsm/renderers/nodes/core/PropertyNode.js

@@ -10,6 +10,12 @@ class PropertyNode extends Node {
 
 	}
 
+	getHash( /*builder*/ ) {
+
+		return this.name;
+
+	}
+
 	generate( builder ) {
 
 		const nodeVary = builder.getVarFromNode( this, this.getNodeType( builder ) );

+ 6 - 0
examples/jsm/renderers/nodes/core/VarNode.js

@@ -11,6 +11,12 @@ class VarNode extends Node {
 
 	}
 
+	getHash( builder ) {
+
+		return this.name || super.getHash( builder );
+
+	}
+
 	getNodeType( builder ) {
 
 		return super.getNodeType( builder ) || this.value.getNodeType( builder );

+ 2 - 0
examples/jsm/renderers/nodes/core/constants.js

@@ -10,6 +10,8 @@ export const NodeUpdateType = {
 };
 
 export const NodeType = {
+	Boolean: 'bool',
+	Integer: 'int',
 	Float: 'float',
 	Vector2: 'vec2',
 	Vector3: 'vec3',

+ 25 - 18
examples/jsm/renderers/nodes/display/ColorSpaceNode.js

@@ -1,9 +1,15 @@
 import TempNode from '../core/Node.js';
-import CodeNode from '../core/CodeNode.js';
-import * as EncodingFunctions from '../functions/EncodingFunctions.js';
+import { ShaderNode } from '../ShaderNode.js';
 
-import { LinearEncoding, sRGBEncoding, RGBEEncoding, RGBM7Encoding, RGBM16Encoding,
-	RGBDEncoding, GammaEncoding, LogLuvEncoding } from 'three';
+import { LinearEncoding/*,
+	sRGBEncoding, RGBEEncoding, RGBM7Encoding, RGBM16Encoding,
+	RGBDEncoding, GammaEncoding, LogLuvEncoding*/ } from 'three';
+
+export const LinearToLinear = new ShaderNode( ( inputs ) => {
+
+	return inputs.value;
+
+} );
 
 function getEncodingComponents ( encoding ) {
 
@@ -11,6 +17,7 @@ function getEncodingComponents ( encoding ) {
 
 		case LinearEncoding:
 			return [ 'Linear' ];
+/*
 		case sRGBEncoding:
 			return [ 'sRGB' ];
 		case RGBEEncoding:
@@ -25,7 +32,7 @@ function getEncodingComponents ( encoding ) {
 			return [ 'Gamma', new CodeNode( 'float( GAMMA_FACTOR )' ) ];
 		case LogLuvEncoding:
 			return [ 'LogLuv' ];
-
+*/
 	}
 
 }
@@ -33,7 +40,7 @@ function getEncodingComponents ( encoding ) {
 class ColorSpaceNode extends TempNode {
 
 	static LINEAR_TO_LINEAR = 'LinearToLinear';
-
+/*
 	static GAMMA_TO_LINEAR = 'GammaToLinear';
 	static LINEAR_TO_GAMMA = 'LinearToGamma';
 
@@ -51,14 +58,14 @@ class ColorSpaceNode extends TempNode {
 
 	static LINEAR_TO_LOG_LUV = 'LinearToLogLuv';
 	static LOG_LUV_TO_LINEAR = 'LogLuvToLinear';
-
-	constructor( method, input ) {
+*/
+	constructor( method, value ) {
 
 		super( 'vec4' );
 
 		this.method = method;
 
-		this.input = input;
+		this.value = value;
 		this.factor = null;
 
 	}
@@ -90,22 +97,22 @@ class ColorSpaceNode extends TempNode {
 		const type = this.getNodeType( builder );
 
 		const method = this.method;
-		const input = this.input;
+		const value = this.value;
 
 		if ( method !== ColorSpaceNode.LINEAR_TO_LINEAR ) {
 
-			const encodingFunctionNode = EncodingFunctions[ method ];
-
-			const encodingFunctionCallNode = encodingFunctionNode.call( {
-				value: input,
-				factor: this.factor
-			} );
+			// disable for now color space
+			const encodingFunctionNode = LinearToLinear;
+			const factor = this.factor;
 
-			return encodingFunctionCallNode.build( builder, type );
+			return encodingFunctionNode( {
+				value,
+				factor
+			} ).build( builder, type );
 
 		} else {
 
-			return input.build( builder, type );
+			return value.build( builder, type );
 
 		}
 

+ 2 - 2
examples/jsm/renderers/nodes/display/NormalMapNode.js

@@ -6,7 +6,7 @@ import OperatorNode from '../math/OperatorNode.js';
 import FloatNode from '../inputs/FloatNode.js';
 import TempNode from '../core/TempNode.js';
 import ModelNode from '../accessors/ModelNode.js';
-import { ShaderNode, cond, add, mul, join, dFdx, dFdy, cross, max, dot, normalize, inversesqrt, equals } from '../ShaderNode.js';
+import { ShaderNode, cond, add, mul, join, dFdx, dFdy, cross, max, dot, normalize, inversesqrt, equal } from '../ShaderNode.js';
 
 import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from 'three';
 
@@ -33,7 +33,7 @@ const perturbNormal2ArbNode = new ShaderNode( ( inputs ) => {
 	const B = add( mul( q1perp, st0.y ), mul( q0perp, st1.y ) );
 
 	const det = max( dot( T, T ), dot( B, B ) );
-	const scale = cond( equals( det, 0 ), 0, mul( faceDirection, inversesqrt( det ) ) );
+	const scale = cond( equal( det, 0 ), 0, mul( faceDirection, inversesqrt( det ) ) );
 
 	return normalize( add( mul( T, mul( mapN.x, scale ) ), mul( B, mul( mapN.y, scale ) ), mul( N, mapN.z ) ) );
 

+ 64 - 101
examples/jsm/renderers/nodes/functions/BSDFs.js

@@ -1,68 +1,43 @@
-import FunctionNode from '../core/FunctionNode.js';
-import { pow2 } from './MathFunctions.js';
+import { ShaderNode,
+	add, sub, mul, div, saturate, dot, pow, pow2, exp2, normalize, max, sqrt, negate,
+	cond, greaterThan, and,
+	transformedNormalView, positionViewDirection,
+	materialDiffuseColor, materialSpecularTint, materialRoughness,
+	PI, RECIPROCAL_PI, EPSILON
+} from '../ShaderNode.js';
 
-export const F_Schlick = new FunctionNode( `
-vec3 F_Schlick( const in vec3 f0, const in float f90, const in float dotVH  ) {
+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
-	float fresnel = exp2( ( -5.55473 * dotVH - 6.98316 ) * dotVH );
-
-	return ( f90 - f0 ) * fresnel + f0;
-
-}` ); // validated
-
-export const G_BlinnPhong_Implicit = new FunctionNode( `
-float G_BlinnPhong_Implicit() {
-
-	// ( const in float dotNL, const in float dotNV )
-	// geometry term is (n dot l)(n dot v) / 4(n dot l)(n dot v)
-
-	return 0.25;
-
-}` ); // validated
-
-export const BRDF_Lambert = new FunctionNode( `
-vec3 BRDF_Lambert( const in vec3 diffuseColor ) {
-
-	return RECIPROCAL_PI * diffuseColor;
-
-}` ); // validated
+	const fresnel = exp2( mul( sub( mul( - 5.55473, dotVH ), 6.98316 ), dotVH ) );
 
-export const getDistanceAttenuation = new FunctionNode( `
-float getDistanceAttenuation( float lightDistance, float cutoffDistance, float decayExponent ) {
+	return mul( sub( f90, f0 ), add( fresnel, f0 ) );
 
-#if defined ( PHYSICALLY_CORRECT_LIGHTS )
+} ); // validated
 
-	// 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
-	float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), 0.01 );
+export const BRDF_Lambert = new ShaderNode( ( inputs ) => {
 
-	if( cutoffDistance > 0.0 ) {
+	return mul( RECIPROCAL_PI, inputs.diffuseColor ); // punctual light
 
-		distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );
+} ); // validated
 
-	}
+export const getDistanceAttenuation = new ShaderNode( ( inputs ) => {
 
-	return distanceFalloff;
+	const { lightDistance, cutoffDistance, decayExponent } = inputs;
 
-#else
+	return cond(
+		and( greaterThan( cutoffDistance, 0 ), greaterThan( decayExponent, 0 ) ),
+		pow( saturate( add( div( negate( lightDistance ), cutoffDistance ), 1.0 ) ), decayExponent ),
+		1.0
+	);
 
-	if( cutoffDistance > 0.0 && decayExponent > 0.0 ) {
-
-		return pow( saturate( -lightDistance / cutoffDistance + 1.0 ), decayExponent );
-
-	}
-
-	return 1.0;
-
-#endif
-
-}` ).setIncludes( [ pow2 ] );
+} ); // validated
 
 //
 // STANDARD
@@ -70,90 +45,78 @@ float getDistanceAttenuation( float lightDistance, float cutoffDistance, float d
 
 // 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 FunctionNode( `
-float V_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {
+export const V_GGX_SmithCorrelated = new ShaderNode( ( inputs ) => {
 
-	float a2 = pow2( alpha );
+	const { alpha, dotNL, dotNV } = inputs;
 
-	float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
-	float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );
+	const a2 = pow2( alpha );
 
-	return 0.5 / max( gv + gl, EPSILON );
+	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 ) ) ) ) );
 
-}` ).setIncludes( [ pow2 ] );
+	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 FunctionNode( `
-float D_GGX( const in float alpha, const in float dotNH ) {
-
-	float a2 = pow2( alpha );
+export const D_GGX = new ShaderNode( ( inputs ) => {
 
-	float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1
+	const { alpha, dotNH } = inputs;
 
-	return RECIPROCAL_PI * a2 / pow2( denom );
+	const a2 = pow2( alpha );
 
-}` ).setIncludes( [ pow2 ] );
+	const denom = add( mul( pow2( dotNH ), sub( a2, 1.0 ) ), 1.0 ); // avoid alpha = 0 with dotNH = 1
 
-// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
-export const BRDF_Specular_GGX = new FunctionNode( `
-vec3 BRDF_Specular_GGX( vec3 lightDirection, const in vec3 f0, const in float f90, const in float roughness ) {
+	return mul( RECIPROCAL_PI, div( a2, pow2( denom ) ) );
 
-	float alpha = pow2( roughness ); // UE4's roughness
+} ); // validated
 
-	vec3 halfDir = normalize( lightDirection + PositionViewDirection );
 
-	float dotNL = saturate( dot( TransformedNormalView, lightDirection ) );
-	float dotNV = saturate( dot( TransformedNormalView, PositionViewDirection ) );
-	float dotNH = saturate( dot( TransformedNormalView, halfDir ) );
-	float dotVH = saturate( dot( PositionViewDirection, halfDir ) );
-
-	vec3 F = F_Schlick( f0, f90, dotVH );
+// GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
+export const BRDF_Specular_GGX = new ShaderNode( ( inputs ) => {
 
-	float V = V_GGX_SmithCorrelated( alpha, dotNL, dotNV );
+	const { lightDirection, f0, f90, roughness } = inputs;
 
-	float D = D_GGX( alpha, dotNH );
+	const alpha = pow2( roughness ); // UE4's roughness
 
-	return F * ( V * D );
+	const halfDir = normalize( add( lightDirection, positionViewDirection ) );
 
-}` ).setIncludes( [ pow2, F_Schlick, V_GGX_SmithCorrelated, D_GGX ] ); // validated
+	const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
+	const dotNV = saturate( dot( transformedNormalView, positionViewDirection ) );
+	const dotNH = saturate( dot( transformedNormalView, halfDir ) );
+	const dotVH = saturate( dot( positionViewDirection, halfDir ) );
 
-export const RE_Direct_Physical = new FunctionNode( `
-void RE_Direct_Physical( inout ReflectedLight reflectedLight, vec3 lightDirection, vec3 lightColor ) {
+	const F = F_Schlick( { f0, f90, dotVH } );
 
-	float dotNL = saturate( dot( TransformedNormalView, lightDirection ) );
-	vec3 irradiance = dotNL * lightColor;
+	const V = V_GGX_SmithCorrelated( { alpha, dotNL, dotNV } );
 
-#ifndef PHYSICALLY_CORRECT_LIGHTS
+	const D = D_GGX( { alpha, dotNH } );
 
-		irradiance *= PI; // punctual light
+	return mul( F, mul( V, D ) );
 
-#endif
+} ); // validated
 
-	reflectedLight.directDiffuse += irradiance * BRDF_Lambert( MaterialDiffuseColor.rgb );
+export const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
 
-	reflectedLight.directSpecular += irradiance * BRDF_Specular_GGX( lightDirection, MaterialSpecularTint, 1.0, MaterialRoughness );
+	const { lightDirection, lightColor, directDiffuse, directSpecular } = inputs;
 
-}` ).setIncludes( [ BRDF_Lambert, BRDF_Specular_GGX ] );
+	const dotNL = saturate( dot( transformedNormalView, lightDirection ) );
+	let irradiance = mul( dotNL, lightColor );
 
-export const PhysicalLightingModel = new FunctionNode( `
-void ( inout ReflectedLight reflectedLight, vec3 lightDirection, vec3 lightColor ) {
+	irradiance = mul( irradiance, PI ); // punctual light
 
-	RE_Direct_Physical( reflectedLight, lightDirection, lightColor );
+	directDiffuse.value = add( directDiffuse.value, mul( irradiance, BRDF_Lambert( { diffuseColor: materialDiffuseColor } ) ) );
 
-}` ).setIncludes( [ RE_Direct_Physical ] );
+	directSpecular.value = add( directSpecular.value, mul( irradiance, BRDF_Specular_GGX( { lightDirection, f0: materialSpecularTint, f90: 1, roughness: materialRoughness } ) ) );
 
-// utils
+} );
 
-// Trowbridge-Reitz distribution to Mip level, following the logic of http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html
-export const getSpecularMIPLevel = new FunctionNode( `
-float ( const in float roughness, const in float maxMIPLevelScalar ) {
+export const PhysicalLightingModel = new ShaderNode( ( inputs/*, builder*/ ) => {
 
-	float sigma = PI * roughness * roughness / ( 1.0 + roughness );
-	float desiredMIPLevel = maxMIPLevelScalar + log2( sigma );
+	// PHYSICALLY_CORRECT_LIGHTS <-> builder.renderer.physicallyCorrectLights === true
 
-	// clamp to allowable LOD ranges.
-	return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );
+	RE_Direct_Physical( inputs );
 
-}` );
+} );

+ 0 - 99
examples/jsm/renderers/nodes/functions/EncodingFunctions.js

@@ -1,99 +0,0 @@
-import CodeNode from '../core/CodeNode.js';
-import FunctionNode from '../core/FunctionNode.js';
-
-export const LinearToLinear = new FunctionNode( `
-vec4 ( in vec4 value ) {
-	return value;
-}` );
-
-export const GammaToLinear = new FunctionNode( `
-vec4 ( in vec4 value, in float gammaFactor ) {
-	return vec4( pow( value.rgb, vec3( gammaFactor ) ), value.a );
-}` );
-
-export const LinearToGamma = new FunctionNode( `
-vec4 ( in vec4 value, in float gammaFactor ) {
-	return vec4( pow( value.rgb, vec3( 1.0 / gammaFactor ) ), value.a );
-}` );
-
-export const sRGBToLinear = new FunctionNode( `
-vec4 ( in vec4 value ) {
-	return vec4( mix( pow( value.rgb * 0.9478672986 + vec3( 0.0521327014 ), vec3( 2.4 ) ), value.rgb * 0.0773993808, vec3( lessThanEqual( value.rgb, vec3( 0.04045 ) ) ) ), value.a );
-}` );
-
-export const LinearTosRGB = new FunctionNode( `
-vec4 ( in vec4 value ) {
-	return vec4( mix( pow( value.rgb, vec3( 0.41666 ) ) * 1.055 - vec3( 0.055 ), value.rgb * 12.92, vec3( lessThanEqual( value.rgb, vec3( 0.0031308 ) ) ) ), value.a );
-}` );
-
-export const RGBEToLinear = new FunctionNode( `
-vec4 ( in vec4 value ) {
-	return vec4( value.rgb * exp2( value.a * 255.0 - 128.0 ), 1.0 );
-}` );
-
-export const LinearToRGBE = new FunctionNode( `
-vec4 ( in vec4 value ) {
-	float maxComponent = max( max( value.r, value.g ), value.b );
-	float fExp = clamp( ceil( log2( maxComponent ) ), -128.0, 127.0 );
-	return vec4( value.rgb / exp2( fExp ), ( fExp + 128.0 ) / 255.0 );
-	// return vec4( value.brg, ( 3.0 + 128.0 ) / 256.0 );
-}` );
-
-// reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html
-export const RGBMToLinear = new FunctionNode( `
-vec4 ( in vec4 value, in float maxRange ) {
-	return vec4( value.rgb * value.a * maxRange, 1.0 );
-}` );
-
-export const LinearToRGBM = new FunctionNode( `
-vec4 ( in vec4 value, in float maxRange ) {
-	float maxRGB = max( value.r, max( value.g, value.b ) );
-	float M = clamp( maxRGB / maxRange, 0.0, 1.0 );
-	M = ceil( M * 255.0 ) / 255.0;
-	return vec4( value.rgb / ( M * maxRange ), M );
-}` );
-
-// reference: http://iwasbeingirony.blogspot.ca/2010/06/difference-between-rgbm-and-rgbd.html
-export const RGBDToLinear = new FunctionNode( `
-vec4 ( in vec4 value, in float maxRange ) {
-	return vec4( value.rgb * ( ( maxRange / 255.0 ) / value.a ), 1.0 );
-}` );
-
-export const LinearToRGBD = new FunctionNode( `
-vec4 ( in vec4 value, in float maxRange ) {
-	float maxRGB = max( value.r, max( value.g, value.b ) );
-	float D = max( maxRange / maxRGB, 1.0 );
-	// NOTE: The implementation with min causes the shader to not compile on
-	// a common Alcatel A502DL in Chrome 78/Android 8.1. Some research suggests
-	// that the chipset is Mediatek MT6739 w/ IMG PowerVR GE8100 GPU.
-	// D = min( floor( D ) / 255.0, 1.0 );
-	D = clamp( floor( D ) / 255.0, 0.0, 1.0 );
-	return vec4( value.rgb * ( D * ( 255.0 / maxRange ) ), D );
-}` );
-
-// LogLuv reference: http://graphicrants.blogspot.ca/2009/04/rgbm-color-encoding.html
-export const cLogLuvM = new CodeNode( 'const mat3 cLogLuvMNode = mat3( 0.2209, 0.3390, 0.4184, 0.1138, 0.6780, 0.7319, 0.0102, 0.1130, 0.2969 );' );
-export const LinearToLogLuv = new FunctionNode( `
-vec4 ( in vec4 value ) {
-	vec3 Xp_Y_XYZp = cLogLuvMNode * value.rgb;
-	Xp_Y_XYZp = max( Xp_Y_XYZp, vec3( 1e-6, 1e-6, 1e-6 ) );
-	vec4 vResult;
-	vResult.xy = Xp_Y_XYZp.xy / Xp_Y_XYZp.z;
-	float Le = 2.0 * log2(Xp_Y_XYZp.y) + 127.0;
-	vResult.w = fract( Le );
-	vResult.z = ( Le - ( floor( vResult.w * 255.0 ) ) / 255.0 ) / 255.0;
-	return vResult;
-}` ).setIncludes( [ cLogLuvM ] );
-
-// Inverse M matrix, for decoding
-export const cLogLuvInverseM = new CodeNode( 'const mat3 cLogLuvInverseMNode = mat3( 6.0014, -2.7008, -1.7996, -1.3320, 3.1029, -5.7721, 0.3008, -1.0882, 5.6268 );' );
-export const LogLuvToLinear = new FunctionNode( `
-vec4 ( in vec4 value ) {
-	float Le = value.z * 255.0 + value.w;
-	vec3 Xp_Y_XYZp;
-	Xp_Y_XYZp.y = exp2( ( Le - 127.0 ) / 2.0 );
-	Xp_Y_XYZp.z = Xp_Y_XYZp.y / value.y;
-	Xp_Y_XYZp.x = value.x * Xp_Y_XYZp.z;
-	vec3 vRGB = cLogLuvInverseMNode * Xp_Y_XYZp.rgb;
-	return vec4( max( vRGB, 0.0 ), 1.0 );
-}` ).setIncludes( [ cLogLuvInverseM ] );

+ 0 - 47
examples/jsm/renderers/nodes/functions/MathFunctions.js

@@ -1,47 +0,0 @@
-import { PI } from '../consts/MathConsts.js';
-import CodeNode from '../core/CodeNode.js';
-import FunctionNode from '../core/FunctionNode.js';
-
-// variadic macros
-export const saturateMacro = new CodeNode( '#define saturate(a) clamp( a, 0.0, 1.0 )' );
-export const whiteComplementMacro = new CodeNode( '#define whiteComplement(a) ( 1.0 - saturate( a ) )' );
-
-export const transformDirection = new FunctionNode( `
-vec3 ( in vec3 dir, in mat4 matrix ) {
-
-	return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz );
-
-}` );
-
-export const inverseTransformDirection = new FunctionNode( `
-vec3 ( in vec3 dir, in mat4 matrix ) {
-
-	// dir can be either a direction vector or a normal vector
-	// upper-left 3x3 of matrix is assumed to be orthogonal
-
-	return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );
-
-}` );
-
-export const pow2 = new FunctionNode( 'float pow2( const in float x ) { return x*x; }' );
-export const pow3 = new FunctionNode( 'float pow3( const in float x ) { return x*x*x; }' );
-export const pow4 = new FunctionNode( 'float pow4( const in float x ) { float x2 = x*x; return x2*x2; }' );
-
-export const average = new FunctionNode( 'float average( const in vec3 color ) { return dot( color, vec3( 0.3333 ) ); }' );
-
-export const max3 = new FunctionNode( 'float max3( vec3 v ) { return max( max( v.x, v.y ), v.z ); }' );
-
-// expects values in the range of [0,1]x[0,1], returns values in the [0,1] range.
-// do not collapse into a single function per: http://byteblacksmith.com/improvements-to-the-canonical-one-liner-glsl-rand-for-opengl-es-2-0/
-export const rand = new FunctionNode( `
-highp float rand( const in vec2 uv ) {
-
-	const highp float a = 12.9898, b = 78.233, c = 43758.5453;
-
-	highp float dt = dot( uv.xy, vec2( a,b ) ), sn = mod( dt, 3.141592653589793 );
-
-	return fract(sin(sn) * c);
-
-}` ).setIncludes( [ PI ] );
-
-export const precisionSafeLength = new FunctionNode( 'float precisionSafeLength( vec3 v ) { return length( v ); }' );

+ 6 - 0
examples/jsm/renderers/nodes/inputs/BufferNode.js

@@ -15,6 +15,12 @@ class BufferNode extends InputNode {
 
 	}
 
+	getNodeType( builder ) {
+
+		return this.bufferType;
+
+	}
+
 }
 
 BufferNode.prototype.isBufferNode = true;

+ 17 - 0
examples/jsm/renderers/nodes/inputs/IntNode.js

@@ -0,0 +1,17 @@
+import InputNode from '../core/InputNode.js';
+
+class IntNode extends InputNode {
+
+	constructor( value = 0 ) {
+
+		super( 'int' );
+
+		this.value = value;
+
+	}
+
+}
+
+IntNode.prototype.isIntNode = true;
+
+export default IntNode;

+ 1 - 1
examples/jsm/renderers/nodes/inputs/TextureNode.js

@@ -51,7 +51,7 @@ class TextureNode extends InputNode {
 				const textureCallSnippet = builder.getTexture( textureProperty, uvSnippet, biasSnippet );
 
 				colorSpace = new ColorSpaceNode();
-				colorSpace.input = new ExpressionNode( textureCallSnippet, 'vec4' );
+				colorSpace.value = new ExpressionNode( textureCallSnippet, 'vec4' );
 				colorSpace.fromDecoding( builder.getTextureEncodingFromMap( this.value ) );
 
 				nodeData.colorSpace = colorSpace;

+ 18 - 12
examples/jsm/renderers/nodes/lights/LightContextNode.js

@@ -1,12 +1,9 @@
 import ContextNode from '../core/ContextNode.js';
-import StructNode from '../core/StructNode.js';
+import VarNode from '../core/VarNode.js';
+import Vector3Node from '../inputs/Vector3Node.js';
+import OperatorNode from '../math/OperatorNode.js';
 import { PhysicalLightingModel } from '../functions/BSDFs.js';
 
-const reflectedLightStruct = new StructNode( {
-	directDiffuse: 'vec3',
-	directSpecular: 'vec3'
-}, 'ReflectedLight' );
-
 class LightContextNode extends ContextNode {
 
 	constructor( node ) {
@@ -15,9 +12,13 @@ class LightContextNode extends ContextNode {
 
 	}
 
-	generate( builder ) {
+	getNodeType( /*builder*/ ) {
 
-		const type = this.getNodeType( builder );
+		return 'vec3';
+
+	}
+
+	generate( builder ) {
 
 		const material = builder.material;
 
@@ -29,10 +30,11 @@ class LightContextNode extends ContextNode {
 
 		}
 
-		const reflectedLightNode = reflectedLightStruct.create();
-		const reflectedLight = reflectedLightNode.build( builder, 'var' );
+		const directDiffuse = new VarNode( new Vector3Node() );
+		const directSpecular = new VarNode( new Vector3Node() );
 
-		this.setContextValue( 'reflectedLight', reflectedLightNode );
+		this.setContextValue( 'directDiffuse', directDiffuse );
+		this.setContextValue( 'directSpecular', directSpecular );
 
 		if ( lightingModel !== null ) {
 
@@ -42,9 +44,13 @@ class LightContextNode extends ContextNode {
 
 		// add code
 
+		const type = this.getNodeType( builder );
+
 		super.generate( builder, type );
 
-		return `( ${reflectedLight}.directDiffuse + ${reflectedLight}.directSpecular )`;
+		const totalLight = new OperatorNode( '+', directDiffuse, directSpecular );
+
+		return totalLight.build( builder, type );
 
 	}
 

+ 27 - 27
examples/jsm/renderers/nodes/lights/LightNode.js

@@ -25,23 +25,6 @@ class LightNode extends Node {
 		this.lightCutoffDistance = new FloatNode( 0 );
 		this.lightDecayExponent = new FloatNode( 0 );
 
-		this.lightPositionView = new Object3DNode( Object3DNode.VIEW_POSITION );
-		this.positionView = new PositionNode( PositionNode.VIEW );
-
-		this.lVector = new OperatorNode( '-', this.lightPositionView, this.positionView );
-
-		this.lightDirection = new MathNode( MathNode.NORMALIZE, this.lVector );
-
-		this.lightDistance = new MathNode( MathNode.LENGTH, this.lVector );
-
-		this.lightAttenuation = getDistanceAttenuation.call( {
-			lightDistance: this.lightDistance,
-			cutoffDistance: this.lightCutoffDistance,
-			decayExponent: this.lightDecayExponent
-		} );
-
-		this.lightColor = new OperatorNode( '*', this.color, this.lightAttenuation );
-
 	}
 
 	update( /* frame */ ) {
@@ -56,21 +39,38 @@ class LightNode extends Node {
 
 		const type = this.getNodeType( builder );
 
-		this.lightPositionView.object3d = this.light;
+		const lightPositionView = new Object3DNode( Object3DNode.VIEW_POSITION );
+		const positionView = new PositionNode( PositionNode.VIEW );
+
+		const lVector = new OperatorNode( '-', lightPositionView, positionView );
+
+		const lightDirection = new MathNode( MathNode.NORMALIZE, lVector );
+
+		const lightDistance = new MathNode( MathNode.LENGTH, lVector );
+
+		const lightAttenuation = getDistanceAttenuation( {
+			lightDistance,
+			cutoffDistance: this.lightCutoffDistance,
+			decayExponent: this.lightDecayExponent
+		} );
+
+		const lightColor = new OperatorNode( '*', this.color, lightAttenuation );
 
-		const lightingModelFunctionNode = builder.getContextValue( 'lightingModel' );
+		lightPositionView.object3d = this.light;
 
-		if ( lightingModelFunctionNode !== undefined ) {
+		const lightingModelFunction = builder.getContextValue( 'lightingModel' );
 
-			const reflectedLightStructNode = builder.getContextValue( 'reflectedLight' );
+		if ( lightingModelFunction !== undefined ) {
 
-			const lightingModelCallNode = lightingModelFunctionNode.call( {
-				lightDirection: this.lightDirection,
-				lightColor: this.lightColor,
-				reflectedLight:	reflectedLightStructNode
-			} );
+			const directDiffuse = builder.getContextValue( 'directDiffuse' );
+			const directSpecular = builder.getContextValue( 'directSpecular' );
 
-			builder.addFlowCode( lightingModelCallNode.build( builder ) );
+			lightingModelFunction( {
+				lightDirection,
+				lightColor,
+				directDiffuse,
+				directSpecular
+			}, builder );
 
 		}
 

+ 55 - 20
examples/jsm/renderers/nodes/math/MathNode.js

@@ -1,4 +1,7 @@
-import TempNode from '../core/Node.js';
+import TempNode from '../core/TempNode.js';
+import ExpressionNode from '../core/ExpressionNode.js';
+import SplitNode from '../utils/SplitNode.js';
+import OperatorNode from './OperatorNode.js';
 
 class MathNode extends TempNode {
 
@@ -30,6 +33,7 @@ class MathNode extends TempNode {
 	static INVERT = 'invert';
 	static DFDX = 'dFdx';
 	static DFDY = 'dFdy';
+	static SATURATE = 'saturate'
 
 	// 2 inputs
 
@@ -42,6 +46,7 @@ class MathNode extends TempNode {
 	static DOT = 'dot';
 	static CROSS = 'cross';
 	static POW = 'pow';
+	static TRANSFORM_DIRECTION = 'transformDirection';
 
 	// 3 inputs
 
@@ -114,13 +119,43 @@ class MathNode extends TempNode {
 		const type = this.getNodeType( builder );
 		const inputType = this.getInputType( builder );
 
-		if ( method === MathNode.NEGATE ) {
+		const a = this.a;
+		const b = this.b;
+		const c = this.c;
 
-			return '( -' + this.a.build( builder, inputType ) + ' )';
+		if ( method === MathNode.TRANSFORM_DIRECTION ) {
+
+			// dir can be either a direction vector or a normal vector
+			// upper-left 3x3 of matrix is assumed to be orthogonal
+
+			let tA = a;
+			let tB = b;
+
+			if ( builder.isMatrix( tA.getNodeType( builder ) ) ) {
+
+				tB = new ExpressionNode( `${ builder.getType( 'vec4' ) }( ${ tB.build( builder, 'vec3' ) }, 0.0 )`, 'vec4' );
+
+			} else {
+
+				tA = new ExpressionNode( `${ builder.getType( 'vec4' ) }( ${ tA.build( builder, 'vec3' ) }, 0.0 )`, 'vec4' );
+
+			}
+
+			const mulNode = new SplitNode( new OperatorNode( '*', tA, tB ), 'xyz' );
+
+			return new MathNode( MathNode.NORMALIZE, mulNode ).build( builder );
+
+		} else if ( method === MathNode.SATURATE ) {
+
+			return `clamp( ${ a.build( builder, inputType ) }, 0.0, 1.0 )`;
+
+		} else if ( method === MathNode.NEGATE ) {
+
+			return '( -' + a.build( builder, inputType ) + ' )';
 
 		} else if ( method === MathNode.INVERT ) {
 
-			return '( 1.0 - ' + this.a.build( builder, inputType ) + ' )';
+			return '( 1.0 - ' + a.build( builder, inputType ) + ' )';
 
 		} else {
 
@@ -129,51 +164,51 @@ class MathNode extends TempNode {
 			if ( method === MathNode.CROSS ) {
 
 				params.push(
-					this.a.build( builder, type ),
-					this.b.build( builder, type )
+					a.build( builder, type ),
+					b.build( builder, type )
 				);
 
 			} else if ( method === MathNode.STEP ) {
 
 				params.push(
-					this.b.build( builder, this.a.getTypeLength( builder ) === 1 ? 'float' : inputType ),
-					this.b.build( builder, inputType )
+					b.build( builder, a.getTypeLength( builder ) === 1 ? 'float' : inputType ),
+					b.build( builder, inputType )
 				);
 
 			} else if ( method === MathNode.MIN || method === MathNode.MAX || method === MathNode.MOD ) {
 
 				params.push(
-					this.a.build( builder, inputType ),
-					this.b.build( builder, this.b.getTypeLength( builder ) === 1 ? 'float' : inputType )
+					a.build( builder, inputType ),
+					b.build( builder, b.getTypeLength( builder ) === 1 ? 'float' : inputType )
 				);
 
 			} else if ( method === MathNode.REFRACT ) {
 
 				params.push(
-					this.a.build( builder, inputType ),
-					this.b.build( builder, inputType ),
-					this.c.build( builder, 'float' )
+					a.build( builder, inputType ),
+					b.build( builder, inputType ),
+					c.build( builder, 'float' )
 				);
 
 			} else if ( method === MathNode.MIX ) {
 
 				params.push(
-					this.a.build( builder, inputType ),
-					this.b.build( builder, inputType ),
-					this.c.build( builder, this.c.getTypeLength( builder ) === 1 ? 'float' : inputType )
+					a.build( builder, inputType ),
+					b.build( builder, inputType ),
+					c.build( builder, c.getTypeLength( builder ) === 1 ? 'float' : inputType )
 				);
 
 			} else {
 
-				params.push( this.a.build( builder, inputType ) );
+				params.push( a.build( builder, inputType ) );
 
-				if ( this.c !== null ) {
+				if ( c !== null ) {
 
-					params.push( this.b.build( builder, inputType ), this.c.build( builder, inputType ) );
+					params.push( b.build( builder, inputType ), c.build( builder, inputType ) );
 
 				} else if ( this.b !== null ) {
 
-					params.push( this.b.build( builder, inputType ) );
+					params.push( b.build( builder, inputType ) );
 
 				}
 

+ 19 - 7
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -31,7 +31,11 @@ class OperatorNode extends TempNode {
 
 		const op = this.op;
 
-		if ( op === '==' ) {
+		if ( op === '=' ) {
+
+			return this.a.getNodeType( builder );
+
+		} else if ( op === '==' || op === '>' || op === '&&' ) {
 
 			return 'bool';
 
@@ -40,7 +44,11 @@ class OperatorNode extends TempNode {
 			const typeA = this.a.getNodeType( builder );
 			const typeB = this.b.getNodeType( builder );
 
-			if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) {
+			if ( typeA === 'float' && builder.isMatrix( typeB ) ) {
+
+				return typeB;
+
+			} else if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) {
 
 				// matrix x vector
 
@@ -68,28 +76,32 @@ class OperatorNode extends TempNode {
 
 	generate( builder, output ) {
 
+		const op = this.op;
+
 		let typeA = this.a.getNodeType( builder );
 		let typeB = this.b.getNodeType( builder );
 
-		let type = this.getNodeType( builder );
+		if ( op === '=' ) {
+
+			typeB = typeA;
 
-		if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) {
+		} else if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) {
 
 			// matrix x vector
 
-			type = typeB = builder.getVectorFromMatrix( typeA );
+			typeB = builder.getVectorFromMatrix( typeA );
 
 		} else if ( builder.isVector( typeA ) && builder.isMatrix( typeB ) ) {
 
 			// vector x matrix
 
-			type = typeB = builder.getVectorFromMatrix( typeB );
+			typeA = builder.getVectorFromMatrix( typeB );
 
 		} else {
 
 			// anytype x anytype
 
-			typeA = typeB = type;
+			typeA = typeB = this.getNodeType( builder );
 
 		}
 

+ 1 - 1
examples/jsm/renderers/nodes/parsers/GLSLNodeFunction.js

@@ -1,7 +1,7 @@
 import NodeFunction from '../core/NodeFunction.js';
 import NodeFunctionInput from '../core/NodeFunctionInput.js';
 
-const declarationRegexp = /^\s*(highp|mediump|lowp)?\s*([a-z_0-9]+)\s*([a-z_0-9]+)?\s*\((.*?)\)/i;
+const declarationRegexp = /^\s*(highp|mediump|lowp)?\s*([a-z_0-9]+)\s*([a-z_0-9]+)?\s*\(([\s\S]*?)\)/i;
 const propertiesRegexp = /[a-z_0-9]+/ig;
 
 const pragmaMain = '#pragma main';

+ 2 - 2
examples/jsm/renderers/nodes/procedural/CheckerNode.js

@@ -25,9 +25,9 @@ class CheckerNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
-		return checkerShaderNode( { uv: this.uv } ).build( builder, output );
+		return checkerShaderNode( { uv: this.uv } ).build( builder );
 
 	}
 

+ 31 - 0
examples/jsm/renderers/nodes/utils/ArrayElementNode.js

@@ -0,0 +1,31 @@
+import Node from '../core/Node.js';
+
+class ArrayElementNode extends Node {
+
+	constructor( node, indexNode ) {
+
+		super();
+
+		this.node = node;
+		this.indexNode = indexNode;
+
+	}
+
+	getNodeType( builder ) {
+
+		return this.node.getNodeType( builder );
+
+	}
+
+	generate( builder ) {
+
+		const nodeSnippet = this.node.build( builder );
+		const indexSnippet = this.indexNode.build( builder, 'int' );
+
+		return `${nodeSnippet}[ ${indexSnippet} ]`;
+
+	}
+
+}
+
+export default ArrayElementNode;

+ 5 - 5
examples/webgpu_lights_custom.html

@@ -29,6 +29,7 @@
 			import { OrbitControls } from './jsm/controls/OrbitControls.js';
 
 			import * as Nodes from './jsm/renderers/nodes/Nodes.js';
+			import { add } from './jsm/renderers/nodes/ShaderNode.js';
 
 			let camera, scene, renderer;
 
@@ -88,14 +89,13 @@
 
 				// custom lighting model
 
-				const customLightingModel = new Nodes.FunctionNode( `
-					void ( inout ReflectedLight reflectedLight, vec3 lightColor ) {
+				const customLightingModel = new Nodes.ShaderNode( ( inputs ) => {
 
-						// lightColor returns the light color with the intensity calculated
+					const { lightColor, directDiffuse } = inputs;
 
-						reflectedLight.directDiffuse += lightColor;
+					directDiffuse.value = add( directDiffuse.value, lightColor );
 
-					}` );
+				} );
 
 				const lightingModelContext = new Nodes.ContextNode( allLightsNode );
 				lightingModelContext.setContextValue( 'lightingModel', customLightingModel );