チュートリアル / Bifrost for Maya Rigging Challenge~一歩先のリグ・アニメーションに挑戦~
第1回:両端を固定し長さが保てるベルトのリグ
- Maya
- アニメ
- キャラクター・リグ
- ゲーム
- コラム
- シミュレーション
- チュートリアル
- 上級者
- 中級者
- 映画・TV
ごあいさつ
みなさんこんにちは。
今回から数回に渡って、Mayaのプラグイン"Bifrost"を使って、リグ、アニメーション、物理シミュレーションなどの観点から作成例を紹介していきます。「どんなことができるのか?」「どのようにノード組めばいいのか?」の参考にしていただけますと幸いです。
一部ではBifrost最新版の機能を取り入れていく予定ですので、実験レベルのものも含む可能性があることをご理解いただければと思います。また、前提としてMayaやBifrostの基本操作と、ある程度のリグや数学(ベクトルや 行列)の理解があるものとして進めていきますので、中〜上級レベルになってくるかと思います。
それでは始めます!
【環境】
・Windows 11
・Maya 2025.1
・Bifrost 2.10.0.0
Bifrostを使う理由
まずはモチベーションを高めるためにも、「なぜMaya標準ノードではなくBifrostを使うのか?」という点について2点ほど確認しておきます。
1つ目は、Maya標準ノードでは出来ないことが出来るようになる点です。具体的な例では、反復計算が必要なもの、多数の頂点等をまとめて扱うもの、前の計算結果を再利用するものなどが挙げられます。これらはいずれもC++/Python APIを使えばできますが、プラグインノードを作成する必要が出てくるかと思います。
2つ目は、パフォーマンスの点です。リグやアニメーションに使うのであれば軽いに越したことはありません。Bifrostで組む場合、そこそこの複雑な処理もC++には届かないにしても比較的軽く出来る可能性があります。
今回は手始めに、反復計算が必要なものとして「両端を固定し、かつ長さが保てるベルトのリグ」を作ってみようと思います。Bifrostの機能的にはiterateコンパウンドのStateポートを活かしていくことになります。成果物を先にお見せすると下のGIFのようなものです。こちらを組んでいきます。
状況の確認
まずはこちらをご確認ください。ベルトのモデルにジョイントが7つ配置してあり、スキニングまで終えている状態になります。このベルトを”長さを保ったまま”動かしたいとします。
まず、思いつくのはSplineIKですが、SplineIKは長さを保てても末端を固定することはできません。motionPathノードを使うという手もありますが、逆に両端を固定できても長さを保つのが困難になります。最終的に行き着くのはcurveシミュレーションですが、これだけのためにわざわざシミュレーション工程が必要になるのもなんだか違います。
それなら、物理シミュレーションでも使われている伸縮性の制御ロジックだけリグに応用してしまおう!というのが今回の試みになります。
長さを一定に保つ
7つあるジョイントと同じ位置にコントローラを置いて動かせるようにするとして、仮にjoint3だけを大きく移動した場合、2-3間と3-4間の長さだけが大幅に伸びてしまうことになります。長さを保つためには、joint3につられてjoint2、4、5、6も少しずつ移動して欲しいですし、joint3自身の移動量も限界付近で止まってほしいです。
ここでまず、ある2点間の距離を一定に保つ"距離制約"を考えます。図で示すと下記のようになります。お互いの間の距離を元の長さ「d」に保つよう、2点のそれぞれが近づく(または離れる)形になります。
計算式(*1)は以下の通りです。元の長さと2点間の距離の差を半分ずつ分け合い、それぞれの位置に足します。
*1)Position Based Dynamics:こちらに距離制約の数式が載っています。質量mやウェイトwを省いて簡略化していますが、基本は同じです。
それではこの数式をBifrostグラフで再現してみます。
p1とp2のコントローラの位置をBifrostグラフに入力し、Bifrostグラフで更新された位置をMayaに返しています。元の長さ「d」はいったん直打ちで20に設定しています。
2点間の距離が一定に保たれるようになりました。ではこれを7つあるジョイントの1-2、2-3、3-4、4-5、5-6、6-7のすべてで実施してみたいと思います。が、困ったことがあります。joint1とjoint7以外の中間の点は影響を受ける制約が2つあります。例えばjoint2の場合はjoint1からもjoint3からもその間の距離を保つよう影響を受けます。では2つの結果の間を取ればいいかというと、それだけでは元の長さが保たれなくなってしまいます。
以下は自由に動ける点p1、p2、p3に対し2つの距離拘束(1-2間、2-3間)を2つ設定しているところです。p2の位置に注目してください。2つの距離拘束を計算した後で平均をとってp2の位置としていますが、平均時に位置が更新されるため長さが変わってしまっています。
反復処理で結果を収束させる
そこで、iterateコンパウンドの”Stateポート”を使って反復計算を行います(*2)。図で示すと以下のようになります。「2つの距離拘束(p1-p2間、p2-p3間)を計算した後で平均をとってp2の位置を定める」の部分を複数回繰り返すと、徐々に結果が収束しすべての条件を可能な限り満たした状態に落ち着いていきます。
*2)CEDEC 2010 - Windows版「ロストプラネット2」にみるDirectX 11フィーチャー(後編) | マイナビニュース:複数の制約を並列処理して平均を取る方法はこちらの「ヤコビソルバ」と同じです
これをBifrostグラフで再現するには、反復したい計算箇所をiterateコンパウンドでまとめ、計算対象をStateポートに設定します。Stateポートに設定した値はiterateで繰り返し計算を行う際に、1周目の結果が2周目の入力へ、2周目の結果が3周目の入力へ、、、と再利用されていきます。
反復計算により結果が収束し、1-2間、2-3間の長さが元の長さのまま保たれるようになりました。
では計7点で、両端が固定されているパターンに戻ります。2点のうち片方を固定点としたい場合は、前述の数式の「1/2」の部分を0と1に変えてしまえば、式①が得られます。ただし、この場合はあえて現在の長さと元の長さの差分を求める必要はないため、式②でも十分です。p1-p2間とp6-p7間の距離拘束はこちらのパターンを使います。
Bifrostグラフは以下のようになります。p1とp7はコントローラの位置そのままですので、p2、3、4、5、6の5点の位置を計算するグラフになります。
これで今回のタイトルでもある「両端を固定+長さを保つ」の部分が完成しました!
ここまでの結果を元にリグを完成させる
では最後に、Bifrostグラフで計算された位置情報を使ってバインドjointを拘束していきますが、その前に、回転値もBifrostグラフ内で計算してしまいます。末端の点(p1、p7)は隣の点(p2、p6)へ伸びるベクトルをエイムベクトルとし、中間の点は前後の点からエイムベクトルを定義します。アップベクトルはいずれもコントローラの回転から得ることにして、エイムコンストレイントのようなものをBifrostグラフ内で組んでしまいます。
これで完成です!
こちらのテストモデルはジョイントが計7つと少なめでしたので全て直接制御しましたが、ジョイント数が多い場合はリボンリグのベース部分に組み込んでみても面白いかもしれません。
ちなみに、反復回数を下げていくと距離の制約が弱まり、0で無効になります。
今回は手始めにベクトル演算ノードを中心に使って点と点の間の長さをコントロールしてみました。実際にやってみたことをおさらいすると、今回は特にですが「Bifrostにリグ用の特別な機能がある」というよりは単に「Bifrostにはベクトルや行列の演算ノード、iterateなどのロジックを組む機能が充実している」ということがわかるかと思います。プログラミングと同じ感覚でアルゴリズムから組むことが出来るので、初見は何が出来るのか分かりにくいですが拡張性はかなり高いと思います。また、scope系のノードは即座にビューポートに情報を描画できるのでトライ&エラーの効率を高めるためにも非常に便利です。最後におまけとしてscope系のグラフをいくつか紹介して終わりにしようと思います。
また次回をお楽しみに!
ポイント・ベクトルの可視化3選
point_scope
位置(Vector3)配列を点として描画するならまずはこちらです。
set_geo_propertyでベクトル情報を追加してしまえば、点に加えてそこから伸びる矢印をまとめて描画することもできます。追加時のプロパティ名と同じ名前をArrow Propertyに記入すればOKです。
transform_scope
位置、回転、スケール、シアーを含めたtransform情報を描画するならこちら。2.10で追加された新ノードになります。リグではかなり重宝します。
create_arrow_strands
始点と終点を入力して矢印を描画できます。まさにベクトル向けです。
今回のまとめ
・ベクトルやクォータニオンなどの演算ノード、iterateなどの制御構造が充実している。
・iterateコンパウンドの”Stateポート”はMaya標準には出来ない反復計算を可能にする。
・細かい機能レベルからその場で作れる。トライ&エラーが高速。