Monday, June 1, 2015

Evan WallaceさんのWebGL Waterのソースを読んでみた

https://github.com/evanw/webgl-water
このレポジトリをローカルに取ってきてじっくり読んでみました。

波紋の作り方


浮動小数点数テクスチャをグリッドとして使っています。RGBAそれぞれを別の意味で使います。Rは水面の高さ、Gは水面の上下方向の加速度、BAは法線を格納するのに使います。

各グリッドのシミュレーションは以下の2つのルールで行います。

  • 上下左右の隣接4グリッドの高さの平均が自分の高さと異なる場合、周囲に高さを合わせるように加速度を調節します。
  • 各グリッドは自身の加速度によってのみ上下します。

グリッドは当然四角形ですが、上のルールだけで放射状に広がる波紋が出来るのが不思議です。

水面の法線の格納方法


シミュレーションが終わったらレンダリング前に各グリッドの法線を求め、上と同じテクスチャのBに法線のX、Aに法線のZを格納します。Yが格納されていませんが、法線は長さが常に1なので、ピタゴラスの定理と同じ原理でXとZからYを推測できます。

この方法の欠点は符号を復元できないことで、ここにXやZではなくYを省略する理由があります。WebGL WaterではY軸が上方向を向いており、常に正の方向になります。対してXとZは符号も保存する必要があります。

コースティック


水面が一切波立っていない場合と、水面が波立っている場合に分けて太陽光を水面で屈折させ、底または壁との衝突点を求めます。これを水面のグリッド全体で行い、波立っていない場合に比べて隣接グリッドの衝突点と自分の衝突点の距離が変化するかを見ます。広がった場合は光の密度が減り、狭まった場合は光の密度が増えると判断します。

密度の計算ですが、dFdx関数とdFdy関数を使うのがユニークです。つまり、各グリッドの波立っている場合と波立っていない場合の衝突点の計算を頂点シェーダで行い、それぞれvaryingでフラグメントシェーダに渡します。隣接グリッドの情報を直接読みに行くのではなくvaryingの変化量を見るだけなので追加のテクスチャフェッチが発生せず、高速に動作しそうです。

WebGLの拡張機能


浮動小数点数のテクスチャを使うため、OES_texture_float 拡張を使っています。
また、dFdx関数とdFdy関数を使うため、OES_standard_derivatives 拡張を使っています。

まとめ


簡潔で無駄の無いコードで、こんなきれいなエフェクトが作れるのはすごいですね。

No comments:

Post a Comment