LoginSignup
4
1

More than 1 year has passed since last update.

Babylon.jsでWebGPUを動かすはじめの一歩

Last updated at Posted at 2022-12-13

この記事はBabylon.js Advent Calendar 2022の14日目の記事です。

はじめに

この記事ではWebGPUの最初の一歩として、WebGPU対応ブラウザのインストールから、Babylon.jsで三角形を描画するまでを解説します。

WebGPUはJavascriptで実行できる低レベルグラフィックスAPIです。WebGPUは現在仕様策定中であり、WebGL、WebGL2よりも高速になることが期待されています。

Babylon.jsは実験的にWebGPUをサポートしており、Playgroundを使って気軽に試すことができます。

準備

WebGPUは現在仕様策定中であり、開発者向けブラウザでのみ動作します。

各ブラウザの対応状況は、こちらから確認できます。

Chrome Canaryのインストール

以下のサイトからChrome Canaryのインストーラをダウンロードします。
https://www.google.com/chrome/canary/
image.png
中央にある「Download Chrome Canary」をクリックすると、インストーラがダウンロードされます。
ダウンロードした「ChromeSetup.exe」を実行するとインストールが開始され、完了するとChrome Canaryが自動で立ち上がります。

フラグの有効化

WebGPUはChrome Canaryをインストールしただけでは動きません。ブラウザの設定からフラグを有効化する必要があります。

WebGPUは実験的な機能であるため、有効化することはセキュリティ上のリスクがあります。有効化した場合は信頼できるサイトのみにアクセスするようにしてください。

Chrome Canaryのアドレスバーに以下を入力し、実験機能の設定画面にアクセスします。
chrome://flags/#enable-unsafe-webgpu

「Unsafe WebGPU」を「Disable」から「Enabled」に変え、「Relaunch」をクリックしてChrome Canaryを再起動します。
image.png
これで、WebGPUを動かす準備が整いました。

WebGPUが動くか確認する

Babylon.jsのPlaygroundにアクセスします。
https://playground.babylonjs.com/

右上のボタンから、描画に使用するグラフィックスAPIを選択できるようになっています。デフォルトでは「WebGL2」になっており、WebGPUを有効化できていれば「WEBGPU」も選択できるようになっているはずです。

こちらを「WEBGPU」に切り替えてみましょう。切り替えた後にシーンが描画されれば準備OKです。

image.png

三角形を描画する

まずは緑色の三角形(クリスマスツリー)を描画してみます。
サンプルコードはこちらです。
screenshot_22-12-14_2-41.png

シェーダーコードの記述

はじめに、頂点シェーダーとフラグメントシェーダーをShadersStoreWGSLに格納しておきます。

shaders.js
BABYLON.ShaderStore.ShadersStoreWGSL["vertexVertexShader"]= ` 
    @vertex
    fn main(input : VertexInputs) -> FragmentInputs {

        var pos = array<vec2<f32>, 3>(
            vec2<f32>(0.0, 1.0),
            vec2<f32>(-1.0, -1.0),
            vec2<f32>(1.0, -1.0)
        );

        gl_Position = vec4<f32>(pos[gl_VertexID], 0.0, 1.0);
    }
`;

BABYLON.ShaderStore.ShadersStoreWGSL["fragmentFragmentShader"]=`
    @fragment
    fn main(input : FragmentInputs) -> FragmentOutputs {
        gl_FragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);;
    }
`;

WebGPUでは、WGSL(WebGPU Shading Language)を使ってシェーダーを記述します。
WGSLのドキュメントを読むと、@binding@location@builtinやらの属性をきちんと記述する必要があってGLSLよりも書くのが大変そうという印象ですが、上記のコードはすごくシンプルになっています。

実は、Babylon.jsは上記のコードを次のように書き換えてコンパイルしており、Babylon.jsが煩雑な部分を隠してくれているおかげでシンプルにコードを記述できるようになっています。

vertexShader.wgsl
var<private> gl_VertexID : u32;
var<private> gl_InstanceID : u32;
var<private> gl_Position : vec4<f32>;

struct VertexInputs {
    @builtin(vertex_index) vertexIndex : u32,
    @builtin(instance_index) instanceIndex : u32,
};

