ThinkingParticlesとボリュームで作る水滴アニメーション

ThinkingParticles(以下TPと略します)はCinema 4D Studioに搭載されているパーティクルシステムで,標準パーティクルより高度な表現が可能です.しかしながらTPはノードベースでパーティクルを制御する特徴があり,手を出しづらい,難しいな,と敬遠されている方もいらっしゃるかもしれません.

このチュートリアルではTPを使って水滴をつくる過程をシェアしたいと思います.とはいえ,TP以外の方法で作ることも可能でこの限りではありません.

この記事はXPressoとユーザーデータの基本ができていることを前提で書いていますので,難易度として中級者向けとなります.もしXPressoやユーザデータを使ったことが無い場合は先にヘルプやトレーニングサイトのXPresso基礎に目を通しておくことを推奨します.

XPressoマニュアル  https://help.maxon.net/jp/#5828
ユーザデータマニュアル  https://help.maxon.net/jp/#5826
Cinema 4Dトレーニングサイト: XPresso基礎 https://c4d-training.jp/?cat=6

今回つくる成果物は次のようなものです.水滴を作るだけなら簡単ですが,せっかくTPを使うのでアニメーションもさせることにします.

サンプル画像
サンプル画像
サンプル画像

シンプルに球体を使って一つ一つのパートを作っていきたいと思います.また,最終的にオブジェクトの差し替えがなるべく簡単に済むように考えながら作るのも重要となります.

1. 球体の表面にパーティクルを発生させよう

シンプルに球体の表面に水滴となるパーティクルを発生させることにします.標準パーティクルではオブジェクトからパーティクルを発生させることができませんが,TPならとても簡単です.

1.1 TP用オブジェクトとXPressoタグの準備

TPは使い方が少し特殊です.必ず「何かのオブジェクト」「XPressoタグ」が必要になります.TPはXPressoを使ってノードでパーティクルを管理するためです.

ヌルオブジェクトを作成し,名前を「TP_Control」などと名前を付けておきます.これをTPのユーザデータ管理用オブジェクトにしようと思います.作成過程でユーザデータを追加してユーザーデータからTPをコントロールできるようにしていきます.TPはパラメータが多くなりがちですし,最終調整の際,毎回XPressoエディタを開いてノードにアクセスするのも少し面倒だからです.

もう一つヌルを作成し「TP_Control」の子オブジェクトにし,XPressoタグを追加します.XPressoタグは通常は関連性が高いオブジェクトに適用した方が後でわかりやすいです.

パーティクル発生用の球体も作成しておきます.<編集可能にする>でポリゴン化させます.TPでオブジェクトを使う場合は基本的にポリゴンオブジェクトである必要があります. サイズはデフォルトの半径200cmにしています.

ヌルとXPressoの準備
ヌルとXPressoの準備

1.2 球体の表面からパーティクルを発生させる

XPresso編集を開き,TP用のノードを追加します.オブジェクトのサーフェイス表面からパーティクルを発生させるノードは「PMatterWaves」です.TP用のノードは,<新規ノード>→<Thinking Particles>に入っています.

PMatterWavesノードの設定を
〈オブジェクト〉…球体をドラッグ&ドロップ
〈生成タイプ〉…ショット
〈ショット〉…100
〈速さ〉…0

にしておきます.

球体表面からパーティクルを出す
球体表面からパーティクルを出す

このままではパーティクルを毎フレーム発生させるので,〈時間〉ノードの〈フレーム〉ポートを出して 〈比較〉ノードへ繋ぎ,比較の入力2に「1」を指定して〈PMatterWaves〉の「オン」ポートへ接続します.

この動作は1フレーム目だけ〈PMatterWaves〉がオンになり,パーティクルを発生させるということです.〈PMattaerWaves〉は選択範囲を指定できるので,任意の選択範囲タグからパーティクルを発生させたり,〈生成タイプ〉にテクスチャやノイズシェーダなどを使ってパーティクル生成位置をコントロールすることもできます.

1フレーム目だけパーティクルを出す
1フレーム目だけパーティクルを出す

「TP_Control」にはユーザデータを作成します.この時点では
データタイプ: リンク…発生源となるオブジェクトリンク
データタイプ: 文字…発生させたいポリゴン選択範囲タグ
データタイプ: 整数…発生させるパーティクル数

を追加しておきます.

