チュートリアル / Bifrost for Maya Rigging Challenge~一歩先のリグ・アニメーションに挑戦~
第5回:ルービックキューブのリグ

  • Maya
  • アニメ
  • キャラクター・リグ
  • ゲーム
  • コラム
  • シミュレーション
  • チュートリアル
  • 上級者
  • 中級者
  • 映画・TV

みなさん、こんにちは。

本コラムではMayaのプラグイン"Bifrost"を使って、リグ、アニメーション、物理シミュレーションなどの観点から作成例を紹介していきます。今回は複雑かつ頻繁な親子関係の切り替えを「動的にペアレントコンストレイントを適用する」ことで実現したいと思います。単純なコンストレイントのターゲット切り替えであればMaya標準のParentConstraintでウェイトにキーを打つことでも可能ですが、今回は "動的に” というところがポイントです。

成果物は以下のGIFの通りです。こちらを作っていきます。

今回の成果物
今回の成果物

【環境】
・Windows 11
・Maya 2025.3
・Bifrost 2.11.0.0

状況の確認

まずはこちらをご確認ください。ご存知の通りルービックキューブです。中心を除き計26個のピースで構成されており、回転させる場所によってその都度ピースのグルーピングが変わります。今のところジョイントやスキンは何も入っていません。

コンストレイントの仕組み

今回の課題の難易度をお伝えするために少しだけ前置きを長めに取らせていただきます。『はよ本題に!』という方は次項「コンストレイントの仕組み」まで飛ばしていただければと思います。

案①

ビューポート操作だけが目的であれば、対象のピースをその都度回転操作用のノードに出し入れすればコントロールが可能です。操作用ノードの選択に合わせてコールバック関数などを仕込んでおけばスピーディに切り替えることも出来るかもしれません。ただし、この方法ではキーフレームアニメーションは作れません。

locator1が操作用ノード
locator1が操作用ノード

案②

全ピースのピボットを中心に配置し個別に回転させる方法も考えられますが、補間が問題になります。オイラー補間のままでは複数回操作を加えた時にジンバルロックが発生し、うまく補間できなくなります。そのためクォータニオン補間に変更する必要がありますが、細かなコントロールは出来なくなります。

すべてのピースのピボットを中心に配置
すべてのピースのピボットを中心に配置
オイラー補間ではRotateYが±90になったピースがジンバルロックを引き起こしている
オイラー補間ではRotateYが±90になったピースがジンバルロックを引き起こしている
Quaternion Slerpなら補間はうまくいくが動きがリニアになってしまい制御が効かない
Quaternion Slerpなら補間はうまくいくが動きがリニアになってしまい制御が効かない

案③

標準機能のParent Constraintを複数設定していくことである程度融通が効くようにもなりますが、かなり手間がかかります。“各ピースがコントローラから相対的にどの位置にいるか”が都度変化するので、1度設定したコンストレイントは別のピース配置では使えなくなります。仮に6回操作した場合、6つのコントローラと、ピース数ぶんのparentConstraintノード、それらに対応する大量のウェイト値にキーを打っていくことになります。後から過去フレームのアニメーションを変えるとその後のアニメーションがすべて壊れますし、なかなか管理が大変そうです。

6回の操作ですでにコントローラが6個、parentConstraintが26個、ウェイト操作のanimCurveが45個
6回の操作ですでにコントローラが6個、parentConstraintが26個、ウェイト操作のanimCurveが45個

このいかにも厄介そうなモデルを出来るだけスマートにアニメートするため、切り替えのタイミングで都度コンストレイントの計算を行い、動的に親子関係を切り替えてみよう!というのが今回の試みになります。

コンストレイントの仕組み

まずはじめにペアレントコンストレイントの仕組みを確認しておきます。ドライバーによって拘束されたドリブンの各行列は次のような手順で求められます。

各記号の定義:
・ドライバーのワールド行列:D
・ドリブンの親のワールド行列:P
・ドリブンのワールド行列:W
・ドリブンのローカル行列:L
・オフセット行列:O

まず、コンストレイント適用時点でのDとWを使って、オフセット行列Oを計算します。これはコンストレイント当初のドライバーとドリブンの相対差分になります。

oの計算

Oは初回のみ計算し、その後は固定の値として使用します。このOを使用して、次のように拘束後のWとLが得られます。

