はじめに
こんにちは。株式会社スパーククリエイティブCTOの広本です。
普段は SPARK GEARの開発をしたり、UEでエンジンの改造をしたり、Unityでレンダラーや揺れもの物理を作ったりしています。
第4回までの内容でやりたかったことは大体できました。
本来は前回で終わりだったのですが、予想外に最適化の話は興味を持ってもらえたので、今回はおまけとして小細工的な最適化を紹介したいと思います。
前回はソルバーそのものの最適化をアルゴリズムレベルで行いましたが、今回はより重箱の隅をつつくような最適化をしていきます。
こういった面倒な最適化は出来ればしたくないのですが、リアルタイム用途ではスペックや電力の都合などがあり限界まで省エネにする必要があるため、やらざるを得ません。
というわけで今回の最適化を行った結果を貼っておきます。

1. ハイブリッド解像度
流体シミュレーションにおいて、解像度(Grid Size)はクオリティの直結するパラメータですが、同時にパフォーマンスを破壊する諸刃の剣でもあります。
解像度を2倍にすれば、計算量は理論上8倍。
特に重い圧力計算(Solver)においては致命的な負荷増となります。
そこで導入したのが、「物理挙動」と「見た目」の解像度を分離するというアプローチです。
なぜ「分離」が有効なのか?
流体の動きを観察してみると、ある特性に気づきます。
・速度場(Velocity): 大きなうねりや流れが主体で、変化は比較的緩やか(低周波)。
・密度場(Density/Reaction): 煙の輪郭や炎の揺らめきなど、繊細なディテールが命(高周波)。
「ならば、動き(速度)は低解像度で計算して計算コストを抑え、見た目(密度)だけを高解像度でシミュレーションすれば良いのでは?」
これが、今回の最適化の鍵です。
実測データが語る「コスパの良さ」
比較画像をご覧ください。
|
|
|---|---|
| ▲ 密度解像度そのまま | ▲ 密度解像度2倍 |
・左:密度解像度そのまま
・全体的にボヤッとした印象です。
・Solver(圧力計算)負荷:1.67ms
・右:密度解像度2倍
・ディテールがパキッと出て、非常に高精細です。
・Solver(圧力計算)負荷:1.76ms
ここが最大のポイントです。
見た目の解像度(体積比)を 8倍 に引き上げたにもかかわらず、シミュレーションのボトルネックである圧力計算(Solver)の時間は 0.1ms 程度しか増えていません。
技術的なカラクリ
仕組みはシンプルかつ強力です。
低解像度のVelocityテクスチャをバイリニア補間でアップサンプリングし、高解像度のDensityテクスチャを移流(Advection)させています。
もちろん、Densityの画素数が増えた分、移流計算(Advection)のコストは増えますが、重い行列計算を解くSolverに比べれば微々たるものです。
「計算が重い部分は低解像度で済ませ、軽い処理で見た目の解像度を稼ぐ」。
このハイブリッド解像度こそが、限られたリソースでリッチな表現を実現する、「手抜き(最適化)」と言えるでしょう。
2. 占有マップによる描画負荷の削減
ボリュームレンダリング、特に影(Self-Shadow)付きの描画は、GPUにとって最も過酷な処理の一つです。
なぜなら、光が届くかを判定するために、レイ上の各ステップからさらに光源に向かってレイを飛ばす必要があるからです。
計算量は爆発的に増加します。
しかし、冷静にシミュレーション空間を見てください。
その空間の半分は、何も存在しない「ただの空気」です。
何もない空間に対して、真面目にレイを進め、テクスチャをサンプリングするのは、GPUリソースの浪費です。
そこで導入したのが Occupancy Map(占有マップ) です。