「TP_Contorl」オブジェクトをオブジェクトノードとして追加して,各ユーザデータから〈PMatterWaves〉の〈オブジェクト〉,〈選択範囲〉,〈ショット〉ポートに渡します.ユーザデータから発生源やパーティクル数を変えられるということは,都度〈PMatteerWaves〉ノードにアクセスする必要がなくなるので,後々ノード数が増えた時や最後の微調整時に便利ですね.もちろん,任意で比較ノードの入力2にパーティクルを発生させたいフレーム用のユーザデータなどを追加してもよいでしょう.この辺りはどのようにTPをコントロールしたいかによって都度変えるとよいと思います.

ユーザデータの作成
ユーザデータの作成

※注意
〈PMatterWaves〉は形状によってはパーティクルがうまく発生しない箇所が出てきます.

PMatteerWavesから上手くパーティクルが出ない時
PMatteerWavesから上手くパーティクルが出ない時

このような場合は,〈PMatterWaves〉の<UVW タイプ>「固定しない」に変更してみましょう.

UVWタイプを変えてみる
UVWタイプを変えてみる

1.3 TPグループを作成する

TPの特徴はパーティクルをグループで管理するという点です.TPを使う上で非常に重要な考え方なので必ず覚えておきましょう.発生したパーティクルをAというグループに入れるとします.Aグループには風や重力をかけて動かして,何かの条件(例えば別のオブジェクトにパーティクルがヒットしたなど)が発生した時にパーティクルを別のBグループへ変更できます.Bグループではフリーズさせて動きが止まるようにしておけば,オブジェクトにヒットした瞬間にグループが変更され,パーティクルが止まるといったことができます.

グループは親子関係も作れるので,親の動きを継承するといったこともできます.パーティクルの発生からグループを変化させながらアニメーションをつくるのがTPの基本的な流れになります.慣れるまでは少し難しいかもしれませんが,この考え方のおかげで標準パーティクルでは難しいアニメーションが作れます.

グループは〈シミュレーション〉メニューの〈Thinking Particles〉→〈Thinking Particles 設定〉から開けます.TPの作業時にはよく使うのでレイアウトに組み込んでおくとよいでしょう.グループを役割毎に作って管理していくのが基本的な使い方となります.

次の画像は例ですが,それぞれのグループには用途に合わせた名前とカラーを割り当てていきます.

TPはグループでパーティクルを管理する
TPはグループでパーティクルを管理する

パーティクルグループはデフォルトで「All」グループが割り当てられます.「All」はシーン内のすべてのTPグループを含むルートグループのようなものです.

では,「All」を右クリックして〈追加〉でグループを追加します.グループの名前は「Static」グループとして,さらに「Static」の子に「Static_Generate」をという名前のグループを作成します.グループは作成しただけでは意味がありません.〈PGroup〉ノードを作成し,ノードに「Static_Generate」グループをドラッグ&ドロップしてパーティクルをグループに割り当てます.

グループを作り球体表面に発生したパーティクルを「Static_Generate」グループに割り当てましょう.グループは色を変えないと分かりにくくなるので,必ず色は変えておきましょう.「Static_Generate」は球体表面に留まるパーティクルとします.

グループを作成して変えてみる
グループを作成して変えてみる

2. 近すぎるパーティクルのスケールを小さくする

「Static_Generate」でパーティクルを発生させましたが,パーティクル同士の距離が非常に近いものがあります.グループを使うことで接近しているパーティクルのスケールを調整することもできます.

2.1 接近しているパーティクルのサイズを変更してみる

「Static」グループの子に「Static_Small」のパーティクルグループを作成します.パーティクル同士の距離は〈PPass AB〉ノードを使うことでAとBのパーティクル同士の距離を調べ,条件に一致する2個のパーティクルを取得できます.

取得したパーティクルの細かいパラメータを取得するために〈PGetData〉ノードを使います.これで片方のパーティクルから〈大きさ〉を取得します.〈計算〉ノードを乗算で使い,〈大きさ〉を変更したうえで〈PSetData〉ノードへ〈大きさ〉をセットします.この時,〈PSetData〉のパーティクルポートには〈P Pass AB〉から出た「B」を接続することで,自分の大きさを変えて自分に再セットする流れが出来上がります.同時にそのパーティクルは〈PGroup〉を使って「Static_Small」グループへ割り当てます.

条件指定でグループから別のグループへ変える
条件指定でグループから別のグループへ変える

