チュートリアル / 読んで触ってよくわかる!Mayaを使いこなす為のAtoZ
第111回:スキニング後に移動・回転・スケールに値が入っていた!と気づいたときの対処編
- Maya
- キャラクター・リグ
- ゲーム
- コラム
- チュートリアル
- モデリング
- 初心者・学生
引き続きスキニングに関わる話題で、メッシュの移動・回転・スケール値(以後TRS値)についてとりあげます。
映像系の仕事の方は、レンダリングして画像ができればとりあえずOKですが、ゲーム製作ではきれいなデータで作らないと、ゲーム内でおかしな結果になることがあります。
よくあるのが、スキニングをしてウェイトも付けたのに、よくよくメッシュを見てみるとTRS値になにかしら値が入っているのに気付き、青ざめるというパターンです。経験のない人はいないはず!
ゲーム向けのスキニングでは、メッシュのTRS値がきれいにリセットされている必要があります。
(TR値は0.0、S値は1.0)
「スキニングする前には必ずフリーズすること!」と製作の手引きに書いてあったか、誰かに言われていた気がするので、バレないうちに黙ってしれっと修正したい…。
そういう方のために、正しい修正方法についてご紹介いたします。
話がややこしくなるので先におすすめの方法をお伝えしておくと、前回のコラムの「しっかりコース」で対応するのが簡単です。
(メッシュを複製 → バインド → ウェイトコピー)
前回合わせて紹介していたスクリプトでちゃっちゃとデータをきれいにしてもらうのが手っ取り早いです。
Keep Historyでバインド解除して、フリーズして、再バインドすればいいんでしょ?
Keep Historyオプションを有効にしてからバインドを解除すると、スキンウェイトを残したままスキニングを一時的に解除できます。
その隙にメッシュをフリーズしてTRS値をリセット、再度バインドすればスキンウェイトが復活するので、動くはずです。
が、一応理論上はそれでOKなはずなのですが、実際にはうまくいかないケースが多いので注意が必要です。(コラムのタイトル画像がそれ。ウェイトが変になっています)
とりあえずどこまで上手くいっていて、どこでおかしくなるのか考えていきましょう。
1) スキンのバインドの解除
メッシュを選択して「スキン > スキンのバインド解除 (Skin > Unbind Skin)」を実行します。
実行する前にオプション設定で、「ヒストリ (History)」を「ヒストリの維持 (Keep History)」にしておきます。
skinClusterノードを残したままでバインド解除できます。バインド解除すると、TRS値のロックが外れます。
スキンウェイトの情報はそのままで、skinClusterのノード状態が変更されます。具体的には、skinCluster.nodeState = 1 (HasNoEffect) とします。
2) フリーズでTRS値をリセット
メッシュを選択したまま「修正 > トランスフォームのフリーズ (Modify > Freeze transformations)」を実行します。
TranslateとRotateは0.0、Scaleは1.0となり、メッシュの形を保つように頂点座標が設定されなおします。
メッシュに作業履歴があるかどうかで、フリーズの挙動が変わります!
作業履歴が無ければ直接頂点のTweak情報として設定されます。(頂点を選択して、Channel BoxのCVsを見ると移動値として記録されていることがわかります。)
作業履歴があればtransformGeometryノードが作られ、指定したTRS値で頂点をオフセットさせます(TransformのTRS値)。
ここではskinClusterの履歴があるので、transformGeometryノードが作られるパターンで進みます。
この違いのせいで、今紹介している手順でウェイトに問題が発生したりしなかったりします…。
3) 再バインド
メッシュとジョイントを選んで「スキン > スキンのバインド (Skin > Bind Skin)」で再バインドします。
既にskinClusterがあれば、それを再度有効にしてくれます。
有効にするというのは、skinCluster.nodeState = 0 (Normal) にするだけ。
実はそれ以外にも重要な変更を行っています。
バインドするときは、今のメッシュのTRS値をskinCluster.geomMatrix (.gm)というアトリビュートに行列として設定しています。
この geomMatrix というアトリビュートは変形の計算で使います。なので、もしバインドしたメッシュのTRS値と一致していなければ、変形がおかしくなります。
geomMatrixの不一致があるとき、変形させると大抵は別の場所をピボットとして変形したような結果になりますので、その時点で気づくことができます。
正しい値になっているかどうかの確認は、getAttrコマンドでできます。
次のようにMELスクリプトを実行すると、行列の16個の数字が返ってきます。
getAttr skinCluster1.geomMatrix;
// Result: 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
例えばメッシュのTranslateがX=1.25, Y=2.3, Z=0.54の時にバインドすると、次のような値になります。
1 0 0 0 0 1 0 0 0 0 1 0 1.25 2.3 0.54 1
ちなみに、あえて変な移動値を入れれば、おかしな変形が起きることを確認できます。
以下の値をskinCluster1に入れてから、ジョイントを動かすとちょっと変な場所を起点として変形するような感じになります。
setAttr skinCluster1.geomMatrix -type "matrix" 1 0 0 0 0 1 0 0 0 0 1 0 2.0 0 0 1;
フリーズをしてTRS値がリセットされてきれいな状態なら、いつでも次のように設定しておきます。(普通はバインド、再バインドしたときに設定されますので、下記は実行不要です。)
setAttr skinCluster1.geomMatrix -type "matrix" 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1;
4) デフォーマ以外のヒストリの削除
メッシュを選択して「編集 > 種類ごとに削除 > デフォーマ以外のヒストリ (Edit > Delete by Type > Non-Deformer History)」を実行します。フリーズの履歴を削除します。
この時点で、ジョイントを回転してみると何かスキニングの結果がおかしい、という状況になります。
ウェイト値を見てみると、次のように変化しているのがわかります。
Non-Deformer Historyは、再バインドする前に実行しても、再バインド後に実行しても結果は同じです。なんどか試してみるとなぜか再バインド前に実行すると上手くいくケースがあったような気もしないでもないですが…。
スキンウェイトがおかしくなるのはなぜ?
課題は「フリーズ」と「デフォーマ以外のヒストリ」の組み合わせにあります。
フリーズすると二つのことが実行されます。「TRS値をリセット」して「ジオメトリが同じ位置にとどまるように頂点を変更(transformGeometryノードの追加)」します。
この取り回しがとてもややこしいため、簡単に話せば、フリーズをすると変形前のメッシュと変形後のメッシュの位置がずれます。
「デフォーマ以外のヒストリ」を実行すると、スキンウェイトをコピーします。
前回のコラムで仕組みを紹介しました。今回のように同じ頂点数のメッシュ同士では「レイキャスト(+対象となるジオメトリが見つからなかった頂点ではClosest point on surface を適用)」でウェイトがコピーされます。ところが、メッシュの位置がずれているため、ウェイトがうまく転送されません。(合掌)
具体的には、skinClusterがある場合、フリーズと履歴削除では次のことが起きます。
フリーズがtransformノードのTRS値をリセットします。そのままではメッシュの位置が変わってしまうため、代わりに変形後のメッシュの前にtransformGeometryノードを追加して、TRS値を入れます。この時点で、変形後のメッシュに関しては、TRS値をリセットしたにも関わらず同じ位置を保てます。
次の図のように、transformGeometryにはもともとのTRS値が入ります。
問題は中間オブジェクトにあります。
中間オブジェクトは変形後のメッシュと同じtransformの下にぶら下がっていることに注意!
中間オブジェクトにはtransformGeometryノードを適用しないわけで、単にtransformのTR値=0、S値=1.0にした場所に現れます。
このメッシュ同士でスキンウェイトをコピーすれば、結果はおのずと…。それを見越して上手くやって欲しい気もしないでもないですが。
スキンウェイトが直れば、万事OKなはず。
ということで、もし上の方法のように手動でフリーズやらデフォーマ以外のヒストリ削除やらするときは、スキンウェイトを事前に書き出して、後で読み込むとよいです。
「デフォーム > ウェイトを書き出し (Deform > Export Weights)」と「ウェイトを読み込み (Import Weights)」をお使いください。上手くいきます。
まとめ
TRS値をリセットするときの方法をご紹介しました。
が、結局のところおすすめは前回の「しっかりコース」を手動か、そこにおまけでつけているスクリプトで実行してもらうのが手っ取り早いです。
大抵の場合このあたりの挙動はMayaのミステリーとして語り継がれることが多いので、今回何が起きているか、仕組みをご紹介してみました。
次回もまだスキニング周りの話が続きます…。
こういうややこしい手順が走っているのをわかっているなら、Softimageみたいに非破壊なクリーンナップに対応して!という声が聞こえてくる気もしますが、あちらはあちらで今回のようなややこしい処理をハードコードして裏でやってのけていたという腕力。その半面、ユーザの作ったノードやツールがもしこのスキニングの履歴に割り込んでくれば、Mayaの今の仕組みは柔軟性が出てくる可能性もありますので、なにとぞご理解を。仕組みさえわかれば、あとはTAの腕の見せ所だと思って、皆さんいいツールを作ってください!