Browse Source

TSL: Move `ShaderNode` to `tslFn()` (#26311)

* Move `ShaderNode` to `fn()`

Move `func()` -> `wgslFn()`
Move `shader()` -> `fn()`

* NodeMaterialLoader: Fix createMaterialFromType()

* revisions

* return stack output if is void

* SkinningNode: Move assign() to stack

* Nodes: Vertex stage revision and rename fn() -> tslFn()

* cleanup
sunag 2 years ago
parent
commit
d8e6aa315e
41 changed files with 252 additions and 221 deletions
  1. 2 2
      examples/jsm/nodes/Nodes.js
  2. 0 2
      examples/jsm/nodes/accessors/InstanceNode.js
  3. 43 62
      examples/jsm/nodes/accessors/SkinningNode.js
  4. 3 0
      examples/jsm/nodes/code/CodeNode.js
  5. 25 4
      examples/jsm/nodes/code/FunctionNode.js
  6. 2 2
      examples/jsm/nodes/core/BypassNode.js
  7. 8 0
      examples/jsm/nodes/core/Node.js
  8. 1 1
      examples/jsm/nodes/core/StackNode.js
  9. 9 9
      examples/jsm/nodes/display/BlendModeNode.js
  10. 7 7
      examples/jsm/nodes/display/ColorAdjustmentNode.js
  11. 4 4
      examples/jsm/nodes/display/ColorSpaceNode.js
  12. 3 3
      examples/jsm/nodes/display/NormalMapNode.js
  13. 8 8
      examples/jsm/nodes/display/ToneMappingNode.js
  14. 5 5
      examples/jsm/nodes/functions/BSDF/BRDF_BlinnPhong.js
  15. 5 5
      examples/jsm/nodes/functions/BSDF/BRDF_GGX.js
  16. 2 2
      examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js
  17. 2 2
      examples/jsm/nodes/functions/BSDF/DFGApprox.js
  18. 2 2
      examples/jsm/nodes/functions/BSDF/D_GGX.js
  19. 3 3
      examples/jsm/nodes/functions/BSDF/EnvironmentBRDF.js
  20. 2 4
      examples/jsm/nodes/functions/BSDF/F_Schlick.js
  21. 2 2
      examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js
  22. 6 6
      examples/jsm/nodes/functions/PhongLightingModel.js
  23. 13 13
      examples/jsm/nodes/functions/PhysicalLightingModel.js
  24. 2 2
      examples/jsm/nodes/functions/material/getGeometryRoughness.js
  25. 3 3
      examples/jsm/nodes/functions/material/getRoughness.js
  26. 1 1
      examples/jsm/nodes/lighting/DirectionalLightNode.js
  27. 2 2
      examples/jsm/nodes/lighting/LightUtils.js
  28. 4 4
      examples/jsm/nodes/lighting/LightingContextNode.js
  29. 2 2
      examples/jsm/nodes/lighting/PointLightNode.js
  30. 2 2
      examples/jsm/nodes/lighting/SpotLightNode.js
  31. 1 1
      examples/jsm/nodes/materials/MeshStandardNodeMaterial.js
  32. 9 9
      examples/jsm/nodes/materials/NodeMaterial.js
  33. 3 3
      examples/jsm/nodes/materialx/lib/mx_hsv.js
  34. 14 14
      examples/jsm/nodes/materialx/lib/mx_noise.js
  35. 4 4
      examples/jsm/nodes/materialx/lib/mx_transform_color.js
  36. 3 3
      examples/jsm/nodes/procedural/CheckerNode.js
  37. 24 4
      examples/jsm/nodes/shadernode/ShaderNode.js
  38. 1 1
      examples/jsm/nodes/utils/LoopNode.js
  39. 9 5
      examples/jsm/renderers/common/Pipelines.js
  40. 2 4
      examples/webgpu_lights_custom.html
  41. 9 9
      examples/webgpu_materials.html

+ 2 - 2
examples/jsm/nodes/Nodes.js

@@ -103,9 +103,9 @@ export { default as ViewportDepthNode, viewZToOrthographicDepth, orthographicDep
 
 // code
 export { default as ExpressionNode, expression } from './code/ExpressionNode.js';
-export { default as CodeNode, code, js } from './code/CodeNode.js';
+export { default as CodeNode, code, js, wgsl, glsl } from './code/CodeNode.js';
 export { default as FunctionCallNode, call } from './code/FunctionCallNode.js';
-export { default as FunctionNode, func, fn } from './code/FunctionNode.js';
+export { default as FunctionNode, wgslFn, glslFn } from './code/FunctionNode.js';
 export { default as ScriptableNode, scriptable, global } from './code/ScriptableNode.js';
 export { default as ScriptableValueNode, scriptableValue } from './code/ScriptableValueNode.js';
 

+ 0 - 2
examples/jsm/nodes/accessors/InstanceNode.js

@@ -60,8 +60,6 @@ class InstanceNode extends Node {
 		builder.stack.assign( positionLocal, instancePosition );
 		builder.stack.assign( normalLocal, instanceNormal );
 
-		return builder.stack;
-
 	}
 
 }

+ 43 - 62
examples/jsm/nodes/accessors/SkinningNode.js

@@ -1,6 +1,6 @@
 import Node, { addNodeClass } from '../core/Node.js';
 import { NodeUpdateType } from '../core/constants.js';
-import { ShaderNode, nodeProxy } from '../shadernode/ShaderNode.js';
+import { nodeProxy } from '../shadernode/ShaderNode.js';
 import { attribute } from '../core/AttributeNode.js';
 import { uniform } from '../core/UniformNode.js';
 import { add } from '../math/OperatorNode.js';
@@ -9,91 +9,72 @@ import { normalLocal } from './NormalNode.js';
 import { positionLocal } from './PositionNode.js';
 import { tangentLocal } from './TangentNode.js';
 
-const Skinning = new ShaderNode( ( inputs, {}, builder ) => {
+class SkinningNode extends Node {
 
-	const { index, weight, bindMatrix, bindMatrixInverse, boneMatrices } = inputs;
+	constructor( skinnedMesh ) {
 
-	const boneMatX = boneMatrices.element( index.x );
-	const boneMatY = boneMatrices.element( index.y );
-	const boneMatZ = boneMatrices.element( index.z );
-	const boneMatW = boneMatrices.element( index.w );
+		super( 'void' );
 
-	// POSITION
+		this.skinnedMesh = skinnedMesh;
 
-	const skinVertex = bindMatrix.mul( positionLocal );
+		this.updateType = NodeUpdateType.OBJECT;
 
-	const skinned = add(
-		boneMatX.mul( weight.x ).mul( skinVertex ),
-		boneMatY.mul( weight.y ).mul( skinVertex ),
-		boneMatZ.mul( weight.z ).mul( skinVertex ),
-		boneMatW.mul( weight.w ).mul( skinVertex )
-	);
+		//
 
-	const skinPosition = bindMatrixInverse.mul( skinned ).xyz;
+		this.skinIndexNode = attribute( 'skinIndex', 'uvec4' );
+		this.skinWeightNode = attribute( 'skinWeight', 'vec4' );
 
-	// NORMAL
+		this.bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' );
+		this.bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' );
+		this.boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );
 
-	let skinMatrix = add(
-		weight.x.mul( boneMatX ),
-		weight.y.mul( boneMatY ),
-		weight.z.mul( boneMatZ ),
-		weight.w.mul( boneMatW )
-	);
+	}
 
-	skinMatrix = bindMatrixInverse.mul( skinMatrix ).mul( bindMatrix );
+	construct( builder ) {
 
-	const skinNormal = skinMatrix.transformDirection( normalLocal ).xyz;
+		const { skinIndexNode, skinWeightNode, bindMatrixNode, bindMatrixInverseNode, boneMatricesNode } = this;
 
-	// ASSIGNS
+		const boneMatX = boneMatricesNode.element( skinIndexNode.x );
+		const boneMatY = boneMatricesNode.element( skinIndexNode.y );
+		const boneMatZ = boneMatricesNode.element( skinIndexNode.z );
+		const boneMatW = boneMatricesNode.element( skinIndexNode.w );
 
-	positionLocal.assign( skinPosition ).build( builder ); // @TODO: For some reason this doesn't work as stack.assign( positionLocal, skinPosition )?
-	normalLocal.assign( skinNormal ).build( builder );
+		// POSITION
 
-	if ( builder.hasGeometryAttribute( 'tangent' ) ) {
+		const skinVertex = bindMatrixNode.mul( positionLocal );
 
-		tangentLocal.assign( skinNormal ).build( builder );
+		const skinned = add(
+			boneMatX.mul( skinWeightNode.x ).mul( skinVertex ),
+			boneMatY.mul( skinWeightNode.y ).mul( skinVertex ),
+			boneMatZ.mul( skinWeightNode.z ).mul( skinVertex ),
+			boneMatW.mul( skinWeightNode.w ).mul( skinVertex )
+		);
 
-	}
+		const skinPosition = bindMatrixInverseNode.mul( skinned ).xyz;
 
-} );
+		// NORMAL
 
-class SkinningNode extends Node {
+		let skinMatrix = add(
+			skinWeightNode.x.mul( boneMatX ),
+			skinWeightNode.y.mul( boneMatY ),
+			skinWeightNode.z.mul( boneMatZ ),
+			skinWeightNode.w.mul( boneMatW )
+		);
 
-	constructor( skinnedMesh ) {
+		skinMatrix = bindMatrixInverseNode.mul( skinMatrix ).mul( bindMatrixNode );
 
-		super( 'void' );
+		const skinNormal = skinMatrix.transformDirection( normalLocal ).xyz;
 
-		this.skinnedMesh = skinnedMesh;
+		// ASSIGNS
 
-		this.updateType = NodeUpdateType.OBJECT;
+		builder.stack.assign( positionLocal, skinPosition );
+		builder.stack.assign( normalLocal, skinNormal );
 
-		//
+		if ( builder.hasGeometryAttribute( 'tangent' ) ) {
 
-		this.skinIndexNode = attribute( 'skinIndex', 'uvec4' );
-		this.skinWeightNode = attribute( 'skinWeight', 'vec4' );
-
-		this.bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' );
-		this.bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' );
-		this.boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );
-
-	}
+			builder.stack.assign( tangentLocal, skinNormal );
 
-	generate( builder ) {
-
-		/*return new ShaderNode( ( {}, stack, builder ) => Skinning.call( {
-			index: this.skinIndexNode,
-			weight: this.skinWeightNode,
-			bindMatrix: this.bindMatrixNode,
-			bindMatrixInverse: this.bindMatrixInverseNode,
-			boneMatrices: this.boneMatricesNode
-		}, stack, builder ) ).build( builder );*/
-		Skinning.call( {
-			index: this.skinIndexNode,
-			weight: this.skinWeightNode,
-			bindMatrix: this.bindMatrixNode,
-			bindMatrixInverse: this.bindMatrixInverseNode,
-			boneMatrices: this.boneMatricesNode
-		}, {}, builder );
+		}
 
 	}
 

+ 3 - 0
examples/jsm/nodes/code/CodeNode.js

@@ -70,6 +70,9 @@ class CodeNode extends Node {
 export default CodeNode;
 
 export const code = nodeProxy( CodeNode );
+
 export const js = ( src, includes ) => code( src, includes, 'js' );
+export const wgsl = ( src, includes ) => code( src, includes, 'wgsl' );
+export const glsl = ( src, includes ) => code( src, includes, 'glsl' );
 
 addNodeClass( CodeNode );

+ 25 - 4
examples/jsm/nodes/code/FunctionNode.js

@@ -4,9 +4,9 @@ import { nodeObject } from '../shadernode/ShaderNode.js';
 
 class FunctionNode extends CodeNode {
 
-	constructor( code = '', includes = [] ) {
+	constructor( code = '', includes = [], language = '' ) {
 
-		super( code, includes );
+		super( code, includes, language );
 
 		this.keywords = {};
 
@@ -99,8 +99,29 @@ class FunctionNode extends CodeNode {
 
 export default FunctionNode;
 
-export const func = ( code, includes ) => nodeObject( new FunctionNode( code, includes ) );
+const nativeFn = ( code, includes, language = '' ) => {
 
-export const fn = ( code, includes ) => func( code, includes ).call;
+	let functionNode = null;
+
+	return ( ...params ) => {
+
+		if ( functionNode === null ) functionNode = nodeObject( new FunctionNode( code, includes, language ) );
+
+		return functionNode.call( ...params );
+
+	};
+
+};
+
+export const glslFn = ( code, includes ) => nativeFn( code, includes, 'glsl' );
+export const wgslFn = ( code, includes ) => nativeFn( code, includes, 'wgsl' );
+
+export const func = ( code, includes ) => { // @deprecated, r154
+
+	console.warn( 'TSL: func() is deprecated. Use nativeFn(), wgslFn() or glslFn() instead.' );
+
+	return nodeObject( new FunctionNode( code, includes ) );
+
+};
 
 addNodeClass( FunctionNode );

+ 2 - 2
examples/jsm/nodes/core/BypassNode.js

@@ -20,7 +20,7 @@ class BypassNode extends Node {
 
 	}
 
-	generate( builder, output ) {
+	generate( builder ) {
 
 		const snippet = this.callNode.build( builder, 'void' );
 
@@ -30,7 +30,7 @@ class BypassNode extends Node {
 
 		}
 
-		return this.outputNode.build( builder, output );
+		return this.outputNode.build( builder );
 
 	}
 

+ 8 - 0
examples/jsm/nodes/core/Node.js

@@ -195,9 +195,17 @@ class Node {
 
 			if ( properties.initialized !== true || builder.context.tempRead === false ) {
 
+				const stackNodesBeforeConstruct = builder.stack.nodes.length;
+
 				properties.initialized = true;
 				properties.outputNode = this.construct( builder );
 
+				if ( properties.outputNode !== null && builder.stack.nodes.length !== stackNodesBeforeConstruct ) {
+
+					properties.outputNode = builder.stack;
+
+				}
+
 				for ( const childNode of Object.values( properties ) ) {
 
 					if ( childNode && childNode.isNode === true ) {

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

@@ -82,7 +82,7 @@ class StackNode extends Node {
 
 		for ( const node of this.nodes ) {
 
-			node.build( builder );
+			node.build( builder, 'void' );
 
 		}
 

+ 9 - 9
examples/jsm/nodes/display/BlendModeNode.js

@@ -1,9 +1,9 @@
 import TempNode from '../core/TempNode.js';
 import { EPSILON } from '../math/MathNode.js';
 import { addNodeClass } from '../core/Node.js';
-import { addNodeElement, ShaderNode, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
+import { addNodeElement, tslFn, nodeProxy, vec3 } from '../shadernode/ShaderNode.js';
 
-export const BurnNode = new ShaderNode( ( { base, blend } ) => {
+export const BurnNode = tslFn( ( { base, blend } ) => {
 
 	const fn = ( c ) => blend[ c ].lessThan( EPSILON ).cond( blend[ c ], base[ c ].oneMinus().div( blend[ c ] ).oneMinus().max( 0 ) );
 
@@ -11,7 +11,7 @@ export const BurnNode = new ShaderNode( ( { base, blend } ) => {
 
 } );
 
-export const DodgeNode = new ShaderNode( ( { base, blend } ) => {
+export const DodgeNode = tslFn( ( { base, blend } ) => {
 
 	const fn = ( c ) => blend[ c ].equal( 1.0 ).cond( blend[ c ], base[ c ].div( blend[ c ].oneMinus() ).max( 0 ) );
 
@@ -19,7 +19,7 @@ export const DodgeNode = new ShaderNode( ( { base, blend } ) => {
 
 } );
 
-export const ScreenNode = new ShaderNode( ( { base, blend } ) => {
+export const ScreenNode = tslFn( ( { base, blend } ) => {
 
 	const fn = ( c ) => base[ c ].oneMinus().mul( blend[ c ].oneMinus() ).oneMinus();
 
@@ -27,7 +27,7 @@ export const ScreenNode = new ShaderNode( ( { base, blend } ) => {
 
 } );
 
-export const OverlayNode = new ShaderNode( ( { base, blend } ) => {
+export const OverlayNode = tslFn( ( { base, blend } ) => {
 
 	const fn = ( c ) => base[ c ].lessThan( 0.5 ).cond( base[ c ].mul( blend[ c ], 2.0 ), base[ c ].oneMinus().mul( blend[ c ].oneMinus() ).oneMinus() );
 
@@ -57,19 +57,19 @@ class BlendModeNode extends TempNode {
 
 		if ( blendMode === BlendModeNode.BURN ) {
 
-			outputNode = BurnNode.call( params );
+			outputNode = BurnNode( params );
 
 		} else if ( blendMode === BlendModeNode.DODGE ) {
 
-			outputNode = DodgeNode.call( params );
+			outputNode = DodgeNode( params );
 
 		} else if ( blendMode === BlendModeNode.SCREEN ) {
 
-			outputNode = ScreenNode.call( params );
+			outputNode = ScreenNode( params );
 
 		} else if ( blendMode === BlendModeNode.OVERLAY ) {
 
-			outputNode = OverlayNode.call( params );
+			outputNode = OverlayNode( params );
 
 		}
 

+ 7 - 7
examples/jsm/nodes/display/ColorAdjustmentNode.js

@@ -2,15 +2,15 @@ import TempNode from '../core/TempNode.js';
 import { dot, mix } from '../math/MathNode.js';
 import { add } from '../math/OperatorNode.js';
 import { addNodeClass } from '../core/Node.js';
-import { addNodeElement, ShaderNode, nodeProxy, float, vec3, mat3 } from '../shadernode/ShaderNode.js';
+import { addNodeElement, tslFn, nodeProxy, float, vec3, mat3 } from '../shadernode/ShaderNode.js';
 
-const saturationNode = new ShaderNode( ( { color, adjustment } ) => {
+const saturationNode = tslFn( ( { color, adjustment } ) => {
 
 	return adjustment.mix( luminance( color ), color );
 
 } );
 
-const vibranceNode = new ShaderNode( ( { color, adjustment } ) => {
+const vibranceNode = tslFn( ( { color, adjustment } ) => {
 
 	const average = add( color.r, color.g, color.b ).div( 3.0 );
 
@@ -21,7 +21,7 @@ const vibranceNode = new ShaderNode( ( { color, adjustment } ) => {
 
 } );
 
-const hueNode = new ShaderNode( ( { color, adjustment } ) => {
+const hueNode = tslFn( ( { color, adjustment } ) => {
 
 	const RGBtoYIQ = mat3( 0.299, 0.587, 0.114, 0.595716, - 0.274453, - 0.321263, 0.211456, - 0.522591, 0.311135 );
 	const YIQtoRGB = mat3( 1.0, 0.9563, 0.6210, 1.0, - 0.2721, - 0.6474, 1.0, - 1.107, 1.7046 );
@@ -58,15 +58,15 @@ class ColorAdjustmentNode extends TempNode {
 
 		if ( method === ColorAdjustmentNode.SATURATION ) {
 
-			outputNode = saturationNode.call( callParams );
+			outputNode = saturationNode( callParams );
 
 		} else if ( method === ColorAdjustmentNode.VIBRANCE ) {
 
-			outputNode = vibranceNode.call( callParams );
+			outputNode = vibranceNode( callParams );
 
 		} else if ( method === ColorAdjustmentNode.HUE ) {
 
-			outputNode = hueNode.call( callParams );
+			outputNode = hueNode( callParams );
 
 		} else {
 

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

@@ -1,11 +1,11 @@
 import TempNode from '../core/TempNode.js';
 import { mix } from '../math/MathNode.js';
 import { addNodeClass } from '../core/Node.js';
-import { addNodeElement, ShaderNode, nodeObject, nodeProxy, vec4 } from '../shadernode/ShaderNode.js';
+import { addNodeElement, tslFn, nodeObject, nodeProxy, vec4 } from '../shadernode/ShaderNode.js';
 
 import { LinearSRGBColorSpace, SRGBColorSpace } from 'three';
 
-const sRGBToLinearShader = new ShaderNode( ( inputs ) => {
+const sRGBToLinearShader = tslFn( ( inputs ) => {
 
 	const { value } = inputs;
 	const { rgb } = value;
@@ -20,7 +20,7 @@ const sRGBToLinearShader = new ShaderNode( ( inputs ) => {
 
 } );
 
-const LinearTosRGBShader = new ShaderNode( ( inputs ) => {
+const LinearTosRGBShader = tslFn( ( inputs ) => {
 
 	const { value } = inputs;
 	const { rgb } = value;
@@ -77,7 +77,7 @@ class ColorSpaceNode extends TempNode {
 		if ( method === ColorSpaceNode.LINEAR_TO_LINEAR )
 			return node;
 
-		return Methods[ method ].call( { value: node } );
+		return Methods[ method ]( { value: node } );
 
 	}
 

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

@@ -8,14 +8,14 @@ import { tangentView } from '../accessors/TangentNode.js';
 import { uv } from '../accessors/UVNode.js';
 import { faceDirection } from './FrontFacingNode.js';
 import { addNodeClass } from '../core/Node.js';
-import { ShaderNode, nodeProxy, vec3, mat3 } from '../shadernode/ShaderNode.js';
+import { tslFn, nodeProxy, vec3, mat3 } from '../shadernode/ShaderNode.js';
 
 import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from 'three';
 
 // Normal Mapping Without Precomputed Tangents
 // http://www.thetenthplanet.de/archives/1180
 
-const perturbNormal2ArbNode = new ShaderNode( ( inputs ) => {
+const perturbNormal2ArbNode = tslFn( ( inputs ) => {
 
 	const { eye_pos, surf_norm, mapN, uv } = inputs;
 
@@ -80,7 +80,7 @@ class NormalMapNode extends TempNode {
 
 			} else {
 
-				outputNode = perturbNormal2ArbNode.call( {
+				outputNode = perturbNormal2ArbNode( {
 					eye_pos: positionView,
 					surf_norm: normalView,
 					mapN: normalMap,

+ 8 - 8
examples/jsm/nodes/display/ToneMappingNode.js

@@ -1,18 +1,18 @@
 import TempNode from '../core/TempNode.js';
 import { addNodeClass } from '../core/Node.js';
-import { ShaderNode, nodeObject, float, mat3 } from '../shadernode/ShaderNode.js';
+import { tslFn, nodeObject, float, mat3 } from '../shadernode/ShaderNode.js';
 
 import { NoToneMapping, LinearToneMapping, ReinhardToneMapping, CineonToneMapping, ACESFilmicToneMapping } from 'three';
 
 // exposure only
-const LinearToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+const LinearToneMappingNode = tslFn( ( { color, exposure } ) => {
 
 	return color.mul( exposure ).clamp();
 
 } );
 
 // source: https://www.cs.utah.edu/docs/techreports/2002/pdf/UUCS-02-001.pdf
-const ReinhardToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+const ReinhardToneMappingNode = tslFn( ( { color, exposure } ) => {
 
 	color = color.mul( exposure );
 
@@ -21,7 +21,7 @@ const ReinhardToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
 } );
 
 // source: http://filmicworlds.com/blog/filmic-tonemapping-operators/
-const OptimizedCineonToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+const OptimizedCineonToneMappingNode = tslFn( ( { color, exposure } ) => {
 
 	// optimized filmic operator by Jim Hejl and Richard Burgess-Dawson
 	color = color.mul( exposure );
@@ -35,7 +35,7 @@ const OptimizedCineonToneMappingNode = new ShaderNode( ( { color, exposure } ) =
 } );
 
 // source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs
-const RRTAndODTFit = new ShaderNode( ( { color } ) => {
+const RRTAndODTFit = tslFn( ( { color } ) => {
 
 	const a = color.mul( color.add( 0.0245786 ) ).sub( 0.000090537 );
 	const b = color.mul( color.add( 0.4329510 ).mul( 0.983729 ) ).add( 0.238081 );
@@ -45,7 +45,7 @@ const RRTAndODTFit = new ShaderNode( ( { color } ) => {
 } );
 
 // source: https://github.com/selfshadow/ltc_code/blob/master/webgl/shaders/ltc/ltc_blit.fs
-const ACESFilmicToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
+const ACESFilmicToneMappingNode = tslFn( ( { color, exposure } ) => {
 
 	// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
 	const ACESInputMat = mat3(
@@ -66,7 +66,7 @@ const ACESFilmicToneMappingNode = new ShaderNode( ( { color, exposure } ) => {
 	color = ACESInputMat.mul( color );
 
 	// Apply RRT and ODT
-	color = RRTAndODTFit.call( { color } );
+	color = RRTAndODTFit( { color } );
 
 	color = ACESOutputMat.mul( color );
 
@@ -118,7 +118,7 @@ class ToneMappingNode extends TempNode {
 
 		if ( toneMappingNode ) {
 
-			outputNode = toneMappingNode.call( toneMappingParams );
+			outputNode = toneMappingNode( toneMappingParams );
 
 		} else {
 

+ 5 - 5
examples/jsm/nodes/functions/BSDF/BRDF_BlinnPhong.js

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

+ 5 - 5
examples/jsm/nodes/functions/BSDF/BRDF_GGX.js

@@ -3,10 +3,10 @@ import V_GGX_SmithCorrelated from './V_GGX_SmithCorrelated.js';
 import D_GGX from './D_GGX.js';
 import { transformedNormalView } from '../../accessors/NormalNode.js';
 import { positionViewDirection } from '../../accessors/PositionNode.js';
-import { ShaderNode } from '../../shadernode/ShaderNode.js';
+import { tslFn } from '../../shadernode/ShaderNode.js';
 
 // GGX Distribution, Schlick Fresnel, GGX_SmithCorrelated Visibility
-const BRDF_GGX = new ShaderNode( ( inputs ) => {
+const BRDF_GGX = tslFn( ( inputs ) => {
 
 	const { lightDirection, f0, f90, roughness } = inputs;
 
@@ -21,9 +21,9 @@ const BRDF_GGX = new ShaderNode( ( inputs ) => {
 	const dotNH = normalView.dot( halfDir ).clamp();
 	const dotVH = positionViewDirection.dot( halfDir ).clamp();
 
-	const F = F_Schlick.call( { f0, f90, dotVH } );
-	const V = V_GGX_SmithCorrelated.call( { alpha, dotNL, dotNV } );
-	const D = D_GGX.call( { alpha, dotNH } );
+	const F = F_Schlick( { f0, f90, dotVH } );
+	const V = V_GGX_SmithCorrelated( { alpha, dotNL, dotNV } );
+	const D = D_GGX( { alpha, dotNH } );
 
 	return F.mul( V ).mul( D );
 

+ 2 - 2
examples/jsm/nodes/functions/BSDF/BRDF_Lambert.js

@@ -1,6 +1,6 @@
-import { ShaderNode } from '../../shadernode/ShaderNode.js';
+import { tslFn } from '../../shadernode/ShaderNode.js';
 
-const BRDF_Lambert = new ShaderNode( ( inputs ) => {
+const BRDF_Lambert = tslFn( ( inputs ) => {
 
 	return inputs.diffuseColor.mul( 1 / Math.PI ); // punctual light
 

+ 2 - 2
examples/jsm/nodes/functions/BSDF/DFGApprox.js

@@ -1,12 +1,12 @@
 import { transformedNormalView } from '../../accessors/NormalNode.js';
 import { positionViewDirection } from '../../accessors/PositionNode.js';
-import { ShaderNode, vec2, vec4 } from '../../shadernode/ShaderNode.js';
+import { tslFn, vec2, vec4 } from '../../shadernode/ShaderNode.js';
 
 // Analytical approximation of the DFG LUT, one half of the
 // split-sum approximation used in indirect specular lighting.
 // via 'environmentBRDF' from "Physically Based Shading on Mobile"
 // https://www.unrealengine.com/blog/physically-based-shading-on-mobile
-const DFGApprox = new ShaderNode( ( inputs ) => {
+const DFGApprox = tslFn( ( inputs ) => {
 
 	const { roughness } = inputs;
 

+ 2 - 2
examples/jsm/nodes/functions/BSDF/D_GGX.js

@@ -1,9 +1,9 @@
-import { ShaderNode } from '../../shadernode/ShaderNode.js';
+import { tslFn } from '../../shadernode/ShaderNode.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 D_GGX = tslFn( ( inputs ) => {
 
 	const { alpha, dotNH } = inputs;
 

+ 3 - 3
examples/jsm/nodes/functions/BSDF/EnvironmentBRDF.js

@@ -1,11 +1,11 @@
 import DFGApprox from './DFGApprox.js';
-import { ShaderNode } from '../../shadernode/ShaderNode.js';
+import { tslFn } from '../../shadernode/ShaderNode.js';
 
-const EnvironmentBRDF = new ShaderNode( ( inputs ) => {
+const EnvironmentBRDF = tslFn( ( inputs ) => {
 
 	const { dotNV, specularColor, specularF90, roughness } = inputs;
 
-	const fab = DFGApprox.call( { dotNV, roughness } );
+	const fab = DFGApprox( { dotNV, roughness } );
 	return specularColor.mul( fab.x ).add( specularF90.mul( fab.y ) );
 
 } );

+ 2 - 4
examples/jsm/nodes/functions/BSDF/F_Schlick.js

@@ -1,8 +1,6 @@
-import { ShaderNode } from '../../shadernode/ShaderNode.js';
+import { tslFn } from '../../shadernode/ShaderNode.js';
 
-const F_Schlick = new ShaderNode( ( inputs ) => {
-
-	const { f0, f90, dotVH } = inputs;
+const F_Schlick = tslFn( ( { f0, f90, dotVH } ) => {
 
 	// Original approximation by Christophe Schlick '94
 	// float fresnel = pow( 1.0 - dotVH, 5.0 );

+ 2 - 2
examples/jsm/nodes/functions/BSDF/V_GGX_SmithCorrelated.js

@@ -1,10 +1,10 @@
 import { div } from '../../math/OperatorNode.js';
 import { EPSILON } from '../../math/MathNode.js';
-import { ShaderNode } from '../../shadernode/ShaderNode.js';
+import { tslFn } from '../../shadernode/ShaderNode.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 V_GGX_SmithCorrelated = tslFn( ( inputs ) => {
 
 	const { alpha, dotNL, dotNV } = inputs;
 

+ 6 - 6
examples/jsm/nodes/functions/PhongLightingModel.js

@@ -4,22 +4,22 @@ import { lightingModel } from '../core/LightingModel.js';
 import { diffuseColor } from '../core/PropertyNode.js';
 import { materialReflectivity } from '../accessors/MaterialNode.js';
 import { transformedNormalView } from '../accessors/NormalNode.js';
-import { ShaderNode } from '../shadernode/ShaderNode.js';
+import { tslFn } from '../shadernode/ShaderNode.js';
 
-const RE_Direct_BlinnPhong = new ShaderNode( ( { lightDirection, lightColor, reflectedLight } ) => {
+const RE_Direct_BlinnPhong = tslFn( ( { lightDirection, lightColor, reflectedLight } ) => {
 
 	const dotNL = transformedNormalView.dot( lightDirection ).clamp();
 	const irradiance = dotNL.mul( lightColor );
 
-	reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor: diffuseColor.rgb } ) ) );
+	reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
 
-	reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_BlinnPhong.call( { lightDirection } ) ).mul( materialReflectivity ) );
+	reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_BlinnPhong( { lightDirection } ) ).mul( materialReflectivity ) );
 
 } );
 
-const RE_IndirectDiffuse_BlinnPhong = new ShaderNode( ( { irradiance, reflectedLight } ) => {
+const RE_IndirectDiffuse_BlinnPhong = tslFn( ( { irradiance, reflectedLight } ) => {
 
-	reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor } ) ) );
+	reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
 
 } );
 

+ 13 - 13
examples/jsm/nodes/functions/PhysicalLightingModel.js

@@ -7,7 +7,7 @@ import { lightingModel } from '../core/LightingModel.js';
 import { diffuseColor, specularColor, roughness, clearcoat, clearcoatRoughness } from '../core/PropertyNode.js';
 import { transformedNormalView, transformedClearcoatNormalView } from '../accessors/NormalNode.js';
 import { positionViewDirection } from '../accessors/PositionNode.js';
-import { ShaderNode, float, vec3 } from '../shadernode/ShaderNode.js';
+import { tslFn, float, vec3 } from '../shadernode/ShaderNode.js';
 
 const clearcoatF0 = vec3( 0.04 );
 const clearcoatF90 = vec3( 1 );
@@ -17,7 +17,7 @@ const clearcoatF90 = vec3( 1 );
 // http://www.jcgt.org/published/0008/01/03/
 const computeMultiscattering = ( singleScatter, multiScatter, specularF90 = float( 1 ) ) => {
 
-	const fab = DFGApprox.call( { roughness } );
+	const fab = DFGApprox( { roughness } );
 
 	const FssEss = specularColor.mul( fab.x ).add( specularF90.mul( fab.y ) );
 
@@ -32,7 +32,7 @@ const computeMultiscattering = ( singleScatter, multiScatter, specularF90 = floa
 
 };
 
-const LM_Init = new ShaderNode( ( context, stack, builder ) => {
+const LM_Init = tslFn( ( context, stack, builder ) => {
 
 	if ( builder.includes( clearcoat ) ) {
 
@@ -41,7 +41,7 @@ const LM_Init = new ShaderNode( ( context, stack, builder ) => {
 
 		const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
 
-		const Fcc = F_Schlick.call( {
+		const Fcc = F_Schlick( {
 			dotVH: dotNVcc,
 			f0: clearcoatF0,
 			f90: clearcoatF90
@@ -56,7 +56,7 @@ const LM_Init = new ShaderNode( ( context, stack, builder ) => {
 
 } );
 
-const RE_IndirectSpecular_Physical = new ShaderNode( ( context ) => {
+const RE_IndirectSpecular_Physical = tslFn( ( context ) => {
 
 	const { radiance, iblIrradiance, reflectedLight } = context;
 
@@ -64,7 +64,7 @@ const RE_IndirectSpecular_Physical = new ShaderNode( ( context ) => {
 
 		const dotNVcc = transformedClearcoatNormalView.dot( positionViewDirection ).clamp();
 
-		const clearcoatEnv = EnvironmentBRDF.call( {
+		const clearcoatEnv = EnvironmentBRDF( {
 			dotNV: dotNVcc,
 			specularColor: clearcoatF0,
 			specularF90: clearcoatF90,
@@ -94,15 +94,15 @@ const RE_IndirectSpecular_Physical = new ShaderNode( ( context ) => {
 
 } );
 
-const RE_IndirectDiffuse_Physical = new ShaderNode( ( context ) => {
+const RE_IndirectDiffuse_Physical = tslFn( ( context ) => {
 
 	const { irradiance, reflectedLight } = context;
 
-	reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor } ) ) );
+	reflectedLight.indirectDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor } ) ) );
 
 } );
 
-const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
+const RE_Direct_Physical = tslFn( ( inputs ) => {
 
 	const { lightDirection, lightColor, reflectedLight } = inputs;
 
@@ -114,17 +114,17 @@ const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
 		const dotNLcc = transformedClearcoatNormalView.dot( lightDirection ).clamp();
 		const ccIrradiance = dotNLcc.mul( lightColor );
 
-		reflectedLight.clearcoatSpecular.addAssign( ccIrradiance.mul( BRDF_GGX.call( { lightDirection, f0: clearcoatF0, f90: clearcoatF90, roughness: clearcoatRoughness, normalView: transformedClearcoatNormalView } ) ) );
+		reflectedLight.clearcoatSpecular.addAssign( ccIrradiance.mul( BRDF_GGX( { lightDirection, f0: clearcoatF0, f90: clearcoatF90, roughness: clearcoatRoughness, normalView: transformedClearcoatNormalView } ) ) );
 
 	}
 
-	reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert.call( { diffuseColor: diffuseColor.rgb } ) ) );
+	reflectedLight.directDiffuse.addAssign( irradiance.mul( BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
 
-	reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX.call( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
+	reflectedLight.directSpecular.addAssign( irradiance.mul( BRDF_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
 
 } );
 
-const RE_AmbientOcclusion_Physical = new ShaderNode( ( context ) => {
+const RE_AmbientOcclusion_Physical = tslFn( ( context ) => {
 
 	const { ambientOcclusion, reflectedLight } = context;
 

+ 2 - 2
examples/jsm/nodes/functions/material/getGeometryRoughness.js

@@ -1,7 +1,7 @@
 import { normalGeometry } from '../../accessors/NormalNode.js';
-import { ShaderNode } from '../../shadernode/ShaderNode.js';
+import { tslFn } from '../../shadernode/ShaderNode.js';
 
-const getGeometryRoughness = new ShaderNode( () => {
+const getGeometryRoughness = tslFn( () => {
 
 	const dxy = normalGeometry.dFdx().abs().max( normalGeometry.dFdy().abs() );
 	const geometryRoughness = dxy.x.max( dxy.y ).max( dxy.z );

+ 3 - 3
examples/jsm/nodes/functions/material/getRoughness.js

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

+ 1 - 1
examples/jsm/nodes/lighting/DirectionalLightNode.js

@@ -24,7 +24,7 @@ class DirectionalLightNode extends AnalyticLightNode {
 
 		if ( lightingModelFunctionNode && lightingModelFunctionNode.direct ) {
 
-			lightingModelFunctionNode.direct.call( {
+			lightingModelFunctionNode.direct( {
 				lightDirection,
 				lightColor,
 				reflectedLight

+ 2 - 2
examples/jsm/nodes/lighting/LightUtils.js

@@ -1,6 +1,6 @@
-import { ShaderNode } from '../shadernode/ShaderNode.js';
+import { tslFn } from '../shadernode/ShaderNode.js';
 
-export const getDistanceAttenuation = new ShaderNode( ( inputs ) => {
+export const getDistanceAttenuation = tslFn( ( inputs ) => {
 
 	const { lightDistance, cutoffDistance, decayExponent } = inputs;
 

+ 4 - 4
examples/jsm/nodes/lighting/LightingContextNode.js

@@ -67,11 +67,11 @@ class LightingContextNode extends ContextNode {
 		Object.assign( context, lighting );
 
 		// @TODO: Call needed return a new node ( or rename the ShaderNodeInternal.call() function ), it's not moment to run
-		if ( lightingModelNode && lightingModelNode.init ) lightingModelNode.init.call( context, builder.stack, builder );
+		if ( lightingModelNode && lightingModelNode.init ) lightingModelNode.init( context, builder.stack, builder );
 
-		if ( lightingModelNode && lightingModelNode.indirectDiffuse ) lightingModelNode.indirectDiffuse.call( context, builder.stack, builder );
-		if ( lightingModelNode && lightingModelNode.indirectSpecular ) lightingModelNode.indirectSpecular.call( context, builder.stack, builder );
-		if ( lightingModelNode && lightingModelNode.ambientOcclusion ) lightingModelNode.ambientOcclusion.call( context, builder.stack, builder );
+		if ( lightingModelNode && lightingModelNode.indirectDiffuse ) lightingModelNode.indirectDiffuse( context, builder.stack, builder );
+		if ( lightingModelNode && lightingModelNode.indirectSpecular ) lightingModelNode.indirectSpecular( context, builder.stack, builder );
+		if ( lightingModelNode && lightingModelNode.ambientOcclusion ) lightingModelNode.ambientOcclusion( context, builder.stack, builder );
 
 		return super.construct( builder );
 

+ 2 - 2
examples/jsm/nodes/lighting/PointLightNode.js

@@ -39,7 +39,7 @@ class PointLightNode extends AnalyticLightNode {
 		const lightDirection = lVector.normalize();
 		const lightDistance = lVector.length();
 
-		const lightAttenuation = getDistanceAttenuation.call( {
+		const lightAttenuation = getDistanceAttenuation( {
 			lightDistance,
 			cutoffDistance: cutoffDistanceNode,
 			decayExponent: decayExponentNode
@@ -52,7 +52,7 @@ class PointLightNode extends AnalyticLightNode {
 
 		if ( lightingModelFunctionNode && lightingModelFunctionNode.direct ) {
 
-			lightingModelFunctionNode.direct.call( {
+			lightingModelFunctionNode.direct( {
 				lightDirection,
 				lightColor,
 				reflectedLight

+ 2 - 2
examples/jsm/nodes/lighting/SpotLightNode.js

@@ -60,7 +60,7 @@ class SpotLightNode extends AnalyticLightNode {
 
 		const lightDistance = lVector.length();
 
-		const lightAttenuation = getDistanceAttenuation.call( {
+		const lightAttenuation = getDistanceAttenuation( {
 			lightDistance,
 			cutoffDistance: cutoffDistanceNode,
 			decayExponent: decayExponentNode
@@ -73,7 +73,7 @@ class SpotLightNode extends AnalyticLightNode {
 
 		if ( lightingModelFunctionNode && lightingModelFunctionNode.direct ) {
 
-			lightingModelFunctionNode.direct.call( {
+			lightingModelFunctionNode.direct( {
 				lightDirection,
 				lightColor,
 				reflectedLight

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

@@ -46,7 +46,7 @@ class MeshStandardNodeMaterial extends NodeMaterial {
 		// ROUGHNESS
 
 		let roughnessNode = this.roughnessNode ? float( this.roughnessNode ) : materialRoughness;
-		roughnessNode = getRoughness.call( { roughness: roughnessNode } );
+		roughnessNode = getRoughness( { roughness: roughnessNode } );
 
 		stack.assign( roughness, roughnessNode );
 

+ 9 - 9
examples/jsm/nodes/materials/NodeMaterial.js

@@ -100,27 +100,27 @@ class NodeMaterial extends ShaderMaterial {
 
 		const object = builder.object;
 
-		let vertex = positionLocal;
+		builder.addStack();
 
-		if ( this.positionNode !== null ) {
+		if ( object.isSkinnedMesh === true ) {
 
-			vertex = vertex.bypass( positionLocal.assign( this.positionNode ) );
+			builder.stack.add( skinning( object ) );
 
 		}
 
 		if ( ( object.instanceMatrix && object.instanceMatrix.isInstancedBufferAttribute === true ) && builder.isAvailable( 'instance' ) === true ) {
 
-			vertex = vertex.bypass( instance( object ) );
+			builder.stack.add( instance( object ) );
 
 		}
 
-		if ( object.isSkinnedMesh === true ) {
+		if ( this.positionNode !== null ) {
 
-			vertex = vertex.bypass( skinning( object ) );
+			builder.stack.assign( positionLocal, this.positionNode );
 
 		}
 
-		builder.context.vertex = vertex;
+		builder.context.vertex = builder.removeStack();
 
 		return modelViewProjection();
 
@@ -149,9 +149,9 @@ class NodeMaterial extends ShaderMaterial {
 
 		// ALPHA TEST
 
-		if ( this.alphaTestNode || this.alphaTest > 0 ) {
+		if ( this.alphaTestNode !== null || this.alphaTest > 0 ) {
 
-			const alphaTestNode = this.alphaTestNode ? float( this.alphaTestNode ) : materialAlphaTest;
+			const alphaTestNode = this.alphaTestNode !== null ? float( this.alphaTestNode ) : materialAlphaTest;
 
 			stack.add( diffuseColor.a.lessThanEqual( alphaTestNode ).discard() );
 

+ 3 - 3
examples/jsm/nodes/materialx/lib/mx_hsv.js

@@ -1,9 +1,9 @@
-import { fn } from '../../code/FunctionNode.js';
+import { glslFn } from '../../code/FunctionNode.js';
 
 // Original shader code from:
 // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_hsv.glsl
 
-export const mx_hsvtorgb = fn( `vec3 mx_hsvtorgb(vec3 hsv)
+export const mx_hsvtorgb = glslFn( `vec3 mx_hsvtorgb(vec3 hsv)
 {
     // Reference for this technique: Foley & van Dam
     float h = hsv.x; float s = hsv.y; float v = hsv.z;
@@ -30,7 +30,7 @@ export const mx_hsvtorgb = fn( `vec3 mx_hsvtorgb(vec3 hsv)
     }
 }` );
 
-export const mx_rgbtohsv = fn( `vec3 mx_rgbtohsv(vec3 c)
+export const mx_rgbtohsv = glslFn( `vec3 mx_rgbtohsv(vec3 c)
 {
     // See Foley & van Dam
     float r = c.x; float g = c.y; float b = c.z;

+ 14 - 14
examples/jsm/nodes/materialx/lib/mx_noise.js

@@ -1,10 +1,10 @@
-import { code } from '../../code/CodeNode.js';
-import { fn } from '../../code/FunctionNode.js';
+import { glsl } from '../../code/CodeNode.js';
+import { glslFn } from '../../code/FunctionNode.js';
 
 // Original shader code from:
 // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_noise.glsl
 
-export const mx_noise = code( `float mx_select(bool b, float t, float f)
+export const mx_noise = glsl( `float mx_select(bool b, float t, float f)
 {
     return b ? t : f;
 }
@@ -602,17 +602,17 @@ vec3 mx_worley_noise_vec3(vec3 p, float jitter, int metric)
 
 const includes = [ mx_noise ];
 
-export const mx_perlin_noise_float = fn( 'float mx_perlin_noise_float( any p )', includes );
-export const mx_perlin_noise_vec2 = fn( 'vec2 mx_perlin_noise_vec2( any p )', includes );
-export const mx_perlin_noise_vec3 = fn( 'vec3 mx_perlin_noise_vec3( any p )', includes );
+export const mx_perlin_noise_float = glslFn( 'float mx_perlin_noise_float( any p )', includes );
+export const mx_perlin_noise_vec2 = glslFn( 'vec2 mx_perlin_noise_vec2( any p )', includes );
+export const mx_perlin_noise_vec3 = glslFn( 'vec3 mx_perlin_noise_vec3( any p )', includes );
 
-export const mx_cell_noise_float = fn( 'float mx_cell_noise_float( vec3 p )', includes );
+export const mx_cell_noise_float = glslFn( 'float mx_cell_noise_float( vec3 p )', includes );
 
-export const mx_worley_noise_float = fn( 'float mx_worley_noise_float( any p, float jitter, int metric )', includes );
-export const mx_worley_noise_vec2 = fn( 'float mx_worley_noise_vec2( any p, float jitter, int metric )', includes );
-export const mx_worley_noise_vec3 = fn( 'float mx_worley_noise_vec3( any p, float jitter, int metric )', includes );
+export const mx_worley_noise_float = glslFn( 'float mx_worley_noise_float( any p, float jitter, int metric )', includes );
+export const mx_worley_noise_vec2 = glslFn( 'float mx_worley_noise_vec2( any p, float jitter, int metric )', includes );
+export const mx_worley_noise_vec3 = glslFn( 'float mx_worley_noise_vec3( any p, float jitter, int metric )', includes );
 
-export const mx_fractal_noise_float = fn( 'float mx_fractal_noise_float( vec3 p, int octaves, float lacunarity, float diminish )', includes );
-export const mx_fractal_noise_vec2 = fn( 'float mx_fractal_noise_vec2( vec3 p, int octaves, float lacunarity, float diminish )', includes );
-export const mx_fractal_noise_vec3 = fn( 'float mx_fractal_noise_vec3( vec3 p, int octaves, float lacunarity, float diminish )', includes );
-export const mx_fractal_noise_vec4 = fn( 'float mx_fractal_noise_vec4( vec3 p, int octaves, float lacunarity, float diminish )', includes );
+export const mx_fractal_noise_float = glslFn( 'float mx_fractal_noise_float( vec3 p, int octaves, float lacunarity, float diminish )', includes );
+export const mx_fractal_noise_vec2 = glslFn( 'float mx_fractal_noise_vec2( vec3 p, int octaves, float lacunarity, float diminish )', includes );
+export const mx_fractal_noise_vec3 = glslFn( 'float mx_fractal_noise_vec3( vec3 p, int octaves, float lacunarity, float diminish )', includes );
+export const mx_fractal_noise_vec4 = glslFn( 'float mx_fractal_noise_vec4( vec3 p, int octaves, float lacunarity, float diminish )', includes );

+ 4 - 4
examples/jsm/nodes/materialx/lib/mx_transform_color.js

@@ -1,10 +1,10 @@
-import { code } from '../../code/CodeNode.js';
-import { fn } from '../../code/FunctionNode.js';
+import { glsl } from '../../code/CodeNode.js';
+import { glslFn } from '../../code/FunctionNode.js';
 
 // Original shader code from:
 // https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/libraries/stdlib/genglsl/lib/mx_transform_color.glsl
 
-export const mx_transform_color = code( `#define M_AP1_TO_REC709 mat3(1.705079555511475, -0.1297005265951157, -0.02416634373366833, -0.6242334842681885, 1.138468623161316, -0.1246141716837883, -0.0808461606502533, -0.008768022060394287, 1.148780584335327)
+export const mx_transform_color = glsl( `#define M_AP1_TO_REC709 mat3(1.705079555511475, -0.1297005265951157, -0.02416634373366833, -0.6242334842681885, 1.138468623161316, -0.1246141716837883, -0.0808461606502533, -0.008768022060394287, 1.148780584335327)
 
 vec3 mx_srgb_texture_to_lin_rec709(vec3 color)
 {
@@ -16,4 +16,4 @@ vec3 mx_srgb_texture_to_lin_rec709(vec3 color)
 
 const includes = [ mx_transform_color ];
 
-export const mx_srgb_texture_to_lin_rec709 = fn( 'vec3 mx_srgb_texture_to_lin_rec709( vec3 color )', includes );
+export const mx_srgb_texture_to_lin_rec709 = glslFn( 'vec3 mx_srgb_texture_to_lin_rec709( vec3 color )', includes );

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

@@ -1,9 +1,9 @@
 import TempNode from '../core/TempNode.js';
 import { uv } from '../accessors/UVNode.js';
 import { addNodeClass } from '../core/Node.js';
-import { addNodeElement, ShaderNode, nodeProxy } from '../shadernode/ShaderNode.js';
+import { addNodeElement, tslFn, nodeProxy } from '../shadernode/ShaderNode.js';
 
-const checkerShaderNode = new ShaderNode( ( inputs ) => {
+const checkerShaderNode = tslFn( ( inputs ) => {
 
 	const uv = inputs.uv.mul( 2.0 );
 
@@ -27,7 +27,7 @@ class CheckerNode extends TempNode {
 
 	generate( builder ) {
 
-		return checkerShaderNode.call( { uv: this.uvNode } ).build( builder );
+		return checkerShaderNode( { uv: this.uvNode } ).build( builder );
 
 	}
 

+ 24 - 4
examples/jsm/nodes/shadernode/ShaderNode.js

@@ -103,7 +103,7 @@ const ShaderNodeObject = function ( obj, altType = null ) {
 
 	} else if ( type === 'shader' ) {
 
-		return shader( obj );
+		return tslFn( obj );
 
 	}
 
@@ -317,10 +317,30 @@ export function ShaderNode( jsFunc ) {
 export const nodeObject = ( val, altType = null ) => /* new */ ShaderNodeObject( val, altType );
 export const nodeObjects = ( val, altType = null ) => new ShaderNodeObjects( val, altType );
 export const nodeArray = ( val, altType = null ) => new ShaderNodeArray( val, altType );
-export const nodeProxy = ( ...val ) => new ShaderNodeProxy( ...val );
-export const nodeImmutable = ( ...val ) => new ShaderNodeImmutable( ...val );
+export const nodeProxy = ( ...params ) => new ShaderNodeProxy( ...params );
+export const nodeImmutable = ( ...params ) => new ShaderNodeImmutable( ...params );
 
-export const shader = ( ...val ) => new ShaderNode( ...val );
+export const shader = ( jsFunc ) => { // @deprecated, r154
+
+	console.warn( 'TSL: shader() is deprecated. Use fn() instead.' );
+
+	return new ShaderNode( jsFunc );
+
+};
+
+export const tslFn = ( jsFunc ) => {
+
+	let shaderNode = null;
+
+	return ( ...params ) => {
+
+		if ( shaderNode === null ) shaderNode = new ShaderNode( jsFunc );
+
+		return shaderNode.call( ...params );
+
+	};
+
+};
 
 addNodeClass( ShaderNode );
 

+ 1 - 1
examples/jsm/nodes/utils/LoopNode.js

@@ -38,7 +38,7 @@ class LoopNode extends Node {
 
 		}
 
-		properties.returnsNode = this.params[ this.params.length - 1 ].call( inputs, builder.addStack(), builder );
+		properties.returnsNode = this.params[ this.params.length - 1 ]( inputs, builder.addStack(), builder );
 		properties.stackNode = builder.removeStack();
 
 		return properties;

+ 9 - 5
examples/jsm/renderers/common/Pipelines.js

@@ -141,14 +141,18 @@ class Pipelines extends DataMap {
 
 		const pipeline = this._releasePipeline( object );
 
-		if ( pipeline.isComputePipeline ) {
+		if ( pipeline && pipeline.usedTimes === 0 ) {
 
-			this._releaseProgram( pipeline.computeProgram );
+			if ( pipeline.isComputePipeline ) {
 
-		} else {
+				this._releaseProgram( pipeline.computeProgram );
+
+			} else {
 
-			this._releaseProgram( pipeline.vertexProgram );
-			this._releaseProgram( pipeline.fragmentProgram );
+				this._releaseProgram( pipeline.vertexProgram );
+				this._releaseProgram( pipeline.fragmentProgram );
+
+			}
 
 		}
 

+ 2 - 4
examples/webgpu_lights_custom.html

@@ -26,7 +26,7 @@
 		<script type="module">
 
 			import * as THREE from 'three';
-			import { ShaderNode, color, lights, toneMapping, MeshStandardNodeMaterial, PointsNodeMaterial } from 'three/nodes';
+			import { tslFn, color, lights, toneMapping, MeshStandardNodeMaterial, PointsNodeMaterial } from 'three/nodes';
 
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
@@ -100,9 +100,7 @@
 
 				// custom lighting model
 
-				const customLightingModel = new ShaderNode( ( inputs ) => {
-
-					const { lightColor, reflectedLight } = inputs;
+				const customLightingModel = tslFn( ( { lightColor, reflectedLight } ) => {
 
 					reflectedLight.directDiffuse.addAssign( lightColor );
 

+ 9 - 9
examples/webgpu_materials.html

@@ -29,7 +29,7 @@
 			import * as THREE from 'three';
 			import * as Nodes from 'three/nodes';
 
-			import { attribute, positionLocal, positionWorld, normalLocal, normalWorld, normalView, color, texture, ShaderNode, func, uv, float, vec2, vec3, vec4, oscSine, triplanarTexture, viewportBottomLeft, js, string, global, loop, MeshBasicNodeMaterial, NodeObjectLoader } from 'three/nodes';
+			import { tslFn, wgslFn, attribute, positionLocal, positionWorld, normalLocal, normalWorld, normalView, color, texture, uv, float, vec2, vec3, vec4, oscSine, triplanarTexture, viewportBottomLeft, js, string, global, loop, MeshBasicNodeMaterial, NodeObjectLoader } from 'three/nodes';
 
 			import WebGPU from 'three/addons/capabilities/WebGPU.js';
 			import WebGPURenderer from 'three/addons/renderers/webgpu/WebGPURenderer.js';
@@ -145,31 +145,31 @@
 
 				// Custom ShaderNode ( desaturate filter )
 
-				const desaturateShaderNode = new ShaderNode( ( input ) => {
+				const desaturateShaderNode = tslFn( ( input ) => {
 
 					return vec3( 0.299, 0.587, 0.114 ).dot( input.color.xyz );
 
 				} );
 
 				material = new MeshBasicNodeMaterial();
-				material.colorNode = desaturateShaderNode.call( { color: texture( uvTexture ) } );
+				material.colorNode = desaturateShaderNode( { color: texture( uvTexture ) } );
 				materials.push( material );
 
 				// Custom ShaderNode(no inputs) > Approach 2
 
-				const desaturateNoInputsShaderNode = new ShaderNode( () => {
+				const desaturateNoInputsShaderNode = tslFn( () => {
 
 					return vec3( 0.299, 0.587, 0.114 ).dot( texture( uvTexture ).xyz );
 
 				} );
 
 				material = new MeshBasicNodeMaterial();
-				material.colorNode = desaturateNoInputsShaderNode;
+				material.colorNode = desaturateNoInputsShaderNode();
 				materials.push( material );
 
 				// Custom WGSL ( desaturate filter )
 
-				const desaturateWGSLNode = func( `
+				const desaturateWGSLNode = wgslFn( `
 					fn desaturate( color:vec3<f32> ) -> vec3<f32> {
 
 						let lum = vec3<f32>( 0.299, 0.587, 0.114 );
@@ -180,12 +180,12 @@
 				` );
 
 				material = new MeshBasicNodeMaterial();
-				material.colorNode = desaturateWGSLNode.call( { color: texture( uvTexture ) } );
+				material.colorNode = desaturateWGSLNode( { color: texture( uvTexture ) } );
 				materials.push( material );
 
 				// Custom WGSL ( get texture from keywords )
 
-				const getWGSLTextureSample = func( `
+				const getWGSLTextureSample = wgslFn( `
 					fn getWGSLTextureSample( tex: texture_2d<f32>, tex_sampler: sampler, uv:vec2<f32> ) -> vec4<f32> {
 
 						return textureSample( tex, tex_sampler, uv ) * vec4<f32>( 0.0, 1.0, 0.0, 1.0 );
@@ -197,7 +197,7 @@
 				//getWGSLTextureSample.keywords = { tex: textureNode, tex_sampler: sampler( textureNode ) };
 
 				material = new MeshBasicNodeMaterial();
-				material.colorNode = getWGSLTextureSample.call( { tex: textureNode, tex_sampler: textureNode, uv: uv() } );
+				material.colorNode = getWGSLTextureSample( { tex: textureNode, tex_sampler: textureNode, uv: uv() } );
 				materials.push( material );
 
 				// Triplanar Texture Mapping