Przeglądaj źródła

init implement uniforms and auto type convertion

sunag 4 lat temu
rodzic
commit
bc1f1ec709

+ 25 - 7
examples/jsm/renderers/nodes/core/InputNode.js

@@ -5,15 +5,28 @@ class InputNode extends Node {
 	constructor( type ) {
 		
 		super( type );
+
+		this.constant = false;
+		
+		Object.defineProperty( this, 'isInputNode', { value: true } );
+		
+	}
+	
+	setConst( value ) {
 		
-		this.isInputNode = true;
+		this.constant = value;
 		
-		// force constant for now
-		this.constant = true;
+		return this;
 		
 	}
 	
-	generateConst( builder, output ) {
+	getConst() {
+		
+		return this.constant;
+		
+	}
+	
+	generateConst( builder ) {
 		
 		console.warn("Abstract function");
 		
@@ -21,13 +34,18 @@ class InputNode extends Node {
 	
 	generate( builder, output ) {
 		
-		if ( this.constant ) {
+		const type = this.getType( builder );
 		
-			return builder.format( this.generateConst( builder, output ), output );
+		if ( this.constant === true ) {
+		
+			return builder.format( this.generateConst( builder ), type, output );
 			
 		} else {
 			
-			builder.createUniformFromNode( node );
+			const nodeUniform = builder.createUniformFromNode( this, builder.shaderStage, this.getType( builder ) );
+			const nsName = builder.getUniformNSName( nodeUniform );
+			
+			return builder.format( nsName, type, output );
 			
 		}
 		

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

@@ -4,7 +4,7 @@ class Node {
 		
 		this.type = type;
 		
-		this.isNode = true;
+		Object.defineProperty( this, 'isNode', { value: true } );
 		
 	}
 	

+ 101 - 54
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -1,21 +1,27 @@
+import NodeUniform from './NodeUniform.js';
+
 const VERSION = '1';
 
 class NodeBuilder {
 
-	constructor() {
+	constructor( material, renderer ) {
+		
+		this.material = material;
+		this.renderer = renderer;
 		
 		this.slots = { vertex: [], fragment: [] };
 		this.defines = { vertex: {}, fragment: {} };
+		this.uniforms = { vertex: [], fragment: [] };
 		
 		this.nodesData = new WeakMap();
 		
-		this.shader = undefined;
+		this.shaderStage = null;
 		
 	}
 	
-	setMaterial( material ) {
+	setShaderStage( shaderStage ) {
 		
-		this.material = material;
+		this.shaderStage = shaderStage;
 		
 	}
 	
@@ -25,9 +31,15 @@ class NodeBuilder {
 		
 	}
 	
-	addDefine( shader, name, value ) {
+	addDefine( shader, name, value = '' ) {
+		
+		this.defines[ shader ][ name ] = value;
+		
+	}
+	
+	generateVec2( x, y ) {
 		
-		this.defines[ shader ][ name ] = value || '';
+		return `vec2( ${x}, ${y})`;
 		
 	}
 	
@@ -37,21 +49,71 @@ class NodeBuilder {
 		
 	}
 	
+	generateVec4( x, y, z, w ) {
+		
+		return `vec4( ${x}, ${y}, ${z}, ${w} )`;
+		
+	}
+	
 	generateFloat( value ) {
 		
 		return value + ( value % 1 ? '' : '.0' );
 		
 	}
-	/*
-	createDataFromNode( node ) {
+	
+	getUniformNSName( nodeUniform ) {
 		
-		return this.nodesData[ node ] = this.nodesData[ node ] || {};
+		return nodeUniform.name;
 		
 	}
-	*/
-	createUniformFromNode( node ) {
+	
+	getTypeLength( type ) {
+
+		if ( type === 'float' ) return 1;
+		else if ( type === 'vec2' ) return 2;
+		else if ( type === 'vec3' ) return 3;
+		else if ( type === 'vec4' ) return 3;
+
+		return 0;
+
+	}
+	
+	createDataFromNode( node, shaderStage = null ) {
+		
+		let nodeData = this.nodesData.get( node );
+		
+		if ( nodeData === undefined ) {
+			
+			nodeData = { vertex: {}, fragment: {} };
+			
+			this.nodesData.set( node, nodeData );
+			
+		}
 		
+		return shaderStage ? nodeData[ shaderStage ] : nodeData;
 		
+	}
+	
+	createUniformFromNode( node, shaderStage, type ) {
+		
+		const nodeData = this.createDataFromNode( node, shaderStage );
+		
+		let nodeUniform = nodeData.uniform;
+		
+		if ( nodeUniform === undefined ) {
+			
+			const uniforms = this.uniforms[shaderStage];
+			const index = uniforms.length;
+			
+			nodeUniform = new NodeUniform( 'nodeU' + index, type, node );
+			
+			uniforms.push( nodeUniform );
+			
+			nodeData.uniform = nodeUniform;
+			
+		}
+		
+		return nodeUniform;
 		
 	}
 	/*
@@ -77,7 +139,7 @@ class NodeBuilder {
 		
 		for ( let name in defines ) {
 			
-			code += `#define NODE_${name} ${defines[name]}\n`;
+			code += `#define ${name} ${defines[name]}\n`;
 			
 		}
 		
@@ -85,27 +147,42 @@ class NodeBuilder {
 		
 	}
 	
-	build( shader ) {
+	build( shaderStage ) {
+		
+		this.setShaderStage( shaderStage );
 		
-		const slots = this.slots[ shader ];
+		const slots = this.slots[ shaderStage ];
+		const uniforms = this.uniforms[ shaderStage ];
 		
 		if ( slots.length ) {
 			
-			this.addDefine( shader, 'NODE', VERSION );
+			this.addDefine( shaderStage, 'NODE', VERSION );
 			
 			for( let i = 0; i < slots.length; i++) {
 			
 				let slot = slots[i];
 				
-				let flowData = this.flowNode( slot.node );
+				let flowData = this.flowNode( slot.node, slot.output );
 				
-				this.addDefine( shader, slot.name, flowData.result );
+				this.addDefine( shaderStage, `NODE_${slot.name}`, flowData.result );
 				
 			}
 			
+			let uniformsCode = '';
+			
+			for( let i = 0; i < uniforms.length; i++) {
+				
+				let uniform = uniforms[i];
+				
+				uniformsCode += `${uniform.type} ${uniform.name}; `;
+				
+			}
+			
+			this.addDefine( shaderStage, 'NODE_UNIFORMS', uniformsCode );
+			
 		}
 		
-		let defines = this.buildDefines( shader );
+		let defines = this.buildDefines( shaderStage );
 		
 		return {
 			defines
@@ -115,45 +192,15 @@ class NodeBuilder {
 	
 	format( code, fromType, toType ) {
 
-		const typeToType = toType + ' <- ' + fromType;
+		const typeToType = `${fromType} -> ${toType}`;
 
-		switch ( typeToType ) {
+		console.log( typeToType );
 
-			case 'f <- v2' : return code + '.x';
-			case 'f <- v3' : return code + '.x';
-			case 'f <- v4' : return code + '.x';
-			case 'f <- i' :
-			case 'f <- b' :	return 'float( ' + code + ' )';
-
-			case 'v2 <- f' : return 'vec2( ' + code + ' )';
-			case 'v2 <- v3': return code + '.xy';
-			case 'v2 <- v4': return code + '.xy';
-			case 'v2 <- i' :
-			case 'v2 <- b' : return 'vec2( float( ' + code + ' ) )';
-
-			case 'v3 <- f' : return 'vec3( ' + code + ' )';
-			case 'v3 <- v2': return 'vec3( ' + code + ', 0.0 )';
-			case 'v3 <- v4': return code + '.xyz';
-			case 'v3 <- i' :
-			case 'v3 <- b' : return 'vec2( float( ' + code + ' ) )';
-
-			case 'v4 <- f' : return 'vec4( ' + code + ' )';
-			case 'v4 <- v2': return 'vec4( ' + code + ', 0.0, 1.0 )';
-			case 'v4 <- v3': return 'vec4( ' + code + ', 1.0 )';
-			case 'v4 <- i' :
-			case 'v4 <- b' : return 'vec4( float( ' + code + ' ) )';
-
-			case 'i <- f' :
-			case 'i <- b' : return 'int( ' + code + ' )';
-			case 'i <- v2' : return 'int( ' + code + '.x )';
-			case 'i <- v3' : return 'int( ' + code + '.x )';
-			case 'i <- v4' : return 'int( ' + code + '.x )';
+		switch ( typeToType ) {
 
-			case 'b <- f' : return '( ' + code + ' != 0.0 )';
-			case 'b <- v2' : return '( ' + code + ' != vec2( 0.0 ) )';
-			case 'b <- v3' : return '( ' + code + ' != vec3( 0.0 ) )';
-			case 'b <- v4' : return '( ' + code + ' != vec4( 0.0 ) )';
-			case 'b <- i' : return '( ' + code + ' != 0 )';
+			case 'float -> vec3' : return `vec3( ${code} )`;
+			
+			case 'vec3 -> float' : return `${code}.x`;
 
 		}
 

+ 26 - 0
examples/jsm/renderers/nodes/core/NodeUniform.js

@@ -0,0 +1,26 @@
+class NodeUniform {
+
+	constructor( name, type, node, needsUpdate = undefined ) {
+
+		this.name = name;
+		this.type = type;
+		this.node = node;
+		this.needsUpdate = needsUpdate;
+
+	}
+
+	get value() {
+		
+		return this.node.value;
+		
+	}
+	
+	set value( val ) {
+		
+		this.node.value = val;
+		
+	}
+
+}
+
+export default NodeUniform;

+ 21 - 0
examples/jsm/renderers/nodes/inputs/FloatNode.js

@@ -0,0 +1,21 @@
+import InputNode from '../core/InputNode.js';
+
+class FloatNode extends InputNode {
+
+	constructor( value ) {
+
+		super( 'float' );
+
+		this.value = value;
+
+	}
+
+	generateConst( builder ) {
+
+		return builder.generateFloat( this.value );
+
+	}
+
+}
+
+export default FloatNode;

+ 2 - 2
examples/jsm/renderers/nodes/inputs/Vector3Node.js

@@ -4,9 +4,9 @@ class Vector3Node extends InputNode {
 
 	constructor( value ) {
 
-		super( 'v3' );
+		super( 'vec3' );
 
-		this.value = value
+		this.value = value;
 
 	}
 

+ 15 - 6
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -15,20 +15,29 @@ import Node from '../core/Node.js';
 
 	getType( builder ) {
 		
-		// ignore auto length for now
+		const typeA = this.a.getType( builder );
+		const typeB = this.b.getType( builder );
 		
-		return this.a.getType( builder );
+		if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
+
+			// use the greater length vector
+
+			return typeB;
+
+		}
+
+		return typeA;
 		
 	}
 
 	generate( builder, output ) {
 
-		const nodeType = this.getType( builder );
+		const type = this.getType( builder );
 
-		const a = this.a.build( builder, nodeType );
-		const b = this.b.build( builder, nodeType );
+		const a = this.a.build( builder, type );
+		const b = this.b.build( builder, type );
 
-		return builder.format( '( ' + a + ' ' + this.op + ' ' + b + ' )', nodeType, output );
+		return builder.format( '( ' + a + ' ' + this.op + ' ' + b + ' )', type, output );
 
 	}
 		

+ 6 - 0
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -55,6 +55,12 @@ class WebGPUBindings {
 
 			}
 
+			// append node material bindings
+
+			const nodeBindings = this.pipelines.getNodeBindings( object );
+
+			bindings = bindings.concat( nodeBindings );
+
 			// setup (static) binding layout and (dynamic) binding group
 
 			const bindLayout = pipeline.getBindGroupLayout( 0 );

+ 118 - 17
examples/jsm/renderers/webgpu/WebGPUNodeBuilder.js

@@ -1,34 +1,49 @@
+import WebGPUUniformsGroup from './WebGPUUniformsGroup.js';
+import { FloatUniform, Vector3Uniform } from './WebGPUUniform.js';
+
 import NodeSlot from '../nodes/core/NodeSlot.js';
 import NodeBuilder from '../nodes/core/NodeBuilder.js';
 
-class WebGPUNodeBuilder extends NodeBuilder {
-
-	constructor() {
-
-		super();
+class WebGPUNodeUniformsGroup extends WebGPUUniformsGroup {
+	
+	constructor( shaderStage ) {
 
+		super( 'nodeUniforms' );
+		
+		let shaderStageVisibility;
+		
+		if ( shaderStage === 'vertex' ) shaderStageVisibility = GPUShaderStage.VERTEX;
+		else if ( shaderStage === 'fragment' ) shaderStageVisibility = GPUShaderStage.FRAGMENT;
+			
+		this.setVisibility( shaderStageVisibility );
+		
+		this.setOnBeforeUpdate( this._onBeforeUpdate );
+		
 	}
 	
-	buildShader( shader, code ) {
+	_onBeforeUpdate( object, camera ) {
 		
-		// use regex maybe for security?
-		const versionStrIndex = code.indexOf("\n");
+		const material = object.material;
 		
-		let shaderCode = code.substr( 0, versionStrIndex ) + "\n";
-
-		let shaderData = this.build( shader );
+		console.log( 1 );
 		
-		shaderCode += shaderData.defines;
+	}
+	
+}
 
-		shaderCode += code.substr( versionStrIndex );
+class WebGPUNodeBuilder extends NodeBuilder {
 
-		console.log( shaderCode );
+	constructor( material, renderer ) {
 
-		return shaderCode;
+		super( material, renderer );
+		
+		this.uniformsGroup = {};
+		
+		this._parseMaterial();
 		
 	}
 	
-	parse( vertexShader, fragmentShader ) {
+	_parseMaterial() {
 		
 		const material = this.material;
 		
@@ -36,12 +51,98 @@ class WebGPUNodeBuilder extends NodeBuilder {
 			
 			if ( material.color.isNode ) {
 				
-				this.addSlot( 'fragment', new NodeSlot( material.color, 'COLOR', 'v3' ) );
+				this.addSlot( 'fragment', new NodeSlot( material.color, 'COLOR', 'vec3' ) );
+				
+			}
+			
+		}
+		
+	}
+	
+	getUniformNSName( nodeUniform ) {
+		
+		return `nodeUniforms.${nodeUniform.name}`;
+		
+	}
+	
+	getBindings() {
+		
+		const bindings = [];
+		
+		const uniformsVertexGroup = this.uniformsGroup[ 'vertex' ];
+		const uniformsFragmentGroup = this.uniformsGroup[ 'fragment' ];
+		
+		if ( uniformsVertexGroup ) bindings.push( uniformsVertexGroup );
+		if ( uniformsFragmentGroup ) bindings.push( uniformsFragmentGroup );
+		
+		return bindings;
+		
+	}
+	
+	createUniformFromNode( node, shaderStage, type ) {
+		
+		const uniformNode = super.createUniformFromNode( node, shaderStage, type ) 
+		const nodeData = this.createDataFromNode( node, shaderStage );
+		
+		if ( nodeData.uniformGroup === undefined ) {
+			
+			let uniformsGroup = this.uniformsGroup[shaderStage];
+			
+			if (uniformsGroup === undefined) {
+				
+				uniformsGroup = new WebGPUNodeUniformsGroup( shaderStage );
+				
+				this.uniformsGroup[shaderStage] = uniformsGroup;
+				
+			}
+			
+			let uniformGroup;
+			
+			if ( type === 'float' ) {
+				
+				uniformGroup = new FloatUniform( uniformNode.name, uniformNode.value );
+				
+			} else if ( type === 'vec3' ) {
+				
+				uniformGroup = new Vector3Uniform( uniformNode.name, uniformNode.value );
+				
+			} else {
+				
+				console.error( `Uniform "${type}" not declared.` );
 				
 			}
 			
+			uniformsGroup.addUniform( uniformGroup );
+			
+			nodeData.uniformGroup = uniformGroup;
+			
 		}
+		
+		return uniformNode;
+		
+	}
+	
+	buildShader( shaderStage, code ) {
+		
+		// use regex maybe for security?
+		const versionStrIndex = code.indexOf("\n");
+		
+		let finalCode = code.substr( 0, versionStrIndex ) + "\n\n";
 
+		const shaderCodes = this.build( shaderStage );
+		
+		finalCode += shaderCodes.defines;
+
+		finalCode += code.substr( versionStrIndex );
+
+		console.log( finalCode );
+
+		return finalCode;
+		
+	}
+	
+	parse( vertexShader, fragmentShader ) {
+		
 		vertexShader = this.buildShader( 'vertex', vertexShader );
 		fragmentShader = this.buildShader( 'fragment', fragmentShader );
 

+ 16 - 5
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -21,6 +21,8 @@ class WebGPURenderPipelines {
 		this.glslang = glslang;
 		this.sampleCount = sampleCount;
 
+		this.nodeBindings = new WeakMap();
+
 		this.pipelines = new WeakMap();
 		this.shaderAttributes = new WeakMap();
 		this.shaderModules = {
@@ -63,12 +65,12 @@ class WebGPURenderPipelines {
 
 			// parse nodes
 
-			const nodeBuilder = new WebGPUNodeBuilder();
+			const nodeBuilder = new WebGPUNodeBuilder( material, this.renderer );
 
-			nodeBuilder.setMaterial( material );
-			
 			shader = nodeBuilder.parse( shader.vertexShader, shader.fragmentShader );
 
+			this.nodeBindings.set( object, nodeBuilder.getBindings() );
+
 			// shader modules
 
 			const glslang = this.glslang;
@@ -210,6 +212,12 @@ class WebGPURenderPipelines {
 
 	}
 
+	getNodeBindings( object ) {
+
+		return this.nodeBindings.get( object );
+
+	}
+
 	getShaderAttributes( pipeline ) {
 
 		return this.shaderAttributes.get( pipeline );
@@ -789,7 +797,6 @@ class WebGPURenderPipelines {
 const ShaderLib = {
 	mesh_basic: {
 		vertexShader: `#version 450
-
 		layout(location = 0) in vec3 position;
 		layout(location = 1) in vec2 uv;
 
@@ -818,7 +825,11 @@ const ShaderLib = {
 		layout(set = 0, binding = 3) uniform sampler mySampler;
 		layout(set = 0, binding = 4) uniform texture2D myTexture;
 
-		#pragma node_uniforms
+		#ifdef NODE_UNIFORMS
+		layout(set = 0, binding = 5) uniform NodeUniforms {
+			NODE_UNIFORMS
+		} nodeUniforms;
+		#endif
 
 		layout(location = 0) in vec2 vUv;
 		layout(location = 0) out vec4 outColor;

+ 8 - 5
examples/webgpu_sandbox_nodes.html

@@ -19,6 +19,7 @@
 			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';
 			import WebGPU from './jsm/renderers/webgpu/WebGPU.js';
 
+			import FloatNode from './jsm/renderers/nodes/inputs/FloatNode.js';
 			import Vector3Node from './jsm/renderers/nodes/inputs/Vector3Node.js';
 			import OperatorNode from './jsm/renderers/nodes/math/OperatorNode.js';
 
@@ -52,7 +53,9 @@
 				const geometryBox = new THREE.BoxBufferGeometry();
 				const materialBox = new THREE.MeshBasicMaterial( { map: texture } );
 
-				materialBox.color = new OperatorNode( '+', new Vector3Node( new THREE.Vector3( 0, .6, 0 ) ), new Vector3Node( new THREE.Vector3( 0, 0, 1 ) ) );
+				//materialBox.color = new OperatorNode( '+', new Vector3Node( new THREE.Vector3( 0, .6, 0 ) ), new Vector3Node( new THREE.Vector3( 0, 0, 1 ) ) );
+				materialBox.color = new OperatorNode( '+', new FloatNode( .5 ), new Vector3Node( new THREE.Vector3( 0, 0, 1 ) ) );
+				//materialBox.color = new Vector3Node( new THREE.Vector3( 0, 0, 1 ) );
 
 				box = new THREE.Mesh( geometryBox, materialBox );
 				box.position.set( 0, 1, 0 );
@@ -65,7 +68,7 @@
 
 				const plane = new THREE.Mesh( geometryPlane, materialPlane );
 				plane.position.set( 0, - 1, 0 );
-				scene.add( plane );
+				//scene.add( plane );
 
 				// compressed texture
 
@@ -76,7 +79,7 @@
 
 				const boxCompressed = new THREE.Mesh( geometryBox, materialCompressed );
 				boxCompressed.position.set( - 2, 1, 0 );
-				scene.add( boxCompressed );
+				//scene.add( boxCompressed );
 
 				// points
 
@@ -94,7 +97,7 @@
 
 				const pointCloud = new THREE.Points( geometryPoints, materialPoints );
 				pointCloud.position.set( 2, - 1, 0 );
-				scene.add( pointCloud );
+				//scene.add( pointCloud );
 
 				// lines
 
@@ -107,7 +110,7 @@
 				const materialLine = new THREE.LineBasicMaterial();
 				const line = new THREE.Line( geometryLine, materialLine );
 				line.position.set( 2, 1, 0 );
-				scene.add( line );
+				//scene.add( line );
 
 				//