Mayaによる補助骨作成の基本

QualiArts Advent Calendar 2023 の12日目の記事になります。

# はじめに
3Dゲーム開発において、キャラクタの3Dモデルを魅力的に表現することは重要です。
しかしリアルタイム3DCGの特性上、特定のポーズで不自然な見た目になってしまう瞬間があります。

これを軽減するために、本記事では手首と肘を例に補助骨の作成を行います。

本記事はテクニカルアーティストやリガーを対象とした初学者向けの内容となっています。
作例もシステマチックな内容になっており、見た目の良さを追求したものでは無い点をご了承ください。

# 補助骨とは
本記事において補助骨とは、基本人体に連動して動く骨を指します。
基本人体は、モーションアーティストによって動きを制御されますが、補助骨はシステムによって自動計算されるため、モーションアーティストは直接制御しません。

連動方法を工夫することで様々な部位に補助骨を作成する事ができます。

# 手首の補助骨例
簡単な補助骨の例として手首を紹介します。
人間の手首の捻り回転は回内・回外運動と呼ばれ、橈骨と尺骨が動くことによって実現されます。
可動域は-90度から90度だと言われています。

これをゲームで再現する場合、手首の骨がRoll回転する動きになります。
簡単な構造では手首を大きく捻った際に体積を失い、細くなってしまいます。
これはリニアスキニングの性質により体積が減少してしまうことが原因です。

これを軽減するために、手首を肘の間に補助骨を1つ追加します。
手首のRoll回転に50%連動して、補助骨動かします。
サンプルモデルを作成し、補助骨なしは青、補助骨ありは赤色で示しました。

| TopView | SideView | PerseView | 
| --- | --- | --- | 
|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/0c1e4642-54ef-9b2f-ac2b-b0d0d1b29aac.gif" width="512" alt="手首の補助骨 上から見た比較動画">| <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/fe2f39c8-12d8-b573-33cf-64f14dcb18cb.gif" width="512" alt="手首の補助骨 横から見た比較動画">|<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/56a81ed1-2db7-bd5e-e7c3-e86030927536.gif" width="512" alt="手首の補助骨 斜めから見た比較動画">|

手首を大きく捻った際の体積のボリューム現象を緩和することができます。
わかりやすいように重ねて表示してみます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/6bde7139-e3a5-8384-40df-0902781490ef.gif" width="512" alt="手首の補助骨 横から重ねて見た比較動画">

この補助骨の作成方法は以下の手順になります。
1. RightForeArmの子供の階層に、RightHand_Hを作成
   1. RightHandよりも、少し根本の位置にRightHand_Hを作成します。
   2. 軸の方向などは、RightHandと同じします。
   3. 回転値が(0, 0, 0)状態であることを確認してください。
   4. アウトライナで確認すると以下の状態です。<br> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/afe3ffa3-218e-d58d-b09f-8c0b231f607d.png" width="512" alt="手首の補助骨 ジョイント構造の画像">
2. Maya標準のUtilityNodeを用いて回転の連動計算を作成
   1. eulerToQuatノード(RightHand_H_eulerToQuat)を作成し、RightHandの回転と接続。
   2. quatToEulerノード(RightHand_H_quatToEuler)を作成し、eulerToQuatノードを接続。回転順序をxyzとする。<br> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/5d8ce486-61fd-3f3b-f310-9b52fa04bc15.png" width="512" alt="手首の補助骨 quatToEulerノードの設定画像">
   3. multiplyDevide(RightHand_H_multiplyDevide)ノードを作成し、quatToEulerノードを接続。ウェイトを(0.5, 0, 0)とする。
   4. RightHand_Hの回転を、multiplyDevideノードと接続。
   5. ノードエディタで確認すると以下の状態です。<br> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/f3184da3-6272-c0bd-a735-13b4b365f67d.png" width="512" alt="手首の補助骨 ノード接続の説明画像">


もし骨数に余裕があるのであれば、補助骨を更に増やすことでより良い結果を得ることが出来ます。
また手首以外の部位に関しても、1軸回転に連動する部位であればこのやり方を使用する事ができます。
ただしウェイトやquatToEulerの回転順序は、部位や軸の方向によって適宜変更する必要があるので、ご自身のモデル試してみてください。

