流体・反応シミュレーション

圧力投影

Pressure Projection / Poisson Solve

圧力から速度を補正し流れの発散を減らす。

3 / 10

インタラクティブデモ

デモを準備しています…

圧力投影

ドラッグで視点を回転、Ctrl(⌘)+ スクロールでズーム。タッチ操作は 2 本指です。

ひとことで

流体の速度を、「どこからも湧き出さず、どこにも吸い込まれない」流れ(発散のない流れ)に直す処理です。各マスの流れ込む量と流れ出る量の差を打ち消すような圧力を解き、圧力の高い所から低い所へ押し出すように速度を補正します。水や煙が障害物をよけて回り込み、渦を巻くのは、この処理があるからです。

ゲームでの使いどころ

  • 格子流体の中心:安定流体法、PIC・FLIP、APIC など、格子を使うほぼすべての流体計算に含まれます。計算時間の大部分を占める、最も重い段階です。
  • 障害物の周りの流れ:キャラクターや柱の周りを煙が回り込む、狭い所で流れが速くなる、といった流体らしい振る舞いを作ります。
  • 風の場:ゲーム内の風(草木の揺れ、パーティクルの流れ)を、建物の形に合わせて回り込ませる用途にも使えます。

しくみ

  1. 発散を求める:各マスについて、右と上へ流れ出る量から、左と下から流れ込む量を引きます。
∇⋅u≈ui+1−ui−12Δx+vj+1−vj−12Δx\nabla\cdot\mathbf{u} \approx \frac{u_{i+1} - u_{i-1}}{2\Delta x} + \frac{v_{j+1} - v_{j-1}}{2\Delta x}

正なら水が湧き出している、負なら吸い込まれている状態で、非圧縮の流体ではどちらもあってはなりません。

  1. 圧力を解く(ポアソン方程式):速度から圧力の傾きを引いたものが発散 0 になるような圧力 pp を求めます。
∇2p=∇⋅u\nabla^2 p = \nabla\cdot\mathbf{u}

格子の上では、各マスについて「周りの 4 マスの圧力の和 − 4 × 自分の圧力 = 発散」という連立方程式になります。ヤコビ法なら、

pi,j←pi−1,j+pi+1,j+pi,j−1+pi,j+1−Δx2 (∇⋅u)i,j4p_{i,j} \leftarrow \frac{p_{i-1,j} + p_{i+1,j} + p_{i,j-1} + p_{i,j+1} - \Delta x^2\,(\nabla\cdot\mathbf{u})_{i,j}}{4}

をくり返します。

  1. 速度を直す:圧力の傾きを速度から引きます。圧力の高い所から低い所へ押し出されます。
u←u−∇p\mathbf{u} \leftarrow \mathbf{u} - \nabla p

壁では、壁の向こうの圧力を自分と同じ(傾き 0)とし、壁を通り抜ける速度を 0 にします。出口など、水が自由に出ていける境界では、圧力を 0 にします。

直感的には、流れが障害物にぶつかる手前で水が「渋滞」して圧力が上がり、その圧力が流れを横へ押し出すことで、水が障害物をよけて回り込みます。

実装のポイント

  • 解き方の選択:ヤコビ法は GPU で並列に解きやすいが収束が遅い。CPU ならガウス=ザイデル法に過緩和(SOR、係数 1.9 程度)をかけると数倍速く収束します。本格的には共役勾配法やマルチグリッド法を使います。
  • 前のフレームの圧力から始める:圧力は時間とともにゆっくり変わるので、前のフレームの結果を初期値にすると少ない反復で済みます(ウォームスタート)。
  • スタガード格子(MAC 格子):速度の横成分をマスの左右の辺に、縦成分を上下の辺に置くと、発散や勾配を隣り合う値の差で正確に書けます。マスの中心にすべてを置くと、市松模様の誤差が出やすくなります。
  • [コンピュートシェーダー](/keywords/implementation/performance/compute-shader/)で解くと、共有メモリを使った高速な反復ができます。

つまずきやすい点

  • 反復が足りない:反復を打ち切ると発散が残り、水が少しずつ消えたり増えたりします。粒子で水を運ぶ方式では、粒子の密集具合を発散に加えて補正する(ドリフト補正)のが一般的です。
  • 圧力の単位を気にしない:ゲームでは ρ\rho や Δt\Delta t を圧力にまとめて扱うことが多いです。本物の圧力の値が必要な場合(浮力の計算など)は、係数を正しく戻します。
  • 境界の扱いの間違い:壁で速度を 0 にする処理と、圧力の境界条件が食い違うと、壁から水が湧き出したり、壁に吸い込まれたりします。

デモで試すこと

  1. 左から流れ込んだ水が、中央の柱をよけて回り込み、白い粒がその流れに乗って運ばれます。
  2. 「圧力投影」をオフにすると、流れは柱をよけることを知らずにまっすぐ突っ込み、粒は柱の中へ消えていきます。背景の赤と青は、水が湧き出したり吸い込まれたりしている「ありえない場所」(発散)です。
  3. 「背景の色」を「圧力」にすると、柱の手前(上流側)で圧力が高まっている様子が見えます。この圧力が流れを横へ押し出しています。
  4. 「圧力を解く反復回数」を 1〜5 に下げると、発散が残り、左下の「発散の平均」の値が大きくなります。

参考資料

GPU Gems — Fast Fluid Dynamics Simulation on the GPU ↗

分野全体の資料で、この項目を直接扱っているとは限りません。