WebGLにおけるジオメトリインスタンシング（ANGLE_instanced_arrays）を丁寧に説明してみる


# はじめに 

みなさんこんにちは。エマ・デュランダルさんです。
最近、WebGL関連のQiita記事を良く書くようになってきました。

WebGLはただなんとなく使う分には楽ですが、複雑な３Dの世界を作ろうとすると、思いの外パフォーマンスが出ないことがあります。

「Three.jsでやるとこんなに速いのに、どうしてWebGLを直接使うとこんなに遅くなるんだろう」という経験をされた方も多いかもしれません。

こうした世のWebGLライブラリには、たくさんの高速化のノウハウが詰まっています。
それらのうちの幾つかの技法を知ることで、あなたのWebGLアプリケーションも確実に高速化することができます。

本記事では、その技法の一つ、WebGLの拡張機能である「ジオメトリインスタンシング（ANGLE_instanced_arrays）」について、ご紹介したいと思います。

# 基本的な考え方

さて、「インスタンシング」とは何か、なぜ必要なのか。そうしたことを説明する前に、まず一般的な３Dモデルの描画の概要からおさらいしましょう。

３Dモデルをモニタに表示するためには、当然ながら３Dの処理を行うGPUに対して、CPU側から3Dモデルについての各種データ（頂点位置、色、テクスチャ座標、法線など）を送らなければなりません。

WebGLでは、そのためにVBO（頂点バッファオブジェクト）というものを作成し（大抵はその実体はGPU上のビデオメモリ上に作られます）、そのVBO領域に各種データ（頂点位置、色、テクスチャ座標、法線など）を送り込むわけです。
（話を単純化するため、今回はモデルのテクスチャについては忘れてください）

で、VBOが完成したら、GPUに「そのVBOデータを使って３Dモデルを描いて！」と描画命令を発行します。

ただし、その際、VBOとは別の付随情報も大抵の場合、必要になります。「どこに描画するのか」を頂点シェーダーで計算するのに使う変換行列などです。これらは通常、Uniform変数という扱いでGPUに送信・設定します。

で、例えばですよ。あなたは画面に大量の岩を描画したいとしましょう。シーン上にある大量の岩を、あらかじめ全部３Dソフトなどで作るのは大変な手間です。
そこで、あなたは考えました。ベースとなる岩を１つだけ３Dソフトで作って、あとはそれを少し拡大・縮小したり色を変えたりして変化をつけながら、何万回も描画していけば、それっぽい岩場が表現できるんじゃないか、と。

実に合理的な考えですし、実際昔から３Dゲームなどではそのようなことが行われてきました（実際はさすがに１種類ではなく、数種類のベースパターンを作って組み合わせると思いますが）。

しかし、ここで問題があります。描画パフォーマンスです。

岩場にある岩は、当然置かれている位置が違います。大きさや色も微妙にそれぞれ違います。こうしたバリエーションを実現するために、描画の都度、いちいちVBOの頂点位置やカラーなどを書き換えていたら処理時間がかかりすぎます。

そこで、表示位置を移動させる変換行列やカラーベクトル、といった「岩ごとに異なる情報」を、シェーダーからアクセスできるUniform変数として、各岩の描画命令を出す前に都度設定する。という方法が現実的なアプローチとなるわけです。シェーダーでは、それらのUniform変数にアクセスして、岩にバリエーションをつけていけばいいわけですね。

ただ、これもまだパフォーマンス的には速いとは言えないんです（岩のVBOデータをその都度、直接書き換えるよりははるかにマシですが）。

「Uniform変数の設定」と「描画命令の発行」、これを、描画したい岩が１万個あったら、１万回やる必要があります。これはCPUにとって非常にヘビーな仕事ですし、GPUはCPUがそれらの仕事でひぃひぃ言っている間、「仕事を（与えられていないので）していない」時間が発生してしまいます。

これではとても最大のパフォーマンスを発揮することはできません。

## 限られたシチュエーションでの別の解決策：「メッシュ情報のマージ」

「メッシュ情報のマージ」という別の解決策もあります。これは、ベースとなる岩のメッシュデータと、それぞれ描画したい岩のバリエーション情報をもとに、１万個描画したいなら、岩１万個分の（それぞれの岩がバリエーション付けされた）岩場のVBOをCPU側でえいやっと作ってしまう、というものです。

