ひとことで
水を「各マスの水の深さ」と「隣のマスへの流れ」だけで表し、高い所から低い所へ流れ下る様子を格子の上で計算する方法です。3 次元の流体をまともに解くより桁違いに軽く、それでいて洪水、決壊、川の流れ、水たまりができる様子をそれらしく再現できます。
ゲームでの使いどころ
- 地形に沿って流れる水:ダムの決壊、洪水、溶岩流、泥流。水が谷を走り、くぼみにたまる様子がそのまま出てきます。
- 掘ると水が流れ込むゲーム:地形を掘ったり盛ったりできるサンドボックスや街づくりで、水路を作って水を引く遊びに使われます。
- 侵食と組み合わせる:流れの速さが分かるので、速い所で地面を削り、遅い所に土を落とすと水食侵食になります。
- GPU で広い範囲を:各マスの計算は隣しか見ないので、GPU で並列に計算しやすく、数百 m 四方の水をリアルタイムで動かせます。
しくみ
水平方向の広がりに比べて水深が浅いなら、水の中の上下の動きは無視でき、「水の柱」の高さと水平の流れだけを考えればよくなります。これが浅水方程式です。ゲームでは「パイプモデル」という分かりやすい近似がよく使われます。
- 隣へ流れ出す量(流量)を更新する:各マスと隣のマスの間に細いパイプがあると考えます。水面の高さ(地面 + 水深)の差があるほど、パイプの流れが加速します。
は地面の高さ、 は水深、 は重力、 はパイプの断面、 はマスの間隔です。前の流量を残す(慣性)ので、水は勢いよく流れ、行き過ぎて波にもなります。
-
持っている水より多くは流さない:4 方向の流出の合計が、そのマスの水の量を超える場合は、比率を保ったまま縮めます。これがないと水深が負になります。
-
水深を更新する:流れ込んだ量と流れ出た量の差で水深を変えます。
- 流速を求める(必要なら):左右の流量の差から水平の速度が分かります。泡を出す、物を流す、侵食する、などに使います。
水の量が「流れ出た分だけ隣に入る」形で受け渡されるので、水の総量が保存されるのがこの方法の大きな長所です。
実装のポイント
- 2 つのループに分ける:流量を全部更新してから水深を更新します。1 つのループで混ぜると、計算順によって結果が偏ります。GPU ならピンポンバッファで読み書きを分けます。
- 時間刻み:水面の波は の速さで伝わります。1 ステップで波が 1 マス以上進むと不安定になるので、 を目安に分割します。
- 描画:水面は地面の上に水深を足した高さの格子で描き、深さで色を変えます。水深がほぼ 0 のマスは地面の下に沈めて隠します。
つまずきやすい点
- 浅い水が地面から浮いて見える:乾いた所と濡れた所の境目で水面がぎざぎざになります。水深がしきい値以下のマスを隠す、水面メッシュの頂点を地面に合わせる、などで目立たなくします。
- 水が止まらずに揺れ続ける:減衰を入れないと、たまった水がいつまでも波打ちます。流量に 1 よりわずかに小さい係数を掛けます。
- 砕ける波や滝は苦手:高さ 1 つで表すので、水が覆いかぶさる形(巻き波)や垂直な滝は表現できません。そこは粒子やSPHなど別の方法と組み合わせます。
デモで試すこと
- 「ダムを決壊させる」を押すと、貯水池の水が谷を走り下り、下流のくぼみにたまっていきます。「水の量」の数値がほとんど変わらないこと(保存されていること)も確かめてください。
- 「水の色」を「流れの速さ」にすると、谷の狭い所や急な所ほど速く流れているのが分かります。
- 地形をクリックすると、その場所に水を注げます。尾根に注ぐと両側の谷へ分かれて流れます。
- 「雨を降らせる」をオンにすると、全体に少しずつ水が増え、低い所から順に水たまりができていきます。