Water, terrain & plants

Height-Field Wave Simulation

高さ場の波動シミュレーション

高さの変化を伝えて波紋を広げる。

4 / 10

Interactive demo

Preparing the demo…

Height-Field Wave Simulation

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

This explanation is currently available in Japanese.

ひとことで

水面を格子状に並んだ点の高さだけで表し、各点が周りの点との高さの差に引っ張られて動くようにすると、1 か所を押し下げただけで波紋が輪になって広がり、壁で跳ね返り、重なり合うようになります。2 次元の波動方程式を差分で解く、とても軽くて効果の大きい水面の作り方です。

ゲームでの使いどころ

  • 物が触れた所の波紋:キャラクターが池を歩く、雨粒が落ちる、石を投げ込む、ボートが進む。インタラクティブな水面の定番です。
  • 狭い水面:池、噴水、水たまり、プール、バスタブ。範囲が限られるので、格子で全体を計算できます。
  • 大きな海と組み合わせる:海の大きなうねりはゲルストナー波や FFT で作り、船の周りだけ高さ場の波紋を重ねる、という使い方もします。
  • 水以外にも:トランポリンの膜、ゼリー、力場のバリアの揺れなど、「押すと周りに伝わる」表面に広く使えます。

しくみ

2 次元の波動方程式は、次のように書けます。

∂2h∂t2=c2∇2h\frac{\partial^2 h}{\partial t^2} = c^2 \nabla^2 h

∇2h\nabla^2 h(ラプラシアン)は「周りの平均より、自分がどれだけ低いか」を表します。周りより低い点は上へ、高い点は下へ加速するので、へこみは周りを引き下げながら外へ広がっていきます。

これを格子で近似すると、前のフレームと今のフレームの高さだけで次のフレームが求まります(ベルレ積分と同じ形)。

hi,jt+1=2hi,jt−hi,jt−1+c2 (hi−1,jt+hi+1,jt+hi,j−1t+hi,j+1t−4hi,jt)h^{t+1}_{i,j} = 2h^{t}_{i,j} - h^{t-1}_{i,j} + c^2\,\big(h^{t}_{i-1,j} + h^{t}_{i+1,j} + h^{t}_{i,j-1} + h^{t}_{i,j+1} - 4h^{t}_{i,j}\big)
  • 速度を持たなくてよい:「今の高さ − 前の高さ」が速さの代わりになるので、高さの配列を 2 枚持って交互に使うだけで済みます。
  • 減衰:結果に (1−減衰率)(1 - \text{減衰率}) を掛けて、波が少しずつ消えるようにします。
  • 障害物:岩や壁のマスの高さを常に 0 にしておくと、そこで波が跳ね返ります。池の縁も同じです。
  • 安定の条件:c2c^2(1 ステップで波が進む距離の 2 乗)が 0.5 を超えると計算が発散します。速い波が欲しいときは、1 フレームを複数ステップに分けます。

実装のポイント

  • GPU なら[ピンポンバッファ](/keywords/implementation/performance/ping-pong-buffers/):読むテクスチャと書くテクスチャを毎フレーム入れ替えます。5122512^2 でもほとんど負荷がかかりません。
  • しずくの落とし方:1 マスだけ下げるとぎざぎざの波になります。半径数マスに、中心ほど深い形でへこみを与えます。
  • 描画:高さを頂点の上下に使うか、法線マップだけに使います(頂点を動かさず、法線の傾きだけで波紋を見せる方法は非常に軽量です)。法線は周りの高さの差から求めます。
  • 平均の高さのずれ:押し下げるだけの操作をくり返すと、水面全体が少しずつ下がっていきます。毎フレーム平均を 0 に戻すか、押し下げたのと同じ量を周りに足します。

つまずきやすい点

  • 格子の向きがにじみ出る:上下左右の 4 近傍だけを使うと、斜め方向の波が少し遅くなり、波紋が四角っぽくなることがあります。斜めの 4 点も重みを付けて加えると丸くなります。
  • 境界での跳ね返りが強すぎる:開けた水面の端では、波が消えてほしいのに跳ね返ってしまいます。端の近くだけ減衰を強くする(吸収層)と自然に消えます。
  • 水の流れは表せない:高さ場の波は「その場で上下する」だけで、水そのものは移動しません。流れや水かさの変化が必要なら浅水波モデルを使います。

デモで試すこと

  1. 水面をクリックすると、しずくが落ちて輪の波紋が広がります。岩に当たると跳ね返り、池の縁でも反射して、波同士が重なり合います。
  2. 「雨を降らせる」をオフにしてから 1 か所をクリックすると、1 つの波紋がきれいに広がる様子を観察できます。
  3. 「波の速さ」を上げると、波紋が速く広がり、波長も長くなります。
  4. 「減衰」を 0 に近づけると波がいつまでも残り、上げるとすぐに静まります。
  5. 「岩で波をさえぎる」をオフにすると、岩の後ろにも波が回り込みます。オンのときは岩の裏に波の届かない影ができます。

References

GPU Gems — Effective Water Simulation ↗

A general reference for this subcategory.