今回は、Pyro Solverを用いた焦げの表現を紹介します。
![](/regular/3d8bd2655d5a07c7cadc6e62078e44774a8f188b.jpg)
Temperatureの利用方法
今回は、Pyroでつくり出されるTemperatureのFieldを使って、ジオメトリにAttributeを作成し、それを用いて焦げていく様子を表現してみたいと思います。Pyroを用いること自体は、そこまで今回は重要なポイントではないのですが、せっかくなのでPyroの基本的な使い方をおさらいしつつ、SOPベースでの焦げをどのようにして表現するか検証します。また、二次的な焦げから発生する煙なども含め、トータルでの反応をどのように表現するかも併せて検証していきます。
Houdiniでは、単純なシミュレーションを組み合わせることで、より複雑な表現が可能になります。それはシミュレーションだけによるものではなく、SOPなどを駆使した前後処理によるところが非常に大きいです。今回の焦げる様子に関しても、基本的にはSOPをベースとした処理によるもので、その基軸に対してシミュレーションが肉付けをしていくというながれになっています。どうしても、こういったエフェクティブな内容だと、シミュレーションに目が行きがちになりますが、Houdiniにおいて重要なのはSOPでの準備と後処理だと言えます。
01 Burner Sim
バーナーのシミュレーションを行います。
![](/regular/images/87f608246ff04cd108a341cb35d1e7935f520841.png)
ベースメッシュ【A】【1】を準備して、それに対してCollision Source SOP【B】でGeometryとSDFへ変換します【2】。
火元となる筒を準備し【C】、それを移動させられるようにします【D】。同じくCollision Source SOP【E】で準備します。この筒の内部に火種となるジオメトリを作成し【F】、Pyro Source SOP【G】を用いて燃焼用に"burn"【3】と"temperature"【4】を作成します。
![](/regular/images/5bf78844b2ba27281603db9d5876868fa227d915.png)
そのPointにPoint Velocity SOP【H】を使って、バーナーの勢いとなるVelocityを作成します【5】。次に、Volume Rasterize Attribute SOP【I】により、PointのAttributeからVolumeを作成します【6】。これでシミュレーションの下準備が完了です。
Dopnet【J】を作成し、内部にPyroのフローを組んでいきます。
![](/regular/images/8ae3fd89f07bb2e154dce64896d4714982381e7d.png)
シミュレーション後は、Object Merge SOP【K】で、DOPからFieldを読み込みます【7】。DOP I/Oなどでも同じことが行えます。最後にキャッシュを取り【L】完了です。
![](/regular/images/01a2c55349530270244e7463f65f03c13b2b8e9c.png)
Collisionを準備します。Ground Plane【M】を作成し、地面とします。続いて、Static Objectでベースのジオメトリ【N】と火元のジオメトリ【O】を作成します。火元のジオメトリは動く想定なので、Use Deforming Geometryをアクティブにしておきます。それぞれVolume SampleでCollisionの設定を行い、Proxy Volumeに対してCollision Source SOPで作成したSDFを読み込みます。
続いて、Pyroの設定を行います。今回はSparseを使ったシミュレーションにします。Smoke ObjectをSparseで作成します【P】。Voxel Sizeを適度に調整しておきます。Pyro Solverも同じくSparseで作成します【Q】。Volume Source【R】で、SOPからSourceとなるVolumeを読み込みます。
今回はSparseを使用するため、Enlarge Fields to Contain Sources【8】をアクティブにします。これは、Sparseを使用する場合、TargetとなるFieldがSourceを包括する必要があるためです。
また、それぞれのFieldを読み込み、Targetへ追加します。今回は燃焼を行うため、"burn"を"flame"へ【9】、"temperature"は"temperature"へ【10】、"v"は"vel"へ【11】それぞれのOperationで追加します。また、燃焼の終息をアニメーションさせます【12】。
![](/regular/images/fcdda84ea1852936b5f63008d75caaa1737e7785.png)
今回のシミュレーションは高速で発生する炎なので、その勢いをある程度フォローできるように、Pyro SolverのMax Substeps【13】を高く設定しています。最終的にはモーションブラーなどで多少の誤魔化しも効きますが、その元となる素材にある程度のディテールは必要になりますので、シミュレーションコストと相談しつつ確定します。
![](/regular/images/e38e1ee4c33841534d52f0036086b74570e271a4.png)
これでバーナーのシミュレーションの完了です【14】。
![](/regular/images/6c27b4cecb1d5a1fa57be3253325ca11f6086485.png)
02 Burnt Solve
焦げのシミュレーションを作成します。
![](/regular/images/028a7ebeaca2bb4a84ee02ec40e5db216f67b213.png)
シミュレーションのキャッシュを使って、ジオメトリに焦げのAttributeを作成していきます。まず、"temperature"のFieldのみをBlast SOP【A】で抜き出します。そこからAttribute from Volume SOP【B】を用いて、ジオメトリにAttributeを作成します【1】。
![](/regular/images/321ab147635eefb5f5beafa3faa3dfb152acb494.png)
このとき、どういった数値が作成されているかを確認して、数値の調整を必要に応じて行います。Attribute Copy SOP【C】を使って、"temperature"のAttributeを"burnt"というAttributeにDuplicateします。今回は、加熱と焦げを別のAttributeとして作成していきたいと思います。Attribute Blur SOP【D】でそれぞれのAttriuteをぼかします。これらに対しSolver SOP【E】を適用し、時間軸で広がっていくような処理を行います。
別途、後述するSmokeの作成のために、"vel"のFieldを抜き出し【F】、VDBに変換しておきます【G】。
最後にジオメトリのキャッシュをとり完了です【H】。
![](/regular/images/5b94fd5b6358f0afbab7914379bf7e59a67f8212.png)
Solver SOP内では、インプットされたままの情報【I】と計算された情報【J】を使って、時間軸で情報が積み上げられていく様子をシミュレーションしていきます。
まず、Switch SOP【K】で、最初のフレームでインプットが切り替わるようにします【2】。
![](/regular/images/57d5142750293dcb04734cf5e386117b410bce0d.png)
次に、"temperature"の拡散を処理します。Attribute Blur SOP【L】でぼかしたAttributeを用いて、Wrangle【M】によって少しだけ温度が周りに拡散する様子を計算します【3】。
このとき、WrangleのInput2をPrev_Frame【J】にすると、さらにどんどんと拡散しますが、今回はそこまではしていません。"temperature"をさらに少しだけぼかします【N】。必要であれば、"burnt"もぼかします【O】。
![](/regular/images/3de2f91903f9c8d9e0c7f3d108ca2b5e8878991f.png)
次に、Wrangle【P】を使って、"temperature"と"burnt"が時間軸で蓄積されていく様子を計算します【4】。"temperature"には係数を乗算し、徐々に冷めていく様子を再現しています【5】。"burnt"はそのまま蓄積の計算です【6】。
![](/regular/images/0b4c4f674f80c107a668e57dae272d6bc5307670.png)
03 Smoke Sim
![](/regular/images/b5f760929c9860dc2f4a74a14d2ec06287a651c2.png)
ジオメトリのキャッシュから、"temperature"のAttributeをVOP【A】で調整し、輪郭にエリアのAttributeを作成します。そのエリアに対してScatter SOP【B】でPointを作成します【1】。
Attributeが転送されていない場合、Scatter SOPのエリア指定が効かないので、Attributeが0の場合はDelete SOP【C】でPointを削除します。また、余分なAttributeをAttribute Delete SOP【D】で削除し、Pyro Source SOP【E】でSmokeのセットアップをします。Attribute Noise SOP【F】でDensityにノイズを追加し、Volume Rasterize Attribute SOP【G】でDensityとTemperatureのVolumeを作成します。
![](/regular/images/c576ad0e5aa22776c0f9d0af689ce2eac148cf72.png)
シミュレーションのセットアップに関して、Collision【L】やSmoke Object【M】、Pyro Solver【N】などは、前述したPyroの設定を踏襲しています。
異なるポイントは、今回は煙なので、Volume Source【O】の設定はSmokeに準拠しているところです。また、バーナーの"vel"のFieldを読み込み、動きをマッチさせるために、Volume Sourceで"vel"をAdvectしています【2】。
![](/regular/images/bc2ff8a8ec5c042839f526c9a39a7b65670175d7.png)
Dopnet【H】を作成します。
シミュレーション後は、Object Merge SOP【I】でFieldを読み込み、必要に応じてVDBへ変換して【J】キャッシュを取り完了です【K】。
![](/regular/images/b15a40b6e5d2e91b97fa502c809d6c1f2915ac3c.png)
04 Operators
●Pyro Solver
![](/regular/images/2c09e9faa4a88b446d93e307601f9d47fd358ea6.png)
今回のコアになった部分ではありませんが、重要な役割を果たしてくれたのが、Pyro Solverです。今回は、Sparseを使用していますが、非常に軽量化されたデータを使用することができて、これまででは難しかったような、大規模なシミュレーションにも手が届くようになっています。
本連載ではあまりPyroをメインとした解説は行なっていませんが、いずれそのようなフルイドシミュレーションメインの解説も取り上げたいと思います。 従来のPyroと大きく異なるのがVolumeのもたせ方で、Sparseの場合は非連続したVoxelで構築することができると言う点です。
これまでは、必ず直方体である必要があったSmoke Objectも、必ずしもその限りではない状態でシミュレーションできるということです。 これによって、空白だったVoxelを節約することが可能になり、従来より無駄の無い計算で低コストになったというわけです。
Houdiniは今やエフェクトの世界ではスタンダードなツールとなりました。そういった状況もあり、今後の進化はさらに優れたものになると予想されます。Pyroはその中でも重要なアイテムなので、これを期に、研究をしてみてください。
TEXT_秋元純一 / Junichi Akimoto
EDIT_小村仁美 / Hitomi Komura(CGWORLD)