〈PGetData〉〈PSetData〉ノードはパーティクル制御の様々な場面で利用できます.今回は自分のサイズを小さくしていますが,他のパーティクルに渡したりも考えられますね.〈P Pass AB〉と計算ノードで使う数値も「TP_Control」のユーザデータに追加して調整できるようにしていきます.

さらにユーザデータを追加する
さらにユーザデータを追加する

この例でいえば,「Distance」がパーティクルの距離で,「Small Static Scale」が取得したパーティクルのサイズに乗算する値となります.ただし,この時点ではパーティクルサイズが目に見えて確認できるわけではありません.

3. 落ちる水滴のパーティクルを作る

「Static_Generate」が表面に留まる水滴としたなら,これとは別に落ちる水滴も作成してみましょう.

3.1 別のグループを作る

TPはXPressoなのでタグは一つでも作業できますが,本チュートリアルでは別のオブジェクトを作成し,XPressoタグも分けて作成していきます.理由としては様々ですが,後で「Static」グループの計算を止めておきたい時などに別々にXPressoを停止するといったことができます.もちろん,管理しやすければどのようにしてもかまいません.

では,あたらしいパーティクルグループを「Drop」として「All」の子に作成します.パーティクルは同じ球体から発生させるので,必要になるノードは基本的に同じですから「Static」グループのXPressoタグを複製しても中身を変更しても構いません.「TP_Control」には「Drop」グループのパーティクル数を決めるためのユーザデータも追加しています.パーティクル数のユーザデータ名が重複してしまったので変更し,後でノードを見た時にどのユーザデータなのか分かりにくくなるのを回避します.

落ちる水滴のパーティクルグループを作成する
落ちる水滴のパーティクルグループを作成する

問題は「Drop」グループのパーティクルをどのようにアニメートさせるかですね.

3.2 パーティクルをVelocityでアニメートさせる

パーティクルグループを別のノードに渡すために〈P Pass〉ノードを使います.〈PPass〉はパーティクルグループのデータを出力するノードです.先に使った〈PGetData〉や〈PSetData〉を使って各パーティクルが持っているデータを取り出したり,または新たにセットしたり,ダイナミクスの効果をつけたりできます.〈P Pass〉はTPを使う上で頻繁に使用することになりますので,〈PGroup〉ノードとセットで覚えてくと良いです.

〈P Pass〉ノードを作成したら,「Drop」グループをドラッグ&ドロップします.「Drop」グループの子に「Drop_Move」グループを作成し,〈PGroup〉を使ってグループを変更します.

もう一つ〈P Pass〉を作成し,「Drop_Move」グループを取り出して,〈PVelocity〉ノードへ渡します.Velocityはパーティクルの速度と向きを制御するノードです.方向を「0, -1, 0」にするとマイナスY方向へパーティクルが移動していきます.通常,パーティクルを生成した「Drop」グループに対してダイナミクスノードを接続してもよいのですが,今回は次の工程で「形状に沿って移動させる」のために〈ダイナミクス近接〉というノードを使用しますので,パーティクルを生成した位置を一旦確定させる必要があるため,「Drop」グループのパーティクルが発生したと同時に「Drop_Move」にグループを変えておきます.

Velocityノードを使ってみる
Velocityノードを使ってみる

Velocityの影響で「Drop_Move」パーティクルは下へ移動していきます.

3.3 球体に沿ってパーティクルを移動させる

パーティクルは下方向に移動するようになりましたが,水滴は当然ながら物体の表面に沿って移動させたいところですね.各パーティクルの位置を球体の表面に移動させるために,〈PGetData〉と〈PSetData〉ノードを使います.さらに〈ダイナミクス 近接〉ノードも作成しておきます.まず〈PGetData〉ノードを作成し,〈P Pass〉からパーティクルポートを接続します.

〈ダイナミクス近接〉ノードはダイナミクスオブジェクト間の距離を得ることができます.その位置をパーティクルの〈PSetData〉の位置へ渡します.

※注意
〈ダイナミクス近接〉ノードに発生源となる「TP_Control」の「Emitter Source」ポートを接続しますが,対象となる球体にはダイナミクスタグが付いている必要があります.ここでは単に衝突タグを設定しています.また,ダイナミクス近接のモードは「形状」にしておきます.

「Drop_Move」パーティクルがVelocityで下に移動しているとき,ダイナミクスオブジェクトの最も近いパーティクルの位置を調べ,その位置をパーティクルの位置として上書きします.

ダイナミクス近接タグの特性
ダイナミクス近接タグ

