マトリクスを使ったポーズ・モーションの左右反転

おはこんばんちわ。
ホラーが苦手・山本ほっさんです。

今回は、モーションアーティストさんから特に要望の多い「ミラーポーズ」や「ミラーモーション」のAutodesk Maya®における対応事例についてご紹介いたします。

精度の高い左右反転は、それだけでモーションの工数削減に直結し、アーティストのクリエイティブに使える時間を増やせるので、絶対に対応したいところです。
しかし、左右反転だからといって、Y軸にマイナスを乗算すればいい、というだけではありません。


結果を先に

blob
私が従来採用している方法は、マトリクスを反転させてミラーしたクォータニオンを取得し、ターゲットノードに戻す、という手法です。

でもなんで?なぜそのような手法を採用したのでしょうか。


Y軸にマイナスではダメなの?

簡単に考えれば、「左右反転なだけなら、単位Y軸にマイナスを乗算すればいいんじゃ?」と発想しがち。
しかし、下の例をご覧ください。

blob
これが…
blob
ヒィッ!

この処置の例では、一応階層や骨の軸方向定義などが影響しないように、ワールド空間上の回転として、y軸回転を反転させてみたものですが…。

うーん。背骨周りはそれっぽいようにも見えますが、手足は全然ですね。

このように、Y軸にマイナスを単に乗算するだけでは理想の結果にならないことがあります。
上の例で左右反転にならなかった原因は、回転順序の関係で、必ずしもY軸での回転反転が空間上の向きの反転と同意義ではない、という点がほとんどです。

オイラー回転は「向き」を保証するものではない。

blob
Mayaがデフォルトで採用・表示しているXYZの三軸で回転を表すオイラー回転は、直感的で理解しやすく、扱いやすい利点があります。
しかし、ジンバルロックや回転順序という概念が存在し、あくまで「三軸が順番にどのように回転したか」を説明しますが、特に「向き」については、意図通りに表現しないことがあって、個人的にはあまり信用していません。


向きを厳密に指定するならクォータニオンがよい

一方で「向き」について信用できるのは、クォータニオンによる回転表現です。オイラー回転は「どの軸に何度回ったか」を表現するのに対し、クォータニオンは「どの向きを向いていて、どれくらいねじれているか」という考え方です。

イメージ的には、クォータニオンを反転して適応してあげたいですね。


反転マトリクスをかける

blob
IKリグなど、向きだけでなく位置でコントロールしているものもあります。 これらは、向きと一緒にある空間軸で反転した位置も取得したいところ。

これらを一挙に解決するために、そのノードの空間=すなわち、マトリクスをある親空間上で反転すれば、向き・位置ともに、ある軸を対称にミラーしてくれそうです。

しかし、反転マトリクスとは? 
ここでは単純に考えて、例えばあるノードをYZ平面で反転したい場合、X軸方向にマイナススケールを入れればそれっぽくなります。 このことは容易に想像できると思います。

この点から、反転させたい軸にマイナススケールを入れたマトリクスを乗算してれば、それっぽくなりそうです。


実際にやってみよう

試しに、X軸にマイナススケールを入れたTransform Node のマトリクスをコードで取得してみます。
blob

import maya.cmds as mc

node = mc.createNode('transform')
mc.setAttr('%s.sx'%node,-1.0)

print mc.xform(node,q=True,ws=True,wd=True,m=True)

↓結果

[-1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]

うーん。

4×4に整列すると…

-1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0

なるほど、こうなっているんですね。
マトリクスのおのおのの軸の意味合いは…

x y z 未使用
1 0 0 0 X軸の定義
0 1 0 0 Y軸の定義
0 0 1 0 Z軸の定義
0 0 0 1 移動

こうなっているので、なるほど、なんとなく合点がいきます。

ここでは簡単に、NodeEditorでのノード接続で実装してみましょう。

まず、ターゲットノードとソースノード用意して…
blob
multiMatrixにソースノードのマトリクスをつないでみます。
blob
次に、反転マトリクスを取得するためのTransfom Nodeを用意して、同様にマトリクスをつなぎます。
blob
マトリクスの要素を分解したいので、decompose Matrixノードに乗算結果をつなぎ、
blob
QuatToEulerノードに乗算結果からのクォータニオンをつなぎ、
blob

Euler結果をターゲットノードに接続してやると…
blob
このとおり!
ミラー結果
さっきのジンバルの現象も…
ハラショー!
おこらない!!
ハラショー!


まとめ

このようなシンプルなロジックであれば、様々なモーション・ポーズのミラーToolへの実装もシンプルに済みそうです。
とくに、マトリクスやクォータニオンの計算には、MayaⓇから提供されているUtility Node周りが大変便利です。

何かの参考になれば幸いです。

でわでわ。

Author: tomohito.yamamoto