ニュース
[CEDEC 2007]GPUによる流体シミュレーションの実際
さて,これまで,流体シミュレーションというと,物理エンジンのデモなどで何度か目にした人もいるだろう。これらのほとんどは,パーティクル(微粒子)による流体のシミュレーションである。パーティクルを細かくしたり,スプライト形状を工夫するとかなり自然な仕上がりになることは,NVIDIA自身もCascadesなどのデモで示している通りだ。
ボリュームといわれてもちんぷんかんぷんな人のために簡単に補足しておくと,今回使用されたボリュームレンダリングというのは,ポリゴンなどとは違い,中身の詰まった立体を表現する手法のことである。物体の表面だけを計算するのではなくて,物体のある部分を3次元の格子空間で指定し(ボクセル化),その情報から3D表示を行うといったもので,とくに半透明な物体表現を得意としている。ボクセルを細かくすると大量のメモリが必要になるので,あまり精度の必要な造形には向かないが,雲などのモヤっとしたものであれば,粗いボクセルでも目立たないのでかなり実用的となる。
さて,流体を表現するにはそれだけでは不十分なので,別途,速度ベクトルフィールドや圧力,密度などを記録しておく必要がある。
流体力学でよく使われるNavier-Stokesの方程式を使用して,速度フィールドを計算していく。質量は保存していかなければならないので,以下のようになるという。
∂u 1
運動量 −− = -(u・∇)u - −− ∇p + f
∂t ρ
質量 ∇・u=0
これを直行分離定理で変形していくらしいのだが,難しい話は置いておくとして,速度ベクトルや密度の分布で一定時間後の状態を計算していく。とりあえず,速度と圧力の関係は5回〜10回程度反復していちばん簡単な手法で解を求めているとのこと。講演の例は,2次元の図で行われていたが,3次元に展開しても差は出ないという。
GPUによる実装では,速度,密度,圧力はテクスチャとして表現される。ボクセルのグリッドサイズで調整しつつ,計算したい点の周囲の4点分のデータ(クワッド)を単位にレンダリングを行っていく。これは,計算したい点が,グリッドの中央とは限らないので補間しつつ計算するためだ。計算にはピクセルシェーダを使用し,結果はテクスチャにレンダリングしていくという過程を経る。
2Dベースの処理を3Dに展開するには,空間を多段に分割し,多層の画像を出力するた感じで行われる。
●炎のシミュレーション
炎は煙の応用で作成される。炎を表現するためには,温度テクスチャというものが必要となる。温度は時間が経つと冷えていくように設定したり,温度の高い部分では,浮力による速度ベクトルへの影響を考慮する。調整要素として,ランダム性などの要素も加えられる。
●水のシミュレーション
水の動きがほかの流体と明らかに異なるのは,水面という境界を持つことによる。そこで水面より上か下かという状態を表すLevel Setというフィールドが導入されている。この部分の値がプラスなら水面より上,マイナスなら水面より下となる。さらに液体の外側では圧力をかけないなどの調整が加わってくる。
これは1フレームごとに処理されるが,頂点情報は中間頂点バッファに保存しておき,空間をスライスした階層数だけ2次元の処理を繰り返していく。ここではインスタンシングを使うほうが効率がよいだろうとのこと。
さて,これでポリゴンモデルに煙を吹きかけるようなシーンでも自然に処理されるようになるのだが,まともにやると計算コストはかなり高くなる。計算に使うポリゴンモデルをローポリモデルにすることで,画質の低下を抑えて処理を軽くすることも可能なので,ローポリモデルの使用が推奨されていた。
流体のレンダリング
こうやって流体力学に基づいて計算された煙や炎は,レイキャスティングによってレンダリング・表示される。
レイキャスティングとは,ボリュームレンダリングでボクセル(分割された空間)をスキャンするための手法の一つで,レイトレーシングのように,視点から各画素めがけて光線(レイ)を飛ばし,その軌道上のボクセル情報を読み取るという技術だ。レイトレーシングのように屈折などの演算はしないものの,あまり軽い処理でないことは想像できるだろう。
このとき,煙の内部に障害物があるとスライスの粗さがマッハバンドになって目立って出てしまう。そこで,障害物がある場合には,障害物との距離を勘案して重み付けを行っている。こうことで滑らかな階調が実現されていた。また,視点自体が煙の中にある場合なども特別な配慮が必要となる。
レイキャスティングは,処理が重い。パフォーマンスを重視する場合には,煙部分を粗い解像度でレンダリングすることが提唱されていた。クオリティはやや下がるものの,かなりパフォーマンスを改善することができる。挙げられた例では3倍のフレームレートが実現されていたので,ゲームでは必須の手法といえるかもしれない。
ただし,煙の部分だけを低解像度でレンダリングすると,障害物との境界部分で段差がはっきり見える画像となってしまう。これを避けるため,エッジ部分だけ解像度を上げてレンダリングする手法が紹介されていた。
炎のレンダリングは,温度が低い場合は煙としてレンダリングしたり温度によって表現を変えるものの,基本的には煙の表現に対して炎をブレンディングすることで実現される。
水のレンダリングは少し複雑だ。煙や炎のようなモヤっとしたものではなく,明確な水面ができているからだ。わりと粗く計算されていたLevel Setではどうしてもガタガタになってしまう。かといって,サンプル数を増やすとパフォーマンスに影響する。そこで,正確な水面位置を算出するため,境界部では2分探索を繰り返して水面位置を限定していく手法が紹介された。
ボクセル化は,空間的にスライスされた状態なので,トライリニアフィルタでの補間も行われる。ここでトライキュービック補間を行うことでさらにクオリティを上げることができることも示された。リニアフィルタは,直線で補間するもの,キュービックフィルタは補間時にsinc関数(sinを変数で割った関数)を用いるもので,3次元方向でこの補間を行うものがトライキュービック関数と呼ばれている。
この処理も負荷を増やす原因となりうるものだが,出力結果は格段に滑らかになることが分かる。ただ,トライキュービックのみでは不完全で,前述の2分探索は不可欠と考えたほうがよさそうだ。パフォーマンス的に両方を実装することが困難な場合は,2分探索を優先したほうがクオリティを上げられるという。
ボリュームレンダリングによる流体表現の問題点
かなり高品位の流体表現がGPUによって可能になることはだいたい分かっていただけたであろう。ただし,計算コストなど,実用に躊躇される点があることも事実である。
そしてリソースの問題。多くのバッファを使用するので,この処理だけで,グラフィックスメモリを50MB以上消費する。最近はほとんどのグラフィックスカードが256MBのグラフィックスメモリを搭載しているのだが,流行りのHDR処理を行うとメモリ使用量は爆発的に増えるので,ちょっと苦しい感じになるだろう。
それでも素人目には多少演算コストが高そうなものの,かなり使えそうな手法に見えるのだが,詳しい人の話では,この方式の弱点の一つにライティングがあるという。煙の一部に光が当たった状態などを考えると,光源からもレイを飛ばして明るさを計算しないといけない。だいたい同じ処理をもう1回する必要があるわけだ。複数の光源があったらどうなるかと考えると憂鬱な気分になるのも無理はないだろう。最近は,パーティクルでも改良型の手法が登場してきており,煙や流体の部分の画質を追求しないのならば,ほかに資源を回したほうが有利という考え方もあるようだ。
PCゲームでは,GPUパワーが上がって,高画質処理でも問題ない時代がくるのは遠くないと思われるが,コンシューマゲーム機の性能は数年間据え置きなので,コンシューマゲーム主体の開発者には手を出しづらいのかもしれない。
今後,ちゃんとした流体表現を行ったゲームなども登場してくる予定なので,なにかのゲームでリアルな煙などを見かけたら,内部ではこのような処理が行われていることを思い出してみてほしい。
- この記事のURL: