فهرست منبع

Merge pull request #21350 from sunag/nodematerial-shared-uniforms

WebGPU: Fix material compile per object and new instance uniform example
Mr.doob 4 سال پیش
والد
کامیت
29fbc8788a

+ 1 - 0
examples/files.json

@@ -315,6 +315,7 @@
 	],
 	],
 	"webgpu": [
 	"webgpu": [
 		"webgpu_compute",
 		"webgpu_compute",
+		"webgpu_instance_uniform",
 		"webgpu_materials",
 		"webgpu_materials",
 		"webgpu_rtt",
 		"webgpu_rtt",
 		"webgpu_sandbox"
 		"webgpu_sandbox"

+ 2 - 1
examples/jsm/renderers/nodes/inputs/ColorNode.js

@@ -1,8 +1,9 @@
 import InputNode from '../core/InputNode.js';
 import InputNode from '../core/InputNode.js';
+import { Color } from '../../../../../build/three.module.js';
 
 
 class ColorNode extends InputNode {
 class ColorNode extends InputNode {
 
 
-	constructor( value ) {
+	constructor( value = new Color() ) {
 
 
 		super( 'color' );
 		super( 'color' );
 
 

+ 2 - 1
examples/jsm/renderers/nodes/inputs/Vector2Node.js

@@ -1,8 +1,9 @@
 import InputNode from '../core/InputNode.js';
 import InputNode from '../core/InputNode.js';
+import { Vector2 } from '../../../../../build/three.module.js';
 
 
 class Vector2Node extends InputNode {
 class Vector2Node extends InputNode {
 
 
-	constructor( value ) {
+	constructor( value = new Vector2() ) {
 
 
 		super( 'vec2' );
 		super( 'vec2' );
 
 

+ 2 - 3
examples/jsm/renderers/webgpu/WebGPUBindings.js

@@ -24,12 +24,11 @@ class WebGPUBindings {
 		if ( data === undefined ) {
 		if ( data === undefined ) {
 
 
 			const pipeline = this.pipelines.get( object );
 			const pipeline = this.pipelines.get( object );
-			const material = object.material;
-
-			const nodeBuilder = this.nodes.get( material );
 
 
 			// each material defines an array of bindings (ubos, textures, samplers etc.)
 			// each material defines an array of bindings (ubos, textures, samplers etc.)
 
 
+			const nodeBuilder = this.nodes.get( object );
+
 			const bindings = nodeBuilder.getBindings();
 			const bindings = nodeBuilder.getBindings();
 
 
 			// setup (static) binding layout and (dynamic) binding group
 			// setup (static) binding layout and (dynamic) binding group

+ 2 - 9
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -57,7 +57,7 @@ class WebGPURenderPipelines {
 
 
 			// get shader
 			// get shader
 
 
-			const nodeBuilder = this.nodes.get( material );
+			const nodeBuilder = this.nodes.get( object );
 
 
 			// shader modules
 			// shader modules
 
 
@@ -792,22 +792,15 @@ class WebGPURenderPipelines {
 function onMaterialDispose( event ) {
 function onMaterialDispose( event ) {
 
 
 	const properties = this.properties;
 	const properties = this.properties;
-	const nodes = this.nodes;
-	const shaderModules = this.shaderModules;
 
 
 	const material = event.target;
 	const material = event.target;
 	const materialProperties = properties.get( material );
 	const materialProperties = properties.get( material );
-	const nodeBuilder = nodes.get( material );
 
 
 	material.removeEventListener( 'dispose', materialProperties.disposeCallback );
 	material.removeEventListener( 'dispose', materialProperties.disposeCallback );
 
 
 	properties.remove( material );
 	properties.remove( material );
-	nodes.remove( material );
 
 
-	shaderModules.vertex.delete( nodeBuilder.vertexShader );
-	shaderModules.fragment.delete( nodeBuilder.fragmentShader );
-
-	// @TODO: still needed remove bindings and pipeline
+	// @TODO: still needed remove nodes, bindings and pipeline
 
 
 }
 }
 
 

+ 7 - 7
examples/jsm/renderers/webgpu/nodes/WebGPUNodes.js

@@ -13,15 +13,15 @@ class WebGPUNodes {
 
 
 	}
 	}
 
 
-	get( material ) {
+	get( object ) {
 
 
-		let nodeBuilder = this.builders.get( material );
+		let nodeBuilder = this.builders.get( object );
 
 
 		if ( nodeBuilder === undefined ) {
 		if ( nodeBuilder === undefined ) {
 
 
-			nodeBuilder = new WebGPUNodeBuilder( material, this.renderer ).build();
+			nodeBuilder = new WebGPUNodeBuilder( object.material, this.renderer ).build();
 
 
-			this.builders.set( material, nodeBuilder );
+			this.builders.set( object, nodeBuilder );
 
 
 		}
 		}
 
 
@@ -29,9 +29,9 @@ class WebGPUNodes {
 
 
 	}
 	}
 
 
-	remove( material ) {
+	remove( object ) {
 
 
-		this.builders.delete( material );
+		this.builders.delete( object );
 
 
 	}
 	}
 
 
@@ -45,7 +45,7 @@ class WebGPUNodes {
 
 
 		const material = object.material;
 		const material = object.material;
 
 
-		const nodeBuilder = this.get( material );
+		const nodeBuilder = this.get( object );
 		const nodeFrame = this.nodeFrame;
 		const nodeFrame = this.nodeFrame;
 
 
 		nodeFrame.material = material;
 		nodeFrame.material = material;

BIN
examples/screenshots/webgpu_instance_uniform.jpg


+ 198 - 0
examples/webgpu_instance_uniform.html

@@ -0,0 +1,198 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgpu - instance uniform</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<link type="text/css" rel="stylesheet" href="main.css">
+	</head>
+	<body>
+		<div id="info">
+			<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - webgpu materials
+		</div>
+
+		<script type="module">
+
+			import * as THREE from '../build/three.module.js';
+
+			import WebGPURenderer from './jsm/renderers/webgpu/WebGPURenderer.js';
+			import WebGPU from './jsm/renderers/webgpu/WebGPU.js';
+
+			import { TeapotGeometry } from './jsm/geometries/TeapotGeometry.js';
+
+			import Node from './jsm/renderers/nodes/core/Node.js';
+			import AttributeNode from './jsm/renderers/nodes/core/AttributeNode.js';
+			import { NodeUpdateType } from './jsm/renderers/nodes/core/constants.js';
+			import ColorNode from './jsm/renderers/nodes/inputs/ColorNode.js';
+
+			import Stats from './jsm/libs/stats.module.js';
+
+			class InstanceUniformNode extends Node {
+
+				constructor() {
+
+					super( 'vec3' );
+
+					this.updateType = NodeUpdateType.Object;
+
+					this.inputNode = new ColorNode();
+
+				}
+
+				update( frame ) {
+
+					const mesh = frame.object;
+
+					this.inputNode.value.copy( mesh.color );
+
+				}
+
+				generate( builder, output ) {
+
+					return this.inputNode.build( builder, output );
+
+				}
+
+			}
+
+			let stats;
+
+			let camera, scene, renderer;
+
+			const objects = [];
+
+			init().then( animate ).catch( error );
+
+			async function init() {
+
+				if ( WebGPU.isAvailable() === false ) {
+
+					document.body.appendChild( WebGPU.getErrorMessage() );
+
+					throw 'No WebGPU support';
+
+				}
+
+				const container = document.createElement( 'div' );
+				document.body.appendChild( container );
+
+				camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
+				camera.position.set( 0, 200, 800 );
+
+				scene = new THREE.Scene();
+
+				// Grid
+
+				const helper = new THREE.GridHelper( 1000, 40, 0x303030, 0x303030 );
+				helper.material.colorNode = new AttributeNode( 'color', 'vec3' );
+				helper.position.y = - 75;
+				scene.add( helper );
+
+				// Materials
+
+				const instanceUniform = new InstanceUniformNode();
+
+				const material = new THREE.MeshBasicMaterial();
+				material.colorNode = instanceUniform;
+
+				// Geometry
+
+				const geometry = new TeapotGeometry( 50, 18 );
+
+				for ( let i = 0, l = 12; i < l; i ++ ) {
+
+					addMesh( geometry, material );
+
+				}
+
+				//
+
+				renderer = new WebGPURenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				container.appendChild( renderer.domElement );
+
+				//
+
+				stats = new Stats();
+				container.appendChild( stats.dom );
+
+				//
+
+				window.addEventListener( 'resize', onWindowResize );
+
+				return renderer.init();
+
+			}
+
+			function addMesh( geometry, material ) {
+
+				const mesh = new THREE.Mesh( geometry, material );
+
+				mesh.color = new THREE.Color( Math.random() * 0xffffff );
+
+				mesh.position.x = ( objects.length % 4 ) * 200 - 400;
+				mesh.position.z = Math.floor( objects.length / 4 ) * 200 - 200;
+
+				mesh.rotation.x = Math.random() * 200 - 100;
+				mesh.rotation.y = Math.random() * 200 - 100;
+				mesh.rotation.z = Math.random() * 200 - 100;
+
+				objects.push( mesh );
+
+				scene.add( mesh );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			//
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				render();
+				stats.update();
+
+			}
+
+			function render() {
+
+				const timer = 0.0001 * Date.now();
+
+				camera.position.x = Math.cos( timer ) * 1000;
+				camera.position.z = Math.sin( timer ) * 1000;
+
+				camera.lookAt( scene.position );
+
+				for ( let i = 0, l = objects.length; i < l; i ++ ) {
+
+					const object = objects[ i ];
+
+					object.rotation.x += 0.01;
+					object.rotation.y += 0.005;
+
+				}
+
+				renderer.render( scene, camera );
+
+			}
+
+			function error( error ) {
+
+				console.error( error );
+
+			}
+
+		</script>
+
+	</body>
+</html>