• SELECT MENU

2017.08.07

【WebGL 2.0】 Transform Feedbackを試す!

TAKUMA OKAZAKI
[ENGINEER]
本業は飼育係

WebGL 2.0 がFireFox(PC)とChrome(PC,Android)に実装されました。
WebGL初心者の僕からすると、まだ1.0でさえまともに扱えないのにもう新しくなるの!?感がありますが、めげずにどんなものか調べてみましょう。。。

下記が非常に参考になるかと思います。

WebGL 2.0の概要
– http://qiita.com/emadurandal/items/4c7bd2a26ef2d732d734

で、この中で一番個人的におおっ!となったのが、表題のTransformFeedbackと呼ばれる機能。
ざっくり説明するとシェーダ内で計算結果を回せるというもの。GPUだけで頂点データの計算を回せるようになるということですね。javascript(CPU)でforループぶん回しても、1万ぐらいが限界で、1.0では大量のパーティクル計算をフレームバッファと呼ばれるものを使いテクスチャとして値を書き込むことで実現していました。

このあたりも上記の方が詳しく説明されているので参考にしてください。

で、肝心の実装方法ですが、どうもいい感じに簡単なサンプルが見つからない。

ネットにあがってるサンプルもなんだか動いてるのと動いてないのがあってようわからん…ということで、初心者(僕)にもわかりやすいように色々サンプル等を参考にしながら簡単なコードにしてみたものを作ってみました。

サンプル

単純にパーティクルが上から降ってくるだけのものになりますが、余計な処理はしていないので、理解しやすいのではないでしょうか。

これで15,000パーティクル!環境にもよると思いますが、10万までは余裕でいけると思います。

なお、ヘルパーとしてdoxas様のglcubic.jsを使用し、初期化メソッドだけWebGL2.0用に少し修正しております。

では、ざっくり説明を…

頂点シェーダから。

#version 300 es
precision highp float;
layout(location = 0) in vec3 position;
layout(location = 1) in float speedY;
uniform mat4 mvpMatrix;
out vec3 vPosition;
void main(){
    gl_Position = mvpMatrix * vec4(position,1.0);
    gl_PointSize = 2.;
    float py = position.y  < -3. ? 3. : position.y - speedY;
    vPosition = vec3(position.x,py,position.z);
}

フラグメントシェーダは普通

#version 300 es
precision highp float;
out vec4 outColor;
void main(){
    outColor = vec4(.8,.8,.9,1.);
}

こんな感じ。

少々1.0から記述が変わっている箇所がありますが、attributeやvaryingがinやoutになってたり、シェーダからロケーションを固定できるようになってたりといった感じです。で、ポイントになっているのは、頂点シェーダ側でout宣言されている、vPosition変数です。この変数に計算結果を代入し、次のフレームに値を渡す準備をします。

js側は

//シェーダ内のフィードバック用の変数を登録
gl.transformFeedbackVaryings(program, ['vPosition'], gl.SEPARATE_ATTRIBS);

このメソッドでリンクされたプログラム内の変数をtransformFeedbackに使います的な宣言をしてあげます。

んで、これを専用のメソッドでバッファにバインドします。

//Feedback先のバッファにバインド
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK,feedBacks[i]);
gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER,0,vertexBuffers[i][POS_LOCATION]);

はい、これで準備完了。ちなみにコードを見てもらえればわかりますが、VAOと呼ばれるバッファの入れ物を2つ用意して、値を更新しながら2つのバッファをぐるぐる回していく形で使用していきます。

では、いよいよループの中で使います。

//ここから
let dist = 1 - frame,
    vao = vertexArrays[frame],
    vbo  = vertexBuffers[dist],
    trans = feedBacks[dist];            

gl.bindVertexArray(vao);
gl.bindTransformFeedback(gl.TRANSFORM_FEEDBACK, trans);

gl.bindBufferBase(gl.TRANSFORM_FEEDBACK_BUFFER, 0,vbo[POS_LOCATION]);

//一旦描画OFF
gl.enable(gl.RASTERIZER_DISCARD);

gl.beginTransformFeedback(gl.POINTS);
gl3.draw_arrays(gl.POINTS,NUM_PARTICLES);
gl.endTransformFeedback();
//描画OFF解除
gl.disable(gl.RASTERIZER_DISCARD);
//ここまでTransformFeedBack

gl.beginTransformFeedbackメソッドでtransformfeedbackを開始、endTransformFeedbackで終了。
2つのバッファを切り替えながら値を更新しているのがわかると思います。

ざっくりですが、こんな感じで使っていくんだとおもいます。
僕のようなポンコツプログラマはノイズがない状態じゃないと理解が難しいので、少しでも助けになればと思います

なお、説明しきれなかった部分にもWebGL2.0特有の記述が入っているので、その辺りはWebGLの聖地を参照してください。(いつもお世話になっております。)

wgld.org WebGL2 contents -> https://wgld.org/d/webgl2/

ゴニョゴニョしてたらこんなのできた。(10万パーティクル)

glasses

[484]

Webテクノロジー

© 2017 Contents Co.,Ltd.