3D流体エフェクトとは

MMDで3D流体シミュレーションは作れるのか?というチャレンジに対する一つの結果です。実験用作品としての側面が強いので使いづらいです。こんな使いづらいエフェクトを喜んで使う動画制作者というのはなかなかいないと思いますから、使えばそれだけで貴方の動画にワンポイントが添えられる事間違いなし!

このエフェクトでは、ラグランジュ法と言って流体を粒子の集合として近似して考える方法を採っています。要するに「くっつくパーティクルエフェクト」みたいなものです。流体を粒子に分けて近似しているという事は使う前に知っといて頂きたいというか、イヤでも分からざるを得ない事です。sdFluid3Dの粒子は基本的にバスケットボールよりデカいので、水滴もそのサイズになります…

2020年の2月ごろに、MMEとして2Dの流体シミュレータを公開しましたが、あちらはオイラー法と言って粒子の流れる空間を格子状に切って、格子点ごとの状態を計算する方法です。3Dの空間を格子で表現するのは解像度的に無謀なのでやめました。圧縮する方法はあるんですけどDirectX9で実装できるか分かりません。

このエフェクトには以下のような特徴が有りまして

良い感じですね!最新のソフトウェアにもMMDは負けてないんですよ、皆さん!と言いたいところですけど、同時に以下のような欠点も有ります

なんだよこれ、全くイケてないじゃねぇか!やっぱMMDは古いソフトですね!と言われそうですけど、何が出来たらMMDは、もっとナウいソフトになれるのか、という可能性を示唆する内容でも有ります。無茶な事はしてみないと可能性を探る事もできないのです。

静止画への出力については要望があると思うんですけど、これを解決するためにはコントローラからの入力が反映できない、という条件を付けても3Dのナビエ-ストークス方程式の解析解を得る必要がありまして、この問題はクレイ研究所のミレニアム懸賞問題の1つだったりします。解析解そのものの式を得る事ではなく解析解があるのか無いのか証明する(数学に詳しい方からはおそらく許しがたい省略が込められているので、詳しい事はちゃんと調べてね)ところまで出来ればなんと、100万ドルです!ご興味が有れば、人生棒に振ってみませんか!?ミレニアムという事は出題が2000年なんですけど、2021年末現在になってもまだ解かれていないんですね。出来るのか、出来ないのかすら今の人類には分からない事なのです。

なので、ナビエ-ストークス方程式の解析解を得なくてもなんとか計算結果を保持して出力時に使えればいいんですけど。なんか方法あるんですかね?

エフェクトの構成

sdFluid3Dは無駄に多くのファイルで構成されているので、使い方を読む前に、どんな構造になっているのかを大体把握しておくと理解が早いと思います。sdFluid3Dでは、流体エフェクトをソルバ・レンダラ・コントローラ・その他の4つに分けて実装しました。

ソルバ

流体の位置・速度を毎フレーム計算・記録するだけのエフェクトで、画面に表示する機能を持ちません。流体シミュレーションではフロードメインといって「ここからここまでの範囲の流体をシミュレーションします」という見えない仕切りの中での流体の動きを計算します。フロードメインとか異世界みたいで良いですね。あなたは異世界フロードメインを救うために召喚された、勇者なのだ!

いやホント、勇者としか言いようがないです、ハイ。

少なくともsdFluid3DSolver.xが無いと流体シミュレーションが行なわれないので何も有効な表示が出来ない、という事です。こいつらをMMDへドロップして、表示順をなるべく上にしておくと良いでしょう。

レンダラ

ソルバで計算した粒子の位置などの情報を元に、絵を作るためのエフェクトです。こいつらが無いと何も画面に表示されません。

レンダラはParticle,RayTraceお好みでどちらか片方でも良いですし、併用しても良いです。描画順はソルバの下、他のポストエフェクトよりは前が良いでしょう。