# 肘の補助骨例
続いて肘の補助骨を紹介します。
肘は尺骨と上腕骨の形状から、肘を曲げた際に外側に出っ張りが出来ます。

しかしゲームで再現する場合、丸まった形状になってしまします。
これを回避するために様々な工夫が考案されていますが、モバイルゲーム開発で使用頻度の高いUnityではアプローチに制限がある場合もあります。
そこで補助骨を用いて丸まりの緩和を行ってみます。

またこの記事では補助骨の有無での違いをわかりやすくするために簡素なモデルをサンプルとしています。
実際には様々なポリゴン割、ウェイト付け、骨位置の工夫からもアプローチされることに注意してください。

まずは上腕と同じ位置に補助骨を1つ追加します。
上腕の曲げに50%連動して補助骨を回転させます、これにより肘の外側部分だけ曲げの影響を受けづらくして丸まりにくくなります。
さらに上腕の曲げに連動して、外側方向に移動するように設定します。これにより出っ張りを強調します。

サンプルモデルを作成し、補助骨なしは青、補助骨ありは赤色で示しました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/5f10e614-0938-3e8b-2195-ee862afbb581.gif" width="512" alt="肘の補助骨 横から見た比較動画">

この補助骨の作成方法は以下の手順になります。

1. RightForeArmの子供の階層に、RightForeArm_Hを作成します。
   1. 軸の方向などは、RightHandと同じします。
   2. 骨の長さは0とします。
   3. 回転値が(0, 0, 0)で、移動値も(0, 0, 0)であることを確認してください。
   4. アウトライナで確認すると以下の状態です。<br> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/c0218286-29b0-669a-99b7-f33ff89e8cf4.png" width="512" alt="肘の補助骨 ジョイント構造の画像">
2. Maya標準のUtilityNodeを用いて回転の連動計算を作成
   1. eulerToQuatノード(RightForeArm_H_eulerToQuat)を作成し、RightForeArmの回転と接続
   2. quatToEulerノード(RightForeArm_H_quatToEuler)を作成し、eulerToQuatノードを接続。回転順序をxyzとする。
   3. multiplyDevide(RightForeArm_H_rotate_multiplyDevide)ノードを作成し、quatToEulerノードを接続。ウェイトを(0.5, 0, 0)とする。
   4. RightForeArm_Hの回転を、multiplyDevideノードと接続。
3. Maya標準のUtilityNodeを用いて移動の連動計算を作成
   1. multiplyDevide(RightForeArm_H_translate_multiplyDevide)ノードを作成し、quatToEulerノードを接続。ウェイトを(0, 0.0005, 0)とする。
   2. RightForeArm_Hの移動を、multiplyDevideノードと接続。
   3. ノードエディタで確認すると以下の状態です。<br> <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/878081/6372f56f-cf4b-906c-97b4-246b9969287e.png" width="512" alt="肘の補助骨 ノード接続の説明画像">

肘を曲げると、肘の外側のポリゴンが突き出すように移動する補助骨にしてみました。
肘の曲げ回転(ry)を、移動(ty)に変換しているわけです。
回転に関する補助骨も、移動に関する補助骨も同じ手順で実現する事ができます。
移動に関しては単位変換ノードのunityConversionが自動で作成されますが、本記事ではわかりやすくするために非表示状態としています。

# まとめ
本記事では手首と肘の補助骨作成を行い、モデルを動かした際のボリューム減少や不自然な丸まりを軽減しました。
作成手順はほぼ同じにしましたので、1軸回転に連動する補助骨としては汎用的な仕組みになっていると思います。

今回の方法以外にもMaya標準のコンストレイントや、ドリブンキーを使用する場合もありますので、ご自身のモデルやプロジェクトにあった方法を選択してもらうのが良いと思います。

また、1軸回転でない部位に関しては、軸ごとの角度の分解/合成が必要になります。
オイラー角やQuatarnion、回転順序など、数学的な話になってしまうので、それはまた別の機会に共有したいと思います。

さらに最終的にはUnityなどのゲームエンジンとの連携も考慮する必要があります、これもまた別の機会とさせてください。

本記事が、3Dゲーム開発の助けになっていれば幸いです。