第3回:【Matrixリギング実践編】さよなら階層構造!フラット構造とゼロアウトの掟|ド文系3DアニメーターのMaya行列入門

当ページのリンクには広告が含まれています。
第3回Matrix入門アイキャッチ

MayaのMatrixリギング連載、第3回からはついに「実践編」が始まります。

前回の第2回「さよならConstraint?1万個のオブジェクトでMatrixの爆速っぷりを徹底検証」|ド文系3DアニメーターのMaya行列入門では、Matrixリグがいかに「爆速」で動くかを数字で証明しました。

そこでは、「DG(Dependency Graph)」「Serial」「Parallel」といったMayaの3つの計算モードの違いについても解説しました。

理屈はわかった!でも、実際どうやってノードを繋ぐの?

コンストレイントを使わないで、どうやって親子の追従を作るの?

今回は、そんな疑問にお答えします。

オフセット用のNullグループを作って、フリーズして、コンストレイントをかけて…という重くてバグりやすい旧時代のワークフローは今日で終わりにしましょう。

アウトライナの階層構造から解放され、ノードの繋がりだけで完結するフラット構造

そして、Matrixリギングにおける絶対ルールゼロアウトの掟

初心者の方には少し難しい話になるかもしれませんが、二度とオイラー角のフリップ(反転)に怯えることのない、安定したリギングの第一歩を踏み出すことが出来ます。

今回はいよいよ、実際にノードを繋いでいくMatrixリギングの「実践基礎編」です。

第1回と第2回をまだご覧になっていない方は、まずは以下の2つの記事から読んでください。

目次

【基本編】コンストレイントのかわりにMarixを使ったリギング

コンストレイントを使わずに、コントローラーでジョイントを動かしてみましょう。

コントローラーの動きにジョイントを完全追従させる(旧Parent Constraint + Scale Constraint)方法です。

簡単なシーンなので、ぜひ、手を動かして確認してみてください

jointが3つあって、それぞれにコントローラーがついています。

オフセットつきのnullグループを使ってそれぞれのコントローラーのチャンネルボックスの中は、TranslateとRotateは「0」、Scaleは「1」の状態です。

そして、joint Orientの値は、jonint_Aは「Y = 33.690」、joint_Bは「Y = -67.380」、joint_Cはすべて「0」です。

joint_Aは、Ctrl_AにParentConstraintとScaleConstraintされています。

joint_Bは、Ctrl_BにParentConstraintとScaleConstraintされています。

joint_Cは、Ctrl_CにParentConstraintとScaleConstraintされています。

これと同じ状態をoffsetParentMatrixを使って行います。

まずは、コンストレイント指定ない状態から始めます。

やり方は驚くほどシンプル。

ノードエディタで、コントローラー(Ctrl_A)の worldMatrix[0] を、ジョイント(joint_A)の offsetParentMatrix にピッ!と1本繋ぐだけです。

これだけで、コンストレイントノードを一切作らずに、完璧な追従が実現します。

ズレてしまいました!

と言いたいところですが、そんなに簡単ではありません。

Matrix時代の「ゼロアウトの掟」:ジョイントは「全部ゼロ(スケールは1)」が絶対条件!

実験では、なぜうまくいかなかったのか?

jointの「translate」「Joint Orient」に数値が入っていたからです。

Mayaは最終的な姿勢を【 ①OPM(offsetParentMatrix) 】×【 ②チャンネルボックス(TRS) 】×【 ③Joint Orient 】という行列の掛け算(合成)で決定しています。

OPMに親の座標が流し込まれた上に、②や③に残っていた数値がさらに掛け合わされて(二重に合成されて)しまったため、ズレてしまったのです(ダブルトランスフォーム)。

もしかしたら、私の過去の記事で、「joint」に対する決まりごとについて読んでくださった方がいるかもしれません。

しかし、Matrixリギングにおいて過去のルールは通用しません。

新しいルールに則ってジョイントを配置する必要があります。

それが、ジョイントの新しい掟です。

項目従来型(コンストレイント)Matrix型(OPM直結)
Translate基本はXのみに数値(骨の長さ)すべて「0」
Rotateすべて「0」すべて「0」
Joint Orientローカル軸を合わせる数値を入れるすべて「0」(最大のポイント!)
Scaleすべて「1」すべて「1」
Matrixリギングにおけるjointの新しい掟

