Просмотр исходного кода

TSL: Add renderer context node (global) (#32295)

sunag 1 месяц назад
Родитель
Сommit
47a70dc50e

+ 1 - 0
src/Three.TSL.js

@@ -464,6 +464,7 @@ export const remapClamp = TSL.remapClamp;
 export const renderGroup = TSL.renderGroup;
 export const renderGroup = TSL.renderGroup;
 export const renderOutput = TSL.renderOutput;
 export const renderOutput = TSL.renderOutput;
 export const rendererReference = TSL.rendererReference;
 export const rendererReference = TSL.rendererReference;
+export const replaceDefaultUV = TSL.replaceDefaultUV;
 export const rotate = TSL.rotate;
 export const rotate = TSL.rotate;
 export const rotateUV = TSL.rotateUV;
 export const rotateUV = TSL.rotateUV;
 export const roughness = TSL.roughness;
 export const roughness = TSL.roughness;

+ 45 - 1
src/materials/nodes/NodeMaterial.js

@@ -25,7 +25,7 @@ import { modelViewMatrix } from '../../nodes/accessors/ModelNode.js';
 import { vertexColor } from '../../nodes/accessors/VertexColorNode.js';
 import { vertexColor } from '../../nodes/accessors/VertexColorNode.js';
 import { premultiplyAlpha } from '../../nodes/display/BlendModes.js';
 import { premultiplyAlpha } from '../../nodes/display/BlendModes.js';
 import { subBuild } from '../../nodes/core/SubBuildNode.js';
 import { subBuild } from '../../nodes/core/SubBuildNode.js';
-import { warn } from '../../utils.js';
+import { error, warn } from '../../utils.js';
 
 
 /**
 /**
  * Base class for all node materials.
  * Base class for all node materials.
@@ -382,6 +382,14 @@ class NodeMaterial extends Material {
 		 */
 		 */
 		this.vertexNode = null;
 		this.vertexNode = null;
 
 
+		/**
+		 * This node can be used as a global context management component for this material.
+		 *
+		 * @type {?ContextNode}
+		 * @default null
+		 */
+		this.contextNode = null;
+
 		// Deprecated properties
 		// Deprecated properties
 
 
 		Object.defineProperty( this, 'shadowPositionNode', { // @deprecated, r176
 		Object.defineProperty( this, 'shadowPositionNode', { // @deprecated, r176
@@ -489,6 +497,32 @@ class NodeMaterial extends Material {
 		const renderer = builder.renderer;
 		const renderer = builder.renderer;
 		const renderTarget = renderer.getRenderTarget();
 		const renderTarget = renderer.getRenderTarget();
 
 
+		// < CONTEXT >
+
+		if ( renderer.contextNode.isContextNode === true ) {
+
+			builder.context = { ...builder.context, ...renderer.contextNode.getFlowContextData() };
+
+		} else {
+
+			error( 'NodeMaterial: "renderer.contextNode" must be an instance of `context()`.' );
+
+		}
+
+		if ( this.contextNode !== null ) {
+
+			if ( this.contextNode.isContextNode === true ) {
+
+				builder.context = { ...builder.context, ...this.contextNode.getFlowContextData() };
+
+			} else {
+
+				error( 'NodeMaterial: "material.contextNode" must be an instance of `context()`.' );
+
+			}
+
+		}
+
 		// < VERTEX STAGE >
 		// < VERTEX STAGE >
 
 
 		builder.addStack();
 		builder.addStack();
@@ -558,6 +592,14 @@ class NodeMaterial extends Material {
 
 
 			if ( isCustomOutput ) resultNode = this.outputNode;
 			if ( isCustomOutput ) resultNode = this.outputNode;
 
 
+			//
+
+			if ( builder.context.getOutput ) {
+
+				resultNode = builder.context.getOutput( resultNode, builder );
+
+			}
+
 			// MRT
 			// MRT
 
 
 			if ( renderTarget !== null ) {
 			if ( renderTarget !== null ) {
@@ -1301,6 +1343,8 @@ class NodeMaterial extends Material {
 		this.fragmentNode = source.fragmentNode;
 		this.fragmentNode = source.fragmentNode;
 		this.vertexNode = source.vertexNode;
 		this.vertexNode = source.vertexNode;
 
 
+		this.contextNode = source.contextNode;
+
 		return super.copy( source );
 		return super.copy( source );
 
 
 	}
 	}

+ 1 - 1
src/materials/nodes/manager/NodeMaterialObserver.js

@@ -259,7 +259,7 @@ class NodeMaterialObserver {
 
 
 		}
 		}
 
 
-		if ( builder.renderer.overrideNodes.modelViewMatrix !== null || builder.renderer.overrideNodes.modelNormalViewMatrix !== null )
+		if ( builder.context.modelViewMatrix || builder.context.modelNormalViewMatrix )
 			return true;
 			return true;
 
 
 		return false;
 		return false;

+ 1 - 1
src/nodes/accessors/ModelNode.js

@@ -123,7 +123,7 @@ export const modelWorldMatrixInverse = /*@__PURE__*/ uniform( new Matrix4() ).on
  */
  */
 export const modelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => {
 export const modelViewMatrix = /*@__PURE__*/ ( Fn( ( builder ) => {
 
 
-	return builder.renderer.overrideNodes.modelViewMatrix || mediumpModelViewMatrix;
+	return builder.context.modelViewMatrix || mediumpModelViewMatrix;
 
 
 } ).once() )().toVar( 'modelViewMatrix' );
 } ).once() )().toVar( 'modelViewMatrix' );
 
 

+ 2 - 2
src/nodes/accessors/Normal.js

@@ -182,9 +182,9 @@ export const transformNormal = /*@__PURE__*/ Fn( ( [ normal, matrix = modelWorld
  */
  */
 export const transformNormalToView = /*@__PURE__*/ Fn( ( [ normal ], builder ) => {
 export const transformNormalToView = /*@__PURE__*/ Fn( ( [ normal ], builder ) => {
 
 
-	const modelNormalViewMatrix = builder.renderer.overrideNodes.modelNormalViewMatrix;
+	const modelNormalViewMatrix = builder.context.modelNormalViewMatrix;
 
 
-	if ( modelNormalViewMatrix !== null ) {
+	if ( modelNormalViewMatrix ) {
 
 
 		return modelNormalViewMatrix.transformDirection( normal );
 		return modelNormalViewMatrix.transformDirection( normal );
 
 

+ 46 - 4
src/nodes/core/ContextNode.js

@@ -1,5 +1,5 @@
 import Node from './Node.js';
 import Node from './Node.js';
-import { addMethodChaining, nodeProxy } from '../tsl/TSLCore.js';
+import { addMethodChaining } from '../tsl/TSLCore.js';
 import { warn } from '../../utils.js';
 import { warn } from '../../utils.js';
 
 
 /**
 /**
@@ -9,6 +9,12 @@ import { warn } from '../../utils.js';
  *
  *
  * ```js
  * ```js
  *node.context( { getUV: () => customCoord } );
  *node.context( { getUV: () => customCoord } );
+ *\// or
+ *material.contextNode = context( { getUV: () => customCoord } );
+ *\// or
+ *renderer.contextNode = context( { getUV: () => customCoord } );
+ *\// or
+ *scenePass.contextNode = context( { getUV: () => customCoord } );
  *```
  *```
  * @augments Node
  * @augments Node
  */
  */
@@ -26,7 +32,7 @@ class ContextNode extends Node {
 	 * @param {Node} node - The node whose context should be modified.
 	 * @param {Node} node - The node whose context should be modified.
 	 * @param {Object} [value={}] - The modified context data.
 	 * @param {Object} [value={}] - The modified context data.
 	 */
 	 */
-	constructor( node, value = {} ) {
+	constructor( node = null, value = {} ) {
 
 
 		super();
 		super();
 
 
@@ -79,6 +85,29 @@ class ContextNode extends Node {
 
 
 	}
 	}
 
 
+	/**
+	 * Gathers the context data from all parent context nodes.
+	 *
+	 * @return {Object} The gathered context data.
+	 */
+	getFlowContextData() {
+
+		const children = [];
+
+		this.traverse( ( node ) => {
+
+			if ( node.isContextNode === true ) {
+
+				children.push( node.value );
+
+			}
+
+		} );
+
+		return Object.assign( {}, ...children );
+
+	}
+
 	/**
 	/**
 	 * This method is overwritten to ensure it returns the member type of {@link ContextNode#node}.
 	 * This method is overwritten to ensure it returns the member type of {@link ContextNode#node}.
 	 *
 	 *
@@ -133,11 +162,24 @@ export default ContextNode;
  *
  *
  * @tsl
  * @tsl
  * @function
  * @function
- * @param {Node} node - The node whose context should be modified.
+ * @param {Node|Object} [nodeOrValue={}] - The node whose context should be modified or the modified context data.
  * @param {Object} [value={}] - The modified context data.
  * @param {Object} [value={}] - The modified context data.
  * @returns {ContextNode}
  * @returns {ContextNode}
  */
  */
-export const context = /*@__PURE__*/ nodeProxy( ContextNode ).setParameterLength( 1, 2 );
+export const context = /*@__PURE__*/ ( nodeOrValue = null, value = {} ) => {
+
+	let node = nodeOrValue;
+
+	if ( node === null || node.isNode !== true ) {
+
+		value = node || value;
+		node = null;
+
+	}
+
+	return new ContextNode( node, value );
+
+};
 
 
 /**
 /**
  * TSL function for defining a uniformFlow context value for a given node.
  * TSL function for defining a uniformFlow context value for a given node.

+ 34 - 1
src/nodes/display/PassNode.js

@@ -1,7 +1,7 @@
 import TempNode from '../core/TempNode.js';
 import TempNode from '../core/TempNode.js';
 import { default as TextureNode/*, texture*/ } from '../accessors/TextureNode.js';
 import { default as TextureNode/*, texture*/ } from '../accessors/TextureNode.js';
 import { NodeUpdateType } from '../core/constants.js';
 import { NodeUpdateType } from '../core/constants.js';
-import { nodeObject } from '../tsl/TSLBase.js';
+import { nodeObject, context } from '../tsl/TSLBase.js';
 import { uniform } from '../core/UniformNode.js';
 import { uniform } from '../core/UniformNode.js';
 import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';
 import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';
 
 
@@ -248,6 +248,22 @@ class PassNode extends TempNode {
 		 */
 		 */
 		this.renderTarget = renderTarget;
 		this.renderTarget = renderTarget;
 
 
+		/**
+		 * An optional global context for the pass.
+		 *
+		 * @type {ContextNode|null}
+		 */
+		this.contextNode = null;
+
+		/**
+		 * A cache for the context node.
+		 *
+		 * @private
+		 * @type {?Object}
+		 * @default null
+		 */
+		this._contextNodeCache = null;
+
 		/**
 		/**
 		 * A dictionary holding the internal result textures.
 		 * A dictionary holding the internal result textures.
 		 *
 		 *
@@ -739,6 +755,7 @@ class PassNode extends TempNode {
 		const currentMRT = renderer.getMRT();
 		const currentMRT = renderer.getMRT();
 		const currentAutoClear = renderer.autoClear;
 		const currentAutoClear = renderer.autoClear;
 		const currentMask = camera.layers.mask;
 		const currentMask = camera.layers.mask;
+		const currentContextNode = renderer.contextNode;
 
 
 		this._cameraNear.value = camera.near;
 		this._cameraNear.value = camera.near;
 		this._cameraFar.value = camera.far;
 		this._cameraFar.value = camera.far;
@@ -759,6 +776,21 @@ class PassNode extends TempNode {
 		renderer.setMRT( this._mrt );
 		renderer.setMRT( this._mrt );
 		renderer.autoClear = true;
 		renderer.autoClear = true;
 
 
+		if ( this.contextNode !== null ) {
+
+			if ( this._contextNodeCache === null || this._contextNodeCache.version !== this.version ) {
+
+				this._contextNodeCache = {
+					version: this.version,
+					context: context( { ...renderer.contextNode.value, ...this.contextNode.getFlowContextData() } )
+				};
+
+			}
+
+			renderer.contextNode = this._contextNodeCache.context;
+
+		}
+
 		const currentSceneName = scene.name;
 		const currentSceneName = scene.name;
 
 
 		scene.name = this.name ? this.name : scene.name;
 		scene.name = this.name ? this.name : scene.name;
@@ -770,6 +802,7 @@ class PassNode extends TempNode {
 		renderer.setRenderTarget( currentRenderTarget );
 		renderer.setRenderTarget( currentRenderTarget );
 		renderer.setMRT( currentMRT );
 		renderer.setMRT( currentMRT );
 		renderer.autoClear = currentAutoClear;
 		renderer.autoClear = currentAutoClear;
+		renderer.contextNode = currentContextNode;
 
 
 		camera.layers.mask = currentMask;
 		camera.layers.mask = currentMask;
 
 

+ 1 - 1
src/nodes/pmrem/PMREMNode.js

@@ -308,7 +308,7 @@ class PMREMNode extends TempNode {
 
 
 		if ( uvNode === null && builder.context.getUV ) {
 		if ( uvNode === null && builder.context.getUV ) {
 
 
-			uvNode = builder.context.getUV( this );
+			uvNode = builder.context.getUV( this, builder );
 
 
 		}
 		}
 
 

+ 26 - 0
src/nodes/utils/UVUtils.js

@@ -1,5 +1,31 @@
 import { Fn, vec2 } from '../tsl/TSLBase.js';
 import { Fn, vec2 } from '../tsl/TSLBase.js';
 import { rotate } from './RotateNode.js';
 import { rotate } from './RotateNode.js';
+import { context } from '../core/ContextNode.js';
+
+/**
+ * Replaces the default UV coordinates used in texture lookups.
+ *
+ * ```js
+ *material.contextNode = replaceDefaultUV( ( textureNode ) => {
+ *
+ *	// ...
+ *	return customUVCoordinates;
+ *
+ *} );
+ *```
+ *
+ * @tsl
+ * @function
+ * @param {function(Node):Node<vec2>} callback - A callback that receives the texture node
+ * and must return the new uv coordinates.
+ * @param {Node} [node=null] - An optional node to which the context will be applied.
+ * @return {ContextNode} A context node that replaces the default UV coordinates.
+ */
+export function replaceDefaultUV( callback, node = null ) {
+
+	return context( node, { getUV: callback } );
+
+}
 
 
 /**
 /**
  * Rotates the given uv coordinates around a center point
  * Rotates the given uv coordinates around a center point

+ 2 - 2
src/renderers/common/Background.js

@@ -1,6 +1,6 @@
 import DataMap from './DataMap.js';
 import DataMap from './DataMap.js';
 import Color4 from './Color4.js';
 import Color4 from './Color4.js';
-import { vec4, context, normalWorldGeometry, backgroundBlurriness, backgroundIntensity, backgroundRotation, positionLocal, cameraProjectionMatrix, modelViewMatrix, div } from '../../nodes/TSL.js';
+import { vec4, normalWorldGeometry, backgroundBlurriness, backgroundIntensity, backgroundRotation, positionLocal, cameraProjectionMatrix, modelViewMatrix, div } from '../../nodes/TSL.js';
 import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
 import NodeMaterial from '../../materials/nodes/NodeMaterial.js';
 
 
 import { Mesh } from '../../objects/Mesh.js';
 import { Mesh } from '../../objects/Mesh.js';
@@ -88,7 +88,7 @@ class Background extends DataMap {
 
 
 			if ( backgroundMesh === undefined ) {
 			if ( backgroundMesh === undefined ) {
 
 
-				const backgroundMeshNode = context( vec4( backgroundNode ).mul( backgroundIntensity ), {
+				const backgroundMeshNode = vec4( backgroundNode ).mul( backgroundIntensity ).context( {
 					// @TODO: Add Texture2D support using node context
 					// @TODO: Add Texture2D support using node context
 					getUV: () => backgroundRotation.mul( normalWorldGeometry ),
 					getUV: () => backgroundRotation.mul( normalWorldGeometry ),
 					getTextureLevel: () => backgroundBlurriness
 					getTextureLevel: () => backgroundBlurriness

+ 1 - 1
src/renderers/common/RenderObject.js

@@ -871,7 +871,7 @@ class RenderObject {
 
 
 		}
 		}
 
 
-		cacheKey = hash( cacheKey, this.camera.id );
+		cacheKey = hash( cacheKey, this.camera.id, this.renderer.contextNode.id, this.renderer.contextNode.version );
 
 
 		return cacheKey;
 		return cacheKey;
 
 

+ 14 - 13
src/renderers/common/Renderer.js

@@ -35,6 +35,7 @@ import { DoubleSide, BackSide, FrontSide, SRGBColorSpace, NoToneMapping, LinearF
 import { float, vec3, vec4 } from '../../nodes/tsl/TSLCore.js';
 import { float, vec3, vec4 } from '../../nodes/tsl/TSLCore.js';
 import { reference } from '../../nodes/accessors/ReferenceNode.js';
 import { reference } from '../../nodes/accessors/ReferenceNode.js';
 import { highpModelNormalViewMatrix, highpModelViewMatrix } from '../../nodes/accessors/ModelNode.js';
 import { highpModelNormalViewMatrix, highpModelViewMatrix } from '../../nodes/accessors/ModelNode.js';
+import { context } from '../../nodes/core/ContextNode.js';
 import { error, warn, warnOnce } from '../../utils.js';
 import { error, warn, warnOnce } from '../../utils.js';
 
 
 const _scene = /*@__PURE__*/ new Scene();
 const _scene = /*@__PURE__*/ new Scene();
@@ -222,17 +223,13 @@ class Renderer {
 		this.info = new Info();
 		this.info = new Info();
 
 
 		/**
 		/**
-		 * Stores override nodes for specific transformations or calculations.
+		 * A global context node that stores override nodes for specific transformations or calculations.
 		 * These nodes can be used to replace default behavior in the rendering pipeline.
 		 * These nodes can be used to replace default behavior in the rendering pipeline.
 		 *
 		 *
-		 * @type {Object}
-		 * @property {?Node} modelViewMatrix - An override node for the model-view matrix.
-		 * @property {?Node} modelNormalViewMatrix - An override node for the model normal view matrix.
+		 * @type {ContextNode}
+		 * @property {Object} value - The context value object.
 		 */
 		 */
-		this.overrideNodes = {
-			modelViewMatrix: null,
-			modelNormalViewMatrix: null
-		};
+		this.contextNode = context();
 
 
 		/**
 		/**
 		 * The node library defines how certain library objects like materials, lights
 		 * The node library defines how certain library objects like materials, lights
@@ -1031,15 +1028,17 @@ class Renderer {
 	 */
 	 */
 	set highPrecision( value ) {
 	set highPrecision( value ) {
 
 
+		const contextNodeData = this.contextNode.value;
+
 		if ( value === true ) {
 		if ( value === true ) {
 
 
-			this.overrideNodes.modelViewMatrix = highpModelViewMatrix;
-			this.overrideNodes.modelNormalViewMatrix = highpModelNormalViewMatrix;
+			contextNodeData.modelViewMatrix = highpModelViewMatrix;
+			contextNodeData.modelNormalViewMatrix = highpModelNormalViewMatrix;
 
 
 		} else if ( this.highPrecision ) {
 		} else if ( this.highPrecision ) {
 
 
-			this.overrideNodes.modelViewMatrix = null;
-			this.overrideNodes.modelNormalViewMatrix = null;
+			delete contextNodeData.modelViewMatrix;
+			delete contextNodeData.modelNormalViewMatrix;
 
 
 		}
 		}
 
 
@@ -1053,7 +1052,9 @@ class Renderer {
 	 */
 	 */
 	get highPrecision() {
 	get highPrecision() {
 
 
-		return this.overrideNodes.modelViewMatrix === highpModelViewMatrix && this.overrideNodes.modelNormalViewMatrix === highpModelNormalViewMatrix;
+		const contextNodeData = this.contextNode.value;
+
+		return contextNodeData.modelViewMatrix === highpModelViewMatrix && contextNodeData.modelNormalViewMatrix === highpModelNormalViewMatrix;
 
 
 	}
 	}
 
 

粤ICP备19079148号