sdFluid3DRayTraceは、デフォルトではかなりザラザラした見た目なので、同梱のポストエフェクトのsdTAAと併用する事がほぼ前提になっています。

コントローラ

以上のエフェクトの動作を制御するためのモジュールです。

その他

ありがちなシナリオとして、「パーテイクルの表示は要らないけど、流体の影をsdPBRで描かれているモデルやsdFluid3DRayTraceで描かれている水面に落としたい」という状況がありますので、そういう時に使いましょう。影を落とすだけでなく、sdHexDoFのようなDoFエフェクトに対して奥行きを報告させたり、幾つか使い道があると思います。sdFluid3DParticleを普通に使う場合は特に必要のない物です。

使ってみよう

とりあえず、どうやって使うのか確かめてみましょう。sdPBR/stageSet/Fluid3Dフォルダに必要なエフェクト類は入っています。

まず、sdFluid3DSolver.xとsdFluid3DParticle.xとsdFluid3DController.pmxをMMDへドロップ

sdFluid3DController.pmxへsdFluid3DDefault.vmdをドロップしてデフォルト設定を読み込む

以上の4つを読み込むと何か画面に出てくる状態になります。作りがあんまり宜しく無いのでどんなPCでも動くわけではないのかもしれませんから、諦めも肝心です。

上述した通り、sdFluid3DSolver.xをドロップすると流体の流れる異世界フロードメインが作られ、その中での出来事を毎フレーム計算するようになります。

そんな感じで物語が始まっても良いんですけど、いつも通り口絵には、とろぽっぷ氏作の天龍ちゃんをお借りしてきました。艦娘ですから。

テンションがいつになく激しいですが、sdFluid3DSolverには表示機能が無いので、sdFluid3DParticle.xも入れる事で、フロードメインでの出来事が目に見えるようになる、という寸法です。sdFluid3DParticleは、sdFluid3DSolverの計算結果である、各粒子の位置に対して、テクスチャを貼ったポリゴンで●を表示しているというだけの、非常に簡素なシェーダになっています。その分、粒の大きさをクソデカにしたりしない限りは、昨今のGPUパワーがあればそんなに重く無いはずです。

※口絵は開発中のエフェクトを元に作っているので、解説通り操作しても全く同じ見た目にはなりませんので、ご了承ください

再生ボタンを押すと、こんな感じで爆発的に粒子が飛び出してくると思いますけど、粒子の出る勢いは特に制御できないので、爆発的に流体が降り注いでくる様子とか欲しくないんだけど…という時は出力した動画の方を適宜カットしてください。

パーティクルによるレンダラは特に照明を考慮していないし、水面を面として表示できないのでお世辞にもリアルな流体とはかけ離れた描写になります。そこで、後の項で説明するレイトレーシングによるレンダラによってより良い見た目を得ることが出来ます。

パーティクルの色や大きさはsdFluid3DController.pmx(以下、単にコントローラと書きます)で制御できます。ここで、コントローラの表情モーフについても説明しておきましょう。全部まとめて書いてしまうので、リファレンスとして使って下さい。

コントローラのモーフとボーン

ボーンについて

センターボーンの位置がフロードメインの下端中央になります。

流入口ボーンの位置から流体が溢れだします。何らかの理由で消滅した粒子はまたここからリスポーンします

表情モーフ左上:フロードメインと流体の設定