「Joint Orientもゼロにしていいの!?」と驚くかもしれません。

従来のリギングでは、Joint Orientの数値合わせが重要でした(Joint Orientを揃えるためのツールまで紹介しました)。

Matrixリギングでは、コントローラーの持つ「ワールド空間での正解の場所」を直接ジョイントに流し込みます。

ジョイント側に数値が残っていると、かえってズレる原因になるので、「ジョイントの数値は空っぽ(ゼロ)にしておく」のが絶対の掟です。

Freezeはもう不要!

チャンネルを「0」にするために Freeze Transformations を使っていませんか?

offsetParentMatrixComposition タブに初期位置を入力すれば、Freezeを使わずに綺麗なゼロアウトが可能です。

「原点情報を失わない」必須のテクニックです。

ゼロアウトするためのPythonスクリプト

以下のスクリプトを使って、「joint」と「コントローラー」を「ゼロアウト」します。

import maya.cmds as cmds
# 選択したオブジェクトを取得
sel = cmds.ls(selection=True)
for obj in sel:
    # 現在のローカルマトリックスを取得
    local_mat = cmds.xform(obj, query=True, matrix=True, objectSpace=True)
    # その数値をOffset Parent Matrixに入れる
    cmds.setAttr(obj + '.offsetParentMatrix', *local_mat, type='matrix')
    # チャンネルボックスの数値をすべて「0(スケールは1)」にする
    cmds.xform(obj, translation=(0,0,0), rotation=(0,0,0), scale=(1,1,1), shear=(0,0,0))
    # もしジョイントなら、Joint Orientも「0」にする
    if cmds.nodeType(obj) == 'joint':
        cmds.setAttr(obj + '.jointOrient', 0, 0, 0)
print("✨ Zero-out complete!")

コントローラーもゼロアウトする場合、「オフセット用のnullグループは存在する意味はない」ので消してしまいましょう。

ゼロアウトしたいオブジェクトをすべて選択して、以下のPythonスクリプトを実行すれば「OK」です。

そうすればOutlinerもよりシンプルになります。

ここで、multMatrixというノードを使う必要があります。

結果だけ示すと、以下のようにmultMatrixでノードをつなぎます。

※ここからは、ジョイントを動かすだけでなく、コントローラー同士のオフセット(階層関係)を数式で作る方法にステップアップします!

multMatrixノードの[0]に、コントローラーworldMatrixを繋ぎます。

multMatrixノードの[1]に、[0]に繋いだコントローラーの親のコントローラーを繋ぎます。

最後に、mutrix Sumを、制御したいjointのoffsetParentMatrixに繋ぎます。

ちなみに、一番上のコントローラー(Ctrl_A)は、親がないので、制御したいjointのoffsetParentMatrixに繋ぐだけでOKです。

【重要】multMatrixを使った「オフセット自動計算」の方法

Matrixでコントローラー同士やジョイントを繋ぐ際、必ず登場するのが multMatrix(掛け算ノード) です。

ここで、Matrixリギングにおける「掛け算の順番」「Inverse(逆行列)の意味」について解説します。

掟:Matrixの掛け算は「順番」がすべて!

「2×3」 も「 3×2」 も同じ結果になる(=交換法則が成り立つ)ような普通の数字(スカラー値)の掛け算とは違い、これには「掛ける順番によって結果が全く変わる(非可換である)」という厳格なルールがあります。

Mayaの multMatrix ノードでは、上から順番に [0] × [1] × [2]… と掛け算されていきます。

親子の関係を作りたい場合の順番は、「子 × 親」です。

  • matrixIn[0] 動かしたいターゲット(子)の worldMatrix
  • matrixIn[1] 基準となる親の worldInverseMatrix(逆行列)

なぜ「Inverse(逆行列)」を掛けるのか?

「親の動きについていきたいんだから、普通に親の worldMatrix を掛ければいいんじゃないの?」

もし、[1]に親の worldMatrix をそのまま掛けてしまうと、子は「親と全く同じ位置(原点)」に強制的にスナップしてしまいます。

