>   >  COYOTE流テクニカルTIPS:TIPS 01:【Maya】ベクトルでBend制御してみよう
TIPS 01:【Maya】ベクトルでBend制御してみよう

TIPS 01:【Maya】ベクトルでBend制御してみよう

クリーク・アンド・リバー社の社内CGスタジオであり、ゲームの3DCGグラフィックス制作を中心に手がけているCOYOTE 3DCG STUDIO。本連載では、同社のTAチームによる制作に役立つ技術TIPSを紹介していく。

TEXT_山本智人 / Tomohito Yamamoto(COYOTE 3DCG STUDIO)
EDIT_小村仁美 / Hitomi Komura(CGWORLD)

連載開始に寄せて

おはこんばんちわ。COYOTE 3DCG STUDIO テクニカルアーティスト(以下、TA)の山本です。TAチームのリーダーとして従事しています。

普段は弊社のブログ(TECH-COYOTE)にていくつか記事を執筆しておりますが、このたびCGWORLD.jpへの寄稿の機会をいただき、私たちTAチームからも有用なテクニカルTips・業務サポートTipsなどを紹介していきたいと思います。

まず最初は、「ノードの回転制御」について、以下のように全3回に渡って取り上げます。

・ベクトルを使ったノードのBend制御
・Quaternionを使ったRoll成分の分解
・ベクトルの活用(内積・外積・三角関数)

今回は第1回として、Mayaにて「ベクトルを使ったノードのBend制御」、すなわち「捻り防止挙動」の実装例を紹介します。

Result:結果を先に

回転成分をBendとRollに分けて考えて、Bend成分のみでノードを回転させます。
手法は、

1:ノードの向きからMatrixを使ってベクトルを算出
2:初期の向きと現在の向きのベクトルからBend回転を取得して、ノードに接続

上記2の計算はUtilityノードでもできますが、エクスプレッションでMELを使って実装することも可能です。


  • ◀エクスプレッション内容

では、上記実装にいたるまでを順に追っていきたいと思います。

Step 01:回転成分を「曲げ=Bend」と「捻り=Roll」に分けて考える

例えば、キャラクターの上腕や手首のジョイント制御を考えた時、捻りの制御を何とかしたいところです。

この問題の解決方法は、既にいくつかのアプローチが知られていますが、今回は回転成分を以下のように「曲げ=Bend」「捻り=Roll」に分解し、Bend成分だけでジョイントを制御する方法を考えてみたいと思います。


  • ▲「曲げ=Bend」
  • ▲「捻り=Roll」


オイラーは回避したい

あれ? なんでオイラー回転を使わないの? 軸方向と回転順序に注意して、ねじれの軸をロックしてしまえば実装できるんじゃ...??

確かに。それにオイラー回転は回転状態を説明するのにとても直感的でわかりやすいため、簡単そうです。

しかしながらオイラー回転の場合、必ずしも同じ値で1つの姿勢を定義するわけではなく、

また、同じ値でも回転順序によっては同じ姿勢にならず......

さらにジンバルロックもあったりして......アァー!(発狂

このことから、補助骨の制御には不向きといえます。

特に、同じ姿勢にもかかわらず値が大きく異なる特性については、ドリブンキーなど他の制御と組み合わせた際に不具合の原因になりやすいと思います。

実際、今まで私が見てきた中で、上腕の捻り防止をオイラー回転で実装した例では制御が安定しておらず、大体何らかの拍子にジョイントがフリップして腕があらぬ方向を向いてしまうという印象があります。

ですので個人的には、リギング・セットアップでの回転制御において、あまりオイラー回転の結果を信頼していません。

Step 02:現在の回転状態から向きを取得する

では、このBend成分を取得する方法を順に考えてみます。

Bend成分とは、つまり現在向いている方向(に、どれくらい回転していたったか)なので、「初期の向き」と「現在の向き」から差分の角度を調べることで実現できそうです。

▲初期の向き(青)、現在の向き(赤)

上記の画像の「向いている方向」を示す「座標」を取得する方法を考えてみます。今回の例では、ジョイントのx軸方向をprimaryAxis・Z軸方向をsecondaryAxisとします。


コンストレインを駆使して向きを取得

例えばparentConstraintなどを使えば、簡単に取得できそうです。まず、対象のジョイントと同じ階層にロケータを作成し、ジョイントのX軸方向正面+1の位置に配置します。

あとはこのロケータとジョイントをparentConstraintでつないでしまえば、この通り!

上の動画のアトリビュートの遷移を見れば、そのときの向きを示す座標が取得できているのがわかります。割と簡単ですね。


Matrixを使ってノードを節約

ただ、上記の方法ではロケータなどのTransformノードやparentConstraintノードが追加されるので、若干煩雑です。ここをMatrixを使ったノードリギングを行うことで、もう少しエレガントな実装が可能になります。

まず、Node Editorを開き、対象のジョイントを表示しておきます。

ここで「composeMatrix」ノードを追加します。このノードは与えられたTransformアトリビュートからMatrixを生成するUtilityノードです。

作成した「composeMatrix」を選択してAttribute Editorを開きます。ジョイントのX軸方向+1の座標を取得したいので、transformXの値に1.0と入力しておきます。

これで、X軸方向に+1移動したマトリクスを生成できました。

このMatrixは対象ジョイントの子階層として挙動してほしいため、対象ジョイントのMatrixの影響下に置く必要があります。そこで、「multMatrix」ノードをNode Editorに生成し、対象ジョイントと先ほどのcomposeMatrixを乗算させます。

composeMatrixのmatrixSumをmultMatrix.matrixIn[0]につなぎ、ジョイントのmatrixをmultMatrix.matrixIn[1]につなぎます。Matrixには計算順序がありますので、つなぐ際は順序に注意しましょう!

今度は、このMatrixから移動成分を抽出します。 Node Editorで「decompose Matrix」ノードを作成し、計算結果のMatrixをdecomposeMatrixのinputMatrixに接続します。