Hello Triangle

この回では、WebGPUでシンプルな三角形を描画してみようと思います。

WebGPUの初期化

WebGPUの初期化はWebGLよりも複雑です。

WebGLではCanvas要素からgetContext("webgl"または"webgl2")としてWebGLレンダリングコンテキストを取得するだけでした。

const gl = canvas.getContext("webgl2");

一方、WebGPUではcontextの取得だけではなくdeviceの取得という作業を行う必要があります。

// webgpuコンテキストの取得 const context = canvas.getContext('webgpu') as GPUCanvasContext; // deviceの取得 const g_adapter = await navigator.gpu.requestAdapter(); const g_device = await g_adapter.requestDevice();

adapterとdeviceの違いですが、adapterは物理デバイス(物理的なGPU)、deviceは論理デバイス(抽象化したGPU)を指しています。

Vulkanなどの一般的な低レベルAPIでは、物理デバイスからはGPUのベンダー情報など、多くのベンダー固有の情報を取得することができるようですが、現状のWebGPUではあまり固有の情報は取れないようです。

次に、contextの設定を行います。

const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); context.configure({ device: g_device, format: presentationFormat, alphaMode: 'opaque', // or 'premultiplied' });

context.configure関数でdevice, format, alphaModeの3つの指定を行います。

deviceには先ほど取得した論理デバイスg_deviceを指定します。

formatには、navigator.gpu.getPreferredCanvasFormat()でcanvasのネイティブのピクセルフォーマットが取得できるのでそれを指定します。この関数では通常は"rgba8unorm"または"bgra8unorm"というフォーマットが返されます。初歩的な利用では、この関数をおまじないとして呼ぶようにしましょう。

alphaModeには'opaque'という文字列を指定します。このalphaModeはCanvasと背景となるHTML要素との合成方法を設定するものです。'opaque'の場合は、WebGPUの描画内容で完全に上書きします。alphaModeには’premultiplied'という値も設定することができ、こちらの場合はWebGPUの描画の結果、Canvas側のピクセルのアルファ要素が1未満だった場合、そのピクセルはcanvas側と背景のHTML要素の色で混合されます。

RenderPipelineの設定

次に、RenderPipelineの設定を行います。
WebGLではGPUに対する設定を様々なgl関数を用いて五月雨式に設定していましたが、WebGPUをはじめとする現在の低レベルAPIでは、一般にPipelineStateObject (PSO)と呼ばれるオブジェクトに対して、GPUに対する大半の設定をまとめて行います。

PSOはGPUの各処理工程(パイプライン工程)に対する設定を抽象化したものです。この設定をあらかじめいくつも作り置きし、状況に応じてGPUにアタッチすることで、GPUのパイプライン状態を高速に変更することが可能となります。

WebGPUではこのPSOはRenderPipelineという名称で呼ばれています。

image.png

image.png

WebGPUの初期化が終わったら、次にこのRenderPipelineの設定に入ります。

RenderPipelineでは主に設定するカテゴリが5種類あります。

  • GPUVertexState
  • GPUFragmentState
  • GPUPrimitiveState
  • GPUDepthStencilState
  • GPUMultisampleState

image.png

このうち、ほぼ必須なのは最初の3つです。
この3つを設定するコードを見てみましょう。

// create a render pipeline const pipeline = g_device.createRenderPipeline({ layout: 'auto', vertex: { module: g_device.createShaderModule({ code: vertWGSL, }), entryPoint: 'main', }, fragment: { module: g_device.createShaderModule({ code: fragWGSL, }), entryPoint: 'main', targets: [ // 0 { // @location(0) in fragment shader format: presentationFormat, }, ], }, primitive: { topology: 'triangle-list', }, });

まず最初のlayoutですが、ここにはほとんどの場合'auto'を指定します。最初はおまじないとして考えておけば結構です。

layout: 'auto',

次に、vertexです。論理デバイスg_deviceのcreateShaderModule関数にシェーダー文字列を渡しています。WebGPUのシェーダー言語はWGSLというWebGLのGLSLとは異なる言語なのですが、これについては後述します。
また、entiryPointでシェーダー内のエントリーポイントとなる関数名を指定しています。

vertex: { module: g_device.createShaderModule({ code: vertWGSL, }), entryPoint: 'main', },

次にfragmentです。moduleとentryPointに対する設定はvertexと同様ですが、他にtargetsというプロパティがあります。これは描画先のレンダーターゲットのフォーマットを指定するものです。ここにはcontextの設定時と同じnavigator.gpu.getPreferredCanvasFormat()で得られたフォーマットを指定しましょう。

fragment: { module: g_device.createShaderModule({ code: fragWGSL, }), entryPoint: 'main', targets: [ // 0 { // @location(0) in fragment shader format: presentationFormat, }, ],

最後にprimitiveです。ここでは描画しようとしているジオメトリのトポロジーの種類を文字列で指定します。

primitive: { topology: 'triangle-list', },

次のページ