ここで欲しいのは、親の位置そのものではなく、「親の位置から見て、どれくらい離れた場所(オフセットされた位置)にあるか」です。

そこで登場するのが worldInverseMatrix(逆行列)です!

Matrixの世界において、Inverse(逆行列)とは、「そのオブジェクトが動いた分をキャンセルし、階層をひとつ上へ『遡る(巻き戻す)』力」を持っています。

子のワールド座標 ×(掛ける) 親のInverse(階層をひとつ上に遡る力)

親のいる場所へ「遡る」強烈な力を子に掛けることで、子も一緒にその分だけズルズルと引っ張られます。

その結果、「もし親が世界の中心(原点)だったとしたら、自分はどこにいるか?」という、親から見た純粋な「距離と角度の差分」だけが算出されるのです!

親の持つ座標エネルギーを Inverse で「無効化(キャンセル)」し、自分自身の純粋なローカルオフセットだけを抽出する。

この計算結果を offsetParentMatrix に流し込むことで、間に「Offsetグループ」を挟まなくても、線1本で完璧な親子の追従を再現できるようになります。

最後は、忘れずにコントローラーのチャンネルボックスの「translate」「scale」の項目をLock and Hide Selectedしておきましょう。

jointは、コントローラーに「Parent Constraint + Scale Constraint)」されているような状態です。

コントローラーの動きに完全に追従してしまうため、「Orient Constraint」だけの場合以上に注意が必要です。

オフセット用のnullグループがなくなったことで階層構造がスッキリし、ConstraintからMatrixに変わったことで、リグが軽くなったはずです。

コンストレイントとMatrixのパフォーマンスの違いについては、Matrix入門の第2回の記事をご覧ください。

【コラム】最新Mayaの「おせっかい」?末端ジョイントの仕様変更

実は、Maya 2024.2〜2025にかけて、ジョイントツールに長年のユーザーの悲願だった「ある修正」がこっそり入りました。

以前のMayaでは、最後に引いた「末端のジョイント」は指し示す先がないため、Joint Orientが強制的にワールド軸 (0, 0, 0) になってしまい、わざわざツールを使用するなどして軸を合わせる必要がありました(私のjoint orientの記事はその頃にかかれてものです)。

現在の最新Mayaでは、これが改善され「末端のジョイントは、自動的に親のJoint Orientを引き継ぐ」という賢い挙動になっています!

ところが、Matrixリギングにおいてはどうでしょうか?

先ほど解説した「ゼロアウトの掟」の通り、親だろうが末端だろうが「Joint Orientはすべてゼロ」にしなければなりません。

つまり、Autodeskがせっかく実装してくれたこの親切な新機能も、Matrixリギングでは「どっちみち全部ゼロにするから関係ない機能」になってしまうのです(笑)。

【応用編】階層構造に依存しないフラットな親子関係のリギング

上のように、コントローラーが「Ctrl_A > Ctrl_B > Ctrl_C」といった階層になっていた場合、親のコントローラーの位置を変更すると子のコントローラーの位置にも影響が及びます。

そして、「Ctrl_A」を選択した場合、子である「Ctrl_B」と孫である「Ctrl_C」もハイライト(緑色)されてしまいます

しかし、親子関係ではない、それぞれが独立した(フラットな)状態ならば、お互いに影響をあたえることなく、自由に移動させることが出来ます(選択されたものだけがハイライトされます)。

Outliner上では、親子関係がないにも関わらず、親子関係のような挙動をするフラットな親子関係multMatrixノードを使用して構築します。

こちらでも、簡単なシーンを作ったので、実際に手を動かして試してみてください。

【実践】修正に強い!ガイドを使った「オールフラット構造」

まずは、ガイド(目印)となるロケーターを用意します。

「Guide_Parent」と「Guide_Child」という2つのロケーターを作りました。

ガイドの位置は以下のようにします。

Guide_ParentGuide_Child
translateX = 0, Y = 10, Z = 0X = 5, Y = 10, Z = 0
rotateX = 0, Y = 0, Z = 0X = 0, Y = 0, Z = 0
scaleX = 1, Y = 1, Z = 1X = 1, Y = 1, Z = 1

ガイドはゼロアウトしないでください!

