Geometry Instancing

Geometry Instancingのデモ

Geometry Instancingを使うと、下記のデモのように大量のオブジェクトを高速に表示できます。

WebGL 2.0 demo - Gepmetry Instancing

右上のUIからトーラス(ドーナツ形状の3Dオブジェクト)の数を変えたり、ワイヤーフレーム表示に切り替えられます。また、マウスドラッグやマウスホイール/上下矢印キーで視点を変更できます。

※このデモはWebGL 2.0で作成しています。WebGL 2.0に対応したブラウザでご覧ください。

従来のオブジェクトの描画方法

通常、WebGLではオブジェクトを描画する場合に下記のようなフローで実行します。まず最初にオブジェクトの各頂点をローカル座標(オブジェクトの中心を決め、その中心から見たそれぞれの頂点の座標)で定義しGPUに転送します。そして、オブジェクトのグローバル座標(オブジェクトの中心を3D空間のどこに配置するのかの座標)や拡縮、回転などのオブジェクトのプロパティを指定しつつドローコールを行いディスプレイに描画します。


WebGLの一般的な描画

従来のオブジェクトの描画方法の問題点

この方法だと一つのオブジェクトの描画に一度のドローコールを行います。1フレームに数百回程度の実行であれば通常の使用の範囲内ですが(モバイル向けの場合はもっと少なくする必要があるでしょうが)、たとえば木や岩などの同じオブジェクト(以下この単位となるオブジェクトをインスタンスとよぶことにします)千個を少しずつ位置や大きさなどのプロパティを変えて描画したい場合には千回のドローコールが必要となります。一般的にドローコールはGPUの演算性能と比べると負荷が高く、大量のドローコールを行うとGPUの性能を発揮できませんし、ドローコール自体のCPU負荷によってフレームレート低下の原因にもなります。


大量のインスタンスごとにそれぞれドローコールを行う

従来の方法で大量のオブジェクトを描画する工夫

この問題の解決として、複数のインスタンスを一つのオブジェクトとして扱う方法があります。インスタンスのそれぞれの頂点をインスタンスごとのグローバル座標や拡縮、回転、その他のプロパティを適用した状態で複数描画したいインスタンスの数だけ用意してGPUに転送しておきます。そうすれば一つのオブジェクトとして一度のドローコールで描画できます。


大量のインスタンスをGPUに定義し、一度のドローコールで描画する

しかし、この方法には問題が二つあります。まず一つはメモリ容量の問題です。複数のインスタンスを一つのオブジェクトとして扱うため、大量の頂点をGPUのメモリ(VRAM)に保持する必要があります。その容量はインスタンスあたりの頂点数 * インスタンス数 * インスタンスごとのプロパティ数に比例します。もう一つの問題は複数のインスタンスを一つのオブジェクトとして扱うことにより、個々のインスタンスのプロパティを個別に更新できないことです。ドローコール時にプロパティを指定する方法はオブジェクト全体に適用されるため、全てのインスタンスを同時に同じ方向に動かすことはできますが、それぞれを別々の方向に動かすことはできません。

従来の方法で大量のオブジェクトを個別に動かす工夫

二つ目の問題にも解決のアプローチはあります。GPUに配置した頂点情報をCPU側から変更する方法です。ドローコールの前に全てのインスタンスのそれぞれの頂点についてCPUで位置の更新を行ってからGPUに転送することでインスタンスごとに個別の動きをさせることができます。


大量のインスタンスの頂点情報をCPUから変更する

ただし、この方法でもまだ問題は残っており、大量のインスタンスの全ての頂点に対してデータ更新をしてGPUに転送する必要があります。このデータ転送量はインスタンスあたりの頂点数 * インスタンス数 * データを更新するプロパティ数に比例します。一般的にCPU側メモリからGPU側メモリへのデータ転送はGPUの演算性能と比べると低速なため、大量のデータ転送はGPUの性能を発揮できません。

Geometry Instancingのメリット

ここで登場するのがGeometry Instancingです。Geometry Instancingは一度のドローコール(描画命令)で複数のオブジェクトを一度に描画する機能です。同時に描画するオブジェクトは同じ形状(頂点)、マテリアル(シェーダー)である必要がありますが、オブジェクトごとのプロパティを個別に設定できます。大量に描画したいインスタンスそれぞれのプロパティをGPUに転送しておくことで、ドローコール時にインスタンスごとの頂点がそれぞれのインスタンスのプロパティを参照して描画されます。


Geometry Instancingで描画する

先ほどの方法では頂点ごとに必要だったインスタンスのプロパティがインスタンスの頂点に共通で参照できることがGeometry Instancingのキモです。問題になっていたGPUのメモリ量はインスタンスあたりの頂点数 + インスタンス数 * インスタンスごとのプロパティ数で済みますし、インスタンスごとに個別にプロパティを変更したい場合もデータ転送量はインスタンス数 * データを更新するプロパティ数で済みます。Geometry Instancingを使用しない場合と比べると、複数描画したいインスタンスあたりの頂点数が多いほど恩恵を受けます。

表題のデモは約1000頂点のトーラスをインスタンスとして1度のドローコールで100〜5000個描画しています。インスタンスごとに色と位置を設定し、それぞれ個別に回転させています。Geometry Instancingを使うことで大量の同じオブジェクトを高速に描画できることがわかります。

※Geometry InstancingはWebGL 1.0でもブラウザによっては拡張機能として使えます。こちらについては後日紹介します。

終わりに

WebGL 2.0で使用できるGeometry Instancingについてイメージがつかめたでしょうか? 大量のオブジェクトを一度に描画できるこの機能は3Dコンテンツを作成したことのある方にとってはとてもうれしい機能だと思います。モバイルを含めまだまだ対応ブラウザが少ない状況ですが、WebGL 2.0にはウェブでの3D表現向上に役立つ機能が備わっています。今のうちにできること学んでおいて対応ブラウザが増える日を楽しみに待ちましょう。

後編ではMultiple Render TargetsTransform Feedbackについて詳しく紹介します。