BifrostグラフでLを計算

試しに、標準機能のペアレントコンストレイントと、BifrostグラフでLを計算したものを比較してみます。

左:標準のペアレントコンストレイント/右:行列演算のコンストレイント
左:標準のペアレントコンストレイント/右:行列演算のコンストレイント
サンプルデータ:constraint_basic.ma
サンプルデータ:constraint_basic.ma
サンプルデータ:constraint_basic.ma
サンプルデータ:constraint_basic.ma

同じ結果になっていそうですね!

複数のドライバーを切り替えたい場合はD1,D2,D3…と、複数のDとそれに対応する複数のOを置き換えれば良いので、さほど難しい問題ではありません。重要なのは“オフセット行列Oが初期に固定されている”という点です。仮にドライバーからの拘束と解放を頻繁に繰り返し、且つ拘束するたびに望ましい相対位置が変わる場合、このオフセット行列Oを適切なタイミングで更新・保存する必要があります。

動的切り替えの仕組みを考える

では、前項のペアレントコンストレイントの仕組みを動的に行うための仕組みを考えます。次のような状況に応じた処理を行えば実現できそうです。

初回&リセット時 初期状態での全てのドライバー(D1, D2…Dn)とドリブン(W)から
オフセット行列(O1, O2…On)を計算して保存
コンストレイント適用中 いずれかの拘束用ドライバー(Dn)と保存されたオフセット行列(On)から
コンストレイント後の行列(W, L)を計算し出力
切り替え時 切り替え時点での全てのドライバーとドリブンから、
オフセット行列を再計算して更新

では、シンプルな例で上記をテストしてみます。以下のグラフは2つのドライバー(赤と黄色のロケーター)で1つのドリブン(トーラス)をコンストレイントしているグラフです。

0フレームでリセットを、10フレームでドライバーの切り替えを行っています。切り替えの瞬間だけオフセット行列の再計算フラグをオンにしています。計算したオフセット行列の保存には第4回でも登場したフィードバックポートを使います。

bifrostGraphShape
bifrostGraphShape
bifrostGraphShape
bifrostGraphShape > parent_constraint
bifrostGraphShape > parent_constraint
bifrostGraphShape > parent_constraint > calc_offset_matrix
bifrostGraphShape > parent_constraint > calc_offset_matrix
bifrostGraphShape > parent_constraint > calc_offset_matrix

10フレームで再計算スイッチをオンにしたときに、オフセット行列を可視化した点がパカっと移動します。この瞬間にオフセット行列の再計算と保存が行われているということです。うまくいっているようですね!

サンプルデータ:dynamic_constraint.ma
サンプルデータ:dynamic_constraint.ma

後から切り替え時のポーズを変更しても、標準のペアレントコンストレイントのようにオブジェクトがジャンプすることがありません。オフセット行列を動的に更新している効果です。

左:標準のペアレントコンストレイント/右:オフセット行列を再計算しているコンストレイント
左:標準のペアレントコンストレイント/右:オフセット行列を再計算しているコンストレイント

ここまでの結果を元にリグを完成させる

いよいよルービックキューブでリグを組んでいきます。前項で最も重要なロジックは出来ていますので、あとは拡張していくだけです。まずはじめにルービックキューブの要素を洗い出しておきます。

・ピースは3×3×3の配置、中央を除いて全26個
・回転軸はXYZの3軸
・F、B、R、L、U、D、S、M、E(*1)の全9種の回転操作がある
・9個(真ん中の列では8個)のピースをまとめて回転させる
・回転操作は原則90°刻み

アルトタグ

このことから、必要なコントロールは

・ドライバーを各軸用に3つ、ルート用を1つで、計4つ
・どの回転操作をアクティブにするかを示すパラメータ
・オフセット行列の再計算を有効にするスイッチ

更新・保存する値は

・各ピースのドライバ数ぶんのオフセット行列
・各ピースがアクティブなコントローラに属しているかを示すフラグ(*2)

となります。

*1) FやBなどの回転記号について詳細は「回転記号 | ルービックキューブ - Wikipedia」をご確認ください。
*2) 前項の単純な例と違って、回転操作の種類とピース配置によってコンストレイントの有無が決まるため、必要になります。

Bifrostグラフは以下のようになりました!