スケールフロードメインはデフォルトでは64x64x64セルのマス目状に区切って管理されており、セル(マス目)のサイズを指定できます。1.0の時セルの1辺は1mになるので、64m(800MMD距離)の範囲がフロードメイン全体の一辺のサイズになります。値に比例してセルのサイズは変わります。(0.1の時はフロードメインの1辺は6.4mになります)、フロードメイン全体を作っているセルの数は設定ファイルを書き換える事で変更できます。
粒子径フロードメインのセルに対する粒子の半径の比率を指定します。0.0の時、粒子の半径はセルのサイズと同じになり、1.0の時、粒子の半径はセルのサイズの6倍になります。0.2上げるごとに+1倍されます。粒子の半径が小さい場合、セルの隙間から漏れやすくなるため、シミュレーションが不安定になりがちです。最低でも0.3程度はあった方が良いようです。フロードメインが大きくなるほど粒子も大きくなります
粒子数フロードメインに流れ込む粒子の総数を指定します。0の時は0個、1.0の時は65535個になります(設定ファイルで変更可)
重力重力の大きさを指定します。0の時は無重力になり、1の時1G(9.8m/s^2)になります。重力は必ず下向き(Y-方向)です
生存時間粒子がフロードメインに出現してから消えるまでの時間を指定します。単位は[分]ですが、0の時はいつまでも消えません。消えた粒子は流入口ボーンで指定される噴出口から即座にまた出てきます。(設定で1度しか出てこないように変更可)
±生存時間ランダムに粒子ごとに生存時間が変わるようになります。0.0の時は全ての粒子で生存時間が同じになり、1.0にすると全ての粒子で0~生存時間までの範囲でバラつくようになります。0.5の時は生存時間の0.5倍~1倍の範囲でバラつきます。±と書いてありますけど、実際にはマイナスにしかなりません。
粘度粒子が他の粒子に引きずられる度合いを指定します。粘度を高くすると水位が一定に揃いづらくなります。とぐろを巻くような非常に高い粘性の表現には異なる計算モデルが必要なので、このエフェクトでは表現できません。
うねり粒子がうねるように運動するようになります。うねりを高くすると落ち着かない動きになり、水位は一定に揃いやすくなります。
抵抗粒子の運動にブレーキが掛かるようになります。粘り気のある物体の表現としては、見た目的に粘度よりもこっちの方が分かりやすいです
ノイズ粒子の運動にランダム性が加わります。水位を整えたい時などにどうぞ
障害物確認流体を押しのける障害物(後述)の位置を表示するようになります

表情モーフ右上:泡についての設定

泡の出やすさ数値を高くすると動きの無い水面にも泡が出やすくなります。0にしても多少は泡が出ます
泡サイズ数値を大きくするほど泡が大きくなります
±泡サイズ泡の大きさがバラつくようになります
泡H/S/V泡の色をHSV色表現で指定します
泡明るく泡の輝度を高くします

表情モーフ左下:レイトレーシングレンダラについての設定

吸収R/G/B流体が吸収する光の成分と度合いを指定します。吸収されて残った分が流体の色として見えるようになります。透明感のある色の付き方になります。1.0より大きくしても問題はありません。
散乱R/G/B流体が散乱する光の成分と度合いを指定します。濁った感じの色の付き方になります。1.0より大きくしても問題はありません。
濃くする上記2つの効果を増大させます。0.1上げるごとに2倍、4倍…と増えていき、1.0の時それぞれが1024倍になります。
面の粗さハイライトの鈍さを指定します。sdPBRのマテリアルパラメータのroughnessとほぼ同義です。

表情モーフ右下:パーティクルレンダラについての設定

粒H/S/V粒子を表示するためのポリゴンの色を変更します
粒明るく粒子を表示するためのポリゴンの色を明るくします
粒色変化粒ごとに色相が変化するようになります
粒大きく/小さく粒の表示サイズを大きく/小さくします。表示サイズだけが変わるので、流体の計算には影響しませんが、流体の落とす影の大きさには影響します。
±粒サイズ粒の表示サイズがばらつくようになります
影大きく/小さく影の表示サイズだけを大きく/小さくします。両方とも0の時は粒の表示上の大きさに等しくなります

レイトレーシングレンダラについて

パーティクルによる表示単体ではあんまり流体っぽくないというか、ポリゴンに張られるテクスチャ(texture.png)を差し替えれば他の雰囲気にもなりますけど、水面が面として表示されないのでいまいち物足りない感じになりがちです。物足りない時は、sdFluid3DRayTrace.xもドロップしてみましょう。ちょっと読み込み時間が長いです。

