2022/10/05

Unityで補助骨のリアルタイム制御

こんにちは、BACKBONEの中川です。

Mayaで作成したジオメトリやジョイント、ウェイトデータなどの情報はFBX形式でUnityに出力できますが、Mayaが定義するノードや機能はFBX形式で出力できません。Unityでリグの再現が必要な場合は『Mayaから出力できる機能』や『Unityでも再現できる機能』を考慮してリグを設定する必要があります。

今回は、Mayaのノードを使用した補助骨の挙動をUnityで再現する手法についてご紹介します。

 

1Mayaでの挙動

下記動画をご覧ください。

・上図:手首ひねり補正の補助骨なし
・下図:手首ひねり補正の補助骨あり

上図は手首をひねった際に手首の形状が破綻していますが、
下図は補助骨により、自然なデフォームが表現できています。

仕組みを簡単にご説明します。

◆工程①:ひねり成分抽出
手首の骨の回転からひねり成分のみを抽出します。

◆工程②:係数倍回転
肘~手首にかけて徐々に前腕のひねりが強くなるように補助骨を設定します。
※『手首のひねり成分』×『係数』で調整できるように設定します。

この仕組みは、手首の骨(wrist_if_L)をソースとして、4つの補助骨の連動で実現しています。
※以降、工程①の補助骨をTwist骨、工程②の補助骨をTwistWeighted骨と呼称します。

wristTwist_if_L:wrist_if_Lから、ひねり成分(rotateX)のみを抽出(工程①)
wristTwistWtA_if_LwristTwist_if_Lの回転値 × 0.25(工程②)
wristTwistWtB_if_LwristTwist_if_Lの回転値 × 0.5  (工程②)
wristTwistWtC_if_LwristTwist_if_Lの回転値 × 0.75(工程②)

 

ひねり成分の抽出は、以下のようなノード接続で実現しています。
※様々な実現方法がありますが、今回はUnityでの再現性を考慮して下図の方法にしました。

工程①では、Quaternion系のノード(eulerToQuat, quatNormalize, quatToEuler)を使用しています。
Euler値をQuaternionに変換したのち、Y成分とZ成分を0にすることで、ひねり回転(rotateX)のみを抽出しています。

工程②では、pairBlendノードを使用しWeightアトリビュートには任意の値をセットしています。
また各pairBlendノードの.inRotate1アトリビュートの値は(0,0,0)をセットしています。
つまり、このpairBlendノードによって、
・.inRotate1:回転していない状態
・.inRotate2:Twistの回転値
この2つをWeight値で補完した回転値が出力されます。

 

2Unityでの再現

さて、MayaのデータをFBX形式で出力し、Unityに読み込んでみます。
手首を回転させてみます。

手首のひねりに対して、前腕が連動していません。
Mayaでは動いてくれていたはずですが…。

それもそのはず、
先ほど補助骨用に設定したMayaのノードは、FBX形式で出力することができないからです。
Unity上でも補助骨が動くように、改めて再設定する必要があります。
先ずは、Unity上でMayaでのノード接続と同じ挙動をするコンポーネントの作成が必要です。

 

◆ 工程① :ひねり成分抽出(Twist骨)のコンポーネント

HierarchyウィンドウでTwist骨(wristTwist_if_L)を選択します。
次に、インスペクターのAdd Componentをクリックし新規コンポーネントを追加します。
今回はNew ScriptのName欄に『Twist』と記入し、『Twist』という名前のコンポーネントを作成し追加します。

作成したTwistコンポーネントにTwist骨の回転に関する処理を記述します。
下図のように記述します。

<上記内容の補足>
Twist骨の回転値としてrotationSourceのひねり成分が抽出された値が代入されます。
Unityでは、transformコンポーネントのrotationプロパティはQuaternion型です。

Mayaでひねり成分抽出のために使用したeulerToQuatのような変換は不要なので、上記のように簡潔に記述することができます。

