Vala プログラミング

WebGPU プログラミング

おなが@京都先端科学大

Babylon.js WebGPU Instancing ( Chrome Canary )

Babylon.js WebGPU Compute Shaders examples にある Compute_Boids
を参考にして、instancing にトライしました。

実行結果
f:id:onagat12:20210525195101g:plain

プログラム
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>