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

NodeMaterial: Three.JS Shader Language (#22603)

* three.js shader language

* cleanup

* Update webgpu_sandbox.jpg

* Rename TJSL to ShaderNode and cleanup

* cleanup
sunag 3 лет назад
Родитель
Сommit
90e2d69e5a

+ 193 - 0
examples/jsm/renderers/nodes/ShaderNode.js

@@ -0,0 +1,193 @@
+// inputs
+import ColorNode from './inputs/ColorNode.js';
+import FloatNode from './inputs/FloatNode.js';
+import Vector2Node from './inputs/Vector2Node.js';
+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';
+
+// utils
+import JoinNode from './utils/JoinNode.js';
+import SplitNode from './utils/SplitNode.js';
+
+// core
+import { Vector2, Vector3, Vector4, Color } from 'three';
+
+const NodeHandler = {
+
+	get: function ( node, prop ) {
+
+		// Split Properties Pass
+
+		if ( typeof prop === 'string' && node[ prop ] === undefined ) {
+
+			const splitProps = prop.match( /^[xyzw]{1,4}$/ );
+
+			if ( splitProps !== null ) {
+
+				return ShaderNodeObject( new SplitNode( node, splitProps[ 0 ] ) );
+
+			}
+
+		}
+
+		return node[ prop ];
+
+	}
+
+};
+
+export const ShaderNodeObject = ( obj ) => {
+
+	const type = typeof obj;
+
+	if ( type === 'number' ) {
+
+		return ShaderNodeObject( new FloatNode( obj ).setConst( true ) );
+
+	} else if ( type === 'object' ) {
+
+		if ( obj.isNode === true ) {
+
+			const node = obj;
+
+			if ( node.isProxyNode !== true ) {
+
+				node.isProxyNode = true;
+
+				return new Proxy( node, NodeHandler );
+
+			}
+
+		}
+
+	}
+
+	return obj;
+
+};
+
+export const ShaderNodeArray = ( array ) => {
+
+	const len = array.length;
+
+	for ( let i = 0; i < len; i ++ ) {
+
+		array[ i ] = ShaderNodeObject( array[ i ] );
+
+	}
+
+	return array;
+
+};
+
+export const ShaderNodeScript = ( jsFunc ) => {
+
+	return ( ...params ) => {
+
+		ShaderNodeArray( params );
+
+		return ShaderNodeObject( jsFunc( ...params ) );
+
+	};
+
+};
+
+export const ShaderNode = ( obj ) => {
+
+	return ShaderNodeScript( obj );
+
+};
+
+//
+// Node Material Shader Syntax
+//
+
+export const uniform = ShaderNodeScript( ( inputNode ) => {
+
+	inputNode.setConst( false );
+
+	return inputNode;
+
+} );
+
+export const float = ( val ) => {
+
+	return ShaderNodeObject( new FloatNode( val ).setConst( true ) );
+
+};
+
+export const color = ( ...params ) => {
+
+	return ShaderNodeObject( new ColorNode( new Color( ...params ) ).setConst( true ) );
+
+};
+
+export const join = ( ...params ) => {
+
+	return ShaderNodeObject( new JoinNode( ShaderNodeArray( params ) ) );
+
+};
+
+export const vec2 = ( ...params ) => {
+
+	return ShaderNodeObject( new Vector2Node( new Vector2( ...params ) ).setConst( true ) );
+
+};
+
+export const vec3 = ( ...params ) => {
+
+	return ShaderNodeObject( new Vector3Node( new Vector3( ...params ) ).setConst( true ) );
+
+};
+
+export const vec4 = ( ...params ) => {
+
+	return ShaderNodeObject( new Vector4Node( new Vector4( ...params ) ).setConst( true ) );
+
+};
+
+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 ) ) );
+
+};

+ 10 - 8
examples/jsm/renderers/nodes/procedural/CheckerNode.js

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

BIN
examples/screenshots/webgpu_sandbox.jpg


+ 3 - 1
examples/webgpu_sandbox.html

@@ -80,7 +80,9 @@
 				const timerScaleNode = new Nodes.OperatorNode( '*', timerNode, new Nodes.Vector2Node( new THREE.Vector2( - 0.5, 0.1 ) ).setConst( true ) );
 				const animateUV = new Nodes.OperatorNode( '+', new Nodes.UVNode(), timerScaleNode );
 
-				materialBox.colorNode = new Nodes.TextureNode( texture, animateUV );
+				const textureNode = new Nodes.TextureNode( texture, animateUV );
+
+				materialBox.colorNode = new Nodes.MathNode( 'mix', textureNode, new Nodes.CheckerNode( animateUV ), new Nodes.FloatNode( .5) );
 
 				// test uv 2
 				//geometryBox.setAttribute( 'uv2', geometryBox.getAttribute( 'uv' ) );