sdFluid3DParticleは一旦表示をオフにすると、以下のような感じになります。

そのままではザラザラした見た目なので、sdTAAも入れると良いです。多少は滑らかな見た目になると思います。上の画像もsdTAAと併用して作っております。

このレイトレーシングレンダラは、sdPBRのskyboxと、平行光源の影響を受けて照明されます。追加ライトの影響は受けません。

コントローラの左下のモーフで色を変えられます。透き通った水などを表現する場合は吸収R,G,Bで調整して表面の粗さは0のままにし、濁った沼などを表現する場合は散乱R,G,Bで調整して表面の粗さは適宜上げると覚えておくと良いと思います。色の付き具合が薄い場合は濃くするモーフを上げましょう。

アクセサリのX,Y,Zパラメータで水面の作られ具合を微調整できます

X面を作る根拠となる流体の密度を増減して計算するようにます。値を増やすと面が作られやすくなり、減らすと面が作られづらくなります。
Y面が作られる範囲を増減します。値を増やすと広い範囲で面がつながるようになりますが、粒子がデカく見えるようになります。
Z面が作られる流体の濃度の閾値を増減します。-1より大きい値で指定し、閾値が(Z+1)倍されます。つまり、値が大きいほど面が作られにくくなります。

流体の上をボールが跳ねまわるような描写が気になる場合は、Xパラメータを少し下げて、Yパラメータを少し上げると丁度よい加減になると思います。上げ下げの幅は0.1ずつくらいで様子を見ながらゆっくり調整してみてください。

泡を入れる

水面が出来た所で泡も入れてみましょう。sdFluid3DFoamSolver.xとsdFluid3DFoam.xをドロップしてください。描画順はsdFluid3DFoamSolver.xはsdFluid3DSolverの真下、sdFluid3dFoam.xはsdFluid3DRayTracing.xより上、Solverたちより下が良いでしょう。

こんな感じで泡がついて、多少良い見た目になると思います。コントローラの右上のモーフで泡のサイズや色は自由に変更できます。泡を大きめにするとコシのある泡になりますが、大きくしすぎると天ぷらみたいになります。

泡の出やすさモーフを増やすと波などが立っていない時でも泡がよく出るようになりますが、泡パーティクルの上限が65535個と決まっているので肝心な時に枯渇している(既に画面に全部出ているので追加で出せない)事が良くあります。特に、スケールモーフでフロードメインを大きめにしている時などはその傾向が顕著になります。モデルの動きに合わせて泡を都合よく出したい場合は、若干下げ気味にすると良いでしょう。

sdFluid3DFoam.xのYパラメータで泡の高さを微調整できます。水面より上に出せるようにした方が泡が目立ちますから、引きの構図で使う場合は適宜Yパラメータを上げて調整した方が見栄えがすると思います。

障害物を入れる

泡が付くのは良いんですけど、モデルの動きに合わせて流体が退いて飛沫が上がったりすると、よりそれっぽくなりますね。

obstaclesフォルダにある、sdFluid3D_O0~7.xをどれでもいいのでドロップすると、流体を退ける障害物を入れる事が出来ます。モデルの外部親に設定すると良いでしょう。

サイズはSiパラメータで変更できます。球体の半径をm単位で指定できます(MMDで表示される座標の1.0を8cmとして換算しています)

モデルの動きに合わせて分かりやすく泡や飛沫を出すには、当たり判定用の球のサイズをかなり大きくしないといけないと思います。粒子が大きく動くところほど泡は出やすいためです。ですので、精密な当たり判定は我慢して、流体を構成している粒子の3倍程度の大きさにするのがよいでしょう。

あんまり多くするとシェーダのコンパイルが通らなくなるので、最大8つくらいが関の山だろうという事で、こうなっています。

