浏览代码

nodematerial webgpu

sunag 4 年之前
父节点
当前提交
86db129432

+ 9 - 9
examples/jsm/renderers/nodes/accessors/UVNode.js

@@ -9,28 +9,28 @@ class UVNode extends AttributeNode {
 		this.index = index;
 
 	}
-	
+
 	getIndexProperty( prefix ) {
-		
+
 		return prefix + ( this.index > 0 ? this.index + 1 : '' );
-		
+
 	}
-	
+
 	getAttributeName( /*builder*/ ) {
 
 		return this.getIndexProperty( 'uv' );
 
 	}
-	
+
 	getAttributeProperty( builder ) {
-		
+
 		// customize 'uv' property
 		const property = this.getIndexProperty( 'vUv' );
-		
+
 		this.setAttributeProperty( property );
-		
+
 		return super.getAttributeProperty( builder );
-		
+
 	}
 
 }

+ 19 - 19
examples/jsm/renderers/nodes/core/AttributeNode.js

@@ -10,43 +10,43 @@ class AttributeNode extends Node {
 		this.property = property;
 
 	}
-	
+
 	setAttributeName( name ) {
-		
+
 		this.name = name;
-		
+
 		return this;
-		
+
 	}
-	
+
 	getAttributeName( /*builder*/ ) {
-		
+
 		return this.name;
-		
+
 	}
-	
+
 	setAttributeProperty( name ) {
-		
+
 		this.property = name;
-		
+
 		return this;
-		
+
 	}
-	
+
 	getAttributeProperty( builder ) {
-		
+
 		const attribute = builder.getAttribute( this.getType( builder ), this.getAttributeName( builder ), this.property );
-		
+
 		return attribute.property;
-		
+
 	}
-	
+
 	generate( builder, output ) {
-		
+
 		const attributeProperty = this.getAttributeProperty( builder );
-		
+
 		return builder.format( attributeProperty, this.getType( builder ), output );
-		
+
 	}
 
 }

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