これで球体に沿ってパーティクルが落ちていくように見えます.

3.4 落ちる向き,速度にムラをつける

すべて一定のスピードで落ちていくと少し不自然なので,少しバラつきを付けたいと思います.

〈PWind〉ノードを作成します.標準パーティクルの風モディファイアとよく似た機能ですが,TPはノードで作成します.また,ヌルオブジェクト(以外でも可)を作成し,名前を「Wind」としておきます.〈PWind〉ノードの上に「Wind」オブジェクトをドラッグ&ドロップすると「Wind」オブジェクトのZ方向に風の方向を示す矢印が出るので下を向くように回転させます. 強度やタービュランスといったパラメータがあるので,調整します.

他のダイナミクスノードで動きを調整する
他のダイナミクスノードで動きを調整する

簡単な設定なら〈PWind〉や〈PFreeze〉,〈PVelocity〉や〈PFriction〉といったノードが扱いやすいでしょう.色々組み合わせてパラメータを少し変更して動きにムラをつけていきます.

3.5 落ちる水滴に跡を引くエフェクトを作る

落下するパーティクルを作りましたが,つづいて「Drop_Move」パーティクルの跡をひくようなパーティクルを作成します.TPではパーティクルから別のパーティクルを発生させることができます.

このグループを「Trace」とします.「All」の子に「Trace」のパーティクルグループを作成し,ヌルも作成し名前を「TP_Trace」としてXPressoタグを適用します.パーティクル生成用ノードは〈PStorm〉を使用します.〈PStorm〉は標準パーティクルのエミッタとよく似ていますが,パーティクルを発生させる位置は別途指定する必要があります.

発生位置を「Drop_Move」パーティクルの位置にしたいので,〈PPass〉ノードから〈PGetData〉を通して「Drop_Move 」パーティクルの位置をエミッタの位置に渡します.ちなみに,〈PStorm〉の向きは<エミッタの配置>ポートで調整できますが,今回は〈PStorm〉自体を小さくするので影響なしと判断して使っていません.

〈PStorm〉ノードの設定
〈生成タイプ〉…「カウンタ」
〈速さ〉…「0」
〈Xの大きさ〉…「1cm」(オブジェクトのサイズに応じて調整します.)
〈Yの大きさ〉…「1cm」(オブジェクトのサイズに応じて調整します.)

XとYの大きさはより小さいオブジェクトを使う場合はもっと小さな値にした方がよいでしょう.(このチュートリアルでは球体の半径が200cm想定のサイズになっています.)

〈PStorm〉の<カウント数>,<寿命>,<寿命の範囲>に「Trace」用に作成したユーザデータを渡します.必要ならXとYの大きさもユーザデータで追加しておけば,オブジェクトサイズが変わったときにユーザデータから調整できるようになります.

パーティクルからパーティクルを発生させる
パーティクルからパーティクルを発生させる

4. 「Static」は「Drop_Move」パーティクルと衝突したら落とす

落ちてきた「Drop_Move」グループのパーティクルは「Static」グループに属している「Static_Generate」と「Static_Small」のパーティクルの近くを通過する可能性があります.もし「Drop_Move」が「Static」グループの近くまたは上を通過したら,「Static」グループのパーティクルも一緒に落ちていくようにしてみます.水滴は現実的にはとても複雑な動きをしていますが,すべてを真面目作るのは大変ですので,ある程度簡略化したアニメーションモデルにします.

ここでは,「Drop_Move」が「Static」の上を通過したら「Static」を「Drop_Move」に変える,という処理を行います.

ここまで作成できていればこれまで使ったノードですぐにできます.ヌルを作成し,名前を「TP_After_Collider」とし,XPressoタグを適用します.〈P Pass AB〉ノードを作成し,パーティクル同士の距離を取得します.測定するパーティクルグループは「Static」と「Drop_Move」グループですね.指定した距離にある「Static」と「Drop_Move」の各パーティクルが取得できます.取得した「Static」を〈PGroup〉を使って「Drop_Move」グループへ変更します.この時の距離も「TP_Control」にユーザデータとして追加しておきます.

「Drop_Move」に変わるということは「Trace」パーティクルを発生させながら落ちていくことになります.もし,「Static」は「Trace」パーティクルを出さないグループに変更するとか,消えるグループに変更するといった他のアクションを取らせることもできますね. 慣れたらもっと凝った動きにも挑戦してみてください.

条件を付けてグループを変える
条件を付けてグループを変える

