Babylon.js WebGPU Instancing ( Chrome Canary )
Babylon.js WebGPU Compute Shaders examples にある Compute_Boids
を参考にして、instancing にトライしました。
実行結果
プログラム
instancing.html
<!DOCTYPE html> <html> <head> <title>Babylon.js WebGPU Instancing</title> <script src="https://preview.babylonjs.com/babylon.js"></script> </head> <body> <canvas id="renderCanvas" width="400" height="400"></canvas> <script> async function init() { const canvas = document.getElementById("renderCanvas"); const engine = new BABYLON.WebGPUEngine(canvas); await engine.initAsync(); const numParticles = 100; var createScene = function () { var scene = new BABYLON.Scene(engine); var camera = new BABYLON.ArcRotateCamera("camera", -Math.PI / 2, Math.PI / 2, 10, BABYLON.Vector3.Zero(), scene); camera.setTarget(BABYLON.Vector3.Zero()); camera.attachControl(canvas, true); const point = new Point(numParticles, scene); //console.log("point numParticles", point.numParticles); scene.onBeforeRenderObservable.add(() => { point.update(); }); return scene; }; class Point { constructor(numParticles, scene) { const engine = scene.getEngine(); this.numParticles = numParticles; const pointMesh = BABYLON.MeshBuilder.CreatePlane("plane", { size: 1 }, scene); this.mesh = pointMesh; pointMesh.forcedInstanceCount = numParticles; const mat = new BABYLON.ShaderMaterial("mat", scene, { vertexSource: renderShader.point_vs, fragmentSource: renderShader.point_fs, }, { attributes: ["a_pos", "a_particlePos"] }); pointMesh.material = mat; const side = 0.02; const vertex = [ -side, -side, side, -side, side, side, -side, side ]; const buffSpriteVertex = new BABYLON.VertexBuffer( engine, vertex, "a_pos", false, false, 2, false ); pointMesh.setIndices([0, 1, 2, 2, 3, 0]); pointMesh.setVerticesBuffer(buffSpriteVertex); const initialParticleData = new Float32Array(numParticles * 4); for (let i = 0; i < numParticles; ++i) { initialParticleData[4 * i + 0] = 2 * (Math.random() - 0.5); initialParticleData[4 * i + 1] = 2 * (Math.random() - 0.5); initialParticleData[4 * i + 2] = 2 * (Math.random() - 0.5) * 0.1; initialParticleData[4 * i + 3] = 2 * (Math.random() - 0.5) * 0.1; } this.particleBuffers = new BABYLON.StorageBuffer( engine, initialParticleData.byteLength, BABYLON.Constants.BUFFER_CREATIONFLAG_VERTEX | BABYLON.Constants.BUFFER_CREATIONFLAG_WRITE ); this.particleBuffers.update(initialParticleData); this.vertexBuffers = new BABYLON.VertexBuffer( engine, this.particleBuffers.getBuffer(), "a_particlePos", false, false, 4, true, 0, 2 ); // compute shader this.cs = new BABYLON.ComputeShader("compute", engine, { computeSource: computeShader }, { bindingsMapping: { "particle": { group: 0, binding: 0 }, } }); this.cs.setStorageBuffer("particle", this.particleBuffers); } update() { this.cs.dispatch(this.numParticles); this.mesh.setVerticesBuffer(this.vertexBuffers, false); } } const renderShader = { point_vs:` attribute vec2 a_pos; attribute vec2 a_particlePos; void main() { vec2 pos = vec2(a_pos.x, a_pos.y); gl_Position = vec4(pos + a_particlePos, 0.0, 1.0); }`, point_fs:` void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); }` }; const computeShader = ` struct Particle { pos : vec2<f32>; vel : vec2<f32>; }; [[block]] struct Particles { particles : array<Particle, ${numParticles}>; }; [[group(0), binding(0)]] var<storage> particle : [[access(read_write)]] Particles; [[stage(compute)]] fn main([[builtin(global_invocation_id)]] GlobalInvocationID : vec3<u32>) { let deltaT: f32 = 0.04; var index : u32 = GlobalInvocationID.x; if (index >= ${numParticles}u) { return; } var vPos : vec2<f32> = particle.particles[index].pos; var vVel : vec2<f32> = particle.particles[index].vel; vPos = vPos + (vVel * deltaT); if (abs(vPos.x) >= 1.0) { vVel.x = -vVel.x; } if (abs(vPos.y) >= 1.0) { vVel.y = -vVel.y; } particle.particles[index].pos = vPos; particle.particles[index].vel = vVel; } `; const scene = createScene(); engine.runRenderLoop(() => { scene.render(); }); }; init(); </script> </body> </html>