Bladeren bron

WebGPU: FogNode (#23606)

* ShaderNode: add functions -> mat3, mat4, uv, attribute, texture

* WebGPU: smoothstep support

* fix .getConst() of integers value

* WebGPU: FogNode

* webgpu_lights_selective: add scene.fogNode

* fix vector xy
sunag 3 jaren geleden
bovenliggende
commit
2813e8deb2

+ 12 - 0
examples/jsm/nodes/Nodes.js

@@ -79,6 +79,10 @@ import NodeMaterialLoader from './loaders/NodeMaterialLoader.js';
 // procedural
 import CheckerNode from './procedural/CheckerNode.js';
 
+// fog
+import FogNode from './fog/FogNode.js';
+import FogRangeNode from './fog/FogRangeNode.js';
+
 // core
 export * from './core/constants.js';
 
@@ -168,6 +172,10 @@ const nodeLib = {
 	// procedural
 	CheckerNode,
 
+	// fog
+	FogNode,
+	FogRangeNode,
+
 	// loaders
 	NodeLoader,
 	NodeObjectLoader,
@@ -258,6 +266,10 @@ export {
 	// procedural
 	CheckerNode,
 
+	// fog
+	FogNode,
+	FogRangeNode,
+
 	// loaders
 	NodeLoader,
 	NodeObjectLoader,

+ 29 - 7
examples/jsm/nodes/ShaderNode.js

@@ -1,6 +1,7 @@
 // core
 import PropertyNode from './core/PropertyNode.js';
 import VarNode from './core/VarNode.js';
+import AttributeNode from './core/AttributeNode.js';
 
 // inputs
 import ColorNode from './inputs/ColorNode.js';
@@ -9,10 +10,14 @@ import IntNode from './inputs/IntNode.js';
 import Vector2Node from './inputs/Vector2Node.js';
 import Vector3Node from './inputs/Vector3Node.js';
 import Vector4Node from './inputs/Vector4Node.js';
+import Matrix3Node from './inputs/Matrix3Node.js';
+import Matrix4Node from './inputs/Matrix4Node.js';
+import TextureNode from './inputs/TextureNode.js';
 
 // accessors
 import PositionNode from './accessors/PositionNode.js';
 import NormalNode from './accessors/NormalNode.js';
+import UVNode from './accessors/UVNode.js';
 
 // math
 import OperatorNode from './math/OperatorNode.js';
@@ -115,7 +120,7 @@ const ShaderNodeObjects = function( objects ) {
 
 };
 
-const ShaderNodeArray = function( array ) {
+const getShaderNodeArray = ( array ) => {
 
 	const len = array.length;
 
@@ -135,7 +140,7 @@ const ShaderNodeProxy = function( NodeClass, scope = null, factor = null ) {
 
 		return ( ...params ) => {
 
-			return new ShaderNodeObject( new NodeClass( ...ShaderNodeArray( params ) ) );
+			return new ShaderNodeObject( new NodeClass( ...getShaderNodeArray( params ) ) );
 
 		};
 
@@ -143,7 +148,7 @@ const ShaderNodeProxy = function( NodeClass, scope = null, factor = null ) {
 
 		return ( ...params ) => {
 
-			return new ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ) ) );
+			return new ShaderNodeObject( new NodeClass( scope, ...getShaderNodeArray( params ) ) );
 
 		};
 
@@ -153,7 +158,7 @@ const ShaderNodeProxy = function( NodeClass, scope = null, factor = null ) {
 
 		return ( ...params ) => {
 
-			return new ShaderNodeObject( new NodeClass( scope, ...ShaderNodeArray( params ), factor ) );
+			return new ShaderNodeObject( new NodeClass( scope, ...getShaderNodeArray( params ), factor ) );
 
 		};
 
@@ -231,13 +236,13 @@ export const color = ( ...params ) => {
 
 export const join = ( ...params ) => {
 
-	return nodeObject( new JoinNode( ShaderNodeArray( params ) ) );
+	return nodeObject( new JoinNode( getShaderNodeArray( params ) ) );
 
 };
 
 export const cond = ( ...params ) => {
 
-	return nodeObject( new CondNode( ...ShaderNodeArray( params ) ) );
+	return nodeObject( new CondNode( ...getShaderNodeArray( params ) ) );
 
 };
 
@@ -307,14 +312,31 @@ export const vec4 = ( ...params ) => {
 
 };
 
+export const mat3 = ( val ) => {
+
+	return nodeObject( new Matrix3Node( val ).setConst( true ) );
+
+};
+
+export const mat4 = ( val ) => {
+
+	return nodeObject( new Matrix4Node( val ).setConst( true ) );
+
+};
+
 export const addTo = ( varNode, ...params ) => {
 
-	varNode.node = add( varNode.node, ...ShaderNodeArray( params ) );
+	varNode.node = add( varNode.node, ...getShaderNodeArray( params ) );
 
 	return nodeObject( varNode );
 
 };
 
+export const uv = new ShaderNodeProxy( UVNode );
+export const attribute = new ShaderNodeProxy( AttributeNode );
+
+export const texture = new ShaderNodeProxy( TextureNode );
+
 export const add = new ShaderNodeProxy( OperatorNode, '+' );
 export const sub = new ShaderNodeProxy( OperatorNode, '-' );
 export const mul = new ShaderNodeProxy( OperatorNode, '*' );

+ 10 - 4
examples/jsm/nodes/core/NodeBuilder.js

@@ -11,6 +11,12 @@ import { REVISION, LinearEncoding } from 'three';
 export const shaderStages = [ 'fragment', 'vertex' ];
 export const vector = [ 'x', 'y', 'z', 'w' ];
 
+const toFloat = ( value ) => {
+
+	return value + ( value % 1 ? '' : '.0' );
+
+};
+
 class NodeBuilder {
 
 	constructor( object, renderer, parser ) {
@@ -150,10 +156,10 @@ class NodeBuilder {
 	getConst( type, value ) {
 
 		if ( type === 'float' ) return value + ( value % 1 ? '' : '.0' );
-		if ( type === 'vec2' ) return `${ this.getType( 'vec2' ) }( ${value.x}, ${value.y} )`;
-		if ( type === 'vec3' ) return `${ this.getType( 'vec3' ) }( ${value.x}, ${value.y}, ${value.z} )`;
-		if ( type === 'vec4' ) return `${ this.getType( 'vec4' ) }( ${value.x}, ${value.y}, ${value.z}, ${value.w} )`;
-		if ( type === 'color' ) return `${ this.getType( 'vec3' ) }( ${value.r}, ${value.g}, ${value.b} )`;
+		if ( type === 'vec2' ) return `${ this.getType( 'vec2' ) }( ${ toFloat( value.x ) }, ${ toFloat( value.y ) } )`;
+		if ( type === 'vec3' ) return `${ this.getType( 'vec3' ) }( ${ toFloat( value.x ) }, ${ toFloat( value.y ) }, ${ toFloat( value.z ) } )`;
+		if ( type === 'vec4' ) return `${ this.getType( 'vec4' ) }( ${ toFloat( value.x ) }, ${ toFloat( value.y ) }, ${ toFloat( value.z ) }, ${ toFloat( value.w ) } )`;
+		if ( type === 'color' ) return `${ this.getType( 'vec3' ) }( ${ toFloat( value.r ) }, ${ toFloat( value.g ) }, ${ toFloat( value.b ) } )`;
 
 		throw new Error( `NodeBuilder: Type '${type}' not found in generate constant attempt.` );
 

+ 24 - 0
examples/jsm/nodes/fog/FogNode.js

@@ -0,0 +1,24 @@
+import Node from '../core/Node.js';
+
+class FogNode extends Node {
+
+	constructor( colorNode, factorNode ) {
+
+		super( 'float' );
+
+		this.colorNode = colorNode;
+		this.factorNode = factorNode;
+
+	}
+
+	generate( builder ) {
+
+		return this.factorNode.build( builder, 'float' );
+
+	}
+
+}
+
+FogNode.prototype.isFogNode = true;
+
+export default FogNode;

+ 27 - 0
examples/jsm/nodes/fog/FogRangeNode.js

@@ -0,0 +1,27 @@
+import FogNode from './FogNode.js';
+import { smoothstep, negate, positionView } from '../ShaderNode.js';
+
+class FogRangeNode extends FogNode {
+
+	constructor( colorNode, nearNode, farNode ) {
+
+		super( colorNode );
+
+		this.nearNode = nearNode;
+		this.farNode = farNode;
+
+	}
+
+	generate( builder ) {
+
+		this.factorNode = smoothstep( this.nearNode, this.farNode, negate( positionView.z ) );
+
+		return super.generate( builder );
+
+	}
+
+}
+
+FogRangeNode.prototype.isFogRangeNode = true;
+
+export default FogRangeNode;

+ 10 - 4
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -180,7 +180,7 @@ class WebGPURenderer {
 		this._geometries = new WebGPUGeometries( this._attributes, this._info );
 		this._textures = new WebGPUTextures( device, this._properties, this._info );
 		this._objects = new WebGPUObjects( this._geometries, this._info );
-		this._nodes = new WebGPUNodes( this );
+		this._nodes = new WebGPUNodes( this, this._properties );
 		this._computePipelines = new WebGPUComputePipelines( device );
 		this._renderPipelines = new WebGPURenderPipelines( this, device, parameters.sampleCount, this._nodes );
 		this._bindings = this._renderPipelines.bindings = new WebGPUBindings( device, this._info, this._properties, this._textures, this._renderPipelines, this._computePipelines, this._attributes, this._nodes );
@@ -308,8 +308,8 @@ class WebGPURenderer {
 		const opaqueObjects = this._currentRenderList.opaque;
 		const transparentObjects = this._currentRenderList.transparent;
 
-		if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, passEncoder );
-		if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, passEncoder );
+		if ( opaqueObjects.length > 0 ) this._renderObjects( opaqueObjects, camera, scene, passEncoder );
+		if ( transparentObjects.length > 0 ) this._renderObjects( transparentObjects, camera, scene, passEncoder );
 
 		// finish render pass
 
@@ -736,7 +736,7 @@ class WebGPURenderer {
 
 	}
 
-	_renderObjects( renderList, camera, passEncoder ) {
+	_renderObjects( renderList, camera, scene, passEncoder ) {
 
 		// process renderable objects
 
@@ -754,6 +754,12 @@ class WebGPURenderer {
 
 			this._objects.update( object );
 
+			// send scene properties to object
+
+			const objectProperties = this._properties.get( object );
+
+			objectProperties.fogNode = scene.fogNode;
+
 			if ( camera.isArrayCamera ) {
 
 				const cameras = camera.cameras;

+ 35 - 4
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -25,7 +25,7 @@ import ColorSpaceNode from 'three-nodes/display/ColorSpaceNode.js';
 import LightContextNode from 'three-nodes/lights/LightContextNode.js';
 import OperatorNode from 'three-nodes/math/OperatorNode.js';
 import WGSLNodeParser from 'three-nodes/parsers/WGSLNodeParser.js';
-import { vec3, add, join, nodeObject } from 'three-nodes/ShaderNode.js';
+import { vec3, add, join, mix, nodeObject } from 'three-nodes/ShaderNode.js';
 import { getRoughness } from 'three-nodes/functions/PhysicalMaterialFunctions.js';
 
 const wgslTypeLib = {
@@ -58,6 +58,16 @@ fn mod( x : f32, y : f32 ) -> f32 {
 
 	return x - y * floor( x / y );
 
+}
+` ),
+
+	smoothstep: new CodeNode( `
+fn smoothstep( low : f32, high : f32, x : f32 ) -> f32 {
+
+	let t = clamp( ( x - low ) / ( high - low ), 0.0, 1.0 );
+
+	return t * t * ( 3.0 - 2.0 * t );
+
 }
 ` ),
 	repeatWrapping: new CodeNode( `
@@ -80,19 +90,26 @@ fn inversesqrt( x : f32 ) -> f32 {
 
 class WebGPUNodeBuilder extends NodeBuilder {
 
-	constructor( object, renderer, lightNode = null ) {
+	constructor( object, renderer ) {
 
 		super( object, renderer, new WGSLNodeParser() );
 
-		this.lightNode = lightNode;
+		this.lightNode = null;
+		this.fogNode = null;
 
 		this.bindings = { vertex: [], fragment: [] };
 		this.bindingsOffset = { vertex: 0, fragment: 0 };
 
 		this.uniformsGroup = {};
 
+	}
+
+	build() {
+
 		this._parseObject();
 
+		return super.build();
+
 	}
 
 	_parseObject() {
@@ -278,9 +295,11 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 			}
 
+			// OUTPUT
+
 			outputNode = join( vec3( outgoingLightNode ), nodeObject( diffuseColorNode ).w );
 
-			// OUTPUT
+			// ENCODING
 
 			const outputEncoding = this.renderer.outputEncoding;
 
@@ -291,6 +310,18 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 			}
 
+			// FOG
+
+			const fogNode = this.fogNode;
+
+			if ( fogNode && fogNode.isFogNode ) {
+
+				outputNode = mix( outputNode, fogNode.colorNode, fogNode );
+
+			}
+
+			// RESULT
+
 			this.addFlow( 'fragment', new VarNode( outputNode, 'Output', 'vec4' ) );
 
 		}

+ 20 - 13
examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js

@@ -3,25 +3,30 @@ import NodeFrame from 'three-nodes/core/NodeFrame.js';
 
 class WebGPUNodes {
 
-	constructor( renderer ) {
+	constructor( renderer, properties ) {
 
 		this.renderer = renderer;
+		this.properties = properties;
 
 		this.nodeFrame = new NodeFrame();
 
-		this.builders = new WeakMap();
-
 	}
 
-	get( object, lightNode ) {
+	get( object ) {
+
+		const objectProperties = this.properties.get( object );
 
-		let nodeBuilder = this.builders.get( object );
+		let nodeBuilder = objectProperties.nodeBuilder;
 
 		if ( nodeBuilder === undefined ) {
 
-			nodeBuilder = new WebGPUNodeBuilder( object, this.renderer, lightNode ).build();
+			const fogNode = objectProperties.fogNode;
+
+			nodeBuilder = new WebGPUNodeBuilder( object, this.renderer );
+			nodeBuilder.fogNode = fogNode;
+			nodeBuilder.build();
 
-			this.builders.set( object, nodeBuilder );
+			objectProperties.nodeBuilder = nodeBuilder;
 
 		}
 
@@ -31,7 +36,9 @@ class WebGPUNodes {
 
 	remove( object ) {
 
-		this.builders.delete( object );
+		const objectProperties = this.properties.get( object );
+
+		delete objectProperties.nodeBuilder;
 
 	}
 
@@ -41,18 +48,18 @@ class WebGPUNodes {
 
 	}
 
-	update( object, camera, lightNode ) {
+	update( object, camera ) {
 
 		const renderer = this.renderer;
 		const material = object.material;
 
-		const nodeBuilder = this.get( object, lightNode );
+		const nodeBuilder = this.get( object );
 		const nodeFrame = this.nodeFrame;
 
-		nodeFrame.material = material;
-		nodeFrame.camera = camera;
 		nodeFrame.object = object;
+		nodeFrame.camera = camera;
 		nodeFrame.renderer = renderer;
+		nodeFrame.material = material;
 
 		for ( const node of nodeBuilder.updateNodes ) {
 
@@ -64,7 +71,7 @@ class WebGPUNodes {
 
 	dispose() {
 
-		this.builders = new WeakMap();
+		this.nodeFrame = new NodeFrame();
 
 	}
 

+ 4 - 1
examples/webgpu_lights_selective.html

@@ -41,6 +41,8 @@
 			import WebGPU from './jsm/capabilities/WebGPU.js';
 			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';
 
+			import { color, float } from 'three-nodes/ShaderNode.js';
+
 			let camera, scene, renderer,
 				light1, light2, light3, light4,
 				stats, controls;
@@ -61,6 +63,7 @@
 				camera.position.z = 70;
 
 				scene = new THREE.Scene();
+				scene.fogNode = new Nodes.FogRangeNode( color( 0xFF00FF ), float( 30 ), float( 300 )  );
 
 				const sphere = new THREE.SphereGeometry( 0.5, 16, 8 );
 
@@ -137,7 +140,7 @@
 
 				controls = new OrbitControls( camera, renderer.domElement );
 				controls.minDistance = 30;
-				controls.maxDistance = 150;
+				controls.maxDistance = 250;
 
 				//stats