瀏覽代碼

Node: add analyze() to optimization and validation (#23475)

* ShaderNode: fix use of swizzle in some nodes

* cleanup

* prevent use of vec4

* fix conversion format

* add Node.analyze()

* TempNode: not created var if it's is used only once
sunag 3 年之前
父節點
當前提交
d9c4c8e1b4

+ 14 - 14
examples/jsm/renderers/nodes/ShaderNode.js

@@ -302,16 +302,16 @@ export const and = ShaderNodeProxy( OperatorNode, '&&' );
 
 export const element = ShaderNodeProxy( ArrayElementNode );
 
-export const normalGeometry = new NormalNode( NormalNode.GEOMETRY );
-export const normalLocal = new NormalNode( NormalNode.LOCAL );
-export const normalWorld = new NormalNode( NormalNode.WORLD );
-export const normalView = new NormalNode( NormalNode.VIEW );
-export const transformedNormalView = new VarNode( new NormalNode( NormalNode.VIEW ), 'TransformedNormalView', 'vec3' );
+export const normalGeometry = ShaderNodeObject( new NormalNode( NormalNode.GEOMETRY ) );
+export const normalLocal = ShaderNodeObject( new NormalNode( NormalNode.LOCAL ) );
+export const normalWorld = ShaderNodeObject( new NormalNode( NormalNode.WORLD ) );
+export const normalView = ShaderNodeObject( new NormalNode( NormalNode.VIEW ) );
+export const transformedNormalView = ShaderNodeObject( new VarNode( new NormalNode( NormalNode.VIEW ), 'TransformedNormalView', 'vec3' ) );
 
-export const positionLocal = new PositionNode( PositionNode.LOCAL );
-export const positionWorld = new PositionNode( PositionNode.WORLD );
-export const positionView = new PositionNode( PositionNode.VIEW );
-export const positionViewDirection = new PositionNode( PositionNode.VIEW_DIRECTION );
+export const positionLocal = ShaderNodeObject( new PositionNode( PositionNode.LOCAL ) );
+export const positionWorld = ShaderNodeObject( new PositionNode( PositionNode.WORLD ) );
+export const positionView = ShaderNodeObject( new PositionNode( PositionNode.VIEW ) );
+export const positionViewDirection = ShaderNodeObject( new PositionNode( PositionNode.VIEW_DIRECTION ) );
 
 export const PI = float( 3.141592653589793 );
 export const PI2 = float( 6.283185307179586 );
@@ -320,11 +320,11 @@ export const RECIPROCAL_PI = float( 0.3183098861837907 );
 export const RECIPROCAL_PI2 = float( 0.15915494309189535 );
 export const EPSILON = float( 1e-6 );
 
-export const diffuseColor = new PropertyNode( 'DiffuseColor', 'vec4' );
-export const roughness = new PropertyNode( 'Roughness', 'float' );
-export const metalness = new PropertyNode( 'Metalness', 'float' );
-export const alphaTest = new PropertyNode( 'AlphaTest', 'float' );
-export const specularColor = new PropertyNode( 'SpecularColor', 'color' );
+export const diffuseColor = ShaderNodeObject( new PropertyNode( 'DiffuseColor', 'vec4' ) );
+export const roughness = ShaderNodeObject( new PropertyNode( 'Roughness', 'float' ) );
+export const metalness = ShaderNodeObject( new PropertyNode( 'Metalness', 'float' ) );
+export const alphaTest = ShaderNodeObject( new PropertyNode( 'AlphaTest', 'float' ) );
+export const specularColor = ShaderNodeObject( new PropertyNode( 'SpecularColor', 'color' ) );
 
 export const abs = ShaderNodeProxy( MathNode, 'abs' );
 export const acos = ShaderNodeProxy( MathNode, 'acos' );

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

@@ -50,6 +50,30 @@ class Node {
 
 	}
 
+	analyze( builder ) {
+
+		const hash = this.getHash( builder );
+		const sharedNode = builder.getNodeFromHash( hash );
+
+		if ( sharedNode !== undefined && this !== sharedNode ) {
+
+			return sharedNode.analyze( builder );
+
+		}
+
+		const nodeData = builder.getDataFromNode( this );
+		nodeData.dependenciesCount = nodeData.dependenciesCount === undefined ? 1 : nodeData.dependenciesCount + 1;
+
+		const nodeKeys = getNodesKeys( this );
+
+		for ( const property of nodeKeys ) {
+
+			this[ property ].analyze( builder );
+
+		}
+
+	}
+
 	build( builder, output = null ) {
 
 		const hash = this.getHash( builder );
@@ -64,6 +88,7 @@ class Node {
 		builder.addNode( this );
 		builder.addStack( this );
 
+		const nodeData = builder.getDataFromNode( this );
 		const isGenerateOnce = this.generate.length === 1;
 
 		let snippet = null;
@@ -71,7 +96,6 @@ class Node {
 		if ( isGenerateOnce ) {
 
 			const type = this.getNodeType( builder );
-			const nodeData = builder.getDataFromNode( this );
 
 			snippet = nodeData.snippet;
 

+ 26 - 19
examples/jsm/renderers/nodes/core/NodeBuilder.js

@@ -58,7 +58,8 @@ class NodeBuilder {
 			console.warn( 'Recursive node: ', node );
 
 		}
-*/
+		*/
+
 		this.stack.push( node );
 
 	}
@@ -449,25 +450,9 @@ class NodeBuilder {
 
 	}
 
-	getAttributes( shaderStage ) {
-
-		let snippet = '';
-
-		if ( shaderStage === 'vertex' ) {
-
-			const attributes = this.attributes;
-
-			for ( let index = 0; index < attributes.length; index ++ ) {
-
-				const attribute = attributes[ index ];
-
-				snippet += `layout(location = ${index}) in ${attribute.type} ${attribute.name}; `;
+	getAttributes( /*shaderStage*/ ) {
 
-			}
-
-		}
-
-		return snippet;
+		console.warn( 'Abstract function.' );
 
 	}
 
@@ -543,12 +528,32 @@ class NodeBuilder {
 
 	build() {
 
+		// stage 1: analyze nodes to possible optimization and validation
+
+		for ( const shaderStage of shaderStages ) {
+
+			this.setShaderStage( shaderStage );
+
+			const flowNodes = this.flowNodes[ shaderStage ];
+
+			for ( const node of flowNodes ) {
+
+				node.analyze( this );
+
+			}
+
+		}
+
+		// stage 2: pre-build vertex code used in fragment shader
+
 		if ( this.context.vertex && this.context.vertex.isNode ) {
 
 			this.flowNodeFromShaderStage( 'vertex', this.context.vertex );
 
 		}
 
+		// stage 3: generate shader
+
 		for ( const shaderStage of shaderStages ) {
 
 			this.setShaderStage( shaderStage );
@@ -565,6 +570,8 @@ class NodeBuilder {
 
 		this.setShaderStage( null );
 
+		// stage 4: build code for a specific output
+
 		this.buildCode();
 
 		return this;

+ 2 - 3
examples/jsm/renderers/nodes/core/TempNode.js

@@ -11,10 +11,9 @@ class TempNode extends Node {
 	build( builder, output ) {
 
 		const type = builder.getVectorType( this.getNodeType( builder, output ) );
+		const nodeData = builder.getDataFromNode( this );
 
-		if ( builder.context.temp !== false && type !== 'void ' && output !== 'void' ) {
-
-			const nodeData = builder.getDataFromNode( this );
+		if ( builder.context.temp !== false && type !== 'void ' && output !== 'void' && nodeData.dependenciesCount > 1 ) {
 
 			if ( nodeData.snippet === undefined ) {
 

+ 1 - 1
examples/jsm/renderers/nodes/functions/BSDFs.js

@@ -107,7 +107,7 @@ export const RE_Direct_Physical = new ShaderNode( ( inputs ) => {
 
 	irradiance = mul( irradiance, PI ); // punctual light
 
-	addTo( directDiffuse, mul( irradiance, BRDF_Lambert( { diffuseColor } ) ) );
+	addTo( directDiffuse, mul( irradiance, BRDF_Lambert( { diffuseColor: diffuseColor.rgb } ) ) );
 
 	addTo( directSpecular, mul( irradiance, BRDF_GGX( { lightDirection, f0: specularColor, f90: 1, roughness } ) ) );
 

+ 1 - 1
examples/jsm/renderers/nodes/math/MathNode.js

@@ -231,7 +231,7 @@ class MathNode extends TempNode {
 
 			}
 
-			return `${ builder.getMethod( method ) }( ${params.join( ', ' )} )`;
+			return builder.format( `${ builder.getMethod( method ) }( ${params.join( ', ' )} )`, type, output );
 
 		}
 

+ 2 - 4
examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js

@@ -1,12 +1,10 @@
-import NodeBuilder from '../../nodes/core/NodeBuilder.js';
+import NodeBuilder, { shaderStages } from '../../nodes/core/NodeBuilder.js';
 import SlotNode from './SlotNode.js';
 import GLSLNodeParser from '../../nodes/parsers/GLSLNodeParser.js';
 import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
 
 import { ShaderChunk, ShaderLib, UniformsUtils, UniformsLib,
-		LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
-
-const shaderStages = [ 'vertex', 'fragment' ];
+	LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
 
 const nodeShaderLib = {
 	LineBasicNodeMaterial: ShaderLib.basic,