WebGL高速化のススメ
WebGL高速化のススメ
今回はあくまでも WebGL 特有の高速化 TIPS です。
WebGL高速化のススメ
今回はあくまでも WebGL 特有の高速化 TIPS です。
WebGL にあまりなじみのない方もいらっしゃると思うので……
そもそも WebGL って? というところから簡単に解説します。
javascript を利用してブラウザから呼び出すことができる OpenGL ES 準拠の API。
javascript を利用してブラウザから呼び出すことができる OpenGL ES 準拠の API。
GPU を直接叩くことができ、ブラウザでネイティブアプリのような高速なグラフィック処理を行うことができる。

Spherical Environment Mapping (MatCap/LitSphere) and Normal Mapping
http://www.clicktorelease.com/code/spherical-normal-mapping/#
大手ブラウザベンダーはほぼ対応済み。
大手ブラウザベンダーはほぼ対応済み。
モバイル対応も徐々に進んでいる。
大手ブラウザベンダーはほぼ対応済み。
モバイル対応も徐々に進んでいる。
それでもそもそも扱える人が少ないので、特に実務で利用されているケースは稀。
STG は画面上に大量のオブジェクトを描かないといけないケースが多い。
STG は画面上に大量のオブジェクトを描かないといけないケースが多い。
派手なエフェクトを盛り込むことでより爽快感などが増す。
STG は画面上に大量のオブジェクトを描かないといけないケースが多い。
派手なエフェクトを盛り込むことでより爽快感などが増す。
いずれも WebGL の得意とする分野!
大量のオブジェクトをスクリーンに描くには高速なグラフィック処理が必要。WebGL は直接 GPU にアクセスすることで高速にオブジェクトを描画できる。
大量のオブジェクトをスクリーンに描くには高速なグラフィック処理が必要。WebGL は直接 GPU にアクセスすることで高速にオブジェクトを描画できる。
また、シェーダを自前で記述できるので、半透明や色の合成といった比較的負荷の大きな処理も高速にさばくことができる。
今回はさらに高速化するためのテクニックを紹介します。
今回はさらに高速化するためのテクニックを紹介します。
もともと WebGL を利用したことがない方にもできる限り理解していただけるように、詳細なコードの記述例を示すよりも、概念を理解できるよう努めつつ解説していきます。
1.頂点データを用意する
1.頂点データを用意する
これは頂点の位置などを格納した単なる配列データ。
1.頂点データを用意する
これは頂点の位置などを格納した単なる配列データ。
var position = [-1.0, 1.0, 0.0,-1.0, -1.0, 0.0, 1.0, 1.0, 0.0, 1.0, -1.0, 0.0];2.シェーダに渡せるように加工する
2.シェーダに渡せるように加工する
WebGL の API を使ってシェーダに渡せる状態にデータを加工。
2.シェーダに渡せるように加工する
WebGL の API を使ってシェーダに渡せる状態にデータを加工。
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);3.シェーダに送る
3.シェーダに送る
WebGL の API で、加工したデータをシェーダへと送る。
3.シェーダに送る
WebGL の API で、加工したデータをシェーダへと送る。
gl.enableVertexAttribArray(location);gl.vertexAttribPointer(location, stride, gl.FLOAT, false, 0, 0);4.描画命令を出す
4.描画命令を出す
描画命令(ドローコール)を発行してレンダリングする。
4.描画命令を出す
描画命令(ドローコール)を発行してレンダリングする。
gl.drawArrays(gl.TRIANGLES, 0, length);canvas2D による描画では、描きたい形状ごとに異なるメソッドがある。
canvas2D による描画では、描きたい形状ごとに異なるメソッドがある。
WebGL の場合は、描かれるのは頂点データによって形成されたポリゴン(点や線含む)なので、事前にシェーダに何を描くのかを通知しておき、それから描画命令を発行するという手順を踏む。
canvas2D による描画では、描きたい形状ごとに異なるメソッドがある。
WebGL の場合は、描かれるのは頂点データによって形成されたポリゴン(点や線含む)なので、事前にシェーダに何を描くのかを通知しておき、それから描画命令を発行するという手順を踏む。
ぶっちゃけ……めんどくさい……
面倒ではあるけど速さには変えられません!
面倒ではあるけど速さには変えられません!
冗長な分だけ改善の余地もあるという無理矢理なこじつけで精神の安定を図ってください。
面倒ではあるけど速さには変えられません!
冗長な分だけ改善の余地もあるという無理矢理なこじつけで精神の安定を図ってください。
実際にどのへんに改善の余地があるの?
毎フレームやらないといけない処理の部分を、初期化時の工夫でいかに負荷軽減させられるかがポイント。
どの部分がボトルネックになりやすいのかを知り、そのうえでピンポイントに対応する。
どの部分がボトルネックになりやすいのかを知り、そのうえでピンポイントに対応する。
基本的に GPU との通信が発生する処理は少ないほうがよい。
どの部分がボトルネックになりやすいのかを知り、そのうえでピンポイントに対応する。
基本的に GPU との通信が発生する処理は少ないほうがよい。
特にドローコールは極力少ないほうがよい。
初級編
初級編
まずは初級編から。
初級編では WebGL というよりも javascript のプログラム自体に対してできる工夫。
サインやコサイン、ラジアンや乱数などは事前に計算してしまい配列に入れておくと便利かつ負荷軽減につながる。
サインやコサイン、ラジアンや乱数などは事前に計算してしまい配列に入れておくと便利かつ負荷軽減につながる。
クォータニオンはそこまで計算の負荷が高くはないものの、行列関係はそれなりに計算が複雑なので、事前に掛け合わせておけるものは極力掛け合わせておく。
バッファ関係は全体の設計によるが、可能であれば事前にバインドしてしまうことで効率化を図る。テクスチャのバインドは工夫すれば最初に一括でできるので、そのような実装になるようにコードを書く。
バッファ関係は全体の設計によるが、可能であれば事前にバインドしてしまうことで効率化を図る。テクスチャのバインドは工夫すれば最初に一括でできるので、そのような実装になるようにコードを書く。
テクスチャユニットの概念を利用したり、できるだけテクスチャの枚数を減らしたりすることで高速化が見込める。
ひとつのテクスチャユニットに、バインドするテクスチャを逐次切り替えながら処理するよりも……
ひとつのテクスチャユニットに、バインドするテクスチャを逐次切り替えながら処理するよりも……
複数のテクスチャユニットにあらかじめすべてのテクスチャをバインドしておいて、シェーダ側でユニットを切り替えながらレンダリングするほうがよい。
ひとつのテクスチャユニットに、バインドするテクスチャを逐次切り替えながら処理するよりも……
複数のテクスチャユニットにあらかじめすべてのテクスチャをバインドしておいて、シェーダ側でユニットを切り替えながらレンダリングするほうがよい。
これはバインドを実行するたびに GPU とのやり取りが必要になるため。
中級編
中級編
続いては中級編。
中級編
続いては中級編。
ここでは WebGL 特有の、頂点データの扱いやシェーダとの連携について考える。
100 頂点のモデルを 3 回のドローコールでレンダリングするなら、300 頂点のモデルとして扱い 1 回のドローコールで描いてしまったほうがパフォーマンスがよい。
100 頂点のモデルを 3 回のドローコールでレンダリングするなら、300 頂点のモデルとして扱い 1 回のドローコールで描いてしまったほうがパフォーマンスがよい。
先述のとおり頂点データは、元々は単なる配列にしか過ぎないので、事前に連結してひとまとめにしてから加工することで、結果ドローコールを節約できる。
たとえばポイントスプライトで 1000 個のパーティクルをレンダリングしたいとする。
たとえばポイントスプライトで 1000 個のパーティクルをレンダリングしたいとする。
こんなときは 1000 回点を描くのではなく、1000 頂点のモデルとして扱い座標変換はシェーダ側でやってもらう。そうすればドローコールはたったの 1 回で済む。
jsstg に出展した、拙作 raychin.tv では、画面上に描かれているのはすべてポリゴンではなくポイントスプライト。
jsstg に出展した、拙作 raychin.tv では、画面上に描かれているのはすべてポリゴンではなくポイントスプライト。
爆発エフェクト用の頂点データは、球体の内側にランダムに頂点をばらまいて、それを時間経過と共に拡大することでそれっぽく見せている。
jsstg に出展した、拙作 raychin.tv では、画面上に描かれているのはすべてポリゴンではなくポイントスプライト。
爆発エフェクト用の頂点データは、球体の内側にランダムに頂点をばらまいて、それを時間経過と共に拡大することでそれっぽく見せている。
raichin.tv ← 参考

