Browse Source

Improved webgpu_compute_particles example (#31092)

* Improved webgpu_compute_particles example.

* Indentation fix.

* Indentation fix.

* Potential fix for code scanning alert no. 3517: Unused variable, import, function or class

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

* Sprite instead of Mesh.

* Clean up.

---------

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
mrdoob 11 tháng trước cách đây
mục cha
commit
e04f92e172

BIN
examples/screenshots/webgpu_compute_particles.jpg


+ 58 - 51
examples/webgpu_compute_particles.html

@@ -38,16 +38,17 @@
 		<script type="module">
 		<script type="module">
 
 
 			import * as THREE from 'three';
 			import * as THREE from 'three';
-			import { Fn, uniform, texture, instancedArray, instanceIndex, float, hash, vec3, If } from 'three/tsl';
+			import { Fn, If, uniform, float, uv, vec2, vec3, hash,
+				instancedArray, instanceIndex } from 'three/tsl';
 
 
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
 			import Stats from 'three/addons/libs/stats.module.js';
 			import Stats from 'three/addons/libs/stats.module.js';
 
 
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 			import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
 
 
-			const particleCount = 500000;
+			const particleCount = 200_000;
 
 
-			const gravity = uniform( - .0098 );
+			const gravity = uniform( - .00098 );
 			const bounce = uniform( .8 );
 			const bounce = uniform( .8 );
 			const friction = uniform( .99 );
 			const friction = uniform( .99 );
 			const size = uniform( .12 );
 			const size = uniform( .12 );
@@ -58,6 +59,8 @@
 			let controls, stats;
 			let controls, stats;
 			let computeParticles;
 			let computeParticles;
 
 
+			let isOrbitControlsActive;
+
 			const timestamps = document.getElementById( 'timestamps' );
 			const timestamps = document.getElementById( 'timestamps' );
 
 
 			init();
 			init();
@@ -67,37 +70,35 @@
 				const { innerWidth, innerHeight } = window;
 				const { innerWidth, innerHeight } = window;
 
 
 				camera = new THREE.PerspectiveCamera( 50, innerWidth / innerHeight, .1, 1000 );
 				camera = new THREE.PerspectiveCamera( 50, innerWidth / innerHeight, .1, 1000 );
-				camera.position.set( 15, 30, 15 );
+				camera.position.set( 0, 5, 20 );
 
 
 				scene = new THREE.Scene();
 				scene = new THREE.Scene();
 
 
-				// textures
-
-				const textureLoader = new THREE.TextureLoader();
-				const map = textureLoader.load( 'textures/sprite1.png' );
-
 				//
 				//
 
 
-				const positionBuffer = instancedArray( particleCount, 'vec3' );
-				const velocityBuffer = instancedArray( particleCount, 'vec3' );
-				const colorBuffer = instancedArray( particleCount, 'vec3' );
+				const positions = instancedArray( particleCount, 'vec3' );
+				const velocities = instancedArray( particleCount, 'vec3' );
+				const colors = instancedArray( particleCount, 'vec3' );
 
 
 				// compute
 				// compute
+				
+				const separation = 0.2;
+				const amount = Math.sqrt( particleCount );
+				const offset = float( amount / 2 );
 
 
 				const computeInit = Fn( () => {
 				const computeInit = Fn( () => {
 
 
-					const position = positionBuffer.element( instanceIndex );
-					const color = colorBuffer.element( instanceIndex );
-
-					const randX = hash( instanceIndex );
-					const randY = hash( instanceIndex.add( 2 ) );
-					const randZ = hash( instanceIndex.add( 3 ) );
+					const position = positions.element( instanceIndex );
+					const color = colors.element( instanceIndex );
+					
+					const x = instanceIndex.mod( amount );
+					const z = instanceIndex.div( amount );
+					
+					position.x = offset.sub( x ).mul( separation );
+					position.z = offset.sub( z ).mul( separation );
 
 
-					position.x = randX.mul( 100 ).add( - 50 );
-					position.y = 0; // randY.mul( 10 );
-					position.z = randZ.mul( 100 ).add( - 50 );
-
-					color.assign( vec3( randX, randY, randZ ) );
+					color.x = hash( instanceIndex );
+					color.y = hash( instanceIndex.add( 2 ) );
 
 
 				} )().compute( particleCount );
 				} )().compute( particleCount );
 
 
@@ -105,8 +106,8 @@
 
 
 				const computeUpdate = Fn( () => {
 				const computeUpdate = Fn( () => {
 
 
-					const position = positionBuffer.element( instanceIndex );
-					const velocity = velocityBuffer.element( instanceIndex );
+					const position = positions.element( instanceIndex );
+					const velocity = velocities.element( instanceIndex );
 
 
 					velocity.addAssign( vec3( 0.00, gravity, 0.00 ) );
 					velocity.addAssign( vec3( 0.00, gravity, 0.00 ) );
 					position.addAssign( velocity );
 					position.addAssign( velocity );
@@ -131,31 +132,26 @@
 
 
 				computeParticles = computeUpdate().compute( particleCount );
 				computeParticles = computeUpdate().compute( particleCount );
 
 
-				// create nodes
-
-				const textureNode = texture( map );
-
 				// create particles
 				// create particles
-
-				const particleMaterial = new THREE.SpriteNodeMaterial();
-				particleMaterial.colorNode = textureNode.mul( colorBuffer.element( instanceIndex ) );
-				particleMaterial.positionNode = positionBuffer.toAttribute();
-				particleMaterial.scaleNode = size;
-				particleMaterial.depthWrite = false;
-				particleMaterial.depthTest = true;
-				particleMaterial.transparent = true;
-
-				const particles = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), particleMaterial );
+				
+				const material = new THREE.SpriteNodeMaterial();
+				material.colorNode = uv().mul( colors.element( instanceIndex ) );
+				material.positionNode = positions.toAttribute();
+				material.scaleNode = size;
+				material.alphaTestNode = uv().mul( 2 ).distance( vec2( 1 ) );
+				material.transparent = false;
+
+				const particles = new THREE.Sprite( material );
 				particles.count = particleCount;
 				particles.count = particleCount;
 				particles.frustumCulled = false;
 				particles.frustumCulled = false;
 				scene.add( particles );
 				scene.add( particles );
 
 
 				//
 				//
 
 
-				const helper = new THREE.GridHelper( 60, 40, 0x303030, 0x303030 );
+				const helper = new THREE.GridHelper( 90, 45, 0x303030, 0x303030 );
 				scene.add( helper );
 				scene.add( helper );
 
 
-				const geometry = new THREE.PlaneGeometry( 1000, 1000 );
+				const geometry = new THREE.PlaneGeometry( 200, 200 );
 				geometry.rotateX( - Math.PI / 2 );
 				geometry.rotateX( - Math.PI / 2 );
 
 
 				const plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
 				const plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
@@ -179,19 +175,19 @@
 
 
 				renderer.computeAsync( computeInit );
 				renderer.computeAsync( computeInit );
 
 
-				// click event
+				// Hit
 
 
 				const computeHit = Fn( () => {
 				const computeHit = Fn( () => {
 
 
-					const position = positionBuffer.element( instanceIndex );
-					const velocity = velocityBuffer.element( instanceIndex );
+					const position = positions.element( instanceIndex );
+					const velocity = velocities.element( instanceIndex );
 
 
 					const dist = position.distance( clickPosition );
 					const dist = position.distance( clickPosition );
 					const direction = position.sub( clickPosition ).normalize();
 					const direction = position.sub( clickPosition ).normalize();
-					const distArea = float( 6 ).sub( dist ).max( 0 );
+					const distArea = float( 3 ).sub( dist ).max( 0 );
 
 
 					const power = distArea.mul( .01 );
 					const power = distArea.mul( .01 );
-					const relativePower = power.mul( hash( instanceIndex ).mul( .5 ).add( .5 ) );
+					const relativePower = power.mul( hash( instanceIndex ).mul( 1.5 ).add( .5 ) );
 
 
 					velocity.assign( velocity.add( direction.mul( relativePower ) ) );
 					velocity.assign( velocity.add( direction.mul( relativePower ) ) );
 
 
@@ -200,12 +196,14 @@
 				//
 				//
 
 
 				function onMove( event ) {
 				function onMove( event ) {
+					
+					if ( isOrbitControlsActive ) return;
 
 
 					pointer.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1 );
 					pointer.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1 );
 
 
 					raycaster.setFromCamera( pointer, camera );
 					raycaster.setFromCamera( pointer, camera );
 
 
-					const intersects = raycaster.intersectObjects( [ plane ], false );
+					const intersects = raycaster.intersectObject( plane, false );
 
 
 					if ( intersects.length > 0 ) {
 					if ( intersects.length > 0 ) {
 
 
@@ -224,17 +222,24 @@
 
 
 				}
 				}
 
 
-				// events
-
 				renderer.domElement.addEventListener( 'pointermove', onMove );
 				renderer.domElement.addEventListener( 'pointermove', onMove );
 
 
-				//
+				// controls
 
 
 				controls = new OrbitControls( camera, renderer.domElement );
 				controls = new OrbitControls( camera, renderer.domElement );
+				controls.enableDamping = true;
 				controls.minDistance = 5;
 				controls.minDistance = 5;
 				controls.maxDistance = 200;
 				controls.maxDistance = 200;
-				controls.target.set( 0, 0, 0 );
+				controls.target.set( 0, -8, 0 );
 				controls.update();
 				controls.update();
+				
+				controls.addEventListener( 'start', () => { isOrbitControlsActive = true; } );
+				controls.addEventListener( 'end', () => { isOrbitControlsActive = false; } );
+
+				controls.touches = {
+					ONE: null,
+					TWO: THREE.TOUCH.DOLLY_PAN
+				};
 
 
 				//
 				//
 
 
@@ -266,6 +271,8 @@
 
 
 				stats.update();
 				stats.update();
 
 
+				controls.update();
+
 				await renderer.computeAsync( computeParticles );
 				await renderer.computeAsync( computeParticles );
 				renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
 				renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
 
 

粤ICP备19079148号