Kaynağa Gözat

NodeMaterial: ShaderNode updates (#22644)

* ShaderNode: updates

* use base values

* unused function
sunag 3 yıl önce
ebeveyn
işleme
bd84a6d870

+ 53 - 47
examples/jsm/renderers/nodes/ShaderNode.js

@@ -6,8 +6,9 @@ import Vector3Node from './inputs/Vector3Node.js';
 import Vector4Node from './inputs/Vector4Node.js';
 
 // math
-import MathNode from './math/MathNode.js';
 import OperatorNode from './math/OperatorNode.js';
+import CondNode from './math/CondNode.js';
+import MathNode from './math/MathNode.js';
 
 // utils
 import JoinNode from './utils/JoinNode.js';
@@ -20,7 +21,7 @@ const NodeHandler = {
 
 	construct( NodeClosure, params ) {
 
-		return NodeClosure( params[ 0 ] );
+		return NodeClosure( ShaderNodeObjects( params[ 0 ] ) );
 
 	},
 
@@ -30,7 +31,7 @@ const NodeHandler = {
 
 		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
 
-			const splitProps = prop.match( /^[xyzw]{1,4}$/ );
+			const splitProps = prop.match( /^[xyzwst]{1,4}$/ );
 
 			if ( splitProps !== null ) {
 
@@ -76,6 +77,18 @@ const ShaderNodeObject = ( obj ) => {
 
 };
 
+const ShaderNodeObjects = ( objects ) => {
+
+	for ( const name in objects ) {
+
+		objects[ name ] = ShaderNodeObject( objects[ name ] );
+
+	}
+
+	return objects;
+
+};
+
 const ShaderNodeArray = ( array ) => {
 
 	const len = array.length;
@@ -90,13 +103,24 @@ const ShaderNodeArray = ( array ) => {
 
 };
 
-const ShaderNodeScript = function ( jsFunc ) {
+const ShaderNodeProxy = ( NodeClass, scope ) => {
 
 	return ( ...params ) => {
 
-		ShaderNodeArray( params );
+		return ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ) ) );
+
+	};
+
+};
+
+
+const ShaderNodeScript = function ( jsFunc ) {
+
+	return ( inputs ) => {
+
+		ShaderNodeObjects( inputs );
 
-		return ShaderNodeObject( jsFunc( ...params ) );
+		return ShaderNodeObject( jsFunc( inputs ) );
 
 	};
 
@@ -134,6 +158,12 @@ export const join = ( ...params ) => {
 
 };
 
+export const cond = ( ...params ) => {
+
+	return ShaderNodeObject( new CondNode( ...ShaderNodeArray( params ) ) );
+
+};
+
 export const vec2 = ( ...params ) => {
 
 	return ShaderNodeObject( new Vector2Node( new Vector2( ...params ) ).setConst( true ) );
@@ -152,44 +182,20 @@ export const vec4 = ( ...params ) => {
 
 };
 
-export const add = ( ...params ) => {
-
-	return ShaderNodeObject( new OperatorNode( '+', ...ShaderNodeArray( params ) ) );
-
-};
-
-export const sub = ( ...params ) => {
-
-	return new OperatorNode( '-', ...ShaderNodeArray( params ) );
-
-};
-
-export const mul = ( ...params ) => {
-
-	return ShaderNodeObject( new OperatorNode( '*', ...ShaderNodeArray( params ) ) );
-
-};
-
-export const div = ( ...params ) => {
-
-	return ShaderNodeObject( new OperatorNode( '/', ...ShaderNodeArray( params ) ) );
-
-};
-
-export const floor = ( ...params ) => {
-
-	return ShaderNodeObject( new MathNode( 'floor', ...ShaderNodeArray( params ) ) );
-
-};
-
-export const mod = ( ...params ) => {
-
-	return ShaderNodeObject( new MathNode( 'mod', ...ShaderNodeArray( params ) ) );
-
-};
-
-export const sign = ( ...params ) => {
-
-	return ShaderNodeObject( new MathNode( 'sign', ...ShaderNodeArray( params ) ) );
-
-};
+export const add = ShaderNodeProxy( OperatorNode, '+' );
+export const sub = ShaderNodeProxy( OperatorNode, '-' );
+export const mul = ShaderNodeProxy( OperatorNode, '*' );
+export const div = ShaderNodeProxy( OperatorNode, '/' );
+export const equals = ShaderNodeProxy( OperatorNode, '==' );
+
+export const floor = ShaderNodeProxy( MathNode, 'floor' );
+export const mod = ShaderNodeProxy( MathNode, 'mod' );
+export const cross = ShaderNodeProxy( MathNode, 'cross' );
+export const max = ShaderNodeProxy( MathNode, 'max' );
+export const min = ShaderNodeProxy( MathNode, 'min' );
+export const dot = ShaderNodeProxy( MathNode, 'dot' );
+export const normalize = ShaderNodeProxy( MathNode, 'normalize' );
+export const inversesqrt = ShaderNodeProxy( MathNode, 'inversesqrt' );
+export const sign = ShaderNodeProxy( MathNode, 'sign' );
+export const dFdx = ShaderNodeProxy( MathNode, 'dFdx' );
+export const dFdy = ShaderNodeProxy( MathNode, 'dFdy' );

+ 4 - 5
examples/jsm/renderers/nodes/core/ContextNode.js

@@ -2,16 +2,13 @@ import Node from './Node.js';
 
 class ContextNode extends Node {
 
-	constructor( node, nodeType, context = {} ) {
+	constructor( node, context = {} ) {
 
-		super( nodeType );
+		super();
 
 		this.node = node;
-
 		this.context = context;
 
-		Object.defineProperty( this, 'isContextNode', { value: true } );
-
 	}
 
 	setContextValue( name, value ) {
@@ -50,4 +47,6 @@ class ContextNode extends Node {
 
 }
 
+ContextNode.prototype.isContextNode = true;
+
 export default ContextNode;

+ 15 - 15
examples/jsm/renderers/nodes/core/TempNode.js

@@ -10,35 +10,35 @@ class TempNode extends Node {
 
 	build( builder, output ) {
 
-		const type = builder.getVectorType( this.getNodeType( builder ) );
+		if ( builder.context.cache !== false ) {
 
-		if ( type !== 'void' ) {
+			const type = builder.getVectorType( this.getNodeType( builder ) );
 
-			const nodeVar = builder.getVarFromNode( this, type );
-			const propertyName = builder.getPropertyName( nodeVar );
+			if ( type !== 'void' ) {
 
-			const nodeData = builder.getDataFromNode( this );
+				const nodeData = builder.getDataFromNode( this );
 
-			let snippet = nodeData.snippet;
+				const nodeVar = builder.getVarFromNode( this, type );
+				const propertyName = builder.getPropertyName( nodeVar );
 
-			if ( snippet === undefined ) {
+				if ( nodeData.snippet === undefined ) {
 
-				snippet = super.build( builder, type );
+					const snippet = super.build( builder, type );
 
-				builder.addFlowCode( `${propertyName} = ${snippet}` );
+					builder.addFlowCode( `${propertyName} = ${snippet}` );
 
-				nodeData.snippet = snippet;
+					nodeData.snippet = snippet;
 
-			}
-
-			return builder.format( propertyName, type, output );
+				}
 
-		} else {
+				return builder.format( propertyName, type, output );
 
-			return super.build( builder, output );
+			}
 
 		}
 
+		return super.build( builder, output );
+
 	}
 
 }

+ 19 - 18
examples/jsm/renderers/nodes/display/NormalMapNode.js

@@ -5,38 +5,39 @@ import MathNode from '../math/MathNode.js';
 import OperatorNode from '../math/OperatorNode.js';
 import FloatNode from '../inputs/FloatNode.js';
 import TempNode from '../core/TempNode.js';
-import FunctionNode from '../core/FunctionNode.js';
 import ModelNode from '../accessors/ModelNode.js';
+import { ShaderNode, cond, add, mul, join, dFdx, dFdy, cross, max, dot, normalize, inversesqrt, equals } from '../ShaderNode.js';
 
 import { TangentSpaceNormalMap, ObjectSpaceNormalMap } from 'three';
 
 // Normal Mapping Without Precomputed Tangents
 // http://www.thetenthplanet.de/archives/1180
 
-export const perturbNormal2Arb = new FunctionNode( `
-vec3 ( vec3 eye_pos, vec3 surf_norm, vec3 mapN, float faceDirection, const in vec2 uv ) {
+const perturbNormal2ArbNode = new ShaderNode( ( inputs ) => {
+
+	const { eye_pos, surf_norm, mapN, faceDirection, uv } = inputs;
 
 	// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
 
-	vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
-	vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
-	vec2 st0 = dFdx( uv.st );
-	vec2 st1 = dFdy( uv.st );
+	const q0 = join( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );
+	const q1 = join( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );
+	const st0 = dFdx( uv.st );
+	const st1 = dFdy( uv.st );
 
-	vec3 N = surf_norm; // normalized
+	const N = surf_norm; // normalized
 
-	vec3 q1perp = cross( q1, N );
-	vec3 q0perp = cross( N, q0 );
+	const q1perp = cross( q1, N );
+	const q0perp = cross( N, q0 );
 
-	vec3 T = q1perp * st0.x + q0perp * st1.x;
-	vec3 B = q1perp * st0.y + q0perp * st1.y;
+	const T = add( mul( q1perp, st0.x ), mul( q0perp, st1.x ) );
+	const B = add( mul( q1perp, st0.y ), mul( q0perp, st1.y ) );
 
-	float det = max( dot( T, T ), dot( B, B ) );
-	float scale = ( det == 0.0 ) ? 0.0 : faceDirection * inversesqrt( det );
+	const det = max( dot( T, T ), dot( B, B ) );
+	const scale = cond( equals( det, 0 ), 0, mul( faceDirection, inversesqrt( det ) ) );
 
-	return normalize( T * ( mapN.x * scale ) + B * ( mapN.y * scale ) + N * mapN.z );
+	return normalize( add( mul( T, mul( mapN.x, scale ) ), mul( B, mul( mapN.y, scale ) ), mul( N, mapN.z ) ) );
 
-}` );
+} );
 
 class NormalMapNode extends TempNode {
 
@@ -69,7 +70,7 @@ class NormalMapNode extends TempNode {
 
 		} else if ( normalMapType === TangentSpaceNormalMap ) {
 
-			const perturbNormal2ArbCall = perturbNormal2Arb.call( {
+			const perturbNormal2ArbCall = perturbNormal2ArbNode( {
 				eye_pos: new PositionNode( PositionNode.VIEW ),
 				surf_norm: new NormalNode( NormalNode.VIEW ),
 				mapN: normalMap,
@@ -78,7 +79,7 @@ class NormalMapNode extends TempNode {
 			} );
 
 			return perturbNormal2ArbCall.build( builder, type );
-			
+
 		}
 
 	}

+ 1 - 1
examples/jsm/renderers/nodes/lights/LightContextNode.js

@@ -11,7 +11,7 @@ class LightContextNode extends ContextNode {
 
 	constructor( node ) {
 
-		super( node, 'vec3' );
+		super( node );
 
 	}
 

+ 48 - 0
examples/jsm/renderers/nodes/math/CondNode.js

@@ -0,0 +1,48 @@
+import TempNode from '../core/TempNode.js';
+import ContextNode from '../core/ContextNode.js';
+
+class CondNode extends TempNode {
+
+	constructor( node, ifNode, elseNode ) {
+
+		super();
+
+		this.node = node;
+
+		this.ifNode = ifNode;
+		this.elseNode = elseNode;
+
+	}
+
+	getNodeType( builder ) {
+
+		const ifType = this.ifNode.getNodeType( builder );
+		const elseType = this.elseNode.getNodeType( builder );
+
+		if ( builder.getTypeLength( elseType ) > builder.getTypeLength( ifType ) ) {
+
+			return elseType;
+
+		}
+
+		return ifType;
+
+	}
+
+	generate( builder ) {
+
+		const type = this.getNodeType( builder );
+
+		const context = { cache: false };
+
+		const nodeSnippet = this.node.build( builder, 'bool' ),
+			ifSnippet = new ContextNode( this.ifNode, context ).build( builder, type ),
+			elseSnippet = new ContextNode( this.elseNode, context ).build( builder, type );
+
+		return `( ${ nodeSnippet } ? ${ ifSnippet } : ${ elseSnippet } )`;
+
+	}
+
+}
+
+export default CondNode;

+ 2 - 0
examples/jsm/renderers/nodes/math/MathNode.js

@@ -28,6 +28,8 @@ class MathNode extends TempNode {
 	static LENGTH = 'length';
 	static NEGATE = 'negate';
 	static INVERT = 'invert';
+	static DFDX = 'dFdx';
+	static DFDY = 'dFdy';
 
 	// 2 inputs
 

+ 38 - 14
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -2,12 +2,26 @@ import TempNode from '../core/TempNode.js';
 
 class OperatorNode extends TempNode {
 
-	constructor( op, a, b ) {
+	constructor( op, a, b, ...params ) {
 
 		super();
 
 		this.op = op;
 
+		if ( params.length > 0 ) {
+
+			let finalB = b;
+
+			for ( let i = 0; i < params.length; i ++ ) {
+
+				finalB = new OperatorNode( op, finalB, params[ i ] );
+
+			}
+
+			b = finalB;
+
+		}
+
 		this.a = a;
 		this.b = b;
 
@@ -15,30 +29,40 @@ class OperatorNode extends TempNode {
 
 	getNodeType( builder ) {
 
-		const typeA = this.a.getNodeType( builder );
-		const typeB = this.b.getNodeType( builder );
+		const op = this.op;
 
-		if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) {
+		if ( op === '==' ) {
 
-			// matrix x vector
+			return 'bool';
 
-			return builder.getVectorFromMatrix( typeA );
+		} else {
 
-		} else if ( builder.isVector( typeA ) && builder.isMatrix( typeB ) ) {
+			const typeA = this.a.getNodeType( builder );
+			const typeB = this.b.getNodeType( builder );
 
-			// vector x matrix
+			if ( builder.isMatrix( typeA ) && builder.isVector( typeB ) ) {
 
-			return builder.getVectorFromMatrix( typeB );
+				// matrix x vector
 
-		} else if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
+				return builder.getVectorFromMatrix( typeA );
 
-			// anytype x anytype: use the greater length vector
+			} else if ( builder.isVector( typeA ) && builder.isMatrix( typeB ) ) {
 
-			return typeB;
+				// vector x matrix
 
-		}
+				return builder.getVectorFromMatrix( typeB );
+
+			} else if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
 
-		return typeA;
+				// anytype x anytype: use the greater length vector
+
+				return typeB;
+
+			}
+
+			return typeA;
+
+		}
 
 	}
 

+ 4 - 6
examples/jsm/renderers/nodes/procedural/CheckerNode.js

@@ -1,13 +1,11 @@
-import FunctionNode from '../core/FunctionNode.js';
 import Node from '../core/Node.js';
 import UVNode from '../accessors/UVNode.js';
 
-import { ShaderNode, float, add, mul, floor, mod, sign } from '../ShaderNode.js';
+import { ShaderNode, add, mul, floor, mod, sign } from '../ShaderNode.js';
 
-// Three.JS Shader Language
-const checkerShaderNode = new ShaderNode( ( uv ) => {
+const checkerShaderNode = new ShaderNode( ( inputs ) => {
 
-	uv = mul( uv, 2.0 );
+	const uv = mul( inputs.uv, 2.0 );
 
 	const cx = floor( uv.x );
 	const cy = floor( uv.y );
@@ -29,7 +27,7 @@ class CheckerNode extends Node {
 
 	generate( builder, output ) {
 
-		return checkerShaderNode( this.uv ).build( builder, output );
+		return checkerShaderNode( { uv: this.uv } ).build( builder, output );
 
 	}