次に、これらのガイドの位置に追従するコントローラーを作成します。

「Ctrl_Parent」と「Ctrl_Child」です。

これら2つのコントローラーは、原点「0, 0, 0」に配置してください

ノードを接続した瞬間にガイドロケーターの位置にスナップします。

ガイドとコントローラーはフラットな状態にします

そして、コントローラーをゼロアウトしたあと(ガイドはしない)で、ノードエディターで以下のようにノードを接続します。

以下のようにノードを接続します。

  • 親のガイド(World Matrix[0]) 👉️親のコントローラー (Offset Parent Matrix)
  • 子のガイド(World Matrix[0]) 👉️ multMatrix (Matrix In[0])
  • 親のガイド(World Inverse Matrix[0]) 👉️ multMatrix(Matrix In[1])
  • 親のコントローラー (World Matrix[0]) 👉️ multMatrix (Matrix In[2])
  • multMatrix(Matrix Sum) 👉️ 子のコントローラー(Offset Parent Matrix)

これで、ガイドを動かすとそれに対応したコントローラーが動くようになります。

さらに、Ctrl_ParentとCtrl_Childが、Outliner上ではフラットな独立した関係でありながらも、完全な親子関係の挙動になります。

なぜわざわざ階層を無くす(フラットにする)必要があるのでしょうか?
それは、入れ子階層は「後からの仕様変更(プロポーションの修正)に弱すぎる」からです。
「腕を5cm伸ばして」と言われたら、階層を解いて、またオフセットし直す繰り返し作業が待っています。
ガイドを使ったフラット構造なら、接続を切ってガイドを動かし、スナップしてスクリプトを押し直すだけで修正に爆速で対応できます()。
※注: 実は、今回の内容(multMatrixによる基本的な接続)だけでは、完全なモジュラーリギングのような柔軟な対応はまだできません。この段階ではまだ完璧ではない、という点にご注意ください。

ガイドに沿ってjointを引く

それでは、ガイド(ロケーター)にスナップさせてjointを引いてみましょう。

このガイドの仕組みは位置のみなので、ガイドを「Modify > Match Transformations > Match All Transforms」を使ってjointの角度をあわせます。

そうしたら、jointをゼロアウトします(ガイドはゼロアウトしないで!)。

あとは、うえで説明したのと同じ要領でjointにコントローラーを接続します。

子のjointには、もうおなじみのmultMatrixノードを使います。

コントローラーをジョイントに接続する方法の理屈は同じなので詳しく説明はしません。

これで、完了です。

ちょっと面倒な方法(わざわざjointの回転にロケーターをあわせるといったこと)ですが、まずは、この方法でMatrixリギングの基本の考え方を習得してください。

ただ、この手順を体験して「いちいち手作業で角度を合わせるなんて、全然自動化されてないじゃないか!」と思った方もいるかもしれません。

おっしゃる通り、こうした手作業での調整が必要なうちは、モジュラーリギングのように「どこにでも柔軟にポン付けできるシステム」とは程遠い状態であることがお分かりいただけるでしょう。

これが、先ほど「この段階ではまだ完璧ではない」とお伝えした最大の理由です。

【特別編】Matrixのつまみ食い!「自由な(すぎる?)親子関係」

multMatrix「Parent Constraint(完全追従)」の代用はできました。

しかし、Matrixを使うと「回転だけグローバル」といった特殊な(自由すぎる?)親子関係を構築することが出来ます。

それでは、普通の親子関係ではありえない「位置だけ(Point Constraint)」「回転だけ(Orient Constraint)」欲しい場合はどうすれば良いのでしょうか?

Matrixから欲しい要素だけとりだせば良いのです。

ちなみに、従来のリギング(コンストレイントを使った方法)での回転だけグローバルといったスペーススイッチのやり方は過去の記事を参照してください。

decomposeMatrix の弱点

位置や回転を取り出す時、一番有名なのが decomposeMatrix です。

しかし、これを姿勢制御に直接使うと「2つの致命的な問題」が発生します。