散らばっている点だけでなく、煙のように見えている部分も実はポリゴンではなくポイントスプライト、つまりただのデカい点。
そのデカい点に、動的に生成したノイズテクスチャを貼り付けて煙や霧のように見せている。
上級編
上級編
上級編では、環境に依存してしまうものの、高速化において重要な役割を持つ WebGL の拡張機能を有効化して、さらにパフォーマンスを上げる。
今回紹介するのはこの二つの拡張機能。
VAO は頂点のバインドをより簡単にする。javascript 側の冗長な処理は簡略化され単純に負担が軽減される。
VAO は頂点のバインドをより簡単にする。javascript 側の冗長な処理は簡略化され単純に負担が軽減される。
通常、頂点が [ 位置・法線・テクスチャ座標 ] という三つの属性を持つ場合、VAO を使わないケースでは 3 回のバインド作業が必要。VAO を用いる場合は 1 回だけバインドすればよい。
ちなみに……
ちなみに……
VAO は 現在ドラフト公開されている WebGL 2.0 では拡張機能ではなく標準で使えるようになる。
ちなみに……
VAO は 現在ドラフト公開されている WebGL 2.0 では拡張機能ではなく標準で使えるようになる。
さらに言うと、OpenGL 3 系では必須扱いになっていることから、最終的には必須機能になると思われるため要チェック!
インスタンスドアレイは使い方がちょっと難しいもののかなり強力。
インスタンスドアレイは使い方がちょっと難しいもののかなり強力。
同じ頂点データからなるモデルであれば、たった一度の描画命令で、基本的に何個でも制限なく同時に描画することができる。
インスタンスドアレイは使い方がちょっと難しいもののかなり強力。
同じ頂点データからなるモデルであれば、たった一度の描画命令で、基本的に何個でも制限なく同時に描画することができる。
しかも位置や色などの頂点属性はモデルごとに個別に指定することが可能。
ちなみに ANGLE_instanced_arrays も、WebGL 2.0 では標準機能に組み込まれます。
ちなみに ANGLE_instanced_arrays も、WebGL 2.0 では標準機能に組み込まれます。
WebGL 2.0 はよ!!
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
基本的な WebGL の実装から、拡張機能の使い方まで解説してくれる便利なサイトが!
ありがとうございました。