@@ -4,7 +4,7 @@ class Node {
 
 		this.type = type;
 
-		//this.onBeforeUpdate = function ( /*object, scene, camera, geometry, material, group*/ ) {};
+		this.needsUpdate = false;
 
 		Object.defineProperty( this, 'isNode', { value: true } );
 
@@ -16,9 +16,15 @@ class Node {
 
 	}
 
+	update( /*frame*/ ) {
+
+		console.warn( "Abstract function." );
+
+	}
+
 	generate( /*builder, output*/ ) {
 
-		console.warn( "Abstract function" );
+		console.warn( "Abstract function." );
 
 	}
 

+ 116 - 82
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -8,6 +8,10 @@ class NodeBuilder {
 		this.renderer = renderer;
 
 		this.nodes = [];
+		this.updateNodes = [];
+
+		this.vertexShader = null;
+		this.fragmentShader = null;
 
 		this.slots = { vertex: [], fragment: [] };
 		this.defines = { vertex: {}, fragment: {} };
@@ -22,13 +26,19 @@ class NodeBuilder {
 	}
 
 	addNode( node ) {
-		
-		if ( this.nodes.indexOf( node ) === -1 ) {
-			
+
+		if ( this.nodes.indexOf( node ) === - 1 ) {
+
+			if ( node.needsUpdate === true ) {
+
+				this.updateNodes.push( node );
+
+			}
+
 			this.nodes.push( node );
-			
+
 		}
-		
+
 	}
 
 	addSlot( shaderStage, slot ) {
@@ -43,48 +53,51 @@ class NodeBuilder {
 
 	}
 
-	getTexture( textureSnippet, uvSnippet ) {
-		
-		
-		
+	getTexture( textureProperty, uvSnippet ) {
+
+
+
 	}
 
 	getConst( type, value ) {
-		
+
 		if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
 		if ( type === 'vec2' ) return `vec2( ${value.x}, ${value.y} )`;
 		if ( type === 'vec3' ) return `vec3( ${value.x}, ${value.y}, ${value.z} )`;
 		if ( type === 'vec4' ) return `vec4( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
-		
+		if ( type === 'color' ) return `vec3( ${value.r}, ${value.g}, ${value.b} )`;
+
+		throw new Error(`Type '${type}' not found in generate constant attempt.`);
+
 	}
-	
+
 	getAttribute( type, name, property = null ) {
-		
+
 		let attribute = this.attributes[ name ];
-		
+
 		if ( attribute === undefined ) {
-			
-			const index = this.attributeCount++;
-			
+
+			const index = this.attributeCount ++;
+
 			if ( property === null ) {
-				
+
 				property = `node_A${index}`;
-				
+
 			}
-			
+
 			attribute = {
 				type,
 				name,
 				index,
 				property
 			};
-			
+
 			this.attributes[ name ] = attribute;
-			
+
 		}
-		
+
 		return attribute;
-		
+
 	}
 
 	getPropertyName( nodeUniform ) {
@@ -93,8 +106,30 @@ class NodeBuilder {
 
 	}
 
+	getVectorType( type ) {
+
+		if ( type === 'color' ) return 'vec3';
+		else if ( type === 'texture' ) return 'vec4';
+
+		return type;
+
+	}
+
+	getTypeFromLength( type ) {
+
+		if ( type === 1 ) return 'float';
+		if ( type === 2 ) return 'vec2';
+		if ( type === 3 ) return 'vec3';
+		if ( type === 4 ) return 'vec4';
+
+		return 0;
+
+	}
+
 	getTypeLength( type ) {
 
+		type = this.getVectorType( type );
+
 		if ( type === 'float' ) return 1;
 		if ( type === 'vec2' ) return 2;
 		if ( type === 'vec3' ) return 3;
@@ -176,80 +211,33 @@ class NodeBuilder {
 	}
 
 	getAttributesBodySnippet( /*shaderStage*/ ) {
-		
-		
-		
+
+
+
 	}
 
 	getAttributesHeaderSnippet( /*shaderStage*/ ) {
-		
-		
-		
-	}
 
-	getUniformsHeaderSnippet( shaderStage ) {
-		
-		const uniforms = this.uniforms[ shaderStage ];
-		
-		let snippet = '';
-			
-		for ( let uniform of uniforms ) {			
 
-			snippet += `${uniform.type} ${uniform.name}; `;
 
-		}
-		
-		return snippet;
-		
 	}
 
-	build() {
+	getUniformsHeaderSnippet( shaderStage ) {
 
-		const shaderStages = [ 'vertex', 'fragment' ];
-		const shaderData = {};
+		const uniforms = this.uniforms[ shaderStage ];
 
-		for ( let shaderStage of shaderStages ) {
-			
-			this.shaderStage = shaderStage;
-			
-			let slots = this.slots[ shaderStage ];
-			
-			for ( let slot of slots ) {
+		let snippet = '';
 
-				let flowData = this.flowNode( slot.node, slot.output );
+		for ( let uniform of uniforms ) {
 
-				this.define( shaderStage, `NODE_${slot.name}`, flowData.result );
+			snippet += `${uniform.type} ${uniform.name}; `;
 
-			}
-			
 		}
 
-		for ( let shaderStage of shaderStages ) {
-
-			this.shaderStage = shaderStage;
-
-			this.define( shaderStage, 'NODE_HEADER_UNIFORMS', this.getUniformsHeaderSnippet( shaderStage ) );
-			this.define( shaderStage, 'NODE_HEADER_ATTRIBUTES', this.getAttributesHeaderSnippet( shaderStage ) );
-			this.define( shaderStage, 'NODE_BODY_ATTRIBUTES', this.getAttributesBodySnippet( shaderStage ) );
-			
-			shaderData[ shaderStage ] = this._buildDefines( shaderStage );
-			
-		}
-		
-		this.shaderStage = null;
-		
-		return shaderData;
+		return snippet;
 
 	}
-	
-	getVectorType( type ) {
-		
-		if ( type === 'texture' ) return 'vec4';
-		
-		return type;
-		
-	}
-	
+
 	format( snippet, fromType, toType ) {
 
 		fromType = this.getVectorType( fromType );
@@ -270,7 +258,7 @@ class NodeBuilder {
 			case 'vec3 to float' : return `${snippet}.x`;
 			case 'vec3 to vec2' : return `${snippet}.xy`;
 			case 'vec3 to vec4' : return `vec4( ${snippet}.x, ${snippet}.y, ${snippet}.z, 1.0 )`;
-			
+
 			case 'vec4 to float' : return `${snippet}.x`;
 			case 'vec4 to vec2' : return `${snippet}.xy`;
 			case 'vec4 to vec3' : return `${snippet}.xyz`;
@@ -281,6 +269,52 @@ class NodeBuilder {
 
 	}
 
+	getHash() {
+
+		return this.vertexShader + this.fragmentShader;
+
+	}
+
+	build() {
+
+		const shaderStages = [ 'vertex', 'fragment' ];
+		const shaderData = {};
+
+		for ( let shaderStage of shaderStages ) {
+
+			this.shaderStage = shaderStage;
+
+			let slots = this.slots[ shaderStage ];
+
+			for ( let slot of slots ) {
+
+				let flowData = this.flowNode( slot.node, slot.output );
+
+				this.define( shaderStage, `NODE_${slot.name}`, flowData.result );
+
+			}
+
+		}
+
+		this.shaderStage = null;
+
+		for ( let shaderStage of shaderStages ) {
+
+			this.define( shaderStage, 'NODE_HEADER_UNIFORMS', this.getUniformsHeaderSnippet( shaderStage ) );
+			this.define( shaderStage, 'NODE_HEADER_ATTRIBUTES', this.getAttributesHeaderSnippet( shaderStage ) );
+			this.define( shaderStage, 'NODE_BODY_ATTRIBUTES', this.getAttributesBodySnippet( shaderStage ) );
+
+			shaderData[ shaderStage ] = this._buildDefines( shaderStage );
+
+		}
+
+		this.vertexShader = shaderData.vertex;
+		this.fragmentShader = shaderData.fragment;
+
+		return this;
+
+	}
+
 }
 
 export default NodeBuilder;

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

@@ -11,18 +11,18 @@ class TextureNode extends InputNode {
 		this.uv = uv;
 
 	}
-	
+
 	generate( builder, output ) {
-	
+
 		const type = this.getType( builder );
-	
-		const textureSnippet = super.generate( builder, type );
+
+		const textureProperty = super.generate( builder, type );
 		const uvSnippet = this.uv.build( builder, 'vec2' );
 
-		const textureCallSnippet = builder.getTexture( textureSnippet, uvSnippet );
+		const textureCallSnippet = builder.getTexture( textureProperty, uvSnippet );
 
 		return builder.format( textureCallSnippet, type, output );
-		
+
 	}
 
 }

+ 2 - 2
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -18,9 +18,9 @@ class OperatorNode extends Node {
 		const typeA = this.a.getType( builder );
 		const typeB = this.b.getType( builder );
 
-		if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
+		// use the greater length vector
 
-			// use the greater length vector
+		if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
 
 			return typeB;
 

+ 6 - 10
examples/jsm/renderers/nodes/utils/TimerNode.js

@@ -6,18 +6,14 @@ class TimerNode extends FloatNode {
 
 		super();
 
-		this.time = performance.now();
+		this.needsUpdate = true;
 
 	}
-	
-	update() {
-		
-		const time = performance.now();
-		
-		this.value += ( time - this.time ) / 1000;
-		
-		this.time = time;
-		
+
+	update( frame ) {
+
+		this.value = frame.time;
+
 	}
 
 }

+ 7 - 81
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -5,7 +5,7 @@ import { WebGPUSampledTexture } from './WebGPUSampledTexture.js';
 
 class WebGPUBindings {
 
-	constructor( device, info, properties, textures, pipelines, computePipelines, attributes ) {
+	constructor( device, info, properties, textures, pipelines, computePipelines, attributes, nodes ) {
 
 		this.device = device;
 		this.info = info;
@@ -14,6 +14,7 @@ class WebGPUBindings {
 		this.pipelines = pipelines;
 		this.computePipelines = computePipelines;
 		this.attributes = attributes;
+		this.nodes = nodes;
 
 		this.uniformsData = new WeakMap();
 
@@ -33,27 +34,12 @@ class WebGPUBindings {
 
 			const pipeline = this.pipelines.get( object );
 			const material = object.material;
-			let bindings;
 
-			// each material defines an array of bindings (ubos, textures, samplers etc.)
-
-			if ( material.isMeshBasicMaterial ) {
-
-				bindings = this._getMeshBasicBindings( object );
-
-			} else if ( material.isPointsMaterial ) {
-
-				bindings = this._getPointsBasicBindings( object );
-
-			} else if ( material.isLineBasicMaterial ) {
-
-				bindings = this._getLinesBasicBindings();
-
-			} else {
+			const nodeBuilder = this.nodes.get( material );
 
-				console.error( 'THREE.WebGPURenderer: Unknwon shader type.' );
+			// each material defines an array of bindings (ubos, textures, samplers etc.)
 
-			}
+			let bindings = this.composeBindings( object, nodeBuilder.getBindings( 'fragment' ) );
 
 			// setup (static) binding layout and (dynamic) binding group
 
@@ -276,7 +262,7 @@ class WebGPUBindings {
 
 	}
 
-	_getMeshBasicBindings( object ) {
+	composeBindings( object, uniforms ) {
 
 		let bindings = [];
 
@@ -308,68 +294,8 @@ class WebGPUBindings {
 
 		bindings.push( modelGroup );
 		bindings.push( cameraGroup );
-		bindings = bindings.concat( this.pipelines.getBindings( object ) );
-
-		return bindings;
-
-	}
-
-	_getPointsBasicBindings( object ) {
-
-		let bindings = [];
-
-		// UBOs
-
-		const modelViewUniform = new Matrix4Uniform( 'modelMatrix' );
-		const modelViewMatrixUniform = new Matrix4Uniform( 'modelViewMatrix' );
-
-		const modelGroup = new WebGPUUniformsGroup( 'modelUniforms' );
-		modelGroup.addUniform( modelViewUniform );
-		modelGroup.addUniform( modelViewMatrixUniform );
-		modelGroup.setOnBeforeUpdate( function ( object/*, camera */ ) {
-
-			modelViewUniform.setValue( object.matrixWorld );
-			modelViewMatrixUniform.setValue( object.modelViewMatrix );
-
-		} );
-
-		const cameraGroup = this.sharedUniformsGroups.get( 'cameraUniforms' );
-
-		//
-
-		bindings.push( modelGroup );
-		bindings.push( cameraGroup );
-		bindings = bindings.concat( this.pipelines.getBindings( object ) );
-
-		return bindings;
-
-	}
-
-	_getLinesBasicBindings() {
-
-		const bindings = [];
-
-		// UBOs
-
-		const modelViewUniform = new Matrix4Uniform( 'modelMatrix' );
-		const modelViewMatrixUniform = new Matrix4Uniform( 'modelViewMatrix' );
-
-		const modelGroup = new WebGPUUniformsGroup( 'modelUniforms' );
-		modelGroup.addUniform( modelViewUniform );
-		modelGroup.addUniform( modelViewMatrixUniform );
-		modelGroup.setOnBeforeUpdate( function ( object/*, camera */ ) {
-
-			modelViewUniform.setValue( object.matrixWorld );
-			modelViewMatrixUniform.setValue( object.modelViewMatrix );
-
-		} );
-
-		const cameraGroup = this.sharedUniformsGroups.get( 'cameraUniforms' );
-
-		//
 
-		bindings.push( modelGroup );
-		bindings.push( cameraGroup );
+		bindings.push( ... uniforms );
 
 		return bindings;
 

+ 48 - 61
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -9,28 +9,23 @@ import {
 	ZeroFactor, OneFactor, SrcColorFactor, OneMinusSrcColorFactor, SrcAlphaFactor, OneMinusSrcAlphaFactor, DstAlphaFactor, OneMinusDstAlphaFactor, DstColorFactor, OneMinusDstColorFactor, SrcAlphaSaturateFactor
 } from '../../../../build/three.module.js';
 
-import ShaderLib from './ShaderLib.js';
-
-import WebGPUNodeBuilder from './nodes/WebGPUNodeBuilder.js';
-
 class WebGPURenderPipelines {
 
-	constructor( renderer, properties, device, glslang, sampleCount ) {
+	constructor( renderer, properties, device, glslang, sampleCount, nodes ) {
 
 		this.renderer = renderer;
 		this.properties = properties;
 		this.device = device;
 		this.glslang = glslang;
 		this.sampleCount = sampleCount;
-
-		this.nodes = new WeakMap();
-		this.bindings = new WeakMap();
+		this.nodes = nodes;
 
 		this.pipelines = new WeakMap();
 		this.shaderAttributes = new WeakMap();
+		
 		this.shaderModules = {
-			vertex: new WeakMap(),
-			fragment: new WeakMap()
+			vertex: new Map(),
+			fragment: new Map()
 		};
 
 	}
@@ -56,77 +51,60 @@ class WebGPURenderPipelines {
 		if ( pipeline === undefined ) {
 
 			const device = this.device;
-			const material = object.material;
-
-			// shader source
-
-			let shader;
-
-			if ( material.isMeshBasicMaterial ) {
-
-				shader = ShaderLib.meshBasic;
-
-			} else if ( material.isPointsMaterial ) {
-
-				shader = ShaderLib.pointsBasic;
+			const properties = this.properties;
 
-			} else if ( material.isLineBasicMaterial ) {
-
-				shader = ShaderLib.lineBasic;
-
-			} else {
-
-				console.error( 'THREE.WebGPURenderer: Unknwon shader type.' );
-
-			}
-
-			// parse nodes
-
-			const nodeBuilder = new WebGPUNodeBuilder( material, this.renderer );
-
-			shader = nodeBuilder.parse( shader.vertexShader, shader.fragmentShader );
+			const material = object.material;
 
-			this.nodes.set( material, nodeBuilder.nodes );
+			// get shader
 
-			this.bindings.set( object, nodeBuilder.getBindings( 'fragment' ) );
+			const nodeBuilder = this.nodes.get( material );
 
 			// shader modules
 
 			const glslang = this.glslang;
 
-			let moduleVertex = this.shaderModules.vertex.get( shader );
+			let moduleVertex = this.shaderModules.vertex.get( nodeBuilder.vertexShader );
 
 			if ( moduleVertex === undefined ) {
 
-				const byteCodeVertex = glslang.compileGLSL( shader.vertexShader, 'vertex' );
+				const byteCodeVertex = glslang.compileGLSL( nodeBuilder.vertexShader, 'vertex' );
 
 				moduleVertex = {
 					module: device.createShaderModule( { code: byteCodeVertex } ),
 					entryPoint: 'main'
 				};
 
-				this.shaderModules.vertex.set( shader, moduleVertex );
+				this.shaderModules.vertex.set( nodeBuilder.vertexShader, moduleVertex );
 
 			}
 
-			let moduleFragment = this.shaderModules.fragment.get( shader );
+			let moduleFragment = this.shaderModules.fragment.get( nodeBuilder.fragmentShader );
 
 			if ( moduleFragment === undefined ) {
 
-				const byteCodeFragment = glslang.compileGLSL( shader.fragmentShader, 'fragment' );
+				const byteCodeFragment = glslang.compileGLSL( nodeBuilder.fragmentShader, 'fragment' );
 
 				moduleFragment = {
 					module: device.createShaderModule( { code: byteCodeFragment } ),
 					entryPoint: 'main'
 				};
 
-				this.shaderModules.fragment.set( shader, moduleFragment );
+				this.shaderModules.fragment.set( nodeBuilder.fragmentShader, moduleFragment );
 
 			}
 
+			// dispose material
+
+			const materialProperties = properties.get( material );
+
+			const disposeCallback = onMaterialDispose.bind( this );
+			materialProperties.disposeCallback = disposeCallback;
+
+			material.addEventListener( 'dispose', onMaterialDispose.bind( this ) );
+
 			// determine shader attributes
 
-			const shaderAttributes = this._parseShaderAttributes( shader.vertexShader );
+			const shaderAttributes = this._parseShaderAttributes( nodeBuilder.vertexShader );
 
 			// vertex buffers
 
@@ -231,18 +209,6 @@ class WebGPURenderPipelines {
 
 	}
 
-	getNodes( material ) {
-
-		return this.nodes.get( material );
-
-	}
-
-	getBindings( object ) {
-
-		return this.bindings.get( object );
-
-	}
-
 	getShaderAttributes( pipeline ) {
 
 		return this.shaderAttributes.get( pipeline );
@@ -254,8 +220,8 @@ class WebGPURenderPipelines {
 		this.pipelines = new WeakMap();
 		this.shaderAttributes = new WeakMap();
 		this.shaderModules = {
-			vertex: new WeakMap(),
-			fragment: new WeakMap()
+			vertex: new Map(),
+			fragment: new Map()
 		};
 
 	}
@@ -819,4 +785,25 @@ class WebGPURenderPipelines {
 
 }
 
+function onMaterialDispose( event ) {
+
+	const properties = this.properties;
+	const nodes = this.nodes;
+	const shaderModules = this.shaderModules;
+
+	const material = event.target;
+	const nodeBuilder = nodes.get( material );
+
+	material.removeEventListener( 'dispose', onMaterialDispose );
+
+	properties.remove( material );
+	nodes.remove( material );
+
+	shaderModules.vertex.delete( nodeBuilder.vertexShader );
+	shaderModules.fragment.delete( nodeBuilder.fragmentShader );
+
+	// @TODO: need implement pipeline
+
+}
+
 export default WebGPURenderPipelines;

+ 9 - 3
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -178,10 +178,10 @@ class WebGPURenderer {
 		this._geometries = new WebGPUGeometries( this._attributes, this._info );
 		this._textures = new WebGPUTextures( device, this._properties, this._info, compiler );
 		this._objects = new WebGPUObjects( this._geometries, this._info );
-		this._renderPipelines = new WebGPURenderPipelines( this, this._properties, device, compiler, parameters.sampleCount );
+		this._nodes = new WebGPUNodes( this );
+		this._renderPipelines = new WebGPURenderPipelines( this, this._properties, device, compiler, parameters.sampleCount, this._nodes );
 		this._computePipelines = new WebGPUComputePipelines( device, compiler );
-		this._nodes = new WebGPUNodes()
-		this._bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes );
+		this._bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes );
 		this._renderLists = new WebGPURenderLists();
 		this._background = new WebGPUBackground( this );
 
@@ -205,6 +205,12 @@ class WebGPURenderer {
 
 	render( scene, camera ) {
 
+		// @TODO: move this to animation loop
+
+		this._nodes.updateFrame();
+
+		//
+
 		if ( scene.autoUpdate === true ) scene.updateMatrixWorld();
 
 		if ( camera.parent === null ) camera.updateMatrixWorld();

+ 108 - 82
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -1,11 +1,13 @@
 import WebGPUNodeUniformsGroup from './WebGPUNodeUniformsGroup.js';
-import { FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform } from './WebGPUNodeUniform.js';
+import { FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, ColorNodeUniform } from './WebGPUNodeUniform.js';
 import WebGPUSampler from '../WebGPUSampler.js';
 import { WebGPUSampledTexture } from '../WebGPUSampledTexture.js';
 
 import NodeSlot from '../../nodes/core/NodeSlot.js';
 import NodeBuilder from '../../nodes/core/NodeBuilder.js';
 
+import ShaderLib from './ShaderLib.js';
+
 class WebGPUNodeBuilder extends NodeBuilder {
 
 	constructor( material, renderer ) {
@@ -19,7 +21,9 @@ class WebGPUNodeBuilder extends NodeBuilder {
 		this.varyIndex = 0;
 
 		this.uniformsGroup = {};
-		
+
+		this.nativeShader = null;
+
 		this._parseMaterial();
 
 	}
@@ -28,11 +32,33 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 		const material = this.material;
 
-		if ( material.isMeshBasicMaterial || material.isPointsMaterial ) {
+		// get shader
+
+		if ( material.isMeshBasicMaterial ) {
+
+			this.nativeShader = ShaderLib.meshBasic;
+
+		} else if ( material.isPointsMaterial ) {
+
+			this.nativeShader = ShaderLib.pointsBasic;
+
+		} else if ( material.isLineBasicMaterial ) {
+
+			this.nativeShader = ShaderLib.lineBasic;
+
+		} else {
+
+			console.error( 'THREE.WebGPURenderer: Unknwon shader type.' );
+
+		}
+
+		// parse inputs
+
+		if ( material.isMeshBasicMaterial || material.isPointsMaterial || material.isLineBasicMaterial ) {
 
 			if ( material.colorNode !== undefined ) {
 
-				this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec3' ) );
+				this.addSlot( 'fragment', new NodeSlot( material.colorNode, 'COLOR', 'vec4' ) );
 
 			}
 
@@ -46,10 +72,10 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	}
 
-	getTexture( textureSnippet, uvSnippet ) {
-		
-		return `texture( sampler2D( ${textureSnippet}, ${textureSnippet}_sampler ), ${uvSnippet} )`;
-		
+	getTexture( textureProperty, uvSnippet ) {
+
+		return `texture( sampler2D( ${textureProperty}, ${textureProperty}_sampler ), ${uvSnippet} )`;
+
 	}
 
 	getPropertyName( nodeUniform ) {
@@ -57,11 +83,11 @@ class WebGPUNodeBuilder extends NodeBuilder {
 		if ( nodeUniform.type === 'texture' ) {
 
 			return nodeUniform.name;
-			
+
 		} else {
-			
+
 			return `nodeUniforms.${nodeUniform.name}`;
-			
+
 		}
 
 	}
@@ -77,19 +103,25 @@ class WebGPUNodeBuilder extends NodeBuilder {
 		const uniformNode = super.getUniformFromNode( node, shaderStage, type );
 		const nodeData = this.getDataFromNode( node, shaderStage );
 
-		if ( nodeData.webgpuUniform === undefined ) {
+		if ( nodeData.uniformGPU === undefined ) {
+
+			let uniformGPU;
 
-			let uniform;
+			const bindings = this.bindings[ shaderStage ];
 
 			if ( type === 'texture' ) {
-				
+
 				const sampler = new WebGPUSampler( `${uniformNode.name}_sampler`, uniformNode.value );
 				const texture = new WebGPUSampledTexture( uniformNode.name, uniformNode.value );
-				
-				// Array.unshift: add first textures in sequence
-				
-				this.bindings[ shaderStage ].unshift( sampler, texture );
-				
+
+				// add first textures in sequence and group for last
+				const lastBinding = bindings[ bindings.length - 1 ];
+				const index = lastBinding && lastBinding.isUniformsGroup ? bindings.length - 1 : bindings.length;
+
+				bindings.splice( index, 0, sampler, texture );
+
+				uniformGPU = { sampler, texture };
+
 			} else {
 
 				let uniformsGroup = this.uniformsGroup[ shaderStage ];
@@ -100,25 +132,29 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 					this.uniformsGroup[ shaderStage ] = uniformsGroup;
 
-					this.bindings[ shaderStage ].push( uniformsGroup );
+					bindings.push( uniformsGroup );
 
 				}
 
 				if ( type === 'float' ) {
 
-					uniform = new FloatNodeUniform( uniformNode );
+					uniformGPU = new FloatNodeUniform( uniformNode );
 
 				} else if ( type === 'vec2' ) {
 
-					uniform = new Vector2NodeUniform( uniformNode );
+					uniformGPU = new Vector2NodeUniform( uniformNode );
 
 				} else if ( type === 'vec3' ) {
 
-					uniform = new Vector3NodeUniform( uniformNode );
+					uniformGPU = new Vector3NodeUniform( uniformNode );
 
 				} else if ( type === 'vec4' ) {
 
-					uniform = new Vector4NodeUniform( uniformNode );
+					uniformGPU = new Vector4NodeUniform( uniformNode );
+
+				} else if ( type === 'color' ) {
+
+					uniformGPU = new ColorNodeUniform( uniformNode );
 
 				} else {
 
@@ -126,11 +162,11 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 				}
 
-				uniformsGroup.addUniform( uniform );
+				uniformsGroup.addUniform( uniformGPU );
 
 			}
-			
-			nodeData.webgpuUniform = uniform;
+
+			nodeData.uniformGPU = uniformGPU;
 
 		}
 
@@ -139,45 +175,45 @@ class WebGPUNodeBuilder extends NodeBuilder {
 	}
 
 	getAttributesHeaderSnippet( shaderStage ) {
-		
+
 		let snippet = '';
-		
+
 		const attributes = this.attributes;
-		
+
 		let attributeIndex = this.attributeIndex;
 		let varyIndex = this.varyIndex;
 
-		for ( let name in attributes ) {			
+		for ( let name in attributes ) {
 
 			let attribute = attributes[ name ];
-			
+
 			let type = attribute.type;
 			let property = attribute.property;
 
 			if ( shaderStage === 'vertex' ) {
-				
-				snippet += `layout(location = ${attributeIndex++}) in ${type} ${name};`;
-				snippet += `layout(location = ${varyIndex++}) out ${type} ${property};`;
-				
+
+				snippet += `layout(location = ${attributeIndex ++}) in ${type} ${name};`;
+				snippet += `layout(location = ${varyIndex ++}) out ${type} ${property};`;
+
 			} else if ( shaderStage === 'fragment' ) {
-				
-				snippet += `layout(location = ${varyIndex++}) in ${type} ${property};`;
-				
+
+				snippet += `layout(location = ${varyIndex ++}) in ${type} ${property};`;
+
 			}
 
 		}
-		
+
 		return snippet;
-		
+
 	}
 
 	getAttributesBodySnippet( shaderStage ) {
-		
+
 		let snippet = '';
-		
+
 		const attributes = this.attributes;
-		
-		for ( let name in attributes ) {			
+
+		for ( let name in attributes ) {
 
 			let attribute = attributes[ name ];
 
@@ -186,55 +222,48 @@ class WebGPUNodeBuilder extends NodeBuilder {
 			snippet += `${property} = ${name};`;
 
 		}
-		
+
 		return snippet;
-		
+
 	}
-	
 
 	getUniformsHeaderSnippet( shaderStage ) {
-		
+
 		const uniforms = this.uniforms[ shaderStage ];
-		
+
 		let snippet = '';
 		let groupSnippet = '';
-		
+
 		let bindingIndex = this.bindingIndex;
 
 		for ( let uniform of uniforms ) {
 
-			if (uniform.type === 'texture') {
+			if ( uniform.type === 'texture' ) {
 
-				snippet += `layout(set = 0, binding = ${bindingIndex++}) uniform sampler ${uniform.name}_sampler;`;
-				snippet += `layout(set = 0, binding = ${bindingIndex++}) uniform texture2D ${uniform.name};`;
+				snippet += `layout(set = 0, binding = ${bindingIndex ++}) uniform sampler ${uniform.name}_sampler;`;
+				snippet += `layout(set = 0, binding = ${bindingIndex ++}) uniform texture2D ${uniform.name};`;
 
 			} else {
-		
-				if (!groupSnippet) {
-					
-					groupSnippet = `layout(set = 0, binding = ${bindingIndex++}) uniform NodeUniforms {`;
-					
-				}
-				
-				groupSnippet += `uniform ${uniform.type} ${uniform.name};`;
-				
+
+				let vectorType = this.getVectorType( uniform.type );
+
+				groupSnippet += `uniform ${vectorType} ${uniform.name};`;
+
 			}
 
 		}
-		
-		if (groupSnippet) {
-			
-			groupSnippet += `} nodeUniforms;`;
-			
-			snippet += groupSnippet;
-			
+
+		if ( groupSnippet ) {
+
+			snippet += `layout(set = 0, binding = ${bindingIndex ++}) uniform NodeUniforms { ${groupSnippet} } nodeUniforms;`;
+
 		}
-		
+
 		return snippet;
-		
+
 	}
 
-	buildShader( code, snippet ) {
+	composeShaderCode( code, snippet ) {
 
 		// use regex maybe for security?
 		const versionStrIndex = code.indexOf( "\n" );
@@ -249,17 +278,14 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	}
 
-	parse( vertexShader, fragmentShader ) {
-		
-		const shader = this.build();
+	build() {
+
+		super.build();
 
-		vertexShader = this.buildShader( vertexShader, shader.vertex );
-		fragmentShader = this.buildShader( fragmentShader, shader.fragment );
+		this.vertexShader = this.composeShaderCode( this.nativeShader.vertexShader, this.vertexShader );
+		this.fragmentShader = this.composeShaderCode( this.nativeShader.fragmentShader, this.fragmentShader );
 
-		return {
-			vertexShader,
-			fragmentShader
-		};
+		return this;
 
 	}
 

+ 27 - 27
examples/jsm/renderers/webgpu/nodes/WebGPUNodeUniform.js

@@ -1,6 +1,6 @@
-import { 
-	FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform, 
-	ColorUniform, Matrix3Uniform, Matrix4Uniform 
+import {
+	FloatUniform, Vector2Uniform, Vector3Uniform, Vector4Uniform,
+	ColorUniform, Matrix3Uniform, Matrix4Uniform
 } from '../WebGPUUniform.js';
 
 class FloatNodeUniform extends FloatUniform {
@@ -12,11 +12,11 @@ class FloatNodeUniform extends FloatUniform {
 		this.nodeUniform = nodeUniform;
 
 	}
-	
+
 	getValue() {
-		
+
 		return this.nodeUniform.value;
-		
+
 	}
 
 }
@@ -30,11 +30,11 @@ class Vector2NodeUniform extends Vector2Uniform {
 		this.nodeUniform = nodeUniform;
 
 	}
-	
+
 	getValue() {
-		
+
 		return this.nodeUniform.value;
-		
+
 	}
 
 }
@@ -48,11 +48,11 @@ class Vector3NodeUniform extends Vector3Uniform {
 		this.nodeUniform = nodeUniform;
 
 	}
-	
+
 	getValue() {
-		
+
 		return this.nodeUniform.value;
-		
+
 	}
 
 }
@@ -66,11 +66,11 @@ class Vector4NodeUniform extends Vector4Uniform {
 		this.nodeUniform = nodeUniform;
 
 	}
-	
+
 	getValue() {
-		
+
 		return this.nodeUniform.value;
-		
+
 	}
 
 }
@@ -84,11 +84,11 @@ class ColorNodeUniform extends ColorUniform {
 		this.nodeUniform = nodeUniform;
 
 	}
-	
+
 	getValue() {
-		
+
 		return this.nodeUniform.value;
-		
+
 	}
 
 }
@@ -102,11 +102,11 @@ class Matrix3NodeUniform extends Matrix3Uniform {
 		this.nodeUniform = nodeUniform;
 
 	}
-	
+
 	getValue() {
-		
+
 		return this.nodeUniform.value;
-		
+
 	}
 
 }
@@ -120,16 +120,16 @@ class Matrix4NodeUniform extends Matrix4Uniform {
 		this.nodeUniform = nodeUniform;
 
 	}
-	
+
 	getValue() {
-		
+
 		return this.nodeUniform.value;
-		
+
 	}
 
 }
 
-export { 
-	FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, 
-	ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform 
+export {
+	FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform,
+	ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform
 };

+ 1 - 1
examples/jsm/renderers/webgpu/nodes/WebGPUNodeUniformsGroup.js

@@ -1,4 +1,4 @@
-import WebGPUUniformsGroup from '../WebGPUUniformsGroup.js'
+import WebGPUUniformsGroup from '../WebGPUUniformsGroup.js';
 
 class WebGPUNodeUniformsGroup extends WebGPUUniformsGroup {
 

+ 40 - 10
examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js

@@ -1,35 +1,65 @@
+import WebGPUNodeBuilder from './WebGPUNodeBuilder.js';
+import NodeFrame from '../../nodes/core/NodeFrame.js';
 
 class WebGPUNodes {
 
-	constructor( ) {
+	constructor( renderer ) {
 
-		this.uniformsData = new WeakMap();
+		this.renderer = renderer;
+
+		this.nodeFrame = new NodeFrame();
+
+		this.builders = new WeakMap();
 
 	}
 
-	get( object ) {
+	get( material ) {
+
+		let nodeBuilder = this.builders.get( material );
 
-		let data = this.uniformsData.get( object );
+		if ( nodeBuilder === undefined ) {
 
-		if ( data === undefined ) {
-			
-			this.uniformsData.set( object, data );
+			nodeBuilder = new WebGPUNodeBuilder( material, this.renderer ).build();
+
+			this.builders.set( material, nodeBuilder );
 
 		}
 
-		return data;
+		return nodeBuilder;
+
+	}
+
+	remove( object ) {
+
+		this.builders.delete( object );
+
+	}
+
+	updateFrame() {
+
+		this.nodeFrame.update();
 
 	}
 
 	update( object, camera ) {
 
-		
+		const material = object.material;
+
+		const nodeBuilder = this.get( material );
+
+		this.nodeFrame.material = object.material;
+
+		for ( let node of nodeBuilder.updateNodes ) {
+
+			this.nodeFrame.updateNode( node );
+
+		}
 
 	}
 
 	dispose() {
 
-		this.uniformsData = new WeakMap();
+		this.builders = new WeakMap();
 
 	}