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

WebGPURenderer: Introduce `NodeMaterialObserver` and updates (#29386)

* Introduce NodeMaterialMonitor

* Update VelocityNode.js

* updates

* Update webgpu_performance_renderbundle.html

* Update Lights.js

* updates

* rename NodeMaterialMonitor -> NodeMaterialObserver

* Update NodeMaterials.js
sunag 1 год назад
Родитель
Сommit
7290d8fd4c

+ 6 - 1
examples/webgpu_performance_renderbundle.html

@@ -249,7 +249,12 @@
 
 			} );
 
-			gui.add( api, 'dynamic' );
+			gui.add( api, 'dynamic' ).onChange( () => {
+
+				group.static = ! group.static;
+
+			} );
+
 
 			// listeners
 

+ 11 - 0
src/materials/nodes/NodeMaterial.js

@@ -22,6 +22,7 @@ import IrradianceNode from '../../nodes/lighting/IrradianceNode.js';
 import { depth } from '../../nodes/display/ViewportDepthNode.js';
 import { cameraLogDepth } from '../../nodes/accessors/Camera.js';
 import { clipping, clippingAlpha } from '../../nodes/accessors/ClippingNode.js';
+import NodeMaterialObserver from './manager/NodeMaterialObserver.js';
 
 class NodeMaterial extends Material {
 
@@ -81,6 +82,12 @@ class NodeMaterial extends Material {
 
 	}
 
+	setupObserver( builder ) {
+
+		return new NodeMaterialObserver( builder );
+
+	}
+
 	setup( builder ) {
 
 		builder.context.setupNormal = () => this.setupNormal( builder );
@@ -171,6 +178,10 @@ class NodeMaterial extends Material {
 
 		builder.addFlow( 'fragment', builder.removeStack() );
 
+		// < MONITOR >
+
+		builder.monitor = this.setupObserver( builder );
+
 	}
 
 	setupClipping( builder ) {

+ 2 - 0
src/materials/nodes/NodeMaterials.js

@@ -1,5 +1,7 @@
 // @TODO: We can simplify "export { default as SomeNode, other, exports } from '...'" to just "export * from '...'" if we will use only named exports
 
+export { default as NodeMaterialObserver } from './manager/NodeMaterialObserver.js';
+
 export { default as NodeMaterial } from './NodeMaterial.js';
 export { default as InstancedPointsNodeMaterial } from './InstancedPointsNodeMaterial.js';
 export { default as LineBasicNodeMaterial } from './LineBasicNodeMaterial.js';

+ 234 - 0
src/materials/nodes/manager/NodeMaterialObserver.js

@@ -0,0 +1,234 @@
+const refreshUniforms = [
+	'alphaMap',
+	'alphaTest',
+	'anisotropy',
+	'anisotropyMap',
+	'anisotropyRotation',
+	'aoMap',
+	'attenuationColor',
+	'attenuationDistance',
+	'bumpMap',
+	'clearcoat',
+	'clearcoatMap',
+	'clearcoatNormalMap',
+	'clearcoatNormalScale',
+	'clearcoatRoughness',
+	'color',
+	'dispersion',
+	'displacementMap',
+	'emissive',
+	'emissiveMap',
+	'envMap',
+	'gradientMap',
+	'ior',
+	'iridescence',
+	'iridescenceIOR',
+	'iridescenceMap',
+	'iridescenceThicknessMap',
+	'lightMap',
+	'map',
+	'matcap',
+	'metalness',
+	'metalnessMap',
+	'normalMap',
+	'normalScale',
+	'opacity',
+	'roughness',
+	'roughnessMap',
+	'sheen',
+	'sheenColor',
+	'sheenColorMap',
+	'sheenRoughnessMap',
+	'shininess',
+	'specular',
+	'specularColor',
+	'specularColorMap',
+	'specularIntensity',
+	'specularIntensityMap',
+	'specularMap',
+	'thickness',
+	'transmission',
+	'transmissionMap'
+];
+
+class NodeMaterialObserver {
+
+	constructor( builder ) {
+
+		this.renderObjects = new WeakMap();
+		this.hasNode = this.containsNode( builder );
+		this.refreshUniforms = refreshUniforms;
+		this.renderId = 0;
+
+	}
+
+	firstInitialization( renderObject ) {
+
+		const hasInitialized = this.renderObjects.has( renderObject );
+
+		if ( hasInitialized === false ) {
+
+			this.getRenderObjectData( renderObject );
+
+			return true;
+
+		}
+
+		return false;
+
+	}
+
+	getRenderObjectData( renderObject ) {
+
+		let data = this.renderObjects.get( renderObject );
+
+		if ( data === undefined ) {
+
+			data = {
+				material: this.getMaterialData( renderObject.material ),
+				worldMatrix: renderObject.object.matrixWorld.clone()
+			};
+
+			if ( renderObject.bundle !== null ) {
+
+				data.version = renderObject.bundle.version;
+
+			}
+
+			this.renderObjects.set( renderObject, data );
+
+		}
+
+		return data;
+
+	}
+
+	containsNode( builder ) {
+
+		const material = builder.material;
+
+		for ( const property in material ) {
+
+			if ( material[ property ] && material[ property ].isNode )
+				return true;
+
+		}
+
+		if ( builder.renderer.nodes.modelViewMatrix !== null || builder.renderer.nodes.modelNormalViewMatrix !== null )
+			return true;
+
+		return false;
+
+	}
+
+	getMaterialData( material ) {
+
+		const data = {};
+
+		for ( const property of this.refreshUniforms ) {
+
+			const value = material[ property ];
+
+			if ( value === null || value === undefined ) continue;
+
+			if ( typeof value === 'object' && value.clone !== undefined ) {
+
+				data[ property ] = value.clone();
+
+			} else {
+
+				data[ property ] = value;
+
+			}
+
+		}
+
+		return data;
+
+
+	}
+
+	equals( renderObject ) {
+
+		const renderObjectData = this.getRenderObjectData( renderObject );
+
+		// world matrix
+
+		if ( renderObjectData.worldMatrix.equals( renderObject.object.matrixWorld ) !== true ) {
+
+			renderObjectData.worldMatrix.copy( renderObject.object.matrixWorld );
+
+			return false;
+
+		}
+
+		// material
+
+		const materialData = renderObjectData.material;
+		const material = renderObject.material;
+
+		for ( const property in materialData ) {
+
+			const value = materialData[ property ];
+			const mtlValue = material[ property ];
+
+			if ( value.equals !== undefined ) {
+
+				if ( value.equals( mtlValue ) === false ) {
+
+					value.copy( mtlValue );
+
+					return false;
+
+				}
+
+			} else if ( value !== mtlValue ) {
+
+				materialData[ property ] = mtlValue;
+
+			}
+
+		}
+
+		// bundle
+
+		if ( renderObject.bundle !== null ) {
+
+			renderObjectData.version = renderObject.bundle.version;
+
+		}
+
+		return true;
+
+	}
+
+	needsRefresh( renderObject, nodeFrame ) {
+
+		if ( this.hasNode || this.firstInitialization( renderObject ) )
+			return true;
+
+		const { renderId } = nodeFrame;
+
+		if ( this.renderId !== renderId ) {
+
+			this.renderId = renderId;
+
+			return true;
+
+		}
+
+		const isStatic = renderObject.object.static === true;
+		const isBundle = renderObject.bundle !== null && renderObject.bundle.static === true && this.getRenderObjectData( renderObject ).version === renderObject.bundle.version;
+
+		if ( isStatic || isBundle )
+			return false;
+
+		const notEqual = this.equals( renderObject ) !== true;
+
+		return notEqual;
+
+	}
+
+}
+
+export default NodeMaterialObserver;

+ 0 - 1
src/nodes/Nodes.js

@@ -135,7 +135,6 @@ export { default as RangeNode } from './geometry/RangeNode.js';
 export { default as ComputeNode } from './gpgpu/ComputeNode.js';
 
 // lighting
-export { default as LightNode } from './lighting/LightNode.js';
 export { default as PointLightNode } from './lighting/PointLightNode.js';
 export { default as DirectionalLightNode } from './lighting/DirectionalLightNode.js';
 export { default as RectAreaLightNode } from './lighting/RectAreaLightNode.js';

+ 1 - 1
src/nodes/TSL.js

@@ -146,7 +146,7 @@ export * from './gpgpu/BarrierNode.js';
 export * from './gpgpu/WorkgroupInfoNode.js';
 
 // lighting
-export * from './lighting/LightNode.js';
+export * from './accessors/Lights.js';
 export * from './lighting/LightsNode.js';
 export * from './lighting/LightingContextNode.js';
 

+ 10 - 16
src/nodes/accessors/Camera.js

@@ -1,19 +1,13 @@
 import { uniform } from '../core/UniformNode.js';
-import { sharedUniformGroup } from '../core/UniformGroupNode.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
 import { Vector3 } from '../../math/Vector3.js';
 
-const cameraGroup = /*@__PURE__*/ sharedUniformGroup( 'camera' ).onRenderUpdate( () => {
-
-	cameraGroup.needsUpdate = true;
-
-} );
-
-export const cameraNear = /*@__PURE__*/ uniform( 'float' ).label( 'cameraNear' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.near );
-export const cameraFar = /*@__PURE__*/ uniform( 'float' ).label( 'cameraFar' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.far );
-export const cameraLogDepth = /*@__PURE__*/ uniform( 'float' ).label( 'cameraLogDepth' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
-export const cameraProjectionMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
-export const cameraProjectionMatrixInverse = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrixInverse' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse );
-export const cameraViewMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
-export const cameraWorldMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraWorldMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld );
-export const cameraNormalMatrix = /*@__PURE__*/ uniform( 'mat3' ).label( 'cameraNormalMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix );
-export const cameraPosition = /*@__PURE__*/ uniform( new Vector3() ).label( 'cameraPosition' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) );
+export const cameraNear = /*@__PURE__*/ uniform( 'float' ).label( 'cameraNear' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.near );
+export const cameraFar = /*@__PURE__*/ uniform( 'float' ).label( 'cameraFar' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.far );
+export const cameraLogDepth = /*@__PURE__*/ uniform( 'float' ).label( 'cameraLogDepth' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
+export const cameraProjectionMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
+export const cameraProjectionMatrixInverse = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrixInverse' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse );
+export const cameraViewMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
+export const cameraWorldMatrix = /*@__PURE__*/ uniform( 'mat4' ).label( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld );
+export const cameraNormalMatrix = /*@__PURE__*/ uniform( 'mat3' ).label( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix );
+export const cameraPosition = /*@__PURE__*/ uniform( new Vector3() ).label( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) );

+ 51 - 0
src/nodes/accessors/Lights.js

@@ -0,0 +1,51 @@
+import { uniform } from '../core/UniformNode.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
+import { Vector3 } from '../../math/Vector3.js';
+import { cameraViewMatrix } from './Camera.js';
+
+let uniformsLib;
+
+function getLightData( light ) {
+
+	uniformsLib = uniformsLib || new WeakMap();
+
+	let uniforms = uniformsLib.get( light );
+
+	if ( uniforms === undefined ) uniformsLib.set( light, uniforms = {} );
+
+	return uniforms;
+
+}
+
+export function lightPosition( light ) {
+
+	const data = getLightData( light );
+
+	return data.position || ( data.position = uniform( new Vector3() ).setGroup( renderGroup ).onRenderUpdate( ( _, self ) => self.value.setFromMatrixPosition( light.matrixWorld ) ) );
+
+}
+
+export function lightTargetPosition( light ) {
+
+	const data = getLightData( light );
+
+	return data.targetPosition || ( data.targetPosition = uniform( new Vector3() ).setGroup( renderGroup ).onRenderUpdate( ( _, self ) => self.value.setFromMatrixPosition( light.target.matrixWorld ) ) );
+
+}
+
+export function lightViewPosition( light ) {
+
+	const data = getLightData( light );
+
+	return data.viewPosition || ( data.viewPosition = uniform( new Vector3() ).setGroup( renderGroup ).onRenderUpdate( ( { camera }, self ) => {
+
+		self.value = self.value || new Vector3();
+		self.value.setFromMatrixPosition( light.matrixWorld );
+
+		self.value.applyMatrix4( camera.matrixWorldInverse );
+
+	} ) );
+
+}
+
+export const lightTargetDirection = ( light ) => cameraViewMatrix.transformDirection( lightPosition( light ).sub( lightTargetPosition( light ) ) );

+ 18 - 1
src/nodes/accessors/ReferenceBaseNode.js

@@ -60,11 +60,20 @@ class ReferenceBaseNode extends Node {
 		this.properties = property.split( '.' );
 		this.reference = object;
 		this.node = null;
+		this.group = null;
 
 		this.updateType = NodeUpdateType.OBJECT;
 
 	}
 
+	setGroup( group ) {
+
+		this.group = group;
+
+		return this;
+
+	}
+
 	element( indexNode ) {
 
 		return nodeObject( new ReferenceElementNode( this, nodeObject( indexNode ) ) );
@@ -73,7 +82,15 @@ class ReferenceBaseNode extends Node {
 
 	setNodeType( uniformType ) {
 
-		this.node = uniform( null, uniformType ).getSelf();
+		const node = uniform( null, uniformType ).getSelf();
+
+		if ( this.group !== null ) {
+
+			node.setGroup( this.group );
+
+		}
+
+		this.node = node;
 
 	}
 

+ 15 - 0
src/nodes/accessors/ReferenceNode.js

@@ -65,6 +65,7 @@ class ReferenceNode extends Node {
 		this.properties = property.split( '.' );
 		this.reference = object;
 		this.node = null;
+		this.group = null;
 
 		this.updateType = NodeUpdateType.OBJECT;
 
@@ -76,6 +77,14 @@ class ReferenceNode extends Node {
 
 	}
 
+	setGroup( group ) {
+
+		this.group = group;
+
+		return this;
+
+	}
+
 	setNodeType( uniformType ) {
 
 		let node = null;
@@ -102,6 +111,12 @@ class ReferenceNode extends Node {
 
 		}
 
+		if ( this.group !== null ) {
+
+			node.setGroup( this.group );
+
+		}
+
 		this.node = node.getSelf();
 
 	}

+ 3 - 0
src/nodes/accessors/RendererReferenceNode.js

@@ -1,5 +1,6 @@
 import ReferenceBaseNode from './ReferenceBaseNode.js';
 import { nodeObject } from '../tsl/TSLCore.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
 
 class RendererReferenceNode extends ReferenceBaseNode {
 
@@ -15,6 +16,8 @@ class RendererReferenceNode extends ReferenceBaseNode {
 
 		this.renderer = renderer;
 
+		this.setGroup( renderGroup );
+
 	}
 
 	updateReference( state ) {

+ 2 - 1
src/nodes/accessors/VelocityNode.js

@@ -7,6 +7,7 @@ import { Matrix4 } from '../../math/Matrix4.js';
 import { uniform } from '../core/UniformNode.js';
 import { sub } from '../math/OperatorNode.js';
 import { cameraProjectionMatrix } from './Camera.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
 
 const _objectData = new WeakMap();
 
@@ -26,7 +27,7 @@ class VelocityNode extends TempNode {
 		this.updateAfterType = NodeUpdateType.OBJECT;
 
 		this.previousModelWorldMatrix = uniform( new Matrix4() );
-		this.previousProjectionMatrix = uniform( new Matrix4() );
+		this.previousProjectionMatrix = uniform( new Matrix4() ).setGroup( renderGroup );
 		this.previousCameraViewMatrix = uniform( new Matrix4() );
 
 	}

+ 4 - 2
src/nodes/core/NodeBuilder.js

@@ -79,6 +79,8 @@ class NodeBuilder {
 		this.updateAfterNodes = [];
 		this.hashNodes = {};
 
+		this.monitor = null;
+
 		this.lightsNode = null;
 		this.environmentNode = null;
 		this.fogNode = null;
@@ -328,13 +330,13 @@ class NodeBuilder {
 
 			if ( updateBeforeType !== NodeUpdateType.NONE ) {
 
-				this.updateBeforeNodes.push( node );
+				this.updateBeforeNodes.push( node.getSelf() );
 
 			}
 
 			if ( updateAfterType !== NodeUpdateType.NONE ) {
 
-				this.updateAfterNodes.push( node );
+				this.updateAfterNodes.push( node.getSelf() );
 
 			}
 

+ 12 - 11
src/nodes/lighting/AnalyticLightNode.js

@@ -15,6 +15,7 @@ import QuadMesh from '../../renderers/common/QuadMesh.js';
 import { Loop } from '../utils/LoopNode.js';
 import { screenCoordinate } from '../display/ScreenNode.js';
 import { HalfFloatType, LessCompare, RGFormat, VSMShadowMap, WebGPUCoordinateSystem } from '../../constants.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
 
 const BasicShadowMap = Fn( ( { depthTexture, shadowCoord } ) => {
 
@@ -26,8 +27,8 @@ const PCFShadowMap = Fn( ( { depthTexture, shadowCoord, shadow } ) => {
 
 	const depthCompare = ( uv, compare ) => texture( depthTexture, uv ).compare( compare );
 
-	const mapSize = reference( 'mapSize', 'vec2', shadow );
-	const radius = reference( 'radius', 'float', shadow );
+	const mapSize = reference( 'mapSize', 'vec2', shadow ).setGroup( renderGroup );
+	const radius = reference( 'radius', 'float', shadow ).setGroup( renderGroup );
 
 	const texelSize = vec2( 1 ).div( mapSize );
 	const dx0 = texelSize.x.negate().mul( radius );
@@ -65,7 +66,7 @@ const PCFSoftShadowMap = Fn( ( { depthTexture, shadowCoord, shadow } ) => {
 
 	const depthCompare = ( uv, compare ) => texture( depthTexture, uv ).compare( compare );
 
-	const mapSize = reference( 'mapSize', 'vec2', shadow );
+	const mapSize = reference( 'mapSize', 'vec2', shadow ).setGroup( renderGroup );
 
 	const texelSize = vec2( 1 ).div( mapSize );
 	const dx = texelSize.x;
@@ -217,7 +218,7 @@ class AnalyticLightNode extends LightingNode {
 		this.light = light;
 
 		this.color = new Color();
-		this.colorNode = uniform( this.color );
+		this.colorNode = uniform( this.color ).setGroup( renderGroup );
 
 		this.baseColorNode = null;
 
@@ -287,9 +288,9 @@ class AnalyticLightNode extends LightingNode {
 				const shadowPassVertical = texture( depthTexture );
 				const shadowPassHorizontal = texture( this.vsmShadowMapVertical.texture );
 
-				const samples = reference( 'blurSamples', 'float', shadow );
-				const radius = reference( 'radius', 'float', shadow );
-				const size = reference( 'mapSize', 'vec2', shadow );
+				const samples = reference( 'blurSamples', 'float', shadow ).setGroup( renderGroup );
+				const radius = reference( 'radius', 'float', shadow ).setGroup( renderGroup );
+				const size = reference( 'mapSize', 'vec2', shadow ).setGroup( renderGroup );
 
 				let material = this.vsmMaterialVertical || ( this.vsmMaterialVertical = new NodeMaterial() );
 				material.fragmentNode = VSMPassVertical( { samples, radius, size, shadowPass: shadowPassVertical } ).context( builder.getSharedContext() );
@@ -303,13 +304,13 @@ class AnalyticLightNode extends LightingNode {
 
 			//
 
-			const shadowIntensity = reference( 'intensity', 'float', shadow );
-			const bias = reference( 'bias', 'float', shadow );
-			const normalBias = reference( 'normalBias', 'float', shadow );
+			const shadowIntensity = reference( 'intensity', 'float', shadow ).setGroup( renderGroup );
+			const bias = reference( 'bias', 'float', shadow ).setGroup( renderGroup );
+			const normalBias = reference( 'normalBias', 'float', shadow ).setGroup( renderGroup );
 
 			const position = object.material.shadowPositionNode || positionWorld;
 
-			let shadowCoord = uniform( shadow.matrix ).mul( position.add( normalWorld.mul( normalBias ) ) );
+			let shadowCoord = uniform( shadow.matrix ).setGroup( renderGroup ).mul( position.add( normalWorld.mul( normalBias ) ) );
 			shadowCoord = shadowCoord.xyz.div( shadowCoord.w );
 
 			let coordZ = shadowCoord.z.add( bias );

+ 1 - 1
src/nodes/lighting/DirectionalLightNode.js

@@ -1,5 +1,5 @@
 import AnalyticLightNode from './AnalyticLightNode.js';
-import { lightTargetDirection } from './LightNode.js';
+import { lightTargetDirection } from '../accessors/Lights.js';
 
 class DirectionalLightNode extends AnalyticLightNode {
 

+ 4 - 3
src/nodes/lighting/HemisphereLightNode.js

@@ -2,7 +2,8 @@ import AnalyticLightNode from './AnalyticLightNode.js';
 import { uniform } from '../core/UniformNode.js';
 import { mix } from '../math/MathNode.js';
 import { normalView } from '../accessors/Normal.js';
-import { objectPosition } from '../accessors/Object3DNode.js';
+import { lightPosition } from '../accessors/Lights.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
 
 import { Color } from '../../math/Color.js';
 
@@ -18,10 +19,10 @@ class HemisphereLightNode extends AnalyticLightNode {
 
 		super( light );
 
-		this.lightPositionNode = objectPosition( light );
+		this.lightPositionNode = lightPosition( light );
 		this.lightDirectionNode = this.lightPositionNode.normalize();
 
-		this.groundColorNode = uniform( new Color() );
+		this.groundColorNode = uniform( new Color() ).setGroup( renderGroup );
 
 	}
 

+ 0 - 61
src/nodes/lighting/LightNode.js

@@ -1,61 +0,0 @@
-import Node from '../core/Node.js';
-import { nodeProxy } from '../tsl/TSLBase.js';
-import { objectPosition } from '../accessors/Object3DNode.js';
-import { cameraViewMatrix } from '../accessors/Camera.js';
-
-class LightNode extends Node {
-
-	static get type() {
-
-		return 'LightNode';
-
-	}
-
-	constructor( scope = LightNode.TARGET_DIRECTION, light = null ) {
-
-		super();
-
-		this.scope = scope;
-		this.light = light;
-
-	}
-
-	setup() {
-
-		const { scope, light } = this;
-
-		let output = null;
-
-		if ( scope === LightNode.TARGET_DIRECTION ) {
-
-			output = cameraViewMatrix.transformDirection( objectPosition( light ).sub( objectPosition( light.target ) ) );
-
-		}
-
-		return output;
-
-	}
-
-	serialize( data ) {
-
-		super.serialize( data );
-
-		data.scope = this.scope;
-
-	}
-
-	deserialize( data ) {
-
-		super.deserialize( data );
-
-		this.scope = data.scope;
-
-	}
-
-}
-
-LightNode.TARGET_DIRECTION = 'targetDirection';
-
-export default LightNode;
-
-export const lightTargetDirection = /*@__PURE__*/ nodeProxy( LightNode, LightNode.TARGET_DIRECTION );

+ 5 - 4
src/nodes/lighting/PointLightNode.js

@@ -1,8 +1,9 @@
 import AnalyticLightNode from './AnalyticLightNode.js';
 import { getDistanceAttenuation } from './LightUtils.js';
 import { uniform } from '../core/UniformNode.js';
-import { objectViewPosition } from '../accessors/Object3DNode.js';
+import { lightViewPosition } from '../accessors/Lights.js';
 import { positionView } from '../accessors/Position.js';
+import { renderGroup } from '../TSL.js';
 
 class PointLightNode extends AnalyticLightNode {
 
@@ -16,8 +17,8 @@ class PointLightNode extends AnalyticLightNode {
 
 		super( light );
 
-		this.cutoffDistanceNode = uniform( 0 );
-		this.decayExponentNode = uniform( 0 );
+		this.cutoffDistanceNode = uniform( 0 ).setGroup( renderGroup );
+		this.decayExponentNode = uniform( 0 ).setGroup( renderGroup );
 
 	}
 
@@ -38,7 +39,7 @@ class PointLightNode extends AnalyticLightNode {
 
 		const lightingModel = builder.context.lightingModel;
 
-		const lVector = objectViewPosition( light ).sub( positionView ); // @TODO: Add it into LightNode
+		const lVector = lightViewPosition( light ).sub( positionView ); // @TODO: Add it into LightNode
 
 		const lightDirection = lVector.normalize();
 		const lightDistance = lVector.length();

+ 5 - 4
src/nodes/lighting/RectAreaLightNode.js

@@ -1,7 +1,8 @@
 import AnalyticLightNode from './AnalyticLightNode.js';
 import { texture } from '../accessors/TextureNode.js';
 import { uniform } from '../core/UniformNode.js';
-import { objectViewPosition } from '../accessors/Object3DNode.js';
+import { lightViewPosition } from '../accessors/Lights.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
 
 import { Matrix4 } from '../../math/Matrix4.js';
 import { Vector3 } from '../../math/Vector3.js';
@@ -23,8 +24,8 @@ class RectAreaLightNode extends AnalyticLightNode {
 
 		super( light );
 
-		this.halfHeight = uniform( new Vector3() );
-		this.halfWidth = uniform( new Vector3() );
+		this.halfHeight = uniform( new Vector3() ).setGroup( renderGroup );
+		this.halfWidth = uniform( new Vector3() ).setGroup( renderGroup );
 
 	}
 
@@ -70,7 +71,7 @@ class RectAreaLightNode extends AnalyticLightNode {
 		const { colorNode, light } = this;
 		const lightingModel = builder.context.lightingModel;
 
-		const lightPosition = objectViewPosition( light );
+		const lightPosition = lightViewPosition( light );
 		const reflectedLight = builder.context.reflectedLight;
 
 		lightingModel.directRectArea( {

+ 7 - 7
src/nodes/lighting/SpotLightNode.js

@@ -1,10 +1,10 @@
 import AnalyticLightNode from './AnalyticLightNode.js';
-import { lightTargetDirection } from './LightNode.js';
 import { getDistanceAttenuation } from './LightUtils.js';
 import { uniform } from '../core/UniformNode.js';
 import { smoothstep } from '../math/MathNode.js';
-import { objectViewPosition } from '../accessors/Object3DNode.js';
 import { positionView } from '../accessors/Position.js';
+import { renderGroup } from '../core/UniformGroupNode.js';
+import { lightViewPosition, lightTargetDirection } from '../accessors/Lights.js';
 
 class SpotLightNode extends AnalyticLightNode {
 
@@ -18,11 +18,11 @@ class SpotLightNode extends AnalyticLightNode {
 
 		super( light );
 
-		this.coneCosNode = uniform( 0 );
-		this.penumbraCosNode = uniform( 0 );
+		this.coneCosNode = uniform( 0 ).setGroup( renderGroup );
+		this.penumbraCosNode = uniform( 0 ).setGroup( renderGroup );
 
-		this.cutoffDistanceNode = uniform( 0 );
-		this.decayExponentNode = uniform( 0 );
+		this.cutoffDistanceNode = uniform( 0 ).setGroup( renderGroup );
+		this.decayExponentNode = uniform( 0 ).setGroup( renderGroup );
 
 	}
 
@@ -56,7 +56,7 @@ class SpotLightNode extends AnalyticLightNode {
 
 		const { colorNode, cutoffDistanceNode, decayExponentNode, light } = this;
 
-		const lVector = objectViewPosition( light ).sub( positionView ); // @TODO: Add it into LightNode
+		const lVector = lightViewPosition( light ).sub( positionView ); // @TODO: Add it into LightNode
 
 		const lightDirection = lVector.normalize();
 		const angleCos = lightDirection.dot( lightTargetDirection( light ) );

+ 9 - 0
src/renderers/common/BundleGroup.js

@@ -10,6 +10,15 @@ class BundleGroup extends Group {
 
 		this.type = 'BundleGroup';
 
+		this.static = true;
+		this.version = 0;
+
+	}
+
+	set needsUpdate( value ) {
+
+		if ( value === true ) this.version ++;
+
 	}
 
 }

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

@@ -63,6 +63,8 @@ export default class RenderObject {
 		this.vertexBuffers = null;
 		this.drawParams = null;
 
+		this.bundle = null;
+
 		this.updateClipping( renderContext.clippingContext );
 
 		this.clippingContextVersion = this.clippingContext.version;
@@ -72,6 +74,7 @@ export default class RenderObject {
 
 		this._nodeBuilderState = null;
 		this._bindings = null;
+		this._monitor = null;
 
 		this.onDispose = null;
 
@@ -128,6 +131,12 @@ export default class RenderObject {
 
 	}
 
+	getMonitor() {
+
+		return this._monitor || ( this._monitor = this.getNodeBuilderState().monitor );
+
+	}
+
 	getBindings() {
 
 		return this._bindings || ( this._bindings = this.getNodeBuilderState().createBindings() );
@@ -329,7 +338,7 @@ export default class RenderObject {
 
 	get needsUpdate() {
 
-		return this.initialNodesCacheKey !== this.getDynamicCacheKey() || this.clippingNeedsUpdate;
+		return /*this.object.static !== true &&*/ ( this.initialNodesCacheKey !== this.getDynamicCacheKey() || this.clippingNeedsUpdate );
 
 	}
 

+ 8 - 1
src/renderers/common/RenderObjects.js

@@ -1,6 +1,8 @@
 import ChainMap from './ChainMap.js';
 import RenderObject from './RenderObject.js';
 
+const chainArray = [];
+
 class RenderObjects {
 
 	constructor( renderer, nodes, geometries, pipelines, bindings, info ) {
@@ -19,7 +21,12 @@ class RenderObjects {
 	get( object, material, scene, camera, lightsNode, renderContext, passId ) {
 
 		const chainMap = this.getChainMap( passId );
-		const chainArray = [ object, material, renderContext, lightsNode ];
+
+		// reuse chainArray
+		chainArray[ 0 ] = object;
+		chainArray[ 1 ] = material;
+		chainArray[ 2 ] = renderContext;
+		chainArray[ 3 ] = lightsNode;
 
 		let renderObject = chainMap.get( chainArray );
 

+ 25 - 10
src/renderers/common/Renderer.js

@@ -460,12 +460,16 @@ class Renderer {
 
 				const renderObject = renderObjects[ i ];
 
-				this._nodes.updateBefore( renderObject );
+				if ( this._nodes.needsRefresh( renderObject ) ) {
 
-				this._nodes.updateForRender( renderObject );
-				this._bindings.updateForRender( renderObject );
+					this._nodes.updateBefore( renderObject );
 
-				this._nodes.updateAfter( renderObject );
+					this._nodes.updateForRender( renderObject );
+					this._bindings.updateForRender( renderObject );
+
+					this._nodes.updateAfter( renderObject );
+
+				}
 
 			}
 
@@ -1574,11 +1578,19 @@ class Renderer {
 
 		//
 
-		this._nodes.updateBefore( renderObject );
+		const needsRefresh = this._nodes.needsRefresh( renderObject );
+
+		if ( needsRefresh ) {
+
+			this._nodes.updateBefore( renderObject );
+
+			this._geometries.updateForRender( renderObject );
+
+			this._nodes.updateForRender( renderObject );
+			this._bindings.updateForRender( renderObject );
+
+		}
 
-		this._nodes.updateForRender( renderObject );
-		this._geometries.updateForRender( renderObject );
-		this._bindings.updateForRender( renderObject );
 		this._pipelines.updateForRender( renderObject );
 
 		//
@@ -1589,11 +1601,13 @@ class Renderer {
 
 			renderBundleData.renderObjects.push( renderObject );
 
+			renderObject.bundle = this._currentRenderBundle.scene;
+
 		}
 
 		this.backend.draw( renderObject, this.info );
 
-		this._nodes.updateAfter( renderObject );
+		if ( needsRefresh ) this._nodes.updateAfter( renderObject );
 
 	}
 
@@ -1605,8 +1619,9 @@ class Renderer {
 
 		this._nodes.updateBefore( renderObject );
 
-		this._nodes.updateForRender( renderObject );
 		this._geometries.updateForRender( renderObject );
+
+		this._nodes.updateForRender( renderObject );
 		this._bindings.updateForRender( renderObject );
 
 		this._pipelines.getForRender( renderObject, this._compilationPromises );

+ 3 - 1
src/renderers/common/nodes/NodeBuilderState.js

@@ -2,7 +2,7 @@ import BindGroup from '../BindGroup.js';
 
 class NodeBuilderState {
 
-	constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, updateAfterNodes, instanceBindGroups = true, transforms = [] ) {
+	constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, updateAfterNodes, monitor, instanceBindGroups = true, transforms = [] ) {
 
 		this.vertexShader = vertexShader;
 		this.fragmentShader = fragmentShader;
@@ -16,6 +16,8 @@ class NodeBuilderState {
 		this.updateBeforeNodes = updateBeforeNodes;
 		this.updateAfterNodes = updateAfterNodes;
 
+		this.monitor = monitor;
+
 		this.instanceBindGroups = instanceBindGroups;
 
 		this.usedTimes = 0;

+ 19 - 2
src/renderers/common/nodes/Nodes.js

@@ -190,6 +190,7 @@ class Nodes extends DataMap {
 			nodeBuilder.updateNodes,
 			nodeBuilder.updateBeforeNodes,
 			nodeBuilder.updateAfterNodes,
+			nodeBuilder.monitor,
 			nodeBuilder.instanceBindGroups,
 			nodeBuilder.transforms
 		);
@@ -334,11 +335,18 @@ class Nodes extends DataMap {
 
 				if ( fog.isFogExp2 ) {
 
-					fogNode = densityFog( reference( 'color', 'color', fog ), reference( 'density', 'float', fog ) );
+					const color = reference( 'color', 'color', fog ).setGroup( renderGroup );
+					const density = reference( 'density', 'float', fog ).setGroup( renderGroup );
+
+					fogNode = densityFog( color, density );
 
 				} else if ( fog.isFog ) {
 
-					fogNode = rangeFog( reference( 'color', 'color', fog ), reference( 'near', 'float', fog ), reference( 'far', 'float', fog ) );
+					const color = reference( 'color', 'color', fog ).setGroup( renderGroup );
+					const near = reference( 'near', 'float', fog ).setGroup( renderGroup );
+					const far = reference( 'far', 'float', fog ).setGroup( renderGroup );
+
+					fogNode = rangeFog( color, near, far );
 
 				} else {
 
@@ -501,6 +509,15 @@ class Nodes extends DataMap {
 
 	}
 
+	needsRefresh( renderObject ) {
+
+		const nodeFrame = this.getNodeFrameForRender( renderObject );
+		const monitor = renderObject.getMonitor();
+
+		return monitor.needsRefresh( renderObject, nodeFrame );
+
+	}
+
 	dispose() {
 
 		super.dispose();

+ 2 - 0
src/renderers/webgpu/utils/WebGPUTextureUtils.js

@@ -122,6 +122,8 @@ class WebGPUTextureUtils {
 		const dimension = this._getDimension( texture );
 		const format = texture.internalFormat || options.format || getFormat( texture, backend.device );
 
+		textureData.format = format;
+
 		let sampleCount = options.sampleCount !== undefined ? options.sampleCount : 1;
 
 		sampleCount = backend.utils.getSampleCount( sampleCount );

+ 1 - 1
src/renderers/webgpu/utils/WebGPUUtils.js

@@ -32,7 +32,7 @@ class WebGPUUtils {
 
 	getTextureFormatGPU( texture ) {
 
-		return this.backend.get( texture ).texture.format;
+		return this.backend.get( texture ).format;
 
 	}
 

粤ICP备19079148号