Godotに実装されたFFTベースの海洋波レンダリング
(github.com/2Retr0)- GodotOceanWavesは、Godot Engineで方向性海洋波スペクトルの逆 Fourier 変換を用いて外洋の海面を生成するレンダリング実験であり、スクリプトからリアルタイム調整可能なパラメータで多様な海洋環境を再現する
- Gerstner wavesは穏やかな水面の低周波ディテールには適しているが、荒れた外洋の海面には限界があるため、このプロジェクトでは経験的海洋学データに基づくスペクトルとFFTを使用する
- 波の生成はTessendorf方式に従い、初期スペクトル状態を作成し、分散関係で時間伝播させた後、逆 Fourier 変換によって変位・法線・フォームマップを生成する
- レンダリングはAtlas GDC講演のBSDFに従いつつGGX分布を使用し、フォームは変位のJacobianが負になる領域で蓄積・減衰し、sea sprayはGodotのGPUParticles3Dとカスタムシェーダーで実装される
- 複数のwave cascade、更新周期の調整、cascadeごとの負荷分散によってタイリングとGPU作業量に対応しているが、cascadeは同じスペクトルテクスチャ解像度を使う必要があり、sea sprayの粒子方式は密度を増やす効率が低い
GodotOceanWavesがすること
- GodotOceanWavesはGodot Engineで外洋レンダリングを実験するプロジェクト
- 波の生成には方向性海洋波スペクトルの逆 Fourier 変換を使用する
- 公開されているパラメータは簡潔に構成されており、スクリプトでリアルタイムに変更して複数の海洋波環境を再現できる
- デモ動画として
ocean_demo.mp4とenvironment_demo.mp4が含まれている
Gerstner wavesではなくFourierベースの波を使う理由
- ゲームで水をアニメーション化する一般的な方法は、Gerstner wavesで頂点を変位させること
- Gerstner wavesは穏やかな水面の低周波ディテールをモデル化するには適しているが、外洋の荒れた表面を正確に表現するには不十分
- 海洋学者が収集した経験的データに基づく海洋波スペクトルを使うと、特定の海況を作るためにどの波とパラメータを変えればよいかを、より直接的に扱える
- FFTは古典的な離散 Fourier 変換の
O(N^2)より低いO(N log N)の計算量を持ち、並列システムへ拡張しやすいためGPU実行に適している - Gerstner wavesでは各スレッドが波の数
N個について計算する必要がある一方、FFTベースの波では各スレッドがlog(N)に相当する計算だけを行えばよい
シェーディング、フォーム、sea spray
- 海洋の照明モデルはおおむねAtlas GDC講演のBSDFに従う
- マイクロファセット分布にはBeckmannではなくGGXを使用する
- GGXのより平坦で滑らかなハイライトの方が、テストした複数の海洋波環境でより均一な見た目を提供したため
- 法線/フォームマップは、ワールド空間のピクセル密度に応じてbicubicとbilinearフィルタリングを混ぜてサンプリングする
- 低い表面解像度ではテクスチャのaliasingアーティファクトを減らし、高い表面解像度ではディテールを維持する
- Sea foamはTessendorf方式に従い、波の峰が内側に巻き込む位置、すなわち変位のJacobianが負である場所で生成を判定する
- フォームは複数回の波更新にわたってテクスチャへ線形に蓄積される
"foam grow rate"と"foam decay rate"パラメータが、それぞれ蓄積と指数減衰を制御する
- Sea sprayはGodotの
GPUParticles3Dノードとカスタムパーティクルシェーダーで実装される- パーティクルは
GPUParticles3Dのバウンディングボックス内の平面に均等分布する - その位置にあるフォーム量に応じてcullingされ、cullingされなかったパーティクルはランダムなオフセットからライフサイクルを開始する
- 各パーティクルは、1枚の静的テクスチャを使うbillboard spriteを使用する
- ライフサイクル中にscaleとdisplacementを変えてsplashの外観を再現し、mesh shaderのdissolve効果で空中で水しぶきがatomizeする感覚を出す
- パーティクルは
- このsea spray方式の大きな欠点は、パーティクル数を大幅に増やしても密度の増加が小さいこと
- パーティクルがバウンディングボックスに均等分布するため、追加したパーティクルの大半がcullingされるため
波シミュレーションのパイプライン
- 水面波の生成方法はTessendorfに密接に従っている
- 方向性海洋波スペクトル関数にGaussian分布の乱数を掛けて、初期スペクトルの海面状態を生成する
- 初期状態は、波の周波数と伝播速度を結び付ける分散関係によって時間とともに伝播する
- 伝播した状態に逆 Fourier 変換を適用して、変位マップと法線マップを生成する
- 実装はGodotの
RenderingDevice抽象化を用いるcompute shader pipelineで構成される
海洋波スペクトルの構成
- 方向性海洋波スペクトル関数
S(ω, θ)は、周波数ωと方向θが与えられたときの波のエネルギーを返す - この関数は無方向スペクトル関数
S(ω)と方向拡散関数D(ω, θ)で構成され、2つの選択は互いに独立している - 無方向スペクトルには、Horvathが説明した**Texel-Marsen-Arsloe(TMA)**スペクトルを使用する
- TMAは風速
U、水深D、fetch lengthFを使用する - TMAは先行するJONSWAPスペクトルと水深減衰関数を組み合わせたもの
- TMAは風速
- 方向拡散関数には、HorvathのflatおよびHasselmann方向拡散を
spreadパラメータμで混合する方式を使用する- Horvathが提案した
swellパラメータξも含め、海洋波のelongationをモデル化する - Hasselmann方向拡散は、正規化係数
Q(σ)に対する近似解析解を提供するため採用されている
- Horvathが提案した
- Tessendorfの提案に従い、結果のスペクトル関数には小波抑制項も掛け合わせる
- この項は波数ベクトルの大きさ
kとdetailパラメータδを使用する
- この項は波数ベクトルの大きさ
- 最終的な方向性海洋波スペクトルは、TMA、mixed directional spread、small-wave suppressionを組み合わせたもの
GPU FFT実装
- FFTはcompute shaderで書かれたカスタムGPU実装を使用する
- Cooley-TukeyではなくStockham FFTアルゴリズムを使い、初期のbit-reversal permutationを回避する
- Flüggeに従い、FFTのデータフローをエンコードする
"butterfly"テクスチャを計算する- このテクスチャはスペクトルテクスチャの解像度が変わるたびに一度だけ計算される
- 2D FFTではまずスペクトルテクスチャの行方向にFFTカーネルを適用する
- その後、compute shaderでテクスチャをtransposeし、同じ行方向FFTカーネルを列方向FFTとして再利用する
- transposeはパイプライン再利用を可能にするだけでなく、メモリアクセスパターンも改善する
Wave cascadeとタイリング対策
- 遠距離では、特にsea foamがある場合にタイリングアーティファクトが非常に目立つ
- 波生成システムは複数のwave cascadeを同時にレイヤー化でき、この問題に対処する
- 各cascadeは独自のタイリングサイズとパラメータセットを持つ
- cascadeはリアルタイムで動的に追加・削除できる
- すべてのcascadeが同じcompute pipelineを使うため、同じスペクトルテクスチャ解像度を持つ必要がある
- 代替案として、波の変位と法線をノイズとブレンドして、より低い性能コストでタイリングアーティファクトを減らすこともできる
- cascadeを重ねる際に波の干渉を避けるには、各cascadeのパラメータとサイズを慎重に選ぶ必要がある
- cascadeの波動位相も互いに干渉しないようオフセットする必要がある
- システムは各cascadeの開始時刻をそれぞれ異なるようにずらして、これを自動的に試みる
- この方法が実際に機能しているかは確かではないという注意書きがある
更新負荷の分散
- 波の動きは不規則であるため、変位を毎フレーム更新しなくても知覚上は滑らかに見えることがある
"update rate"パラメータは、wave cascadeを1秒あたり何回更新するかを制御する- フレームをスキップするとFFTに使うGPU時間は減るが、波生成パイプラインが実行されるフレームでは依然としてstutterが発生する
- Godotのlocal
RenderingDeviceでcascade更新を非同期計算する実験では、CPUとGPU間のテクスチャ転送により大きな性能オーバーヘッドが生じた - その代わりに、システムはcascadeの負荷分散を試みる
- フレーム時間がupdate rateより短い場合、1フレームあたり1つのcascadeだけを更新する
- この方式はstutterを減らしつつ、フレームスキップで得られるGPU作業量低減の利点を維持する
参考資料
- Realtime GPGPU FFT Ocean Water Simulation: Fynn-Jorin Flügge, Hamburg University of Technology, 2017
- I Tried Simulating The Entire Ocean: Garrett Gunnell, 2023
- Empirical Directional Wave Spectra for Computer Graphics: Christopher J. Horvath, DigiPro, 2015
- Simulating Ocean Water: Jerry Tessendorf, SIGGRAPH, 2004
- Implementing Fast Fourier Transform Algorithms of Real-Valued Sequences: Robert Matusiak, Texas Instruments, 2001
- Wakes, Explosions and Lighting: Interactive Water Simulation in 'Atlas': Mark Mihelich, GDC, 2019
- FFT-Ocean: Ivan Pensionerov, GitHub, 2020
1件のコメント
Hacker News のコメント
この人が作ったほかの Godot リポジトリ2つも非常に興味深い。リポジトリを説明するときに細部まで多く書いてくれるところがよく、特にこのリポジトリが目を引く: https://github.com/2Retr0/GodotGaussianSplatting
学生なのか気になる。ほかの研究を頻繁に引用していて、最近発表された資料もかなりよく理解しているように見える
ENGR96A-coursework,Relevant coursework for ENGR 96A Introduction to Engineering Design F23で、コミット日時から見ると F23 は2023年秋学期という意味のようだもちろん、リポジトリに言及されている UCLA の授業が誰にでも公開されている可能性もあるので、伝統的な意味で UCLA に在籍している学生でなければ受けられない、というわけではないかもしれない
20年前なら、レンダリングで海面を何か月も調整してもこのレベルには近づけなかったと思う。本当によくできた成果物だ
ただ、デモ動画は少し誇張されて見える。航海記録に外洋航海が5万海里以上ある立場から見ると、あれほど鋭く高い波なら、風はもっとずっと強く吹いているはずだ。それでもパラメータを調整すれば済む部分だろう
Godot で作られているならリアルタイムレンダリングと見ていいのだろうか? モンスター級 GPU が必要なのかも気になる
それに、ここでの海のしぶきはポストエフェクトのように見えるが、実際の海ではしぶきが波の鋭さを鈍らせ、それが視覚的にもおぼろげに表れる
もちろん、この優雅で単純かつ効果的なモデルにはほとんど物理が入っていないので、こういう話は腕組みして批判しているというより、あれこれ突いて試してみる方向を提案している程度に受け取ってほしい
このテーマで好きな動画の1つ: https://m.youtube.com/watch?v=yPfagLeUa7k
これもよい: https://m.youtube.com/watch?v=kGEqaX4Y4bQ
その代わり、週末ごとに水シェーダーをいじって、望む見た目になるまで遊んでいる。ところが自分のゲームはパズルゲームなので、水は必要ない。今のところ、かっこいいスタート画面ができただけだ
今ではゲームエンジンのチェックボックス1つにすぎず、そういう機能が無数にある。現代のエンジンのおかげでどれほど恵まれた環境になっているか、人々はあまり想像できていない
この作品の価値を下げたいわけではなく、本当に素晴らしい成果で、制作者に賛辞を送りたい。ただ、私たちが今どれほどよい時代にいるかを言いたかった
それでも非常によく見えるし、最近ざっと探した範囲では、可能なものの中でおそらく最高レベルだと思う
個人的には、外洋の波がふつうもっとリズム感を持つ様子を見たい。15ノットの風が1kmの水面上を何時間も吹くシミュレーションが、実際に観察するもの、つまり比較的整った砕波が形成されては散っていく様子と合うのか気になる
こういうものが自分をコンピュータの世界に引き込んだのに、いつの間にかライブラリとエンドポイント、企業的なたわごとをつなぎ合わせるだけの楽で退屈な生活にはまり、バーンアウトにつながっている
いつかまた戻れることを願っている
やってみてほしい。みんな応援している
アプローチは違うが、波/海洋シミュレーションに興味があるなら、Acerola がこのテーマについていつものように素晴らしい動画を出している。 https://youtu.be/PH9q0HNBjT4 と https://youtu.be/yPfagLeUa7k
追記: 片方は参考文献にすでにリンクされていたが、ニックネームではなく本名になっていたので今になって気づいた
この問題がどれほど難しいかは興味深い。何十年も試みられてきたのに、いまだに不気味の谷にとどまっている
この場面を一時停止すると、盛り上がった波が雪をかぶった山のように見える。水がこんな鋭い形状を持つにはかなり強い風が必要だが、風が真上に吹くわけがないので非現実的に感じる。こうした鋭い特徴には方向性があるべきだ
それでも、うねりの性質はかなりよく捉えている。水平移動より上下の動きのほうが大きく、外洋で期待できる姿だ
光の透過をどうシミュレートしているのか分からないが、うまく機能している。日の出や日没のときに波の間から差し込む光が素晴らしく、効果を本当にそれらしくしている
少し絵画的とは言えるかもしれないが、間違って見えるわけではない
海辺に何年も住んでいた。海辺に住むと、海は魅惑的で感覚的に心地よく、何時間も眺めてしまう。これを見てレンダリングだとは一瞬も思わなかっただろう
Shadertoy にもよい例がある: https://www.shadertoy.com/view/Ms2SD1
Fourier は潮汐波を測定しているうちに波動の周波数変換を思いついたのだから、ある意味ではほとんど一周して戻ってきたことになる
非常に印象的だ
素晴らしいが、このアプローチは本当に大きな波では非線形性のため、ある程度破綻するだろう。さざ波は正弦波ではないが、小さな波や中程度の高さの波では十分に似ているので、この方法はとてもうまく機能する
非常に大きな波は正弦波からさらに大きく離れるだけでなく、波同士が非線形に相互作用し始めるため、ここで使っている線形重ね合わせの方式では捉えられない
だから、ほとんどの海況では幻想的なほどよい。しかし「パーフェクト ストーム」のような波をリアルに作りたいなら、別のアプローチが必要だ
非常に印象的で、これまで見たものより優れているが、うねりが少しずれている感じがする。あえて説明するなら、動画のように高周波の波が低周波の波の上を乗って移動することはないと思う