123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175 |
- <html lang="en">
- <head>
- <title>three.js - WebGPU - Compute</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 - Compute - 300000 Points
- </div>
- <script type="importmap">
- {
- "imports": {
- "three": "../build/three.webgpu.js",
- "three/tsl": "../build/three.webgpu.js",
- "three/addons/": "./jsm/"
- }
- }
- </script>
- <script type="module">
- import * as THREE from 'three';
- import { Fn, uniform, storage, attribute, float, vec2, vec3, color, instanceIndex } from 'three/tsl';
- import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
- let camera, scene, renderer;
- let computeNode;
- const pointerVector = new THREE.Vector2( - 10.0, - 10.0 ); // Out of bounds first
- const scaleVector = new THREE.Vector2( 1, 1 );
- init();
- function init() {
- camera = new THREE.OrthographicCamera( - 1.0, 1.0, 1.0, - 1.0, 0, 1 );
- camera.position.z = 1;
- scene = new THREE.Scene();
- // initialize particles
- const particleNum = 300000;
- const particleSize = 2; // vec2
- // create buffers
- const particleBuffer = new THREE.StorageInstancedBufferAttribute( particleNum, particleSize );
- const velocityBuffer = new THREE.StorageInstancedBufferAttribute( particleNum, particleSize );
- const particleBufferNode = storage( particleBuffer, 'vec2', particleNum );
- const velocityBufferNode = storage( velocityBuffer, 'vec2', particleNum );
- // create function
- const computeShaderFn = Fn( () => {
- const particle = particleBufferNode.element( instanceIndex );
- const velocity = velocityBufferNode.element( instanceIndex );
- const pointer = uniform( pointerVector );
- const limit = uniform( scaleVector );
- const position = particle.add( velocity ).toVar();
- velocity.x = position.x.abs().greaterThanEqual( limit.x ).select( velocity.x.negate(), velocity.x );
- velocity.y = position.y.abs().greaterThanEqual( limit.y ).select( velocity.y.negate(), velocity.y );
- position.assign( position.min( limit ).max( limit.negate() ) );
- const pointerSize = 0.1;
- const distanceFromPointer = pointer.sub( position ).length();
- particle.assign( distanceFromPointer.lessThanEqual( pointerSize ).select( vec3(), position ) );
- } );
- // compute
- computeNode = computeShaderFn().compute( particleNum );
- computeNode.onInit = ( { renderer } ) => {
- const precomputeShaderNode = Fn( () => {
- const particleIndex = float( instanceIndex );
- const randomAngle = particleIndex.mul( .005 ).mul( Math.PI * 2 );
- const randomSpeed = particleIndex.mul( 0.00000001 ).add( 0.0000001 );
- const velX = randomAngle.sin().mul( randomSpeed );
- const velY = randomAngle.cos().mul( randomSpeed );
- const velocity = velocityBufferNode.element( instanceIndex );
- velocity.xy = vec2( velX, velY );
- } );
- renderer.compute( precomputeShaderNode().compute( particleNum ) );
- };
- // use a compute shader to animate the point cloud's vertex data.
- const particleNode = attribute( 'particle', 'vec2' );
- const pointsGeometry = new THREE.BufferGeometry();
- pointsGeometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( 3 ), 3 ) ); // single vertex ( not triangle )
- pointsGeometry.setAttribute( 'particle', particleBuffer ); // dummy the position points as instances
- pointsGeometry.drawRange.count = 1; // force render points as instances ( not triangle )
- const pointsMaterial = new THREE.PointsNodeMaterial();
- pointsMaterial.colorNode = particleNode.add( color( 0xFFFFFF ) );
- pointsMaterial.positionNode = particleNode;
- const mesh = new THREE.Points( pointsGeometry, pointsMaterial );
- mesh.count = particleNum;
- scene.add( mesh );
- renderer = new THREE.WebGPURenderer( { antialias: true } );
- renderer.setPixelRatio( window.devicePixelRatio );
- renderer.setSize( window.innerWidth, window.innerHeight );
- renderer.setAnimationLoop( animate );
- document.body.appendChild( renderer.domElement );
- window.addEventListener( 'resize', onWindowResize );
- window.addEventListener( 'mousemove', onMouseMove );
- // gui
- const gui = new GUI();
- gui.add( scaleVector, 'x', 0, 1, 0.01 );
- gui.add( scaleVector, 'y', 0, 1, 0.01 );
- }
- function onWindowResize() {
- camera.updateProjectionMatrix();
- renderer.setSize( window.innerWidth, window.innerHeight );
- }
- function onMouseMove( event ) {
- const x = event.clientX;
- const y = event.clientY;
- const width = window.innerWidth;
- const height = window.innerHeight;
- pointerVector.set(
- ( x / width - 0.5 ) * 2.0,
- ( - y / height + 0.5 ) * 2.0
- );
- }
- function animate() {
- renderer.compute( computeNode );
- renderer.render( scene, camera );
- }
- </script>
- </body>
- </html>
|