チュートリアル / Bifrost for Maya Rigging Challenge~一歩先のリグ・アニメーションに挑戦~
第3回:コリジョンデフォーマー
- Maya
- アニメ
- キャラクター・リグ
- ゲーム
- コラム
- シミュレーション
- チュートリアル
- 上級者
- 中級者
- 映画・TV
みなさん、こんにちは。
本コラムではMayaのプラグイン"Bifrost"を使って、リグ、アニメーション、物理シミュレーションなどの観点から作成例を紹介していきます。今回は、コリジョンの接触によりメッシュを変形させる、コリジョンデフォーマーを作ってみたいと思います。スキンで変形したメッシュに後付けもできるので必要なときに気軽に使えそうな気がします。
成果物は以下のGIFの通りです。こちらを作っていきます。
【環境】
・Windows 11
・Maya 2025.2
・Bifrost 2.11.0.0
自動ループで簡単デフォーマー
メッシュを変形するには多くの頂点をまとめて制御する必要があります。そして頂点を動かすためには各頂点の位置情報が必要です。まずは基本形としてBifrostで頂点位置を扱うグラフを先に作成しておきます。Object型の入力ポートでMayaからmeshを受け取り、get_point_positionで頂点の位置配列を得ます。位置配列はset_point_positionで元のObjectに戻し、出力ポートへ繋ぎます。このままでは位置に変更を加えていないので全く意味はありませんが、この頂点位置の配列をうまく操作できればメッシュを変形させられることになります。
多数の要素をひとつひとつ取り出していくには自動ループを使います。前回も登場したiterateコンパウンドでも構いませんが、単純なケースでは自動ループが便利です。コンパウンドの入力ポートを配列ではなく単一の値で作成しておき、そこに配列を接続すると、自動的に配列の要素を取り出して繰り返してくれるという便利な機能です。
これで、全頂点をY軸方向に10移動するだけのデフォーマーが完成しました。ここから色々と変更を加えていきます!
余談ですが、自動ループではなくiterateを使うべき状況をいくつか挙げておきます。
・配列の一部だけループしたい場合(10要素のうち前半5要素だけ、など)
・ループ内部でインデックスが必要な場合(前後の要素を参照する、など)
・反復の過程で何らかの値の累積を行いたい場合(=Stateポートを使いたい場合)
接触した頂点を押し込む
それでは早速コリジョンによる頂点位置の補正を作成していきます。まずは単純な例として、水平な平面(ワールドXZ平面)をコライダーとする位置補正コンパウンドを作成します。処理内容は非常に単純で、入力位置のY座標が平面の高さhを下回っていたら、位置のYをhで上書きするだけです。
Bifrostグラフは次のようになります。自動ループを使用していきますので単一のfloat3を入出力とするコンパウンドで作成しておきます。
グラフへ入力している変形前のメッシュ(pTorus1)を移動してみると、変形後のメッシュ(bifrostGraphShape)が下記GIFのように表示されるかと思います。
接触箇所周辺を膨らませる
このままでは、ただ内側に押し込まれただけで体積が減っている見た目になってしまっています。次に、先ほど押し込んだ箇所の周辺を逆に外側へ押し出し、(厳密ではありませんが)体積が保たれているような表現を追加したいと思います。“接触してはいないが接触している箇所に近い頂点”を得たいので、ここではシンプルに“コリジョン面からの距離が一定距離内の頂点”を取得することとします。
先ほどのコンパウンドを拡張して、以下のように変更します。追加された出力ポートは、コリジョンまでの距離(distance)と接触したのかどうかを表すbool値(is_collide)の2点です。
これらの情報を使って次に法線方向に押し出すコンパウンドを作成します。以下は、enableがtrueかつコリジョンまでの距離が指定値以内の場合に限り、法線方向に位置を移動するコンパウンドです。こちらも各入出力ポートは単一の値にしておきます。距離に応じて膨らみ具合が変化するよう、押し出し幅の設定箇所にevaluate_fcurveを加えてあります。
さて、困ったことになりました。接触中はそこそこ良い見た目になりましたが、全く接触していない時にも膨らみが生じてしまいました。「接触していなくて、コリジョン面から指定距離内の頂点」という条件で判定したので当然といえば当然です。
ではこれを修正していきます。具体的には、直前の押し込みを行うフェーズで“押し込まれた距離の総和”を取得しておきます。頂点が少しも押し込まれていなければゼロ、押し込まれた頂点数および深さが深ければ深いほど、大きな値が得られます。先ほどのコンパウンドをさらに拡張して、以下のように変更します。
得られた「押し込まれた距離」の配列はコンパウンドの外側で合算し、後の押し出しフェーズでのウェイト値として使用します。ウェイト値に利用する際、メッシュの分割密度にウェイトが左右されにくくなるよう頂点数で割っておきます。
これで基本形は完成になります!
その他のコリジョン形状
前項では単なる水平平面をコリジョンとしていましたが、いくつか別の形状にも対応してみたいと思います。
まずは、球です。頂点の位置をp、球コリジョンの中心をc、コリジョンの半径をr、とするとp,c間の距離がrよりも小さかったら、点pは球の内部に侵入しています。このとき、cからcpベクトル方向へrだけ離れた位置にpを移動します。
衝突判定の条件式が下図①、位置補正の式が②です。
これをBifrostグラフで組むと次のようになります。先ほど作った水平平面コリジョンと同じ構成で作っておきたいので、頂点位置に加え球コリジョンの情報(位置と半径)を追加入力し、出力は同じにしてあります。
次は、移動回転ができる無限平面です。頂点の位置をp、平面上の点をc、平面の法線をncとして、pから平面までの最短距離hを出します。この距離がマイナス値になっていたら、点pは平面に埋まっています。このとき、pからnc方向に埋まった距離分だけpを移動します。
これをBifrostグラフで組むと次のようになります。基本構成はこれまでのパターンと同じです。
最後はメッシュです。頂点位置pにおいて、コリジョンメッシュ上の最近傍点をq、qの法線nqとします。qpベクトルとnqの内積が負の値になっていたら点pはコリジョン内部に侵入しています。このとき、qの位置にpを移動します。
Bifrostグラフは次のようになります。こちらもコリジョン情報(メッシュ情報)以外の構成はこれまでのパターンと同じにしておきたいのですが、最近傍点を取得するノード「get_closest_point」関連が配列で構成されていますので、今回に限り位置の入出力ポートを配列にしてしまいます。
ここまでに作成した主要なコンパウンドはサンプルデータ「compounds.ma」にもまとめて入れておきます!
ところで、bifrostグラフへ入力する変形用のメッシュやコリジョン用のメッシュはいずれもその他のデフォーマーと併用することができます。以下は『skinClusterで変形したメッシュをさらに球コリジョンで変形させているもの』と『skinClusterで変形したメッシュをコリジョンにして別のメッシュを変形しているもの』です。
これで冒頭にお見せした成果物は全て完成です!
今回は少し趣向を変えて、自動ループによる頂点制御を行いました。即席のデフォーマーをその場で作れるようになると、リグに限らずアニメーション工程でのちょっとした味付けに使えるかもしれません。頂点数が多すぎるとパフォーマンスが落ちやすくなるため使い所はよく考える必要がありますが、自由度が増していることは間違いないかと思います。
ちなみに、ここからさらに頂点に対して簡易的な遅延処理を追加すると、押し込まれた後で緩やかに戻る(または戻らずその場にとどまる)などの表現が可能になります。これについては後々の回で取り上げるかもしれません!
また次回をお楽しみに!
今回のまとめ
・大量のデータをまとめて処理するなら自動ループが便利!
・プリミティブなら軽い計算だけでコリジョンが可能
・任意のメッシュの場合は最近傍点を得ることでコリジョンが可能