個々の.xファイルにはメッシュが無いため見えないので、障害物の位置はコントローラ左下の障害物確認モーフを上げると見えるようになります。

モデルが流体の影を受けられるようにする

透明度の低い流体を表示する場合、モデルが流体によって影を受けられるとよりそれっぽくなります。

sdPBR_SD0~sdPBR_SD3タブの、sdFluid3DParticle.xのエフェクトファイルに、map_sdFluid3DParticle_OrthoShadow0~3をそれぞれ指定すると、流体の影をモデルが受けられるようになります。また、流体による平行光源のボリュームライトも表現されるようになります(追加ライトの影響は受けません)

レイトレーシングのレンダラしか使わないんだけど影は欲しい、という場合は、sdFluid3DPaticle.xの代わりにsdFluid3DShadowCaster.xをドロップし、それについて上述の通りsdPBR_SD0~sdPBR_SD3タブにエフェクトファイルの設定を行ってください。

デフォルトでこれらの設定が行われないのは、透明度の高い流体の場合はモデルに流体の影が落ちると却って不自然だからというのもあるんですが、頂点数の多いパーティクル用の.xファイルに対してVertexShaderが走ると余計にGPUパワーを食うので、極力そうしたファイルにはVertexShaderが走らないように関係ないタブからはチェックを外す事をお薦めします。

DoFエフェクトを付ける

雰囲気の有る絵作りにはDoFエフェクトが欠かせませんから、DoFエフェクトを水面に対して比較的正しく掛ける方法についても紹介しましょう。

sdDoFZタブのsdFluid3DParticle(またはsdFluid3DShadowCaster)にmap_sdFluid3DParticle_Depth.fxを指定すればDoFエフェクトから水面の奥行きを取れるようになり、被写界深度によってボケる効果が出るようになります。

コントローラ以外の設定

ここまでで一応の使い方を一通り説明しましたが、設定ファイルであるsdFluid3DDefs.fxsubをいじると以下のような項目についても編集できます。

シミュレーションの時間解像度の設定が意外に重要でして、デフォルトでは60fpsの動画に出力することを念頭に動作し、1フレーム経過するごとにシミュレータの内部状態は1/60秒後の状態に更新されます。ですから、設定を変えずに30fpsで出力すると1/2倍速になってしまいます。

デフォルトのフロードメインは64x64x64しか無いのでどうしても大雑把なシミュレーションになりがちですが、こちらもいじるとセル数を増やせます。但し、セル数を増やすと爆発的に重くなっていくのでご注意ください。

いじり方については当該ファイルに書いてある通りですから、バックアップを取ってからお好きなようにいじってみてください。

フロードメインの未来

一通りいじってみると色々とフロードメインには問題があると分かると思います。まず粒子のサイズが超デカくてアリエッティ状態ですし、粒子の寿命を無制限に設定しているのに粒子が勝手に消滅して、流入口からリスポーンしたり、いつまでも水面が落ち着かずにつねにユラユラしたりします。

粒子が極端に圧縮されたりするとシミュレータの管理から外れがちで、シミュレータの管理から外れると回収されてリスポーンするという事になっており、特に粒子径が小さい場合はザルの目から漏れるように何らかの拍子に粒子が管理から外れてしまう事が良く起こるのです。要するにシミュレータ自体がそんなに精度の良いものではなく、「なんとなく動いているだけ」という程度の物だからです。

どうしてこうなったのかというと、DirectX9で無理やり作っているため、設計の時点で既に解決できない問題が山積みで、それがそのまま実装にも反映されてしまっているためです。本来はComputeShader等が使えるDirectX11以降で作るのが望ましいのですが、現状で広く通用しているMMEはDirectX9準拠なので仕方ありません。

ちなみに、流体とメッシュの当たり判定を正確に取りたい、という要望も必ずあると思うのですが、シェーダからそれをやるにはハードウェアレイトレーシングが必要なので、DirectX12以降でないと不可能です。