bifrostGraphShape
bifrostGraphShape
bifrostGraphShape > init_arrays
bifrostGraphShape > init_arrays
bifrostGraphShape > rubiks_cube_constraint
bifrostGraphShape > rubiks_cube_constraint
bifrostGraphShape > rubiks_cube_constraint > select_axis
bifrostGraphShape > rubiks_cube_constraint > select_axis
bifrostGraphShape > rubiks_cube_constraint > select_matrix
bifrostGraphShape > rubiks_cube_constraint > select_matrix
bifrostGraphShape > rubiks_cube_constraint > calc_offset_matrix
bifrostGraphShape > rubiks_cube_constraint > calc_offset_matrix
bifrostGraphShape > rubiks_cube_constraint > const_flag
bifrostGraphShape > rubiks_cube_constraint > const_flag
bifrostGraphShape > rubiks_cube_constraint > const_flag > is_F
bifrostGraphShape > rubiks_cube_constraint > const_flag > is_F
bifrostGraphShape > rubiks_cube_constraint > const_flag > is_E
bifrostGraphShape > rubiks_cube_constraint > const_flag > is_E
bifrostGraphShape > rubiks_cube_constraint > const_flag > is_E

以下の図のように切り替え操作に2フレームほど使い、オフセット行列とコンストレイントフラグの再計算を実施させます。

操作上の注意として
・切り替えタイミングを跨いでフレームをジャンプさせないこと
・オフセット行列の再計算スイッチがオンの状態で回転箇所を変更すること
・切り替え操作および再計算を中途半端な姿勢の時に行わないこと
などがあります。

サンプルデータ:rubiks_cube_rig.ma
サンプルデータ:rubiks_cube_rig.ma
サンプルデータ:rubiks_cube_rig.ma

最後に、ランダムに操作を加えた状態を初期姿勢として、それを解くアニメーションをつけてみます。次の例は「L D' F' M' U' L B' R S E 」という操作でピースを混ぜた状態でスクリプト①を実行し初期位置を記録、その後逆の操作「E' S' R' B L' U M F D L'」で解くアニメーションをつけなおしています。

スクリプト①:現在のピース配置を初期状態にする

# 1. 現在のピース配置を初期状態にする
from maya import cmds
for i in range(26):
    idx = str(i+1).zfill(2)
    cmds.matchTransform(f'p_{idx}_init', f'p_{idx}')

スクリプト②:初期姿勢を解けた状態に戻したいとき用

# 2. 解けた状態を初期状態に戻す
from maya import cmds
for i in range(26):
    idx = str(i+1).zfill(2)
    cmds.matchTransform(f'p_{idx}_init', f'p_{idx}_solved')
サンプルデータ:rubiks_cube_rig_solve_animation.ma
サンプルデータ:rubiks_cube_rig_solve_animation.ma

こちらで完成です!

公式サンプル「parent_matrix」

余談ですが、今回取り上げた『オフセット行列の動的更新』は実は公式サンプルにもあります。Bifrost BrowserのRiggingカテゴリにある「parent_matrix」です。

オフセット行列の動的更新
ペアレントを実施

切り替えのタイミングでオフセット行列を計算しキャッシュ、その後、2.11で追加された新しいコンパウンド「parent_matrix」と新しいデータ型「Rigging::Solver::ParentMatrixTarget」を使用してペアレントを実施しています。

parent_matrix

ルービックキューブでは解説の都合上4x4行列の乗算を直接扱いましたが、状況に応じてこちらの「parent_matrix」も選択肢に入ってくるかと思います。

今回は行列演算を中心に動的コンストレイントにチャレンジしてみました。値を保存する用途でのフィードバックポート使用例も紹介できて良かったです。例によって「フィードバックポート使用時はベイク処理が必要になる」という注意事項がありますので扱いやすさの点では一考の余地がありますが、Bifrostならではの面白い組み方ができたのではないかと思っております!

また次回をお楽しみに!

今回のまとめ

・フィードバックポートで、通常は事前計算が必要な「コンストレイントのオフセット要素」を動的に更新できる
・ルービックキューブはピース配置によって拘束対象が変化するため、これについても動的に計算する


製品購入に関するお問い合わせ
オートデスク メディア&エンターテインメント 製品のご購入に関してご連絡を希望される場合は、こちらからお問い合わせください。