Skip to content

Commit 45e9b50

Browse files
mrdoobgithub-advanced-security[bot]
authored andcommitted
Improved webgpu_compute_particles example (mrdoob#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>
1 parent 5400bed commit 45e9b50

File tree

2 files changed

+58
-51
lines changed

2 files changed

+58
-51
lines changed
187 KB
Loading

examples/webgpu_compute_particles.html

Lines changed: 58 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,17 @@
3838
<script type="module">
3939

4040
import * as THREE from 'three';
41-
import { Fn, uniform, texture, instancedArray, instanceIndex, float, hash, vec3, If } from 'three/tsl';
41+
import { Fn, If, uniform, float, uv, vec2, vec3, hash,
42+
instancedArray, instanceIndex } from 'three/tsl';
4243

4344
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
4445
import Stats from 'three/addons/libs/stats.module.js';
4546

4647
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
4748

48-
const particleCount = 500000;
49+
const particleCount = 200_000;
4950

50-
const gravity = uniform( - .0098 );
51+
const gravity = uniform( - .00098 );
5152
const bounce = uniform( .8 );
5253
const friction = uniform( .99 );
5354
const size = uniform( .12 );
@@ -58,6 +59,8 @@
5859
let controls, stats;
5960
let computeParticles;
6061

62+
let isOrbitControlsActive;
63+
6164
const timestamps = document.getElementById( 'timestamps' );
6265

6366
init();
@@ -67,46 +70,44 @@
6770
const { innerWidth, innerHeight } = window;
6871

6972
camera = new THREE.PerspectiveCamera( 50, innerWidth / innerHeight, .1, 1000 );
70-
camera.position.set( 15, 30, 15 );
73+
camera.position.set( 0, 5, 20 );
7174

7275
scene = new THREE.Scene();
7376

74-
// textures
75-
76-
const textureLoader = new THREE.TextureLoader();
77-
const map = textureLoader.load( 'textures/sprite1.png' );
78-
7977
//
8078

81-
const positionBuffer = instancedArray( particleCount, 'vec3' );
82-
const velocityBuffer = instancedArray( particleCount, 'vec3' );
83-
const colorBuffer = instancedArray( particleCount, 'vec3' );
79+
const positions = instancedArray( particleCount, 'vec3' );
80+
const velocities = instancedArray( particleCount, 'vec3' );
81+
const colors = instancedArray( particleCount, 'vec3' );
8482

8583
// compute
84+
85+
const separation = 0.2;
86+
const amount = Math.sqrt( particleCount );
87+
const offset = float( amount / 2 );
8688

8789
const computeInit = Fn( () => {
8890

89-
const position = positionBuffer.element( instanceIndex );
90-
const color = colorBuffer.element( instanceIndex );
91-
92-
const randX = hash( instanceIndex );
93-
const randY = hash( instanceIndex.add( 2 ) );
94-
const randZ = hash( instanceIndex.add( 3 ) );
91+
const position = positions.element( instanceIndex );
92+
const color = colors.element( instanceIndex );
93+
94+
const x = instanceIndex.mod( amount );
95+
const z = instanceIndex.div( amount );
96+
97+
position.x = offset.sub( x ).mul( separation );
98+
position.z = offset.sub( z ).mul( separation );
9599

96-
position.x = randX.mul( 100 ).add( - 50 );
97-
position.y = 0; // randY.mul( 10 );
98-
position.z = randZ.mul( 100 ).add( - 50 );
99-
100-
color.assign( vec3( randX, randY, randZ ) );
100+
color.x = hash( instanceIndex );
101+
color.y = hash( instanceIndex.add( 2 ) );
101102

102103
} )().compute( particleCount );
103104

104105
//
105106

106107
const computeUpdate = Fn( () => {
107108

108-
const position = positionBuffer.element( instanceIndex );
109-
const velocity = velocityBuffer.element( instanceIndex );
109+
const position = positions.element( instanceIndex );
110+
const velocity = velocities.element( instanceIndex );
110111

111112
velocity.addAssign( vec3( 0.00, gravity, 0.00 ) );
112113
position.addAssign( velocity );
@@ -131,31 +132,26 @@
131132

132133
computeParticles = computeUpdate().compute( particleCount );
133134

134-
// create nodes
135-
136-
const textureNode = texture( map );
137-
138135
// create particles
139-
140-
const particleMaterial = new THREE.SpriteNodeMaterial();
141-
particleMaterial.colorNode = textureNode.mul( colorBuffer.element( instanceIndex ) );
142-
particleMaterial.positionNode = positionBuffer.toAttribute();
143-
particleMaterial.scaleNode = size;
144-
particleMaterial.depthWrite = false;
145-
particleMaterial.depthTest = true;
146-
particleMaterial.transparent = true;
147-
148-
const particles = new THREE.Mesh( new THREE.PlaneGeometry( 1, 1 ), particleMaterial );
136+
137+
const material = new THREE.SpriteNodeMaterial();
138+
material.colorNode = uv().mul( colors.element( instanceIndex ) );
139+
material.positionNode = positions.toAttribute();
140+
material.scaleNode = size;
141+
material.alphaTestNode = uv().mul( 2 ).distance( vec2( 1 ) );
142+
material.transparent = false;
143+
144+
const particles = new THREE.Sprite( material );
149145
particles.count = particleCount;
150146
particles.frustumCulled = false;
151147
scene.add( particles );
152148

153149
//
154150

155-
const helper = new THREE.GridHelper( 60, 40, 0x303030, 0x303030 );
151+
const helper = new THREE.GridHelper( 90, 45, 0x303030, 0x303030 );
156152
scene.add( helper );
157153

158-
const geometry = new THREE.PlaneGeometry( 1000, 1000 );
154+
const geometry = new THREE.PlaneGeometry( 200, 200 );
159155
geometry.rotateX( - Math.PI / 2 );
160156

161157
const plane = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { visible: false } ) );
@@ -179,19 +175,19 @@
179175

180176
renderer.computeAsync( computeInit );
181177

182-
// click event
178+
// Hit
183179

184180
const computeHit = Fn( () => {
185181

186-
const position = positionBuffer.element( instanceIndex );
187-
const velocity = velocityBuffer.element( instanceIndex );
182+
const position = positions.element( instanceIndex );
183+
const velocity = velocities.element( instanceIndex );
188184

189185
const dist = position.distance( clickPosition );
190186
const direction = position.sub( clickPosition ).normalize();
191-
const distArea = float( 6 ).sub( dist ).max( 0 );
187+
const distArea = float( 3 ).sub( dist ).max( 0 );
192188

193189
const power = distArea.mul( .01 );
194-
const relativePower = power.mul( hash( instanceIndex ).mul( .5 ).add( .5 ) );
190+
const relativePower = power.mul( hash( instanceIndex ).mul( 1.5 ).add( .5 ) );
195191

196192
velocity.assign( velocity.add( direction.mul( relativePower ) ) );
197193

@@ -200,12 +196,14 @@
200196
//
201197

202198
function onMove( event ) {
199+
200+
if ( isOrbitControlsActive ) return;
203201

204202
pointer.set( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1 );
205203

206204
raycaster.setFromCamera( pointer, camera );
207205

208-
const intersects = raycaster.intersectObjects( [ plane ], false );
206+
const intersects = raycaster.intersectObject( plane, false );
209207

210208
if ( intersects.length > 0 ) {
211209

@@ -224,17 +222,24 @@
224222

225223
}
226224

227-
// events
228-
229225
renderer.domElement.addEventListener( 'pointermove', onMove );
230226

231-
//
227+
// controls
232228

233229
controls = new OrbitControls( camera, renderer.domElement );
230+
controls.enableDamping = true;
234231
controls.minDistance = 5;
235232
controls.maxDistance = 200;
236-
controls.target.set( 0, 0, 0 );
233+
controls.target.set( 0, -8, 0 );
237234
controls.update();
235+
236+
controls.addEventListener( 'start', () => { isOrbitControlsActive = true; } );
237+
controls.addEventListener( 'end', () => { isOrbitControlsActive = false; } );
238+
239+
controls.touches = {
240+
ONE: null,
241+
TWO: THREE.TOUCH.DOLLY_PAN
242+
};
238243

239244
//
240245

@@ -266,6 +271,8 @@
266271

267272
stats.update();
268273

274+
controls.update();
275+
269276
await renderer.computeAsync( computeParticles );
270277
renderer.resolveTimestampsAsync( THREE.TimestampQuery.COMPUTE );
271278

0 commit comments

Comments
 (0)