decomposeMatrix Offset Parent Matrixに渡すには不向き

  1. チャンネルボックスの「ゼロ」が崩れる 出力が「数値」になるためOPMに直結できず、Translate等に繋ぐことになり、二重変形(ダブルトランスフォーム)の原因になります。
  2. 回転のフリップ(反転):Matrixの箱から「オイラー角(XYZ)」に無理やり翻訳するため、特定の角度に達した瞬間にジョイントがクルッと反転する危険性があります。

では、decomposeMatrixは無駄なノードなのか?

もちろん、そんなことはありません。

decomposeMatrixMayaの従来のノードに『翻訳』して渡す」時に必須

  • オートストレッチ用に distanceBetween に位置(Translate)を渡す時。
  • 肘の角度を数値で取り出して、筋肉のブレンドシェイプを駆動させる時。
  • etc.

「Matrix → 数値」に変換する最強の翻訳機です!

それでは、簡単なシーンを作って試してみましょう。

ガイドのロケーター2つと、それに対応するポリゴンキューブを2つ用意します。

ポリゴンキューブはガイドに繋げた時に移動するので、原点に2つ作ってください。

ガイドのロケーターはゼロアウトしないでください。

このようなシーンを作ります。

「pickMatrix」を使って疑似Point Constraint

ここで登場するのが、Maya2020から追加された神ノード pickMatrix(ピック・マトリックス) です!

ノードはこのように繋いでください

アトリビュートを開くと、Use TranslateUse Rotate などのチェックボックスが並んでいます。

  • 位置だけ欲しい 👉 Use Translate のみチェック
  • 回転だけ欲しい 👉 Use Rotate のみチェック
  • スケールだけ欲しい 👉 Use Scale のみチェック

例えば「移動」だけが欲しいなら、Use Translate 以外のチェックをすべて外すだけです。

Use Translate以外はチェックをはずす

あとは、そのまま offsetParentMatrix に繋ぎましょう。

これだけで、安定したMatrixのデータ形式を保ったまま、欲しい要素(Translate)だけを抽出(フィルター)できます。

blendMatrix は?
要素の抽出に blendMatrix を使う方法もありますが、入力に「自分自身の初期位置(Matrix)」を繋ぐ構成になりやすく、Mayaが「サイクルエラー(循環参照)」を出して接続を拒否してしまう危険性があります。
オフセットを維持しつつ安全に要素を取り出すには、ベースの計算をすべて multMatrix に任せ、pickMatrix を使う方法がベターです。

※ちなみに、Matrixに潜む「シアー(せん断)」という要素は、リガーを長年悩ませてきた「一軸スケール時の歪み」の犯人でもあります。この天敵への対策については、連載のどこかで取り上げる予定です。

位置(Translate)だけを取り出すのは安全

Matrixの4x4行列の中には、「位置・回転・スケール・シアー(せん断)」という4つの情報が詰まっています。

ちょっとマニアックなMatrixの裏話:なぜ「位置」だけは安全なの?

$$\begin{bmatrix} ScaleX / Rotate / Shear & Rotate / Shear & Rotate / Shear & 0 \\ Rotate / Shear & ScaleY / Rotate / Shear & Rotate / Shear & 0 \\ Rotate / Shear & Rotate / Shear & ScaleZ / Rotate / Shear & 0 \\ T_x & T_y & T_z & 1 \end{bmatrix}$$

このように、Mayaの内部にある4x4のMatrixデータ(行列)を見ると、上部のマス目では「回転・スケール・シアー(せん断)」が混ざり合って保存されています。

しかし、「位置(Translate)」の数値だけは、一番下の行に完全に独立して保存されています。

そのため、位置だけを取り出す行為は、計算エラーが入り込む余地がなく、極めて安全で安定しているのです。

「Matrixの4x4行列(その中の左上の3x3行列)がどういったものなのか?」については、Matrix入門第1回の記事をご覧ください。

【保存版】旧コンストレイント → Matrix代用チートシート

それでは、Point Constraint以外のConstraintを再現する場合はどうすればよいのでしょうか?

以下に使用するノードと使い方を表にまとめておきました。

旧コンストレイント使用するMatrixノード設定のポイント(pickMatrix)
Parent (位置+回転(スケール、シアーも含む))multMatrixのみ子×親Inverseを計算し直結!
Point (位置のみ)multMatrix👉️pickMatrixUse Translate のみチェック
Orient (回転のみ)multMatrix👉️pickMatrixUse Rotate のみチェック
Scale (スケールのみ)multMatrix👉️pickMatrixUse Scale のみチェック
厳密な Parent (スケール無視)multMatrix👉️pickMatrixUse TranslateUse Rotate両方にチェック