仕組み:「大まかな地図」を持ってから歩く
処理は非常にシンプルです。
シミュレーションの更新時、密度があるセルを低解像度のマップ(占有マップ)に書き込んでおきます。
レイマーチング時、まずこのマップを参照します。
「ここは空(Empty)」 と判定されたら、その区間は計算をせず、一気にレイをスキップします。
実測データ:影生成コストが「3分の1」に
比較画像をご覧ください。
特に Rendering (描画) の内訳にある Shadow の数値にご注目ください。
|
|
|---|---|
| ▲ 占有マップなし | ▲ 占有マップあり |
・占有マップなし:
・Shadow計算:6.55ms (重すぎてお話になりません)
・Rendering合計:8.51ms
・占有マップあり:
・Shadow計算:2.16ms (約67%削減!)
・Rendering合計:3.62ms
特に影の計算において効果が絶大なのは、光源に向かうレイ(Shadow Ray)が「何もない空間」を長距離移動することが多いためです。
この「無駄」をスキップできたことが、数値に直結しています。
3. Dynamic Boundsによるシミュレーション負荷の圧縮
占有マップによって「描画」の無駄は省けました。
しかし、流体シミュレーションの計算(Simulation)自体はどうでしょうか?グリッドベースのシミュレーションにおいて、最も単純な実装は「バウンディングボックス全体を常に計算し続ける」ことです。
しかし、煙がまだ発生したばかりでちょろちょろと昇っている時も、画面いっぱいに広大なグリッド全体を計算し続けるのは、完全に無駄です。
そこで導入するのが Dynamic Bounds(動的境界) です。。
「必要な場所」だけを計算する
この手法のコンセプトは明確です。
毎フレーム、煙や炎が存在する領域(Active Region)の最小・最大座標(AABB)を計算し、その範囲内だけシミュレーションを実行します。

これは物理的な厳密性とはトレードオフになります。
本来であれば、煙がない場所でも風(移流)は発生しているはずだからです。
しかし、煙がない場所の風が正しかろうが間違っていようが、見た目には影響しません。
ならば、リアルタイムグラフィックスにおいては計算しないのが正解です。
実測データ:帯域消費を物理的に遮断
Intel UHD Graphicsでの比較結果を見てみましょう。
|
|
|---|---|
| ▲ Dynamic Boundsなし | ▲ Dynamic Boundsあり |
・Dynamic Bounds なし:
・Simulation合計:32.66ms
・Advection(移流):10.33ms
・Dynamic Bounds あり:
・Simulation合計:26.96ms (約6ms短縮)
・Advection(移流):7.28ms
「約 6ms」の短縮。
60FPSを目指す場合、1フレームの予算は16msです。
その中で6msを稼ぎ出すというのは、最適化として極めて大きな成果です。
技術的な勝因:DispatchIndirect
なぜこれほど効くのでしょうか? iGPUのような環境では、計算能力(ALU)よりもメモリ帯域(Bandwidth)がボトルネックになりがちです。
Dynamic Boundsを用いて DispatchIndirect でスレッド発行数を絞ると、計算しなかった領域に対する VRAMへの Read / Write が物理的に発生しません。
単なる計算スキップ以上に、「メモリ帯域の節約」としてダイレクトに効いてくるのです。
爆発のように局所的に発生して広がるエフェクトにおいては、その効果は絶大。
メモリ容量以外は気にせず、自動的に最小コストでシステムが回る、非常にスマートな最適化と言えます。
まとめ
今回の検証を通じて、Hybrid Resolution(解像度分離)、Occupancy Map(占有マップ)、そして Dynamic Bounds(動的境界) という3つのアプローチを紹介しました。
これらは決して派手な新技術ではありませんが、組み合わせることで劇的な効果を生み出します。
ローエンドからハイエンドまで

最後の画像にある通り、これらの最適化を施した結果、Intel UHD Graphics という統合型GPUであっても、十分な速度で大規模な流体シミュレーションが動作することを確認できました。
そして、ローエンドで動くコードは、RTX 4070 のようなハイエンド環境に持っていけば、圧倒的なフレームレートと解像度を叩き出します。
|
|
|---|---|
| ▲ Intel UHD Graphics | ▲ RTX 4070 Laptop |
ゲーム開発の現場では、魔法のようなアルゴリズム一発で全てが解決することは稀です。
今回紹介したような、細かいテクニックの組み合わせや、地道なプロファイリングと修正を数ヶ月かけて延々と繰り返すことこそが、最終的な「ヌルヌル動く」体験を作ります。
今回は、これらの最適化を行っていくことで、ノートPCの Intel UHD Graphics 上で動かして、7ms程度という十分にリアルタイム動作が可能なレベルになりました。
お問い合わせ
最後に、SPARK CREATIVE社 では、こうした最適化業務も含めた開発支援を行っています。
Unity / Unreal Engine といった汎用エンジンはもちろん、内製ゲームエンジンの開発や、シェーダー、レンダラー、揺れもの物理の実装など、グラフィックス周りのディープな領域を得意としています。
「描画負荷が下がらない」「物理挙動をリッチにしたいが重すぎる」といった課題があれば、ぜひお気軽にご相談ください。
我々のようなエンジニアが、ボトルネックを徹底的に潰しに行きます。