　これなら、その巨大なVBOを一回描画するだけで、岩場を表示させることができます。Uniform変数の設定と描画命令を１万回やる必要は無くなるわけです。これで相当速くなります。
　
　ただし、デメリットとして、「岩」ではなく「岩場全体」を描画するわけですから、Uniform変数などで変化をつけられるのは、当然ながら「岩」単位ではなく「岩場全体」の単位になってしまいます。岩場の各「岩」を動かしたり、色を変えたり、といったことはできなくなります。
　つまり、一度作ったそれぞれの「岩」のバリエーションを途中で変更するのは困難（もしやるとすれば、巨大な「岩場」VBOの作り直し）ということになります。
　
　せいぜい、できる移動系の演出としては、岩場全体を動かすとか、カメラを動かす、くらいでしょうか（表示結果的には同じことですが）
　
なので、この「メッシュ情報のマージ」手法は、使えるシチュエーションが結構限定されてしまいます。

では、各「岩」にバリエーションをつけ、しかもそのそれぞれのバリエーションを随時変更・更新することができ、それでいて描画が高速にできる、他に良い方法はないのか！？

あるんです。それが、「ジオメトリインスタンシング（ANGLE_instanced_arrays拡張）」です。

## 柔軟性と高速性を両立する「ジオメトリインスタンシング」

ジオメトリインスタンシングでは、「各岩のバリエーション情報」を、前もってGPUに送っておきます。
VBOの方は、ベースとなる岩１つ分のデータを準備するだけです。

そして、ジオメトリインスタンシング専用のWebGL関数で設定・描画を行うと、
その描画関数で「描画したい岩の数」を１万個、と指定した場合は、その一回の描画関数の呼び出しだけで、１万個の岩を一気に描画することができます。

しかも、前もってGPUに送っておいた「各岩のバリエーション情報」にもアクセスできるので、各岩にバリエーションもついた状態で描画されるのです！　素晴らしいですね！

図にするとこんな感じ。

![インスタンシング図解.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/5f6988a3-18a0-6f78-ce1a-a01193eeb5c0.jpeg "インスタンシング図解.jpg")

Uniform変数の岩毎の設定。描画関数の岩毎の呼び出し。そうしたコストがなくなりますから、もう超速いです。描画したい岩（これを一般化して言うと「インスタンス」と呼びます）の数が多ければ多いほど、通常のやり方で描画した時とのパフォーマンスの差はどんどん大きくなっていきます。

もちろん、無限大のパフォーマンスを持っているわけではありません。「ジオメトリインスタンシング」はCPUの処理コストやGPUヘのデータ渡しのコストを激減させることで、GPUのスループットを向上させる技法です。

描画するインスタンスの数をあまりに増やすと、当然GPUの頂点パイプライン、頂点シェーダやフラグメントシェーダ、メモリ帯域の負荷が上がりますので、最近のGPUがいかに速いと言えど、どこかで必ず限界はきます。そのことは覚えておいてくださいね。

## 「インスタンス」って用語よく見るけどぶっちゃけて言うとなんなん？

なお、以降の説明で「インスタンス」という言葉をしょっちゅう使いますが、別にインスタンス描画をする際に、WebGL(OpenGL)のリソースとして「インスタンス」なるモノが生成されるわけではありません。

念のため、「インスタンス」とは何か。もう一度、一言で説明します。
インスタンシング描画では、描画する際のベースとなるメッシュがまずあるわけですが、これを複数描画した際のそれぞれの描画物のことです。あくまで概念的な呼称です。
オブジェクト指向言語でいう、「クラス」とそれをベースに実体が（複数）生成される「インスタンス」の関係と似ているかもしれませんね。
つまり、

「インスタンス描画のベースとなるメッシュ」＝オブジェクト指向でいう「クラス」
「インスタンス描画で複数描画した際のそれぞれの描画物」＝「インスタンス」

という対比イメージで覚えれば良いかと。

# WebGLでの「ジオメトリインスタンシング」の具体的な使い方

さて、いよいよ実践です。
主に３つ方法があります。

観点は、「インスタンス毎のデータをどのような形で用意して」「どのような方法でそれにアクセスするか」です。

以下のアプローチ２と３はWebGL2からでないと使えないので、広い環境で使えるようにという意味では、基本はアプローチ１だと思ってください。

（注：WebGL1.0でも *工夫すれば* アプローチ２と３実装できそうです。[^1] をご覧ください。）

## アプローチ１：インスタンス毎データをVBOで用意して、`vertexAttribDivisorANGLE`を使ってアクセス

インスタンス毎に異なるデータを用意する先として、VBOを使う方法です。現行最も普及しているWebGLであるWebGL1では、ほぼこれになると思います。

まず、インスタンスの元となるベースメッシュのVBOを用意します。まぁこれは頂点位置やカラー、テクスチャ座標ごとに個別のVBOを作ってもいいですし、[この記事](http://qiita.com/emadurandal/items/0bb83b545670475f51a3)で説明しているように、単一のインターリーブVBOとして作っても構いません。好きにやってください。

次に、「インスタンス毎に異なるデータ」（以後、「バリエーションデータ」と呼ぶことにします）のためのVBOを作成します。これもデータの種類毎にVBOをそれぞれ作ってもいいですし、単一のインターリーブVBOにしても構いません。（それどころか、ベースメッシュVBOにバリエーションデータを含めることすらできるはずです。まぁ、ややこしくなるので今回はやりませんが…）

さて、VBOを作ったら、そのVBOをバインドして、gl.vertexAttribPointerを呼んでGPUの頂点アトリビュート（レジスタ）とVBOを関連付けるんでしたね。
この時にインスタンシングで必要になってくるのが、`vertexAttribDivisorANGLE`関数です。

あ、事前に以下のようなコードで、ANGLE_instanced_arrays拡張を有効化してくださいね。

```javascript
var ext;
ext = gl.getExtension('ANGLE_instanced_arrays');
if(ext === null){
    alert('ANGLE_instanced_arrays拡張がサポートされていません。');
    ...
}
```

上記だと、`vertexAttribDivisorANGLE `の呼び出しは実際には`ext.vertexAttribDivisorANGLE(..., ...)`のようになります。

で、なぜ`vertexAttribDivisorANGLE`関数なるものが必要なんでしょう。ちょっとここで考えてみてください。

「バリエーションデータ」をVBOで用意しました。で、ベースとなるメッシュのデータもVBOです。
で、それぞれ頂点アトリビュートにアタッチして、通常のglDrawArrays/glDrawElementsとかで描画するとどうなるか…。

（以下、説明がかなりくどいですが、`vertexAttribDivisorANGLE`の役割がイマイチよく分からない、という方が結構いらっしゃるようなので、くどいくらい前提を説明しています）

ベースとなるメッシュのVBOが１万頂点あるとしましょう。つまり、ベースメッシュVBOのデータ個数は１万個です。
一方、「バリエーションデータ」の方は、描画したいインスタンスの数が１００個だったら、「バリエーションデータのVBO」（以後、「バリエーションVBO」と呼ぶことにします）のデータ個数は１００個用意することになります。（逆に１００万個表示したい場合だったら、データ個数は１００万個用意する必要があります。当然ですけど）。つまり、ベースメッシュVBOと「バリエーションVBO」のデータ個数は揃わないわけです。

この状態で、例えば表示したいインスタンス個数が１００個（つまり「バリエーションVBO」のデータ個数が１００個しか用意されていない）の場合に、
gl.drawArrays/gl.drawElementsを実行した場合…。これらの描画関数は、すべての頂点アトリビュートについて、gl.drawArraysなら引数で指定した頂点数、gl.drawElementsなら引数で指定したインデックス数分、各VBOにアクセスしに行きます。

単純にgl.drawArraysで考えてみると、１万頂点ですから各VBOに１万個分データアクセスします。ベースメッシュVBOはもちろんその個数あるので大丈夫です。ですが、「バリエーションVBO」の方は、たかだか１００個分しかありません。メモリ範囲をオーバーしてエラーになってしまいます。

逆に、インスタンス１００万個の場合はメモリ範囲オーバーにならないですが、でもそれだって正しい描画結果にはなりません。

それもそのはず、よく考えてみてください。「ベースメッシュVBO」は頂点毎/インデックス毎にアクセスされるべきデータで、一方の「バリエーションVBO」はそうではなく、 **インスタンス毎にアクセスされるべき** データなのです。それと、「ベースメッシュVBO」と一緒くたに頂点毎/インデックス毎にアクセスされたら、正しい結果になるわけがありません。

「ベースメッシュVBO」と「バリエーションVBO」は、アクセス単位が違う！　この違いをGPUに伝える手段はないものか！
それが、`vertexAttribDivisorANGLE`なのです。

`vertexAttribDivisorANGLE`は引数が２つありまして、一つ目は対象となる頂点アトリビュート（レジスタ）インデックス、２つ目は「除数」と呼ばれるものです。ここではとりあえず、除数の説明は後回しにします。

WebGLのデフォルトでは、描画命令がgl.drawArraysの場合は、VBOのデータ読み取り位置のインクリメントは頂点数単位、描画命令がgl.drawElementsの場合は、インデックス単位（インクリメントというより、インデックス参照だから飛び飛び移動ですね）です。

ところが、`vertexAttribDivisorANGLE`を呼ぶと、引数で指定した頂点アトリビュートにバインドしたVBOについては、データ読み取り位置のインクリメント単位が頂点/インデックス単位ではなく、インスタンス単位になるのです。

そう、『「ベースメッシュVBO」と「バリエーションVBO」のアクセス単位が違う』問題が、これで解決できるわけですね。

図にするとこんな感じ。

![インスタンシング図解2.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/b577b9b5-1e30-8311-3b2c-53259bf106a3.jpeg "インスタンシング図解2.jpg")

あ、ちなみにインスタンシング描画するには、先ほどまで説明として`gl.drawArrays`と`gl.drawElements`を引き合いに出しましたが、実際の描画命令はこれらではなく、それらと対応するインスタンシング版である`ext.drawArraysInstancedANGLE`/`ext.drawElementsInstancedANGLE `を使ってください。

### インデックスバッファのインデックスによる飛び飛びアクセスになっちゃわない？

ここで疑問に持たれた人がいるかもしれません。「`gl.drawElements`はVBO上を指定したインデックスバッファのインデックスが示す位置に飛び飛びアクセスするけど、`ext.drawElementsInstancedANGLE`はどうなの？「バリエーションVBO」上もインデックスによる飛び飛びアクセスになってしまわない？」と。

結論から言いますと、そうはなりません。「バリエーションVBO」（より厳密に言うと、`vertexAttribDivisorANGLE`で指定された頂点アトリビュートにバインドされたVBO）には、インデックスバッファのインデックスによる飛び飛びアクセスではなく、処理するインスタンスの順番によるシーケンシャルアクセスになります。安心だね！

ちなみに、一度アクセス単位をインスタンス単位にした頂点アトリビュートを、元の頂点/インデックス単位アクセスに戻したい場合は、もう一度`ext.drawArraysInstancedANGLE`を読んで、その時に第２引数である除数に0を設定します。

### 除数について

そうそう、その除数についてですが、これは通常は1を指定するんですが、それ以外の数字（2以上の数字）を指定した時、どうなるかというと、

**「新しいインデックス値」＝「その時描画しようとしているインスタンスの処理番号（インデックス）」/「除数」**　（整数演算なので結果は小数切捨てです）

としたとき、この「新しいインデックス値」で「バリエーションVBO」にデータアクセスします。

つまり、除数を２にすると、「バリエーションデータVBO」上のデータ読み取り位置のインクリメントの頻度が半分に、除数を４にすると、４分の１になります。

具体的な例を出すと例えば、「バリエーションデータVBO」のデータの一つとしてカラー情報があり、

そのデータが「赤、黄色、緑、青、黒」だった場合、

除数が１で５個インスタンスを描画すると、「赤、黄色、緑、青、黒」の色のインスタンスが描画されます。

除数が２で５個インスタンスを描画すると、「赤、赤、黄色、黄色、緑」という色の取り合わせでインスタンスが描画されます。

除数が３で５個インスタンスを描画すると、「赤、赤、赤、黄色、黄色」という色の取り合わせでインスタンスが描画されます。

図にするとこんな感じ。

![インスタンシング図解4.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/4fb21983-7520-289f-87b7-1ca7adfa0dae.jpeg "インスタンシング図解4.jpg")

### WebGL2でアプローチ１をやる場合

WebGL2では、ジオメトリインスタンシング機能は標準でサポートされていますので、`gl.getExtension`メソッドでいちいち拡張オブジェクトを取ってくる必要はありません。

`vertexAttribDivisorANGLE`、`drawArraysInstancedANGLE`、`drawElementsInstancedANGLE`を呼ぶときも、

`ext.vertexAttribDivisorANGLE ` ではなく `gl.vertexAttribDivisor` の形で呼ぶことができます。最後の`ANGLE`という文字列部分はいりません。

### サンプルコード

アプローチ１のサンプルコードを以下に示します。

[Approach1 on Runtant](http://runstant.com/emadurandal/projects/507fa001)

インスタンス毎の変換行列を３つの頂点アトリビュートに分けていますが、考え方はアプローチ３と同様です。
また、各Cubeが動かないのは味気ないので、インスタンス毎の変換行列データを毎フレーム更新しています（動かす必要がないなら、更新しなくても構いません）

![スクリーンショット 2016-06-11 1.48.21.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/ef59aa2a-629e-57d3-c6bf-d53b07b20f0a.jpeg "スクリーンショット 2016-06-11 1.48.21.jpg")


### ちょっと宣伝：glTips

本記事のサンプルコードでは、私が作った[glTips](https://github.com/emadurandal/glTips)という、WebGLコードで良くある定石コードをまとめたスニペット集を使っています。独自の定数やクラス等は一切作っておらず、本当に素の意味でのWebGLコード（を関数にまとめたもの）になっています。ライセンスはな、なんとPublic Domain！
どんな用途に使ってもいいですし、コードの一部をコピペして使っても文句言いません。好きに使ってください ^o^/

って、まだ公開したばかりで、そんな偉そうに言えるほどコードスニペット貯まってないですけどね^^;
徐々にコードを増やしていきますので、生暖かく見守ってくださいw

## アプローチ２：インスタンス毎データをUniform変数配列で用意して、GLSLのgl_InstanceID組み込み変数を使ってアクセス（要WebGL2）

WebGL2からになりますが、頂点シェーダーにおいて組み込み整数変数`gl_InstanceID`が使えるようになります。[^1]

ここには、ジオメトリインスタンシングをやっている時に、その時処理しているインスタンス番号が入ってきます。

これと、Uniform変数配列を使えば、`gl_InstanceID`でその配列を添字アクセスすることで、インスタンス毎にバリエーションをつけることができます。
基本的な例としては

```glsl
unform mat4 worldMatrices[100];
```

みたいな感じで、World行列のUniform行列変数配列を利用することですね。

ただし、このやり方は手軽なんですが、Uniform変数配列で確保できる容量は、実はそれほど大きくありません（GPUのVRAMでなく、シェーダーユニットのローカルメモリ上に確保されるため）。その点は注意ですね。

あともちろん、`gl_InstanceID`を配列の添字として使うだけでなく、普通に数値として使って（floatにキャストする必要があります）何か動的に値を計算、それをバリエーションとして使うっていうのでもいいです。

### 類似アプローチ：Uniform Buffer Objectを使う。

UniformBufferObjectとは何かというと、まぁGL系でよくある~~~~BufferObjectの扱いで、Uniform変数データを用意できるものです。
通常のUniform変数との違いとして、複数のシェーダープログラム間で共有することができる、とか、確保できるサイズが比較的大きい（実装依存）とか、Uniform変数よりもデータの更新が速い、といったことがあります。

もういくつもサンプル作るのめんどい（←ぉぃ）し、基本的にはアプローチ２の仲間だと思うので、簡単な紹介に留めましたが、気になる人は是非トライしてみてください。

### サンプルコード

アプローチ２のサンプルコードを以下に示します。
（WebGL2対応のブラウザ（Chrome CanaryまたはFirefox Nightly）で、WebGL2を有効化してみてください）

[Approach2 on Runtant](http://runstant.com/emadurandal/projects/a6e7d408)

インスタンス毎の変換行列を３つのUniform変数配列に分けていますが、考え方はアプローチ３と同様です。
もちろん、３つのvec4のUniform変数配列ではなく、１つのmat4のUniforom変数配列で、丸ごと変換行列を設定するやり方でやってもいいと思います。

![スクリーンショット 2016-06-11 1.50.09.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/c5c40520-d18d-889d-c0a4-c8eea3b7acb6.jpeg "スクリーンショット 2016-06-11 1.50.09.jpg")


## アプローチ３：インスタンス毎データをテクスチャで用意して、GLSLのgl_InstanceID組み込み変数を使ってアクセス（要WebGL2）

このアプローチも`gl_InstanceID`を使うのでWebGL2限定です。[^1]

アプローチ２は、確保できるUniform変数配列の容量の制約により、バリエーションをつけられるインスタンス数に限りがありました。

なら、インスタンス毎のデータを、Uniform変数配列ではなくて、テクスチャに格納して、それを`gl_InstanceID`を使ってフェッチすればいいじゃない！　というのがこのアプローチです。

ただし、注意点として、よほど「荒い」精度のデータで十分というケースを除いて（つまりほとんどのケースで）、テクスチャフォーマットはFP16（half float浮動小数点）である必要があります。

ただ、テクスチャの画素（テクセル）には、４要素しか詰めることができません。テクスチャの１画素分を１個のインスタンスのデータに相当させるのがわかりやすいので、ではその条件でWorld行列を詰めるにはどうしたらいいでしょうか。

方法は幾つかあると思うのですが、一番シンプルな方法としては、テクスチャを３枚使う、というものです。で、各テクスチャにWorld行列の各行成分を格納します。
あれ？　４枚じゃないの？　と思われたと人もいるかと思います。まぁ４枚でもいいんですけど、実質３枚で十分です。

というのは、World行列に与える変化というのは大抵、移動と回転と拡大縮小の３種類だと思います。このうち回転と拡大縮小は３行３列の行列で表すことが可能です。移動に関してのみ

![スクリーンショット 2016-06-11 1.18.07.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/7cb79940-40bb-a3a0-7f60-242044843080.jpeg "スクリーンショット 2016-06-11 1.18.07.jpg")

という行列になり、４列目に移動成分が来ます。しかしこれなら、３枚のテクスチャの第４成分に格納可能ですね。
で、移動・回転・拡大縮小の操作が組み合わさったWorld行列は、４行目成分が必ず「0 0 0 1」になります。
なので、テクスチャとして用意するのはWorld行列の１〜３行目成分に相当する、３枚でOKというわけです。

図にするとこんな感じ。

![インスタンシング図解3.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/cc270c2c-a9e9-0121-88c8-c19da1f644a6.jpeg "インスタンシング図解3.jpg")

テクスチャアクセスの方法としては、 @YVT さん（[Hyper3D](https://hyper3d.github.io)というThree.jsベースのハイクオリティレンダラーの開発者でいらっしゃいます）の[この記事](http://qiita.com/YVT/items/c695ab4b3cf7faa93885)を参考にすると良いでしょう（実際にうまくいくかは皆さんで検証してみてください）。

本記事では、@YVTさんの記事の最後に書かれていたように、WebGL2 (ES 3.0相当)なのでシンプルに`texelFetch`を使う方向でいきます。 

### サンプルコード

アプローチ３のサンプルコードを以下に示します。
（Firefox NightlyまたはChrome CanaryでWebGL2を有効化してみてください）

[Approach3 on Runtant](http://runstant.com/emadurandal/projects/ed6b6992)

![スクリーンショット 2016-06-11 1.51.04.jpg](https://qiita-image-store.s3.amazonaws.com/0/12753/a9501a1d-2961-7f88-a3e1-8f210ef01203.jpeg "スクリーンショット 2016-06-11 1.51.04.jpg")


## 各アプローチの比較表

各アプローチを比較してみるとだいたいこんな感じ

|            | アプローチ１(VBO)  | アプローチ２(Uniform配列)   | アプローチ３(テクスチャ)  |
|:-----------|:-----------:|:------------:|:-----------:|
| 扱えるインスタンス数| 大量  |    それほど多くない     |   大量   |
| 実装難易度     |    普通 |    易しい    |     やや難しい       |
| インスタンス毎データの更新速度     |  CPU側からの書き換えは遅い。Transform Feedback(WebGL2のみ)を使えば速い。 |    まぁ速い？    | CPU側からの書き換えだと遅い。Render to Textureなら速い。       |
| 備考         | WebGL1から主要ブラウザで使える（正確には拡張機能としての扱い） |      WebGL2が必要      | WebGL2が必要        |

まぁ、諸々を考えると、別にアプローチ１で良さそうな感じがしますね^^;

「インスタンス毎データの更新速度」は何かというと、インスタンス毎につけるバリエーション情報も、アプリケーションの状況に応じて変更したいことだってあると思うんですが、その際のデータの書き換えがどれくらい速くできるか、という項目です。

# インスタンシングやってみても非インスタンシング時とほとんどパフォーマンスが変わらない場合

これは実際私がハマりまして、 @YVT さんの助言により解決したのですが、（ @YVTさん、ありがとうございます。 ）
ベースメッシュの頂点数（インデックス数）が多い場合、GPUの頂点パイプラインに負荷がかかり、性能が頭打ちになるようです。

多分、

頂点パイプラインに流れ込む頂点データの量　＝　ベースメッシュの頂点数（インデックス数） × インスタンス数

と思われます。
なお、OpenGLのglDraw*系の命令は、（ドローコールは重いんだよ〜とよく言われる印象と比べると）意外と高速です。

したがって、インスタンシング版と非インスタンシング版を比較した時に、それぞれ

インスタンシング版：　ベースメッシュの頂点数（インデックス数） × インスタンス数　が多い
非インスタンシング版：　CPU側のドローコール処理に余裕がある。（そして、インスタンシング版と同量の頂点を描画している）

という場合は、上記理由から、インスタンシング版が非インスタンシング版と比べてほどんど性能が上がらない、ということが起こりえます。

インスタンシングの効果をはっきり実感したい方は、ベースメッシュの頂点数（インデックス数）を思い切って減らしてみてください。

# まとめ

いかがでしょうか。ジオメトリインスタンシング、その概念から使い方まで、最初は結構とっつきにくいものだと思います。
なので、今回かなり丁寧な説明を、動くサンプル付きでQiita記事にしてみました。

@cx20 さんの記事によると、現在、ANGLE_instanced_array拡張は主要なブラウザ全てでサポートされているようです。実質、どんどん使ってしまって構わないと思います。

# ジオメトリインスタンシングをさらに超えていけ…！（非WebGLだけど）

ジオメトリインスタンシングはかなり強力な描画機能ですが、
ネイティブ環境におけるDirectXやOpenGL（デスクトップ向け）の最新バージョンでは、さらに強力な描画機能が用意されるようになってきました。WebGLとは当面関係ありませんが、ちょっと紹介しましょう。

## インダイレクト描画

これは何かというと、GPU側に頂点データ（VBO）だけでなく、glDrawElementsやglDrawElementsInstanced（OpenGLでのインスタンス描画命令）で指定するような引数情報までをもGPU上のバッファ（描画命令バッファ）に前もって転送（またはCompute Shaderなどで動的生成）しておき、それらのデータを使って描画を行ってしまおうというものです。

これ、Compute Shaderと組み合わせれば、描画に必要な全情報をGPU自身が計算して用意して描画する、というCPUの手をわずらわせなくても独立して描画をすることができます。いわばGPUが自身でGPUを駆動するとでもいいましょうか。

WebGLでも早くできるようになるといいですね！

## glMultiDrawElementsIndirect()

長い名前ですねw　これ、すごいですよ。通常のジオメトリインスタンシングは、「同じ形状データ」のインスタンスを少しパラメータを変えて複数描画する、というものでしたが、
このglMultiDrawElementsIndirect()は、「複数の形状の異なる」メッシュデータを、インスタンス＆インダイレクト描画してしまうというものです。

もう、やけくそって感じですよね。私も使ったことありませんw
こういう最新機能を駆使すれば、Vulkanを使わずともかなり高速な3DCGアプリケーションが作れるかもしれません！

WebGLでも早く…（ry

## 最後に

今回はインスタンシングを使わない普通のバージョンとの速度比較までは行いませんでした。
が、それは次の機会で必ずやります。→[書きました](http://qiita.com/emadurandal/items/c393283b1ff53573594f)。
というのもね。結構面白いネタをまた考えてるんすよw
その昔、本物のインスタンシングに憧れていた人たちがおってだね…え、何の話か？　おっと、これ以上はまだ言えねぇ言えねぇww

というわけで、また[次回のエマさんQiita記事](http://qiita.com/emadurandal/items/c393283b1ff53573594f)でお会いしましょう〜〜〜♪


[^1]: [YVTさんの記事](http://qiita.com/YVT/items/4c29293d9c24d32ee8de#instanced-stereo-rendering)では、WebGL1.0で`gl_InstanceID`相当のことを実現するTipsが紹介されています。これを使えばアプローチ２や３をWebGL1.0でも実装可能と思われます。