struct FragmentInputs {
    @builtin(position) position : vec4<f32>,
};

struct Internals {
    yFactor_: f32,
    textureOutputHeight_: f32,
};

@group(1) @binding(0) var<uniform> internals : Internals;

@vertex
fn main(input : VertexInputs) -> FragmentInputs {  var output : FragmentInputs;
    gl_VertexID = input.vertexIndex;
    gl_InstanceID = input.instanceIndex;

    var pos = array<vec2<f32>, 3>(
        vec2<f32>(0.0, 1.0),
        vec2<f32>(-1.0, -1.0),
        vec2<f32>(1.0, -1.0)
    );

    gl_Position = vec4<f32>(pos[gl_VertexID], 0.0, 1.0);
    output.position = gl_Position;
    output.position.y = output.position.y * internals.yFactor_;
    return output;
}
fragmentShader.wgsl
var<private> gl_FragCoord : vec4<f32>;
var<private> gl_FrontFacing : bool;
var<private> gl_FragColor : vec4<f32>;
var<private> gl_FragDepth : f32;

struct FragmentInputs {
    @builtin(position) position : vec4<f32>,
    @builtin(front_facing) frontFacing : bool,
};

struct FragmentOutputs {
    @location(0) color : vec4<f32>,
};

struct Internals {
    yFactor_: f32,
    textureOutputHeight_: f32,
};

@group(1) @binding(1) var<uniform> internals : Internals;

@fragment
fn main(input : FragmentInputs) -> FragmentOutputs {  var output : FragmentOutputs;
    gl_FragCoord = input.position;
    gl_FrontFacing = input.frontFacing;

    gl_FragColor = vec4<f32>(0.0, 1.0, 0.0, 1.0);
    output.color = gl_FragColor;
    return output;
}

createScene関数

createScene関数の中身もシンプルです。シェーダーコードをコンパイルして、画面に描画する処理を書きます。

createScene.js
let createScene = function() {
    
    let scene = new BABYLON.Scene(engine);

    let camera = new BABYLON.Camera("camera", BABYLON.Vector3.Zero(), scene);

    // シェーダーコードをコンパイルする
    const shader = new BABYLON.EffectWrapper({
        engine: engine,
        useShaderStore : true,
        fragmentShader: "fragment",
        vertexShader: "vertex",
        shaderLanguage: BABYLON.ShaderLanguage.WGSL,
    });

    // レンダラーを準備する
    const renderer = new BABYLON.EffectRenderer(engine);

    // シェーダーに描画指令を出す
    scene.onAfterRenderObservable.add(() => {
        renderer.render(shader, null);
    })

    return scene;
}

三角形にグラデーションで色を付ける

シェーダーを次のように書き換え、各頂点に別々の色を設定するとグラデーションで三角形が描画されます。
サンプルコードはこちらです。

screenshot_22-12-14_0-51.png

shaders.js
BABYLON.ShaderStore.ShadersStoreWGSL["vertexVertexShader"]= ` 
    varying vColor : vec4<f32>;

    @vertex
    fn main(input : VertexInputs) -> FragmentInputs {

        var pos = array<vec2<f32>, 3>(
            vec2<f32>(0.0, 1.0),
            vec2<f32>(-1.0, -1.0),
            vec2<f32>(1.0, -1.0)
        );

        var color = array<vec4<f32>, 3>(
            vec4<f32>(1.0, 0.0, 0.0, 1.0),
            vec4<f32>(0.0, 1.0, 0.0, 1.0),
            vec4<f32>(0.0, 0.0, 1.0, 1.0)
        );

        gl_Position = vec4<f32>(pos[gl_VertexID], 0.0, 1.0);
        vColor = color[gl_VertexID];
    }
`;

BABYLON.ShaderStore.ShadersStoreWGSL["fragmentFragmentShader"]=`
    varying vColor : vec4<f32>;

    @fragment
    fn main(input : FragmentInputs) -> FragmentOutputs {
        gl_FragColor = vColor;
    }
`;

おわりに

Babylon.jsの公式ドキュメントにもWebGPUのサンプルコードがいくつかありますので、ぜひのぞいてみてください。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1