Upgrade to Pro — share decks privately, control downloads, hide ads and more …

軽率にBabylon.jsの WebGPUエンジンを使って ComputeShaderに入門した / learn-about-babylonjs-webgpu-computeshader

軽率にBabylon.jsの WebGPUエンジンを使って ComputeShaderに入門した / learn-about-babylonjs-webgpu-computeshader

IwakenLab Shaderこんな感じ発表会#2

にー兄さん

September 30, 2023
Tweet

More Decks by にー兄さん

Other Decks in Technology

Transcript

  1. 軽率にBabylon.jsの
    WebGPUエンジンを使って
    ComputeShaderに入門した
    Shaderってこんな感じ発表会 #2
    にー兄さん(@ninisan_drumath)

    View full-size slide

  2. にー兄さん(@ninisan_drumath)
    ソフトウェアエンジニア
    株式会社ホロラボ / IwakenLab
    Unity / ロケーションベースAR / WebAR /
    Babylon.js / Azure Kinect
    最新技術を使った検証開発や
    デモンストレーションが好き
    唐突な
    うちの猫たち→

    View full-size slide

  3. 本日の話:
    「WebGPUのComputeShaderって
    こんな感じ」
    Babylon.jsの

    View full-size slide

  4. アジェンダ
    - WebGPUとかWGSLとかComputeShaderとか
    - Babylon.jsでもComputeShaderが使いたい
    - おわりに

    View full-size slide

  5. WebGPUとかWGSLとか
    ComputeShaderとか

    View full-size slide

  6. WebGPUとは①
    ブラウザ向けグラフィクスAPIの一種
    従来はWebGL・WebGL2などがあり、
    WebGL2が主流になろうとしている(ここの温度感あってる?)
    今年の5月くらいにPC向けGoogle Chrome 113から
    特殊な設定なしで利用できるように

    View full-size slide

  7. WebGPUとは②
    嬉しいところ
    - 低レベルなAPIを採用され、処理効率が向上
    - モダングラフィクスAPIの流れを汲んでいる
    - コンピュートシェーダが使える
    これからに期待
    - 2023年現在 使える環境が限られている
    - WebXRでは使えない、はず(要出典)

    View full-size slide

  8. WGSL
    WebGPUで採用されているシェーダ言語
    Rustっぽい構文
    下記はCYOSで生成されたコードの比較(少し調整済み)
    #version 300 es
    precision highp float;
    in vec3 position;
    uniform mat4 worldViewProjection;
    out vec4 vPosition;
    void main() {
    vec4 p = vec4(position, 1.f);
    vPosition = p;
    gl_Position = worldViewProjection * p;
    }
    #include
    #include
    attribute position : vec3;
    varying vPosition : vec4;
    @vertex
    fn main(input: VertexInputs) -> FragmentInputs {
    let p = vec4(vertexInputs.position, 1.0);
    vertexOutputs.vPosition = p;
    vertexOutputs.position = scene.viewProjection * mesh.world * p;
    }
    WGSL
    GLSL

    View full-size slide

  9. Compute Shader
    汎用的な計算をGPUで行うための仕組み
    つまりGPGPU(General Purpose GPU)
    WebGL系ではこの仕組みが無かったので
    vertex/fragmentシェーダで実現していた
    WebGPUには標準搭載されており、WGSLで書ける

    View full-size slide

  10. Babylon.jsでも
    ComputeShaderが使いたい

    View full-size slide

  11. 完成イメージ

    View full-size slide

  12. どんなものを作るか
    GPUパーティクルを意識して
    大量のオブジェクトの属性計算をGPGPUで処理したい
    (今回は90,000パーティクル)
    平面上のランダムな位置にパーティクル位置を初期化
    StorageBufferにPositionの配列を格納
    3D Sin Waveを適用(dispatch)
    PointsCloudSystemにPosition配列を適用
    時間によってアニメーションさせて上記処理を繰り返す

    View full-size slide

  13. 環境
    - Windows 10 Home
    - Google Chrome 117
    - Babylon.js 6.7.0
    - WebGPU Engine使用
    - Vite + TypeScript
    - もし素でWebGPUをTSで開発する場合
    @webgpu/typesに型定義があります
    - Babylon.jsでは不要です
    サンプルはGitHubで公開しています(docs整備中)
    https://github.com/drumath2237/babylon-compute-shader-sandbox

    View full-size slide

  14. Babylon.jsにおけるWebGPUとComputeShader
    WebGPUは ほぼフルサポート 一部機能は動かないらしい
    基本的にEngineをWebGPUEngineに差し替えれば
    既存のWebGLアプリから移行できる
    const engine = new WebGPUEngine(renderCanvas, {
    antialias: true,
    });
    await engine.initAsync();
    ComputeShaderはWebGPUエンジンでのみ使用可能
    > Note that in Babylon.js this is a WebGPU feature only (starting at v5.0), WebGL does
    not support compute shaders.

    View full-size slide

  15. ComputeShaderを書く(3dSinWave.wgsl)
    struct Params {
    time: f32
    }
    @group(0) @binding(0) var params : Params;
    @group(0) @binding(1) var positionBuffer : array;
    @compute @workgroup_size(2,1,1)
    fn main(@builtin(global_invocation_id) global_id: vec3) {
    var particleId = global_id.x * u32(300) + global_id.y;
    var x = positionBuffer[particleId * u32(3)];
    var z = positionBuffer[particleId * u32(3) + u32(2)];
    var distance = sqrt(x * x + z * z);
    positionBuffer[particleId * u32(3) + u32(1)] = sin(distance * 1.2 - params.time);
    }
    拡張機能を入れれば
    VSCodeで普通に書ける

    View full-size slide

  16. ComputeShaderインスタンスの作成
    Compute Shaderのコードと
    bindingsのLayoutを定義して
    インスタンス化
    const sinWaveComputeShader = new ComputeShader(
    "3d sin wave",
    engine,
    { computeSource: computeShaderSource },
    {
    bindingsMapping: {
    params: { group: 0, binding: 0 },
    positionBuffer: { group: 0, binding: 1 },
    },
    }
    );

    View full-size slide

  17. UniformBufferを作成
    プリミティブな値は
    UniformBufferとして渡している
    時間によるアニメーションのための
    float32を渡して更新する
    let time = 5;
    const waveParamsUniformBuffer = new UniformBuffer(
    engine,
    undefined,
    undefined,
    "params"
    );
    waveParamsUniformBuffer.addUniform("time", 1);
    waveParamsUniformBuffer.updateFloat("time", time);
    sinWaveComputeShader.setUniformBuffer(
    "params",
    waveParamsUniformBuffer
    );

    View full-size slide

  18. Position配列の初期化とStorageBufferの作成
    PointCloudSystemと
    Compute Shaderの相互で
    参照するPosition配列(Float32Array)
    ComputeShaderには
    StorageBufferに変換して渡している
    const positionBuffer
    = new Float32Array(PARTICLE_COUNT * 3);
    for (let i = 0; i < PARTICLE_COUNT; i++) {
    positionBuffer[i * 3]
    = randomNumberBetween(-10, 10);
    positionBuffer[i * 3 + 1] = 0;
    positionBuffer[i * 3 + 2]
    = randomNumberBetween(-10, 10);
    }
    const positionStorage = new StorageBuffer(
    engine, positionBuffer.byteLength
    );
    positionStorage.update(positionBuffer);
    sinWaveComputeShader.setStorageBuffer(
    "positionBuffer", positionStorage);

    View full-size slide

  19. 繰り返しdispatch
    シーンのレンダリングループで
    時間を更新して位置を再計算
    dispatchのコスト高そうなので
    あまりよくない……?
    scene.registerBeforeRender(async () => {
    time += 0.1;
    waveParamsUniformBuffer.updateFloat("time", time);
    waveParamsUniformBuffer.update();
    await sinWaveComputeShader.dispatchWhenReady(
    PARTICLE_ONE_SIDE,
    PARTICLE_ONE_SIDE
    );
    const res = await positionStorage.read();
    positionBuffer.set(new Float32Array(res.buffer));
    pointCloud.setParticles();
    });

    View full-size slide

  20. おわりに

    View full-size slide

  21. まとめと感想
    WebGPUのComputeShader気になっていたので
    入門できてうれしい
    Babylon.jsだと普通に動くっぽいのでみんなでやろう

    View full-size slide

  22. 参考
    WebGPUがついに利用可能に WebGL以上の高速な描画と、計算処理への可能性
    https://ics.media/entry/230426/
    IwakenLabShader勉強会でBabylon.jsにおけるComputeShaderでGPUパーティクルを行うデモの準備
    https://zenn.dev/drumath2237/scraps/082ff30318fb2e
    WGSL 仕様メモ
    https://ikorin2.hatenablog.jp/entry/2023/04/23/213426
    Compute Shaders | Babylon.js Documentation
    https://doc.babylonjs.com/features/featuresDeepDive/materials/shaders/computeShader

    View full-size slide