Matrixリギングが圧倒的に安定している理由は、「オイラー角への翻訳を一度も行わないから」です。

数学的に絶対に破綻しない箱のまま計算し、そのままOPMに流し込む。

これこそが、「オイラーフィルターの存在を忘れられる強靭なリグ」の正体なのです!

【コラム】Softimage XSIの「ポーズコンストレイント」の思い出

かつて私が実務で「Softimage XSI」というソフトを使っていた頃の話です。

XSIにはMayaの「Parent Constraint」に相当する機能がなく、代わりに位置(Position)と回転(Orientation)の2つのコンストレイントを使って作業していました。

そんな私を見て、職場の先輩が「そっちを使うよりも『ポーズコンストレイント(Pose Constraint)』を使った方が安定するよ」とアドバイスを受けたことがあります。

当時は「結果は同じように見えるのに、なんでこっちの方が安定するんだろう?」と意味がよく分かっていませんでした(1つで済むから楽なんだ…くらいにしか思っていませんでした)。

しかし、Matrixを学習した今なら分かります。

ポーズコンストレイントの正体こそが、まさにオイラー角のフリップを起こさない行列(Matrix)による空間のトランスフォームだったのです!

時を超えて、あの時のアドバイスの真意にたどり着き、Matrixの強力さを改めて実感しました。

XSIについての熱い歴史や、3DCGソフトの覇権争いについては、こちらの記事で詳しく書いています!ぜひあわせてご覧ください。

まとめ

今回は、重くて不安定な旧コンストレイントを卒業し、Matrixノードを使って効率的かつ強靭なリグを構築するための「基本のキ」を解説しました。

重要ポイントのおさらい

  • ゼロアウトの掟: ジョイントの数値はすべて「0(スケールは 1)」にリセットしてからOPMに繋ぐ。
  • フラットな親子関係: アウトライナの入れ子階層(オフセットNull)は不要。ガイドを使って並列に配置することで、プロポーション修正に爆速で対応できる構造を作る。
  • Inverse(逆行列)を掛ける: multMatrix【 子 × 親のInverse 】を計算することで、階層を遡り、親の座標エネルギーを無効化して純粋なオフセット(差分)だけを抽出できる。
  • オイラーフィルターからの解放: decomposeMatrix ではなく、pickMatrix で要素を抽出することで、フリップしない安定した制御が可能になる。

これまで「当たり前」だと思っていた階層構造やコンストレイントを使わないアプローチに、最初は戸惑ったかもしれません。

しかし、これでMatrixリギングの強力な「基礎体力」が身についたはずです!

もう旧来のコンストレイントには戻れなくなるはずです。

しかし、Matrixが本当の力を発揮するのはこれからです。

今回紹介した「ガイドを使ったフラット構造」は、実は次なるステップへの布石に過ぎません。

次回、第4回のテーマは「たかがFK、されどFK。モジュラーリギング的なFKを構築する!」です。

アニメーターが触る「ただのFK」の裏側で、高度なMatrixリグがどのように設計され、レゴブロックのように拡張性の高い「モジュール」として構築されているのか?

aimMatrixなどを駆使して、ついにその真髄に迫ります。

真のMatrixリグがどのように作られているのか、ついにその真髄に迫ります。

どうぞお楽しみに!

第3回Matrix入門アイキャッチ

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

フリーの3DCGアニメーター。
メインツールはMaya(使用歴20年以上)です。お仕事ではSoftimage(XSI)やMotionBuilderの使用経験もあり。思いに手付け(キーフレームアニメーション)が多いですが、キャプチャーもいけます。サブスキルとしてはリグ(Maya限定ですが)も少々いけます。

ゲームが昔から大好きなので「ゲーマー」としての側面からもいろいろ発信出来ればと考えています。
CG歴よりもゲーマー歴のほうがずっと長いもので(笑)。

twitterアカウント
@inopoa1

よろしくお願いします。

コメント

コメントする

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

目次