VRM 1.0 で決められた『親指の定義』とまたリターゲットの話

※ この記事は VRM 1.0 における変更点をざっと見た感想を述べるものであって、VRM 0.x 対応アプリを VRM 1.0 に対応させるための手順などを詳しくまとめるようなものではないことをあらかじめ断っておきます

## VRM 1.0 所感

### より glTF 拡張フォーマットとして正しくなった VRM 1.0

VRM 1.0 のリリースおめでとうございます。このリリースにおいて、個人的に大きな変更であると感じているのは以下の３つです。

1. アセットが +Z 軸向きに格納されるようになった
2. モデル内部の座標パラメータが OpenGL 座標系で統一された
3. ボーンのローカル軸を保持するようになった

これらは glTF 拡張フォーマットを名乗る以上は VRM 1.0 で絶対に直して欲しかった所だったので、それらが修正された今、VRM はようやくアバターの統一規格としての第一歩を踏み出せたのではないでしょうか。（僕は本当にこれがスタート地点だと思っています）

### SNS での反応

ところで VRM 1.0 についての反応を SNS ですこしリサーチしてみた所、あまり良くない反応というかボヤきをちょっと見かけたので、VRM コンソーシアムとは全く関係なく僕が個人的に回答をしてみようと思います。

#### SpringBone がボーン個別にパラメータを設定するようになったので面倒

これは完全に GUI の問題であって、VRM 1.0 のフォーマットそのものが悪いわけでは決してありません。例えば、Unity 側で開始ボーンと終了ボーンをセットしてカーブを描く事でそれらのボーンに値を設定するような GUI を UniVRM もしくは何らかの VRM 作成アプリで実装すれば解決できる問題だと考えます。

#### VRM 0.x の IK 設定をそのまま持ってくると壊れる

[前回の記事](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250)で書いた通り、[IK における回転軸の決定方法によってはその挙動が壊れる可能性がある](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250#ik%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%80%83%E5%AF%9F)と思います。VRM 0.x で正規化された軸を決め打ちで指定している場合、Unity においてはその参照を [UniVRM の ControlRig](https://vrm-c.github.io/UniVRM/ja/vrm1/vrm1_controlrig.html) に置き換えることで互換性を保つことが出来るのではないかと予想します。（未検証）

## 本題

### VRM 1.0 の親指の向きの定義

※ ここからはボーンやリターゲットについての用語を交えて話をします。可能であれば[前々回の記事](https://qiita.com/TokageItLab/items/e5880123a9f508b2769d)や[前回の記事](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250)をざっと流し読む事をオススメします

さて、VRM 1.0 で T-pose についてのガイドラインも更新されました。概ね問題ないと見ていますが、その中で少し引っかかる点がありました。

```md
#### 定義 1.8. 見た目上、親指は X 軸と +Z 軸の中間の方向に伸びていて、地面と平行であると定義します。
見た目上、親指は地面と平行に、X 軸と +Z 軸の中間 45 度の方向に真っすぐ伸びていると定義します。

また爪の面は他の 4 本の指とは違い 90 度外側にロールした方向を向きます。
```

なるほど、**親指の向きについての定義**ができたようです。ただし、あくまでこれは<ins>メッシュの形状についての定義</ins>であり、<ins>関節ルールすなわちローカル軸の向きの定義ではない</ins>事に注意が必要です。

とりあえずメッシュについては、<ins>親指が伸びる方向は他の指と同じく水平で、人差し指に腹を完全に向ける状態</ins>が VRM 1.0 の親指としては正しいという事になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/7552c744-2421-e4b4-455d-9003196ebde9.png)

この画像の指はロボットハンドなので問題なく見えますが、一般的な人型モデルの場合、**最初からこの状態で指をモデリングする事はまずありえない**と思います。とはいえ、数値・目安としては分かりやすいのでこれが悪い定義だとは言い切れません。

この定義が意味する所は、**指をモデリングする時は好きなように作ってもよいが、VRM としてエクスポートする時だけこのような向きでエクスポートする必要がある**という事です。

Blender で例えるなら、指を作成した後に Edit モードではなく Pose モードで指を回転させた状態で Armature モディファイアを適用し、『Apply Pose as Rest Pose』を行なうという操作がエクスポート前だけ必要になります。

当然、この操作を行った時点で**親指の根本周辺のメッシュが乱れますが、VRM 1.0 としては問題ありません**。後々ボーンを本来の角度にアニメーションさせればメッシュも元通りになるからです。したがって、<ins>この操作が原因で発生したメッシュの乱れを手直しする必要は無く、むしろ避けるべきです</ins>。

<font color="red">**この親指についての定義があまり周知されていないように見受けられ、定義に沿っていない VRM 1.0 モデルが大量に出回ってしまうのではないか**</font>という事について、僕はかなり危険視しています。

定義に沿っていない VRM 1.0 モデルが出回ってしまったとして、どのぐらい問題になるのか、何か対策できることはないか、そもそも指の向きに定義が必要なのか、色々と考察してみましょう。

:::note info
**補足１：エクスポート時のメッシュの乱れについて**

[@mimyquality](https://twitter.com/mimyquality/status/1672026696403525632) さんから、「90 度ロールした親指のメッシュは乱れていない方がよい」との意見がありました。調べてみた所、<ins>Unity の Humanoid Muscle には指のロールをアニメーションさせる機能がない</ins>そうで、Humanoid Muscle を利用する環境では『メッシュの乱れをアニメーションの中で再度ロールして戻す』ことが容易ではないようです。

参考記事：[Unity Humanoid制御に指ロールを追加したい](https://qiita.com/mkt_/items/d6dc4ff3846d39b1522d)

とはいえ、Unity Humanoid Muscle もレガシーなシステムになってきていると感じます。

少なくとも glTF には指ロールのアニメーションを持たせる事が可能であり、また VRM 1.0 は Unity への依存性を排除した独立した glTF の拡張フォーマットとするのであれば、下位環境に合わせて指ロールを切り捨て、アバターとしての表現力を落とす事は避けるべきでしょう。

<ins>Unity Humanoid Muscle での利用のみを前提とするなら</ins>、メッシュの乱れはエクスポート時に解決しておいた方がよいかもしれませんが、VRM 1.0 が Unity 外にも普及する将来を見据えるのであれば、指ロールをアニメーションさせる事を前提としたセットアップをすべきだと考えています。

:::

:::note info
**補足２：VRM 1.0 のアニメーションについて**

VRM 1.0 は現在（2023年6月） アニメーションの為のフォーマットを整備しており、その中で指ロールをよしなに扱えるようにして欲しいとの Issue を立てたところ、現時点においては Humanoid の HumanPose を介さない形でアニメーションを適用する事を想定しているという旨の[回答](https://github.com/vrm-c/UniVRM/issues/2095)を頂きました。つまる所、glTF に記録されている Transform 値がそのまま VRM 1.0 のボーンに適用されるため、指ロールのアニメーションも正しく再生されるとの事です。

この事からも、やはり <ins>VRM 1.0 としての利用を前提とするなら</ins>、親指がアニメーションにより自然なロール値に戻る事を前提としてセットアップする（エクスポート時のメッシュの乱れはわざと解消しないでおく）方が良いであろうと現段階では結論づけられます。

:::

### そもそも指の向きに定義が必要なのか

ここで基本に立ち返って、なぜモデルが T-pose のような形でシルエットが統一されていると便利なのか考えてみましょう。[前回の記事](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250)で、glTF におけるボーンアニメーションの適用方法には [AbsoluteAnimation](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250#absoluteanimation)・[LocalAnimation](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250#localanimation%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%AA%E3%82%BF%E3%83%BC%E3%82%B2%E3%83%83%E3%83%88)・[GlobalAnimation](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250#globalanimation%E3%82%B0%E3%83%AD%E3%83%BC%E3%83%90%E3%83%AB%E3%83%AA%E3%82%BF%E3%83%BC%E3%82%B2%E3%83%83%E3%83%88) の３通り考えられる事について説明しました。

Blender のアニメーションシステムは LocalAnimation を採用しており、また VRM 0.x の正規化は GlobalAnimation の適用を静的に行うような物です。そして、**LocalAnimation と GlobalAnimation は初期ポーズからの相対的なアニメーションを行う**ので、それらでは初期ポーズのシルエットを一致させる必要があるのです。

それを踏まえると、<ins>一般的なアプリケーションとの互換性や VRM 0.x の正規化を考慮する場合、初期ポーズのシルエットが一致している方が望ましい</ins>という事が言えます。

なぜ A-pose ではなく T-pose を利用すべきなのかというかという話については今はあまり関係ないので、余談として最後に書いておきます。

### 指については AbsoluteAnimation が使えるのではないか

ここで AbsoluteAnimation についてもう一度考え直します。**AbsoluteAnimation は[レスト成分](https://qiita.com/TokageItLab/items/e5880123a9f508b2769d#%E3%83%AC%E3%82%B9%E3%83%88%E3%81%A8%E3%83%9D%E3%83%BC%E3%82%BA)を含む**と[以前説明しました](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250#absoluteanimation)。

レスト成分とはすなわちモデルの初期ポーズであり、つまり AbsoluteAnimation はモデルの初期ポーズを考慮せずにアニメーションを行ってしまいます。なので、そのままアニメーションを共有したのではモデルの体型を大きく変化させてしまいます。

このため、[前回の記事](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250)では AbsoluteAnimation でアニメーションを共有する為には[初期ポーズのローカル軸の完全一致を必要としている](https://qiita.com/TokageItLab/items/beaf219762e1e34a6250#absoluteanimation)と説明しました。

ここで、『初期ポーズを考慮せずにアニメーションを行う』という点に注目します。

以前、[関節ルール](https://qiita.com/TokageItLab/items/e5880123a9f508b2769d#%E5%90%84%E5%9B%9E%E8%BB%A2%E8%BB%B8%E3%81%AE%E5%BD%B9%E5%89%B2%E3%82%92%E7%B5%B1%E4%B8%80%E3%81%97%E3%81%9F%E5%AE%9A%E7%BE%A9%E3%82%92%E8%A1%8C%E3%81%86)の統一についても触れました。そこで**関節ルールが統一されている場合、LocalAnimation では関節の向きを尊重したアニメーションの共有が可能**になると説明しました。

ここからが重要なのですが、**関節ルールが統一されている場合、AbsoluteAnimation ではモデルの初期ポーズに関係なくアニメーションの共有が可能**になります。つまり、<ins>A-pose のモデルで作成したアニメーションと T-pose のモデルで作成したアニメーションを共有できます</ins>。

これだけだと便利に聞こえるかもしれませんが、例えば I 字背骨のモデルと S 字背骨のモデルでアニメーションを共有すると、初期ポーズすなわち背骨の形を無視するために、体幹の歪みが発生します。

![bone-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/dbc93358-5d6a-f4e8-9e22-1e3732beb063.png)

また、位置アニメーションの共有はできません。これは初期ポーズの『骨の長さ』を変化させてしまうため、チビキャラを無理やり８等身に引き伸ばすような変形が発生してしまうからです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/00e90629-875f-ed09-0cd3-ea91f44e181e.png)
引用：https://github.com/TokageItLab/realtime_retarget
low-poly-godette / CC by [SirRichard94](https://github.com/SirRichard94/low-poly-godette)

背骨はモデルのスタイルに影響するために初期ポーズが重要になります。

では指についてはどうでしょうか？ モデルによって I 字だったり S 字だったりする事はあるでしょうか？ 位置アニメーションは必要でしょうか？

ほとんどの場合、指についてはモデルに依存したスタイルの違いを気にする必要はないと考えます。

つまり、**関節ルールを統一して AbsoluteAnimation でのアニメーションの共有を行うという前提においては、指については明確な T-pose の定義は必要ない**という結論になります。

![tt.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/ed2533a4-678d-4262-c077-5d33e62808e2.gif)
引用：https://github.com/TokageItLab/realtime_retarget

結果として、**よりよいリターゲットのためには、関節ルールを決めた上で、背骨から肩にかけては GlobalAnimation、腕や脚には LocalAnimation、指には AbsoluteAnimation というような適用方法が望ましい**のではないかと考えられます。

### とはいえ...

残念ながら VRM 1.0 には関節ルールの定義は盛り込まれませんでした。

また実際には VRM モデルは、先に述べたようなリターゲットアルゴリズムを選択できる環境で使われるとは限りません。Unity のように出力時の指の形ができるだけ揃っていたほうがよい環境も存在します。

VRM 1.0 では親指の向きが定義されましたが、その周知についてまだまだ不十分です。【親指は地面と平行に、45 度程度開き、かつ爪は 90 度外側に向いている】という状態は、ごく一部の環境では比較的有効である反面、人体において自然な姿勢とは言えません。はじめからこのような形でモデリングしているモデラーはほぼいないでしょう。

一応、最近のアップデートで UniVRM の方には設定すべき親指の爪の向きを示すガイドが実装されてはいますが、その周知はまだまだ足りていないように感じます。

![197494380-a1076303-6fcd-4e6e-b801-e37b5ad9cfbf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/a2a25de1-bebd-56e6-6277-ddcbd3961a0f.png)

引用：https://github.com/vrm-c/UniVRM/pull/1885

アニメーションを破綻なく共有するために、**制定した親指の定義について、モデル出力時のワークフローまで含めてしっかりと説明していく必要があります**。

もちろん**関節ルール、つまりボーンのローカル軸についても定義していくべき**ということも、引き続き提唱していきます。

ちなみに [Godot に実装したリターゲット用の Humanoid](https://docs.godotengine.org/en/latest/tutorials/assets_pipeline/retargeting_3d_skeletons.html#rest-fixer) には以前僕が[提唱した関節ルール](https://qiita.com/TokageItLab/items/e5880123a9f508b2769d#%E5%90%84%E5%9B%9E%E8%BB%A2%E8%BB%B8%E3%81%AE%E5%BD%B9%E5%89%B2%E3%82%92%E7%B5%B1%E4%B8%80%E3%81%97%E3%81%9F%E5%AE%9A%E7%BE%A9%E3%82%92%E8%A1%8C%E3%81%86)を採用しました。

## 余談１ T-pose は A-pose よりも適切な定義か

最近 Godot に色々実装している中で、ブレンドアニメーションのような回転の合成においては初期ポーズから 180 度以上回転しないというような計算を行うことにより、ポーズ同士を補間する際にボーンが体を貫通するのを防ぐことが出来るという事を覚えました。

![blending5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/2361f894-ddfa-25a5-6b3c-9aef8dbf9637.gif)
引用：https://docs.godotengine.org/en/latest/tutorials/animation/animation_tree.html#for-better-blending

つまり、モデルの初期ポーズはできるだけ関節の可動域の中間であるとプログラム上も助かるという訳で、T-pose や A-pose はその需要を満たしてくれます。

ただ、実際に関節の可動域の中間を考えると、Unity Humanoid が内部に持つようなバイクポーズの方がより適切かもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/a1f5bd97-4d4a-9598-9066-588108210069.png)
引用：https://docs.unity3d.com/Manual/MuscleDefinitions.html

しかし、T-pose はボーンに設定すべきローカル軸の多くがワールド XYZ 軸とほぼ直交しており、A-pose やバイクポーズよりも目標とするポーズが分かりやすいので、<ins>プログラマーとデザイナーの両側から見てちょうどよい塩梅の初期ポーズ</ins>であり、これが自然とスタンダードなポーズになったのではないかと僕は推測しています。

## 余談２ VRM 1.0 でも UV の向きはまだ統一されていない？

Unity（DirectX 系）と OpenGL 系では UV 座標の V 軸の向きが異なります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/8c3771a6-dfbb-00f3-f227-f68b938571b6.png)
引用：https://www.charlezz.com/?p=1007

~~故に MToon の V スクロール値が正の時に視覚的にどちらの方向にすすむか定義した上で、UniVRM からの VRM 出力においては 3D 座標パラメータのように MToon の V スクロール値も正負を反転して VRM 内に格納すべきなように思いますが、どうなのでしょうか......。ただ、この辺りは他の Khronos のシェーダ拡張がどのようになっているのかとかも調査してすり合わせていく必要がありそうで、今後も経過を見守る必要がありそうです。~~

一応 U スクロール値が正の時に左向き正の時に左向きに V スクロール値が正の時に上向きに進むように定義されているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/936d549d-85f0-f8d7-5251-a66516457f7f.png)

引用：https://github.com/vrm-c/vrm-specification/tree/master/samples/VRMC_materials_mtoon_UV_Animation_Test

OpenGL はメモリ内部でテクスチャの上下が逆転するため結果としてテクスチャから見た UV は U-Right V-Down となり、スクロールによりそれと逆方向にテクスチャが移動するという事であれば一貫性はとれているようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1196620/3797248e-6625-d88c-28cd-378423d985fb.png)

引用：https://www.puredevsoftware.com/blog/2018/03/17/texture-coordinates-d3d-vs-opengl/

ただ、これらの実装については [KHR_texture_transform](https://github.com/KhronosGroup/glTF/blob/main/extensions/2.0/Khronos/KHR_texture_transform/README.md) に従うものらしく、そもそもの Khronos 側でまだハッキリと決まっていない部分でもあるようなので経過観察が必要だと思われます。

- [KHR_texture_transform has the wrong rotation matrix #1563](https://github.com/KhronosGroup/glTF/issues/1563)
- [Clarify descriptions of rotation direction in KHR_texture_transform #1624](https://github.com/KhronosGroup/glTF/pull/1624)