土屋つかさの技術ブログは今か無しか

土屋つかさが主にプログラミングについて語るブログです。

#unity インポートしたキャラモデルにモーションを流したら関節がくにゃった時の対処法(補助ボーンとRotation Constraintコンポーネントの話)

はじめに

 今回は「補助ボーン」について。キャラモデルのfbxデータをunityでインポートした時に、補助ボーンが原因で肘や膝の関節がぐにゃってしまうことがあります。その原因と対処方法を解説します。

 例によって間違いを書いてる所もあるかと思うので、気づいた方はコメントで指摘していただければと思います。

導入:補助ボーン問題と映像作家さん

 普通の開発では、ゲームで使うキャラモデルのデータ構造については、アーティストとエンジニアが緊密に連携を取りながら進めるので齟齬が起きることはありません*1

 しかし、既存のリソースを使ってプロトタイプを作成することになった場合*2、あるいは、非ゲーム用途で作られたデータを使う必要がある場合*3、既存のキャラモデルのデータ構造がゲーム用に最適化されていないために上手く動いてくれないということがあります。

 ずいぶん前にこのシチュエーションに遭遇しまして、その際、表題の「補助ボーン」の対応にかなり手間取りました。それで、前回お話しした映像作家さんが、たまたま同じトラブルに巻き込まれたので、多少は普遍性があるかと思い、その時の解決方法をメモっておきます。

 なお今回、スキニング自体については長くなるので説明を飛ばしました(土屋がちゃんと理解出来てないという事もある)。それについては改めて記事にするかもです。

unityのhumanoidによるボーン制御メカニズムについて

 unityにはhumanoidというメカニズムがあり、キャラモデルのfbxデータをインポートした時、そのfbxに格納されているボーンをhumanoidの共通ボーン仕様に自動的にマッピングします。自動マッピングに失敗した場合は手動で設定し直します*4

 上画像はCandyRockStar VerのUnityちゃんのHumanoidです。キャラモデルの中には用途に応じて様々なボーンが組み込まれています。このうち、緑色のボーンはHumanoidが自動マッピングの対象したボーンで、灰色のボーンはHumanoidの管理外にあるボーンという事になります。ちなみに、ボーンのウェイト設定はHumanoidとは別のメカニズムでポリゴン頂点に反映されるので、管理外ボーンを動かせばモデルも連携されます。

 本来、ボーンには規格も命名規則もありません。また、モーションデータはキャラモデルとセットでして、あるモーションデータを別の(ボーンの構造や名前が異なる)モデル上で再生することは想定されていません。humanoidの共通ボーン仕様を介することで、モデルとモーションを自由に組み合わせることができているわけです。これはunityが持つ強力な武器と言えます。

補助ボーンとはなにか

 さて、humanoidは非常に便利ですが、キャラモデルが「補助ボーン」を使っている時に、描画がおかしくなることがあります。

 補助ボーンというのは、通常のボーンへのウェイト設定だけでは十分なスキニングが出来ない場合に追加で設定するボーンのことを言います。肘や膝など、ジョイント間の距離が長く、かつ、関節が大きく曲がる所では、通常のボーンだけでは歪みが大きくなるため、補助ボーンを追加して、そちらにもウェイト設定して補正をかけることが多いようです。

 補助ボーンは、ほとんどの場合対応する通常ボーンと一緒に動くことを想定しています。しかし、humanoidはこの補助ボーンを管理しません。それでも多くの場合は問題ないのだと思いますが*5、場合によっては描画がおかしな事になる場合があります。

Rotation Constraintによる補助ボーン補正

 下の画像は、つみ式ミクさん部屋着風をblenderでfdxデータにコンバートしてからunityにインポートし、モーションを流し込んだ物です*6。左側では肘の部分がぐにゃっています。これは右肘の補助ボーンが通常ボーンに連携していないためで、連携させると右側のように正しい描画になります。

 この補正には、Rotation Constraintというコンポーネントを使います。このモデルでは、右肘の通常ボーンがひじ.R、補助ボーンがひじS.Rという名前です。Blender上で見るとこんな風に重なってます*7。小さい方が補助ボーンです。

 pmxエディタ上ではこんな感じ。非表示になっている補助ボーンを表示させています。

 ひじS.RにRotation Constraintsをアタッチし、sourcesの項目を1個増やし、連携先ボーンとしてひじ.Rを設定し、Activateボタンを押せばアクティブ化されます*8。これで補助ボーンが通常ボーンに連携し、正しく描画されます。

 ひざにも同じようにRotation Constraintsを仕込みました。

 このモデルでは通常ボーン●●.Rに補助ボーンがある場合は、同階層に●●S.Rという名前で配置されているようなので、見つけた補助ボーン全部にアタッチしています。両手両足に3箇所ずつ計12箇所あったと思いますが、補正が必要無いボーンも結構ありました。

 補助ボーンの名称もルールはありませんが、通常ボーンの名称にauxとかauxiliaryなどを付与することが多いようです。

