超弾幕も可能? WebGL高速化のススメ
- WebGL プログラミングにおける高速化・効率化 -
今日のテーマ
WebGL高速化のススメ
今日のテーマ
WebGL高速化のススメ
今回はあくまでも WebGL 特有の高速化 TIPS です。
今日のテーマ
WebGL高速化のススメ
今回はあくまでも WebGL 特有の高速化 TIPS です。
WebGL にあまりなじみのない方もいらっしゃると思うので……
そもそも WebGL って? というところから簡単に解説します。
WebGL とは
WebGL とは
javascript を利用してブラウザから呼び出すことができる OpenGL ES 準拠の API。
WebGL とは
javascript を利用してブラウザから呼び出すことができる OpenGL ES 準拠の API。
GPU を直接叩くことができ、ブラウザでネイティブアプリのような高速なグラフィック処理を行うことができる。
WebGL を利用した例や参考 URL
Spherical Environment Mapping (MatCap/LitSphere) and Normal Mapping
http://www.clicktorelease.com/code/spherical-normal-mapping/#
WebGL のブラウザ事情
WebGL のブラウザ事情
大手ブラウザベンダーはほぼ対応済み。
WebGL のブラウザ事情
大手ブラウザベンダーはほぼ対応済み。
モバイル対応も徐々に進んでいる。
WebGL のブラウザ事情
大手ブラウザベンダーはほぼ対応済み。
モバイル対応も徐々に進んでいる。
それでもそもそも扱える人が少ないので、特に実務で利用されているケースは稀。
STG と WebGL の相性は?
STG と WebGL の相性は?
STG は画面上に大量のオブジェクトを描かないといけないケースが多い。
STG と WebGL の相性は?
STG は画面上に大量のオブジェクトを描かないといけないケースが多い。
派手なエフェクトを盛り込むことでより爽快感などが増す。
STG と WebGL の相性は?
STG は画面上に大量のオブジェクトを描かないといけないケースが多い。
派手なエフェクトを盛り込むことでより爽快感などが増す。
いずれも WebGL の得意とする分野!
どうして相性がいいの?
どうして相性がいいの?
大量のオブジェクトをスクリーンに描くには高速なグラフィック処理が必要。WebGL は直接 GPU にアクセスすることで高速にオブジェクトを描画できる。
どうして相性がいいの?
大量のオブジェクトをスクリーンに描くには高速なグラフィック処理が必要。WebGL は直接 GPU にアクセスすることで高速にオブジェクトを描画できる。
また、シェーダを自前で記述できるので、半透明や色の合成といった比較的負荷の大きな処理も高速にさばくことができる。
ただでさえ速い WebGL だけど……
ただでさえ速い WebGL だけど……
今回はさらに高速化するためのテクニックを紹介します。
ただでさえ速い WebGL だけど……
今回はさらに高速化するためのテクニックを紹介します。
もともと WebGL を利用したことがない方にもできる限り理解していただけるように、詳細なコードの記述例を示すよりも、概念を理解できるよう努めつつ解説していきます。
3D プログラミング(WebGL)の基本
3D プログラミング(WebGL)の基本
1.頂点データを用意する
3D プログラミング(WebGL)の基本
1.頂点データを用意する
これは頂点の位置などを格納した単なる配列データ。
3D プログラミング(WebGL)の基本
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
];
3D プログラミング(WebGL)の基本
2.シェーダに渡せるように加工する
3D プログラミング(WebGL)の基本
2.シェーダに渡せるように加工する
WebGL の API を使ってシェーダに渡せる状態にデータを加工。
3D プログラミング(WebGL)の基本
2.シェーダに渡せるように加工する
WebGL の API を使ってシェーダに渡せる状態にデータを加工。
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(position), gl.STATIC_DRAW);
3D プログラミング(WebGL)の基本
3.シェーダに送る
3D プログラミング(WebGL)の基本
3.シェーダに送る
WebGL の API で、加工したデータをシェーダへと送る。
3D プログラミング(WebGL)の基本
3.シェーダに送る
WebGL の API で、加工したデータをシェーダへと送る。
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(location, stride, gl.FLOAT, false, 0, 0);
3D プログラミング(WebGL)の基本
4.描画命令を出す
3D プログラミング(WebGL)の基本
4.描画命令を出す
描画命令(ドローコール)を発行してレンダリングする。
3D プログラミング(WebGL)の基本
4.描画命令を出す
描画命令(ドローコール)を発行してレンダリングする。
gl.drawArrays(gl.TRIANGLES, 0, length);
3D プログラミング(WebGL)の基本
canvas2D による描画では、描きたい形状ごとに異なるメソッドがある。
3D プログラミング(WebGL)の基本
canvas2D による描画では、描きたい形状ごとに異なるメソッドがある。
WebGL の場合は、描かれるのは頂点データによって形成されたポリゴン(点や線含む)なので、事前にシェーダに何を描くのかを通知しておき、それから描画命令を発行するという手順を踏む。
3D プログラミング(WebGL)の基本
canvas2D による描画では、描きたい形状ごとに異なるメソッドがある。
WebGL の場合は、描かれるのは頂点データによって形成されたポリゴン(点や線含む)なので、事前にシェーダに何を描くのかを通知しておき、それから描画命令を発行するという手順を踏む。
ぶっちゃけ……めんどくさい……
ぶっちゃけめんどくさいけど……
面倒ではあるけど速さには変えられません!
ぶっちゃけめんどくさいけど……
面倒ではあるけど速さには変えられません!
冗長な分だけ改善の余地もあるという無理矢理なこじつけで精神の安定を図ってください。
ぶっちゃけめんどくさいけど……
面倒ではあるけど速さには変えられません!
冗長な分だけ改善の余地もあるという無理矢理なこじつけで精神の安定を図ってください。
実際にどのへんに改善の余地があるの?
3D プログラミング(WebGL)の基本
- 頂点データを用意する
- シェーダに渡せるように加工する
- シェーダに送る
- 描画命令を出す
3D プログラミング(WebGL)の基本
- 頂点データを用意する ← 初期化時に用意
- シェーダに渡せるように加工する ← 初期化時に用意
- シェーダに送る
- 描画命令を出す
3D プログラミング(WebGL)の基本
- 頂点データを用意する ← 初期化時に用意
- シェーダに渡せるように加工する ← 初期化時に用意
- シェーダに送る ← 毎フレームごとに行う(場合が多い)
- 描画命令を出す ← 毎フレームごとに行う
3D プログラミング(WebGL)の基本
- 頂点データを用意する ← 初期化時に用意
- シェーダに渡せるように加工する ← 初期化時に用意
- シェーダに送る ← 毎フレームごとに行う(場合が多い)
- 描画命令を出す ← 毎フレームごとに行う
毎フレームやらないといけない処理の部分を、初期化時の工夫でいかに負荷軽減させられるかがポイント。
高速化におけるポイント
どの部分がボトルネックになりやすいのかを知り、そのうえでピンポイントに対応する。
高速化におけるポイント
どの部分がボトルネックになりやすいのかを知り、そのうえでピンポイントに対応する。
基本的に GPU との通信が発生する処理は少ないほうがよい。
高速化におけるポイント
どの部分がボトルネックになりやすいのかを知り、そのうえでピンポイントに対応する。
基本的に GPU との通信が発生する処理は少ないほうがよい。
特にドローコールは極力少ないほうがよい。
高速化への道
初級編
高速化への道
初級編
まずは初級編から。
初級編では WebGL というよりも javascript のプログラム自体に対してできる工夫。
高速化への道:初級編
- 先に計算できるもんは全部計算しておけ!
高速化への道:初級編
- 先に計算できるもんは全部計算しておけ!
- 行列・クォータニオン
- サイン・コサイン・ラジアン
- 乱数
高速化への道:初級編
- 先に計算できるもんは全部計算しておけ!
- 行列・クォータニオン
- サイン・コサイン・ラジアン
- 乱数
サインやコサイン、ラジアンや乱数などは事前に計算してしまい配列に入れておくと便利かつ負荷軽減につながる。
高速化への道:初級編
- 先に計算できるもんは全部計算しておけ!
- 行列・クォータニオン
- サイン・コサイン・ラジアン
- 乱数
サインやコサイン、ラジアンや乱数などは事前に計算してしまい配列に入れておくと便利かつ負荷軽減につながる。
クォータニオンはそこまで計算の負荷が高くはないものの、行列関係はそれなりに計算が複雑なので、事前に掛け合わせておけるものは極力掛け合わせておく。
高速化への道:初級編
- 先にバインドできるもんは全部バインドしておけ!
高速化への道:初級編
- 先にバインドできるもんは全部バインドしておけ!
- 頂点バッファ・インデックスバッファ
- テクスチャ(テクスチャユニットをうまく使う)
高速化への道:初級編
- 先にバインドできるもんは全部バインドしておけ!
- 頂点バッファ・インデックスバッファ
- テクスチャ(テクスチャユニットをうまく使う)
バッファ関係は全体の設計によるが、可能であれば事前にバインドしてしまうことで効率化を図る。テクスチャのバインドは工夫すれば最初に一括でできるので、そのような実装になるようにコードを書く。
高速化への道:初級編
- 先にバインドできるもんは全部バインドしておけ!
- 頂点バッファ・インデックスバッファ
- テクスチャ(テクスチャユニットをうまく使う)
バッファ関係は全体の設計によるが、可能であれば事前にバインドしてしまうことで効率化を図る。テクスチャのバインドは工夫すれば最初に一括でできるので、そのような実装になるようにコードを書く。
テクスチャユニットの概念を利用したり、できるだけテクスチャの枚数を減らしたりすることで高速化が見込める。
テクスチャユニット補足
ひとつのテクスチャユニットに、バインドするテクスチャを逐次切り替えながら処理するよりも……
テクスチャユニット補足
ひとつのテクスチャユニットに、バインドするテクスチャを逐次切り替えながら処理するよりも……
複数のテクスチャユニットにあらかじめすべてのテクスチャをバインドしておいて、シェーダ側でユニットを切り替えながらレンダリングするほうがよい。
テクスチャユニット補足
ひとつのテクスチャユニットに、バインドするテクスチャを逐次切り替えながら処理するよりも……
複数のテクスチャユニットにあらかじめすべてのテクスチャをバインドしておいて、シェーダ側でユニットを切り替えながらレンダリングするほうがよい。
これはバインドを実行するたびに GPU とのやり取りが必要になるため。
高速化への道
中級編
高速化への道
中級編
続いては中級編。
高速化への道
中級編
続いては中級編。
ここでは WebGL 特有の、頂点データの扱いやシェーダとの連携について考える。
高速化への道:中級編
- 頂点データは極力まとめろ!
高速化への道:中級編
- 頂点データは極力まとめろ!
100 頂点のモデルを 3 回のドローコールでレンダリングするなら、300 頂点のモデルとして扱い 1 回のドローコールで描いてしまったほうがパフォーマンスがよい。
高速化への道:中級編
- 頂点データは極力まとめろ!
100 頂点のモデルを 3 回のドローコールでレンダリングするなら、300 頂点のモデルとして扱い 1 回のドローコールで描いてしまったほうがパフォーマンスがよい。
先述のとおり頂点データは、元々は単なる配列にしか過ぎないので、事前に連結してひとまとめにしてから加工することで、結果ドローコールを節約できる。
高速化への道:中級編
- javascript でやるかシェーダでやるか見極めろ!
高速化への道:中級編
- javascript でやるかシェーダでやるか見極めろ!
たとえばポイントスプライトで 1000 個のパーティクルをレンダリングしたいとする。
高速化への道:中級編
- javascript でやるかシェーダでやるか見極めろ!
たとえばポイントスプライトで 1000 個のパーティクルをレンダリングしたいとする。
こんなときは 1000 回点を描くのではなく、1000 頂点のモデルとして扱い座標変換はシェーダ側でやってもらう。そうすればドローコールはたったの 1 回で済む。
ちなみに……
ちなみに……
jsstg に出展した、拙作 raychin.tv では、画面上に描かれているのはすべてポリゴンではなくポイントスプライト。
ちなみに……
jsstg に出展した、拙作 raychin.tv では、画面上に描かれているのはすべてポリゴンではなくポイントスプライト。
爆発エフェクト用の頂点データは、球体の内側にランダムに頂点をばらまいて、それを時間経過と共に拡大することでそれっぽく見せている。
ちなみに……
jsstg に出展した、拙作 raychin.tv では、画面上に描かれているのはすべてポリゴンではなくポイントスプライト。
爆発エフェクト用の頂点データは、球体の内側にランダムに頂点をばらまいて、それを時間経過と共に拡大することでそれっぽく見せている。
raichin.tv ← 参考
散らばっている点だけでなく、煙のように見えている部分も実はポリゴンではなくポイントスプライト、つまりただのデカい点。
そのデカい点に、動的に生成したノイズテクスチャを貼り付けて煙や霧のように見せている。
高速化への道
上級編
高速化への道
上級編
上級編では、環境に依存してしまうものの、高速化において重要な役割を持つ WebGL の拡張機能を有効化して、さらにパフォーマンスを上げる。
高速化への道:上級編
- 拡張機能を駆使しろ!
高速化への道:上級編
- 拡張機能を駆使しろ!
- OES_vertex_array_object (VAO)
- ANGLE_instanced_arrays
高速化への道:上級編
- 拡張機能を駆使しろ!
- OES_vertex_array_object (VAO)
- ANGLE_instanced_arrays
今回紹介するのはこの二つの拡張機能。
高速化への道:上級編
- OES_vertex_array_object (VAO)
高速化への道:上級編
- OES_vertex_array_object (VAO)
VAO は頂点のバインドをより簡単にする。javascript 側の冗長な処理は簡略化され単純に負担が軽減される。
高速化への道:上級編
- OES_vertex_array_object (VAO)
VAO は頂点のバインドをより簡単にする。javascript 側の冗長な処理は簡略化され単純に負担が軽減される。
通常、頂点が [ 位置・法線・テクスチャ座標 ] という三つの属性を持つ場合、VAO を使わないケースでは 3 回のバインド作業が必要。VAO を用いる場合は 1 回だけバインドすればよい。
高速化への道:上級編
- OES_vertex_array_object (VAO)
ちなみに……
高速化への道:上級編
- OES_vertex_array_object (VAO)
ちなみに……
VAO は 現在ドラフト公開されている WebGL 2.0 では拡張機能ではなく標準で使えるようになる。
高速化への道:上級編
- OES_vertex_array_object (VAO)
ちなみに……
VAO は 現在ドラフト公開されている WebGL 2.0 では拡張機能ではなく標準で使えるようになる。
さらに言うと、OpenGL 3 系では必須扱いになっていることから、最終的には必須機能になると思われるため要チェック!
高速化への道:上級編
- ANGLE_instanced_arrays
高速化への道:上級編
- ANGLE_instanced_arrays
インスタンスドアレイは使い方がちょっと難しいもののかなり強力。
高速化への道:上級編
- ANGLE_instanced_arrays
インスタンスドアレイは使い方がちょっと難しいもののかなり強力。
同じ頂点データからなるモデルであれば、たった一度の描画命令で、基本的に何個でも制限なく同時に描画することができる。
高速化への道:上級編
- ANGLE_instanced_arrays
インスタンスドアレイは使い方がちょっと難しいもののかなり強力。
同じ頂点データからなるモデルであれば、たった一度の描画命令で、基本的に何個でも制限なく同時に描画することができる。
しかも位置や色などの頂点属性はモデルごとに個別に指定することが可能。
高速化への道:上級編
ちなみに ANGLE_instanced_arrays も、WebGL 2.0 では標準機能に組み込まれます。
高速化への道:上級編
ちなみに ANGLE_instanced_arrays も、WebGL 2.0 では標準機能に組み込まれます。
WebGL 2.0 はよ!!
まとめてみると……
まとめてみると……
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
まとめてみると……
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
- 行列や角度の計算などはあらかじめやっておく
まとめてみると……
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
- 行列や角度の計算などはあらかじめやっておく
- 頂点データは配列を極力結合してから加工する
まとめてみると……
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
- 行列や角度の計算などはあらかじめやっておく
- 頂点データは配列を極力結合してから加工する
- シェーダ関連の処理も事前にやれるだけやっとく
まとめてみると……
WebGL に特有の高速化は、いかに準備を整えた状態でメインループを開始できるかに掛かっている。
- 行列や角度の計算などはあらかじめやっておく
- 頂点データは配列を極力結合してから加工する
- シェーダ関連の処理も事前にやれるだけやっとく
- 実行環境を限定してしまうが、限界までチューニングするなら拡張機能の導入を検討する
WebGL に興味を持ったなら
WebGL に興味を持ったなら
基本的な WebGL の実装から、拡張機能の使い方まで解説してくれる便利なサイトが!
ありがとうございました。