TPはこのようにグループを作成して条件やアクションに応じてグループを変化させることができるので,標準パーティクルに比べるとできることが多彩です.今ではMoGraphの機能が優れているのでTPを使わなくても同じような事ができますが,TPは拡張性が高いので使いこなせるとより複雑な動きの表現ができるでしょう.また,MoGraphと組み合わせて使うことができる点もメリットです.たとえばTPの動きをマルチインスタンスの配置に使うなどもできます.

5. TPグループをボリューム化

5.1 ボリュームビルダーにTPグループを使う

TPはパーティクルジオメトリと〈PShape〉ノードでオブジェクトをパーティクルとすることができますが,今回はボリュームビルダーを使っています.ボリュームビルダーにTPグループをドラッグ&ドロップして追加していきます.TPグループを使うとボリュームサイズにパーティクルサイズを使うことができるので,パーティクルごとにサイズを変えておけばここに反映されます.パーティクルサイズは〈PMatteerWaves〉や〈PStorm〉の〈大きさ〉が使用されます.もうお分かりでしょうが,「TP_Control」でユーザデータからパーティクルの大きさも調整できるようにしておくとボクセル調整作業が少し楽になります.

TPグループをボリューム化
TPグループをボリューム化

後はボリュームの〈ボクセルサイズ〉やフィルタを使って形状を調整していきます.

※注意
ボクセルサイズを小さくしすぎたり,パーティクルサイズを大きくしすぎる,フィルタのかけすぎは計算負荷が急激に増加するので注意してください.場合によっては応答不能となります.保存を忘れずに.

5.2 アニメーションが重いときは

ボリューム化できたら,アニメーションさせて確認します.このままではとても重たいので,ボリュームメッシュを右クリックして〈Alembicとしてベイク〉を実行します.ベイク出来たらボリュームビルダーとTP用XPressoもオフにすると軽くなるはずです.TP自体のキャッシュがとれないのが欠点ですが,Alembicとして書き出したり,クローナーやマトリクスのモードをオブジェクトにしてTPグループを割り当てた後,MoGraphキャッシュをとってTPキャッシュのように使うことができます.

6. ユーザデータで調整できるようにしたメリット

今回はユーザデータをかなり使用していますが,これのメリットは,例えば他のオブジェクトから水滴を出したい時,「TP_Control」の「Emitter Source」オブジェクトにドラッグ&ドロップすればすべてのパーティクルグループの発生源が同時に変更できる点です.(ダイナミクス衝突タグは忘れないように!)

パーティクル数やサイズの他,必要なパラメータ,たとえば風の強度や摩擦用の調整パラメータなどを追加してカスタマイズしてもよいかと思います.ただ,ユーザデータも増やしすぎると調整が難しくなるので,どのパラメータを調整できるようにしたいか,よく検討して追加していったほうがよいでしょう.

オブジェクトを差し替えて,合わせてパーティクルサイズや発生数を変更もできます.

オブジェクトを差し替えてみる
オブジェクトを差し替えてみる

サンプルファイルをこちらからダウンロードできます.このデータではより扱いやすいように20cmの球体を想定したパーティクルサイズになっています.
サンプルデータのダウンロード

今回のチュートリアルの内容では,残念ながらより複雑な起伏がある形状ではパーティクルのアニメートが難しくなります.例えば障害物にヒットしたら動きが止まるような動きができません.このような動きを作りたい場合は,パーティクルの向きやポリゴンの法線ベクトルを光線の衝突を使って検知して,パーティクルの移動する方向を決めるプログラムを書く必要がでてきます.Cinema 4DではPyhtonノードを使ってこれらのプログラムを記述できますので,一歩踏み込んだ表現をしたいと感じたらぜひ挑戦してみてください.

<ダイナミクス近接>を使わずに,Pythonノードや光線の衝突を使ってベクトルの計算を行うとより複雑なパーティクルの動きを作れます.例えば下りと平坦な移動はするけれど,登りになったら止まるパーティクル.もう少し工夫すれば登りを検知したら向きを変えてなるべく平坦なルートを辿らせるようなこともできます.

色々な条件をつけて動きをコントロールできる
進行方向が高くなったら止める

少し長めのThinkingParticlesのチュートリアルでしたがいかがだったでしょうか.標準パーティクルと比べて自分の動かしたいようにアニメートできるのがThinking Particlesの強みでもあります.また,MoGraphとの組み合わせもできるので,いろいろと試してみてください.