おしまい

 スキニングには触れなくても結構な分量の記事になりましたね……。ゲーム開発は覚えること多くて大変だなあ(遠い目)。

 unityで既存キャラモデルをインポートした時、Tポーズでは問題ないのに、モーションを再生するとねじれてしまう時は、まずは補助ボーンの存在を疑ってみてください。

余談1:ゲームでは補助ボーンを使うのはやめよう。

 これだけ書いておいてなんですが、補助ボーン不具合への一番簡単な対処方法は、補助ボーンを使わない形でスキニングをやり直す事です。これが一番確実です。
 しかし、現場では色々な事情で元のモデルを弄れないことが多々あります。また、今後キャラモデルが高精細化していくことで、ゲームでも補助ボーン利用が一般化していくかもしれません。覚えておくと役に立つかもしれません。

余談2:「補助ボーン」という呼称について。

 「補助ボーン」は通称で、公式な用語ではないようです。「補助ジョイント」「準標準ボーン(pmxの場合)」とも呼ばれます。英語では「extra bone」と呼ばれることが多いようです。本来通常の(?)ボーンと補助ボーンには本来区別は無いので*9、DCCツール側では呼び分ける必要がないのだと思います*10

余談3:pmxデータのボーンをunityに持ち込むの大変

 今回pmxデータをblender経由でfbxに変換してunityにインポートしたんですが、humanoidとして動かすまでが結構大変でした。これはキャラモデルデータの問題ではなく、上記でも書いた通り、スキニングのデータがそもそも可搬性を想定していないためです。
 下記画像の、左は土屋がインポートしたモデルを調整した物。右はMM4DMecahimというUnity上で直接pmxを読み込むツールが自動補正した物です。

 humanoidでは、ベースのモデルのボーンはTポーズになっている必要があります。このモデルは元々Aポーズで作られていて、そのままにしていたら上手くモーションが再生されなかったので肩周りだけなんとなくTポーズにしました。指先は面倒で直しておらず、その部分が赤くなっています。
 左は緑色のボーン同士が繋がっていませんがこれは問題ありません。本来ボーンは根元の座標と方向だけの情報で、ボーンの「長さ」は可読性の為に描画してるだけだからです(多分)。
 ただし、本番環境でなにが起こるかはわからないので、検証以上の作業をする場合にはちゃんと調整した方が良いと思います。

参考リンク

docs.unity3d.com
Constraintsコンポーネントには、Rotation以外にもAim/Look At/Parent/Position/Scaleと色々あるんですが、それらの使い道は知りません。近いうちに調べるかも。

3d.nicovideo.jp
サンプルのキャラモデルにつみだんごさんの「つみ式みくさん部屋着風」を使用させていただきました。ありがとうございました。

*1:実際には頻繁に齟齬は起きるし、そもそも緊密に連携を取らないと勧められないこと自体が課題だと思っていますが今回は置きます

*2:こういうことはよくある

*3:こういうこともよくある

*4:階層の位置と、ボーンの名称で推定してるっぽいので、変なボーン名だと結構ミスる

*5:補助ボーンもスケルトンのツリーに組み込まれてるので、一緒に動くので問題無いのだと思われる(自信無し)

*6:例としてpmxデータを使っていますが、補助ボーンはハイポリモデルでは普通に使う手法なので、どのデータでも同じです

*7:ただ、Blender上ではひじ.RにもひじS.Rにもウェイトが設定されているように見えない。土屋がまだスキニングの理屈を勘違いしている可能性が高く、この辺分かったら修正します

*8:IsActiveをチェックするだけだと、Lockが有効にならないことがある

*9:無い筈。UnityがHumanoidで使うボーンを特別扱いしてるだけ

*10:ただ、それで言うと、unityのサポートが不要ならhumanoid使う必要無い気もするんだけど、人型モデルをgenericでまともに動かせたことがないので、なにか勘違いしてるのかもしれない