Water, terrain & plants

FFT Ocean / Spectral Ocean

FFT海洋

波のスペクトルから広い海面を合成する。

2 / 10

Interactive demo

Preparing the demo…

FFT Ocean / Spectral Ocean

Drag to orbit, Ctrl/⌘ + scroll to zoom. Use two fingers on touch screens.

This explanation is currently available in Japanese.

ひとことで

風の強さと向きから「どの長さ・向きの波がどれだけあるか」(波のスペクトル)を決め、何千〜何万もの波を高速フーリエ変換(FFT)で一度に足し合わせて海面を作る方法です。映画『タイタニック』の海で使われた Tessendorf の手法として知られ、今では多くのゲームの海の標準的な作り方になっています。

ゲームでの使いどころ

  • 広く自然な外洋:船や海戦のゲーム、オープンワールドの海。数本の波を重ねるゲルストナー波では出せない、細かく不規則な波が得られます。
  • 天候による変化:風速のパラメータを変えるだけで、凪からしけまで海の表情が変わります。
  • タイルとして敷き詰める:FFT で作った海面は端と端がつながる(周期的)ので、同じタイルを並べて無限の海にできます。大きさの違うタイルを数枚重ねると繰り返しが目立ちません。
  • 泡や法線も同時に:高さと一緒に、水平方向のずれ・法線・波頭の縮み具合(泡の量)も FFT で求められます。

しくみ

  1. スペクトルを作る:波数ベクトル k\mathbf{k}(波の向きと細かさ)ごとに、その波の強さを Phillips スペクトルなどで決めます。
P(k)=A e−1/(kL)2k4 ∣k^⋅w^∣2,L=V2gP(\mathbf{k}) = A\,\frac{e^{-1/(kL)^2}}{k^4}\,\big|\hat{\mathbf{k}} \cdot \hat{\mathbf{w}}\big|^2, \qquad L = \frac{V^2}{g}

VV は風速、w^\hat{\mathbf{w}} は風向きです。LL は「この風で育つ一番大きなうねり」の長さで、風が強いほど長い波が強くなります。∣k^⋅w^∣2|\hat{\mathbf{k}}\cdot\hat{\mathbf{w}}|^2 で、風と同じ向きの波ほど強くします。

  1. 初期の振幅にばらつきを与える:各 k\mathbf{k} について、ガウス乱数 ξr,ξi\xi_r, \xi_i を使って h~0(k)=12(ξr+i ξi)P(k)\tilde{h}_0(\mathbf{k}) = \frac{1}{\sqrt 2}(\xi_r + i\,\xi_i)\sqrt{P(\mathbf{k})} とします。これは最初に 1 回だけ計算します。

  2. 時間とともに位相を回す:深い海の波は ω=gk\omega = \sqrt{g k} の速さで進むので、毎フレーム

h~(k,t)=h~0(k) eiωt+h~0∗(−k) e−iωt\tilde{h}(\mathbf{k}, t) = \tilde{h}_0(\mathbf{k})\,e^{i\omega t} + \tilde{h}_0^{*}(-\mathbf{k})\,e^{-i\omega t}

とします。2 項目は、結果の高さが実数になるようにするための対の成分です。

  1. 逆 FFT で高さに戻す:h~(k,t)\tilde{h}(\mathbf{k}, t) の格子(例:2562256^2)を逆 FFT すると、海面の高さの格子が得られます。N2N^2 個の波を普通に足すと O(N4)O(N^4) かかりますが、FFT なら O(N2log⁡N)O(N^2 \log N) で済みます。

  2. 波頭を尖らせる:−i kkh~-i\,\frac{\mathbf{k}}{k}\tilde{h} も逆 FFT すると、各点の水平方向のずれが得られます。これを足すと、ゲルストナー波と同じように山が尖ります(「チョッピー波」)。

実装のポイント

  • GPU で計算する:実際のゲームではコンピュートシェーダーで FFT を行い、バタフライ演算の各段をピンポンバッファで往復させます。2562256^2 でも 1 ms 程度で済みます。
  • 格子の符号に注意:逆 FFT の結果は、1 マスおきに符号が反転した形で出てくることがあります(原点を中央に置いたスペクトルの場合)。(−1)x+z(-1)^{x+z} を掛けて戻します。
  • 複数の大きさのタイルを重ねる:例えば 250 m、37 m、5 m の 3 種類を足すと、うねりから小さなさざ波までそろい、繰り返しの周期も目立たなくなります。

つまずきやすい点

  • 格子より短い波の扱い:スペクトルは格子の細かさまでしか表現できません。さらに短い波は、法線マップや細かいタイルで補います。
  • チョッピー波の上げすぎ:横方向のずれを大きくしすぎると、水面が裏返ります。縮み具合(ヤコビアン)が負になる所を泡として白くし、係数を抑えます。
  • 風速の単位:L=V2/gL = V^2/g は風速 20 m/s で約 40 m になり、タイルの大きさを超える波は表現できません。タイルの大きさと風速の範囲を合わせます。

デモで試すこと

  1. 右下のスペクトル画像は、中心が長い波、外側が短い波の強さです。「風速」を上げると、中心付近(長いうねり)が強くなり、海面の起伏が大きくなります。
  2. 「風向き」を変えると、スペクトルの明るい方向が回り、波が進む向きがそろって変わります。
  3. 「波頭の尖り」を 0 にすると、丸い波になります。上げるほど山が細く鋭くなります。
  4. 「タイルの継ぎ目を表示」をオンにすると、同じ 32 m 四方のタイルを並べていることが分かります。継ぎ目で波がとぎれずにつながっているのが FFT 海洋の特徴です。

References

GPU Gems — Effective Water Simulation ↗

A general reference for this subcategory.