Packed RBDを使って大量シミュレーションを構築します。
Packed RBDとInstanceの組み合わせ
今回は、Instanceのレンダリングを前提とした、大量のジオメトリをシミュレーションします。
大規模なRBDをシミュレーションするために必要なのは、メモリと時間が前提ですが、なるべくそのコストを下げて、画づくりのリテイクに時間をかけられるようにしたいと言うのが、アーティストの本音だと思います。
Houdiniでは、Bulletを使った高速なRBDシミュレーションに対し、Packを組み合わせることで、さらに低コストなシミュレーションを行うことができます。また、Pack自体のメモリの消費量が少ないこともあり、レンダリングコストも同時に下げることができます。
今回は、さらにマニュアルな制御をすることで、よりコントロールのしやすいワークフローを検証したいと思います。今回のレンダリングにはRedshiftを使用していますが、通常のMantraやKarmaなどでも同等の結果を得ることが可能です。
01 Proxy Geometry
Proxy Geometryを作成します。
最初に、シミュレーションに向けてProxyのGeometryを作成します。通常、BulletではGeometryを再構築して、軽量化してシミュレーションを行うことができますが、今回は大規模シミュレーションを前提としているので、さらに最適化した状態に準備しておきます。
まず、ベースとなるGeometryを読み込み、シミュレーションサイズに調整します【A】【1】。
次に、それを囲うようなジオメトリにするために、いったんVDBを使ってVoxel化して、元のGeometryから少し膨らんだ状態のPolygonにコンバートします【B】【2】。
元のGeometryに対して、Attribute Paint SOP【C】を使って、"area"のAttributeをペイントし【3】、それをAttribute Transfer SOP【D】で、コンバートしたGeometryに転送します【4】。
このAttributeを使って、PolyReduce【E】します。このとき、Retain Density by Attributeを使うことで、指定した"area"でPolygonの密度を調整することが可能です。今回は、蟹の脚の部分の形状をなるべく維持するために使用しています【5】。
次に、VOP【F】を使って、ケージのGeometryが元の形状に対してある程度フィットするように再調整をかけます。XYZ Distance VOP【G】で近しいPrimitiveを見つけて、Primitive Attribute VOP【H】でそのPrimitiveの位置にフィットさせます。その状態からさらにDisplace Along Normal VOP【I】を使って、法線方向に膨らませ、オフセットをかけます。これで、ProxyのGeometryが完成しました【6】。
Instance用のPointを作成します。Add SOP【J】でPointを作り、Wrangle【K】で“N”と“up”のVectorを作成し、後々選択しやすいようにGroup【L】を作成しておきます【7】。
続いて、シミュレーションの発生源を作成します。Sphere【M】などを使って範囲を作成し、そこにVDB【N】などでVolumeを作ります。そのVolumeに対してScatter SOP【O】で点群を作成します。Seedを毎フレーム変えることで、RBDが発生しているようになります。また、Relax Point SOP【P】を使って、Proxyが重ならないようにPoint間の距離を調整します。Attribute Randomize SOP【Q】で、法線をランダムにします。このPointを基に、ProxyをCopy【R】します。その際、Pack and Instanceをアクティブにして、Packとして複製していきます。これで、RBDソースの完成です【8】。
ソースは、発生源を広げたり、発生するPoint数にアニメーションを付けることで、より自然に発生をコントロール可能です。
後々必要になるInstance用のハイメッシュのジオメトリもここで出力しておきます【S】。今回はRedshiftを使ったレンダリングなので、データタイプは".rs"としてアーカイブを出力していますが、Mantraなどでレンダリングする場合は、".bgeo”で出力してください。
02 Packed Simulation
RBDのシミュレーションを行います。
Dopnet【A】を作成し、Proxyを使ったRBDの設定をしていきます。
RBD Packed Object【B】を使って、RBDのシミュレーションを行います。CollisionのセッティングはConcaveにして、Proxyを使用するようにします。再度ジオメトリを作成してしまうと無駄にPolygonが増えてしまうので、注意が必要です。
今回はRBDを発生させる必要があるので、POP Source DOP【C】を使って、All Geometryにして毎フレームProxyを読み込みます。これをPOP Solver DOP【D】を通して、Multisolver【E】に接続します。RBDのシミュレーションには、Bullet Solver【F】を使用します。これも同様にMultisolverに接続し、複数のSolverが計算できるようにします。
CollisionはシンプルにGround Plane【G】を使用します。必要に応じて、Static ObjectでCollisionを増やしても良いでしょう。
最後にGravity DOP【H】で重力を付けて完了です。
シミュレーション結果をDOP Import SOP【I】で読み込みます。それをFile Cache SOP【J】でキャッシュをとります。
Unpack SOP【K】で一度Geometryを戻し、Blast【L】でInstance用のPointのみ抽出します。ここには、“N”や“up”のVectorがついている状態です【1】。
※本来、Unpackをすることで重いGeometryを一度介する必要が出てきてしまうのを避けたいところですが、Packのシミュレーション結果のOrientを適用させる必要があり、方法が少々複雑になってしまうので、今回はシンプルなアプローチを取っています。
今回のレンダリングで使用した色情報をVOP【M】を使ってPointに与えます。
最後に、Attribute Create SOP【N】を使って、"instancefile"のAttributeを作成します【2】。Stringの値は、配置したいGeometryのパスをそのまま入れ込んでいます【3】。
これによって、Instanceに対応する各レンダラの設定で、Instanceの設定に調整するだけで、簡単に大量のPolygonをレンダリングすることが可能になります。今回の場合では、最大で66億Polygonのレンダリングになっています。それも、メモリがスワップすることなく、安定してレンダリング可能です【4】【5】。
03 Operators
●Instance
今回は、主要なオペレータと言うわけではなく、ワークフローのひとつになりますが、Instanceについて解説します。HoudiniのInstanceは、いわゆるレンダリング時におけるProcedureなしくみのことを指します。Instanceのアプローチはいくつもあり、それぞれでメモリの使用方法なども変わってきますし、厳密にレンダリングのどの時点でどういう風にジオメトリを構築するかで、レンダリングの結果(かなり厳密に言えば)が異なります。
Houdiniに限らず、レンダリングと言うのは、Geometryを構築する際のプロセスがありますので、そこでInstanceの振る舞いが変われば、結果が変わってくるのは当然と言えるでしょう。
Houdiniは、主要レンダラとの連携が近年は向上しており、今回のようにRedshiftをはじめとするサードパーティ製のレンダラを使用することが増えてきています。それと同時に、Instanceのような、特殊な手続きを必要とするワークフローの対応もどんどん改善されており、ほぼMantraなどと変わらない状態でレンダリングすることが可能になってきています。
HoudiniのInstanceは、レンダラとの連携を密接にすることで、無駄のない、低コストで大規模なレンダリングを可能にしています。これも、Houdiniの基本概念である、データをシーン内部に持たないと言うところも大きく、いわば、レンダリング時にのみ、最小限で呼び出す遅延読み込みが重要なキーになります。
"instancefile"のようなAttributeでの制御なども、そのしくみを知ることで、より複雑で大規模なシーン構築を可能にする、基本的考え方のひとつなので、今回のアプローチをぜひ研究してみてください。
秋元純一 / Junichi Akimoto(トランジスタ・スタジオ/取締役副社長)
日本でも指折りのHoudini アーティスト。
アーティスト業務の傍ら、Houdiniアーティスト育成や布教活動に勤しむ
www.transistorstudio.co.jp
blog.junichiakimoto.com