これで、Mayaにおける以下の接続と同等の処理が再現できました。

このコンポーネントがwristTwist_if_Lにアタッチされていることを確認し、
rotationSourceをwrist_if_Lに設定すれば工程①の完成です。

 

◆ 工程② :係数倍回転(TwistWeighted骨)のコンポーネント

同様に、TwistWeighted骨にも新規コンポーネント『Weighted』を追加します。
作成したWeightedコンポーネントにTwistWeighted骨の回転に関する処理を記述します。
下図のように記述します。

<上記内容の補足>
Slerp関数は、MayaにおけるpairBlendノードと同じような挙動を再現するため、

・変数weight=0:Quatenion.identity(回転していない状態)
・変数weight=1:twistSource(Twist骨)の回転値

この2つをweight変数で補完した回転値が出力されます。

変数weightの値はインスペクターで指定できるようにpublicで宣言していますので、
各TwistWeighted骨で同じコンポーネントを利用できます。

これで工程②の完成です。
挙動を確認してみましょう。

前腕のひねりを再現できていることが確認できました!

 

3Humanoidにリターゲットすると…?

これで前腕のひねりは完成!
……と思いきや……
このモデルとアニメーションをGenericからHumanoidにリターゲットしてみると……

ひねり以外は問題ありません。
ひねりの時も一見問題ないようですが、よく見ると肘付近の挙動に変化があります。
前項(Generic)の挙動を再掲し比較します。

赤枠部分に注目すると、Genericと比較して根元からひねられています。

原因は、手首のひねりはHumanoidにリターゲットする際に一度「前腕のひねり」として変換され、
その「前腕のひねり」は、ある係数で肘の骨と手首の骨に分配されるからです。
(こちらを参考にさせていただきました:Mecanim Humanoid の基礎的技術解説

 

この係数は、デフォルトでは50%に設定されています。たとえば、手首を80°ひねったアニメーションをHumanoidでリターゲットすると、手首に40°、肘に40°ずつ分配されます。

この分配の仕組みによって、補助骨用のコンポートネントを設定しなくても、最初に動画でお見せした状態(補助骨が動作せず手首だけがひねられ、前腕が全くひねられない状態)よりはきれいに見えるというメリットがあります。一方で、今回のようにコンポーネントで補助骨を制御したい場合は不要な仕組みです。

この分配の係数は、以下から変更可能です。

 Humanoid設定 > Muscle & Settings > Additional Settings > Lower Arm Twist

デフォルト値は0.5ですが、0にすると肘への分配率を0に変更できます。
上腕、上腿、下腿も必要に応じて0または1に変更することで各部位の調整が可能です。

このように設定を変更して、改めてアニメーションを確認すると……

無事Humanoidでも、意図通りに補助骨でひねることができました!

 

4まとめ

Mayaのノード接続で動作させた補助骨は、(例外はありますが)Unity上でコンポーネントを作成すれば同じ挙動を実現することができます。
今回はTwist骨やTwistWeighted骨のみをご紹介しましたが、曲げ成分のみ抽出するBend骨はもちろん、ドリブンキーやaimConstraintなど複雑な制御も再現可能です。

最後に、今回の方法を参考にされる際にご注意いただきたい点をお伝えします。
Mayaから出力したデータをUnityに読み込む際、JointOrientの値はRotateに変換されます。
JointOrientに値が設定されている場合は、コンポーネント作成の際にJointOrient成分も考慮する必要がありますのでご注意ください。
※今回の記事では、Unityに読み込んだ際にRotateの初期値が0(前腕のJointOrientにも値を設定していない)になるようにデータを作成しています。

それでは、ぜひいろいろ試してみてください。

 

※免責事項※
本記事内で公開している全ての情報について、その完全性、正確性、適用性、有用性等いかなる保証も行っておりません。
これらの情報のご利用により、何らかの不都合や損害が発生したとしても、当社は何らの責任を負うものではありません。
自己責任でご使用ください。



ブログTOPへ戻る