vrm-c / vrm-specification

vrm specification

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

正規化されたT-Poseモデルではなく、正規化されていないT-Poseモデルを格納する

ousttrue opened this issue · comments

ローカル回転を除去すると Constraint の実装が難しいので、回転の除去を断念しました。
改めて、関連する仕様を検討させてください。

  • スケール
  • ミラー
  • Mesh と Bone の不一致を Bind 行列で吸収している可能性の排除
    • 例えば blender 由来の Mesh が Z-UP で寝ているのを root 付近の X90度回転で引き起こしているモデル

は引き続き除去する。

#34

@0b5vr

互換モード
モーション受付用の Renderer の無い骨格を用意する。
Rendererのある骨格に回転を転送する。
import 時に正規化すると constraint が解決できない。

標準のボーン構成(推奨。義務では無い)も決めちゃう。
とりあえず、Blender の Rigify の humanoid。

muscle みたいに抽象化するとよいのだが、やり過ぎのような気もする。

UniVRM にある程度実装してから、知見に基づいて仕様にフィードバックします。

  • Export
  • Import
  • ComaptiblityMode

突然の変更に驚いておりますが、ローカル回転の保持については、今後あらゆる拡張や実装において間違いなく必要であると考えているので、強く賛同致します。

ここで、定義についていくつか段階を分けて、明確にしておきたいと考えております。

  • 定義は軸とその役割を対応付けたものにすべきか
  • 回転の役割の名称をどうすべきか
  • 回転の役割の対応付けの方法をどうすべきか
  • Rigify の定義をそのまま用いるべきか
  • 推奨値やバリデーションの実装をどう行うか
  • 手の推奨値をどうすべきか
  • 互換モードとは何を互換するものか

定義は軸とその役割を対応付けたものにすべきか

まず、「定義」と言える程度に、各モデル間での互換性を保証できる制約が必要です。

VRM 0 では全てのローカル軸の回転が Quat(0,0,0;1) という強い制約がありましたが、VRM 1 においては Unity Mecanim と似たように「軸とその回転の役割を対応付ける」という制約となるか確認がしたいです。

また、標準のボーン構成の例として Blender Rigify の画像が示されていますが、Rigify のスケルトンはそれ自体にスキニングを行うものではなく、既存のモデルの姿勢に合わせて編集されることが前提となっており、Rigify スケルトンのデフォルトの姿勢は A スタンスとも T スタンスとも言えない非常に曖昧なものです。各ボーンのローカル軸の参考としては有用ですが、Rigify スケルトンの姿勢を真似るべきではありません。

回転の役割の名称をどうすべきか

ここからは「軸とその回転の役割を対応付ける」という定義を前提とした話になりますが、回転の役割を定義に持ち込む以上は、回転の役割の名称を決める必要があります。

例えば、Unity Muscle においては Stretch, Twist, In-Out, Twist In-Out, Up-Down のように見た目をそのままパラメータの名称としているような形になっています。また、解剖学においては 屈曲, 進展, 外旋, 内旋, 外転, 内転, 回外, 回内 といった具合になっています。加えて、以前こちらの記事においては XYZ という3つの軸に合わせて 屈伸, 回転, 開閉 という3つの用語を定義するというような提案をさせていただきました。

ただし、何れにしても図解は必要になると考えます。また、以下の対応付けの方法との兼ね合いも考えてこの辺りは定義を行っていくのが良いと考えています。

回転の役割の対応付けの方法をどうすべきか

対応付けを行う方法としては大きく分けて2つになると思います。

ひとつは、FBX アニメーションにおけるオイラー回転順のようにメタデータとしてファイル内でモデルごとに回転の役割を指定する方法です。

{
    "LeftUpperArm": {
        "Twist": "+Y",
        "Up-Down": "-X",
        "In-Out": "-Z"
    },
    "RightUpperArm": {
        "Twist": "+Y",
        "Up-Down": "-X",
        "In-Out": "+Z"
    },
    "RightFoot": {
        "Stretch": "+X",
        "Twist In-Out": "+Y",
        "In-Out": "-Z"
    },
}

(スキーマとしての値は Enum を使うことになる)
(ここで正負の値が必要なのは、Muscle のスライダーのように各役割を指定して値を操作する場合に左右対称の操作を行うため)

例えばですが、このようなメタデータをモデルごとに持つことになり、値においてはモデルによって "Roll": "+Y" だったり "Roll": "-Y" だったりする事が考えられます。利点として、GLTF としては元のデータから軸を変更する必要がない事が考えられますが、結局アプリケーション上でモデルを利用する際にはこのデータを用いて任意の軸に統一する事が考えられるので、わざわざこれらのデータをメタデータとして持っておく必要があるかどうかは疑問です。ユースケースがあるとすれば VRM から元の編集データに戻したい場合はこの情報があるといいのかもしれませんが...。アニメーションの共有等を考える際にもこの辺りは規格として統一すべき所なのではないかと思います。

そこで、もうひとつは VRM 規格側で軸との役割の対応付けを完全に行ってしまうというものです。

この場合、メタデータには何も記録する必要はありませんが、ユーティリティとしての JSON データ(定義を示したもの)が必要になると考えられます。

また、前項で付言した「3つの軸に合わせて3つの役割を用語として定義する」を利用する場合、XYZ それぞれの軸が完全に1対1で対応付けられます。そのとき、一つの軸においては常に正となるような定義を行うことが出来るので、例として屈伸(Bend)における屈曲を行う回転を常に +X とした場合、回転(Roll)と開閉(Spread)の2つの正負の値のみ JSON で定義すれば良いことになります。

{
    "LeftUpperArm": {
        "Roll": -1,
        "Spread": -1
    },
    "RightUpperArm": {
        "Roll": -1,
        "Spread": 1
    },
    "RightFoot": {
        "Roll": 1,
        "Spread": -1
    },
}

(スキーマとしての値は Enum を使うことになる? 真偽値でも良さそうだが...とりあえず二値あればよい)

この時、例えばですが Roll の正回転は対称部分では体の外へ向かう回転、非対称部分では右回転、Spread の正回転は開く動き、のように統一できていると良いと思います。この場合は基本的に可動範囲が大きい側を正として考えましたが、この辺りは議論の余地があるように思います。

Rigify の定義をそのまま用いるべきか

「3つの軸に合わせて3つの役割を用語として定義する」場合、Rigify の軸と相容れない部分が出てきます。Rigify の定義をそのまま用いた場合に生じるメリットは、既存の Brender Rigify 利用モデルをエクスポートするための前準備が容易になるという1点だけです。

Rigify システムとは、まず特定のカスタムプロパティを持った MetaRig を配置し、その姿勢をモデルの形状に沿うように編集し、編集した MetaRig から、モデル形状に合った複雑で高機能な Rigify リグを新たに生成するものです。モデルのスキニングはこの生成された Rigify リグに対して行います。MetaRig には Rigify リグを生成すること以外の用途はありません。

参考: https://dskjal.com/blender/introduction-to-rigify-279.html

標準設定で生成された Rigify リグは、4本の 背骨(hip を含む)、2本の首ボーン、腕と脚の捩り補間用ボーンなどを含んでおり(MetaRig とはボーンの数が異なる)、仮にローカル軸の修正が不要になったからといって、どのみち無編集でエクスポートすることはできません。

一方、MetaRig の段階で適切な設定をしておけば、ボーン数やローカル軸をある程度任意に制御して Rigify リグを生成することができます。また、ローカル軸についてはすでに生成済みの Rigify リグを直接編集することでも変更が効きます。

後からでも対応が可能な Rigify の定義に縛られるよりは、一般的なデータとして扱いやすく、合理的な定義を行うべきだと思います。

推奨値やバリデーションの実装をどう行うか

推奨値はバリデーションや VRM 0 からの変換に必要となります。

ここで問題となるのは以下の2点です。

  • T ポーズとローカル回転のバリデーションを別で持つかどうか(#275
  • 初期ポーズから曲げ軸を決定するアルゴリズムを用いる IK に適した値とするかどうか

あくまでも推奨値なので強制は出来ませんが、バリデーションの実装を行う場合、2段階のバリデーションが必要になると考えています。例えばローカル回転においては、各ジョイントのローカル回転のリストを用意することでバリデーションが可能ですが、ローカル回転についてのみバリデーションを行った場合でも、そのモデルが T ポーズであるという事は保証されません。言うならば、これは4足歩行のモデルのローカル回転のみが推奨値にされた状態を示しています。

そこで実際にバリデーションを行う場合は、まず親子付けされた2つのジョイントの座標から内積などを用いてモデルが T ポーズであるかどうかというバリデーションを行う必要があります。この時、指のように 1 対 n となるジョイントにおいては平均を取るか、または T ポーズのバリデーションにおいては腕や脚などに対してのみ行う、という実装が考えられます。

またこの T ポーズバリデーションの際に、初期状態のローカル回転から曲げ方向を決定するアルゴリズムの IK のために肘や膝、フルトラのための太ももの位置のようなバリデーションを行うかどうかも検討する必要があると思います。肘や膝については少し曲げた状態で問題ないとしても、太ももの位置についてはモデルの見た目にも影響するので慎重に決める必要がありそうです。

そして T ポーズのバリデーションを行った上で、T ポーズエラーを無視するかどうかをユーザーに選択させてローカル回転のバリデーションを行うようにする必要があると思います。Unity においては T ポーズバリデーションの後は Mecanim に全てリターゲットされるのでローカル回転のバリデーションは必要ないのですが、汎用フォーマットとする以上 VRM においてはこのバリデーションが必要となります。この辺りはアニメーション等に関わってくるので、後ほど追加で説明します。

手の推奨値をどうすべきか

手について、モデリングの際には基本的に少し角度を付けてモデリングを行いますが、問題としては手には T ポーズ相当の定義が未だ存在していないことがあります。

ここで危惧すべきは、少し指を広げた状態がデフォルトのキャラクターで生成した指を閉じるアニメーションを、指をぴったりくっつけた状態がデフォルトであるキャラクターに適用すると、物理的にありえない指の交差状態が生まれることです。

そもそも T ポーズの意義を考えるに、手を除いた関節がいずれかの軸に最も近くなり、またいくつかの関節においては可動範囲の限界、あるいはほぼ中間に該当するという事が根本にあると思っています。して A スタンスの存在意義としては、T ポーズでのモデリング時に破綻が起こりやすい肩の部分が伸び切り自然な状態となることでリギングや UV 展開が容易なので、A スタンスでモデリングを行い、出力は T ポーズで行うという話を聞くことがあります。

なので指については、モデリングは少し開いた状態で行うとしても、出力時にはぴったりとくっつけた状態を T ポーズ相当のものとして定義するのも一つの手段ではないかと考えています。近い定義としては VRC の指に関してはすべての指をぴったりくっつけ、親指も内向きにして軸が90度にスナップするような状態を推奨しています。

気になっているのが、以前より @pixiv-sigepon 氏が付言している自然な拳を作るための必要条件です。指をぴったりくっつけた状態にしたとして、ロール値の許容範囲を広めに取ればその辺りが許容できる程度であるかどうか、実際のユースケースと照らし合わせる必要があるように思います。

一番の理想としては軸を合わせた上で、「グー」や「パー」などのいくつかのポージングを実際にモデラーに作ってもらうことで各関節の可動範囲の上限と下限をモデルごとにキャリブレーションして貰うことだと考えており、これは指以外にも適用できる技術なのですが、いかんせんモデラーへの負担やプラグインへの実装コストが高いように思えます。余談ですが Unity Mecanim が生成するバイクポーズは可動範囲の上限と下限のほぼ中間(実際はスライダーの正負により少し偏りがある)を意味したポーズとなっているようです。

この辺りの妥協点としてはローカル回転の調整時に、テストとしてサンプルポーズをプラグイン上などで簡単に適用できる形であれば理想的なのではないかと考えています。恐らく、次項で付言するようにアニメーションデータの定義も行う必要があると思われるので...。

互換モードとは何を互換するものか

互換モードについては意味する所がよく理解できていないのですが、VRM の為のアニメーションデータを定義するということでしょうか? GLTF としてアニメーションを作成する際には確かに元のノードが必要なのでローカル回転を破棄したスケルトンと一緒にアニメーションデータを出力するという事は理解できなくもありませんが...。

そもそも VRM 0 の時点でアニメーションデータは存在しないので互換性を気にする必要はなく、また GLTF としてアニメーションをそのまま出力してしまうと HumanBone のリネーム等も必要になり、処理やデータ構造が複雑になってしまうように思います。

#118 にアニメーションデータの定義の Issue があるので、この際アニメーションデータを簡潔な JSON として定義してしまった方が良いように思います。その際にローカル回転を使用する、使用しないというオプションがアニメーションデータの中にあると良いように思われます。これは腰にはグローバル回転を用いたいが、指にはローカル回転を用いたいというようなケースが存在するからです。


これらのそれなりにボリュームのある項目についての検討が必要なので、VRM 0 ではローカル軸の回転を破棄していたこともあるかと思いますが、この変更により開発環境に囚われない更なる汎用性を取得する事を切に願っております。

commented

私も賛同しますが、上記でトカゲさんが述べるような、より慎重な方針の決定が期待されていると思います。

本議題については私自身もあまり知見がなく、本当に申し訳ないです……

おそらく、詳細に各ボーンの回転方向などを定義する仕様については、VRMコンソーシアムとして本議題を詳細に検討する能力が出来た暁に、 VRMC_vrm とは別に拡張仕様を設けて今後推進していく方針となると思うのですが、いかがでしょう。

commented

本議題とはtangentialですが、指の向きに代表されるような、Tポーズに関しての詳細な規定はいずれにせよ仕様として持つべきと思います。
おそらく、VRM標準のサンプルモデルを我々で用意するという形でクリアされると思います。

とりあえず、各ノードに tailNode と mainAxis(xyz+-の6種類しか選べない) というパラメータを追加する方向で実験しています(vrm0 のモデルはマイグレート時に T-Pose であることを利用して決め打ちして付与することができる)。

これはいくら回転を残すといっても完全な無秩序にしては
扱いづらくなるからで、
ローカル軸に最低限の制約を加えることを意図しています。

  • ロール軸がボーンの head と tail を結ぶものである
  • 主軸がローカル軸のいずれかで、ロール軸と直交しなければならない

です。

blender を基準に考えればロール軸は Y になるんだけど、
他の DCC 使っている人は違うので、
軸の入れ替えは許容しようとしています。

実力不足で実装に失敗するかもしれません。
(Exporter と Importer の負担が増大するし、移植するのも重くなる)
ちょっと作ってみないとわからん、という感じです。

@ousttrue
ロール軸のズレを防ぐために Blender の Head-Tail 形式を参考にするのは一つの手段としてありだと考えますが、ロール軸のズレを許さないという実装は各アプリケーションによるバリデーションがかなり厳しくなりそうなのが少し心配な所ではあります(親子が 1 対 n のノードの問題もあるので)。

ある程度の自動整列は可能だと思いますが、以前の VRM 0 の Force T-pose と似た問題で初心者に対して暗黙的な変更が加えられる可能性がありそうです――ロール軸を親から子へ必ず向ける、という知識がある方に対しては問題ないと考えます。初心者向けに自動でロール軸を揃えることは可能ですが、他の2軸が暗黙的に変更されて混乱を招くのではないかと考えられます。この辺りはガイドラインや UI/UX によりクリアできる問題かもしれません。

また、ノードにローカル回転を保持するのであれば、Tail の必要性に疑問を感じます。

Head-Tail 形式は2座標とロール値からローカル回転を決定するものなので、ローカル回転と Tail が同時に存在するのは役割が重複しているように思えます。この時、親子が 1 対 n のノードに対しても Head-Tail 形式上でロール軸を揃えたとして、Node 形式に戻した時にはただの n 個の子を持つ親ジョイントのローカル回転になるのであまり意味があるようには思えません。

ロール軸のズレを防ぐという目的であれば、単純にロール軸が親から子へ向かっているかのバリデーションをアドオン上などで行うだけで良いと考えますが、いかがでしょう。

余談:
参考までに...というと押し付けがましく聞こえるかもしれませんが、以前、ローカル軸を保持できる VRM を生成するための Blender アドオンを個人的に試作しておりました(最近着手できておりませんが)。その中では、ローカル回転を揃えるための補助を行うメソッドをいくつか実装しようかと考えていました。

スクリーンショット 2021-11-08 21 59 53

正規化をしない方向に倒す方針について
ご指摘のとおりさまざまな課題があるのですが、事前に全ての課題を潰しきるというのは難しいだろうということもあり、進め方についてあらためて技術委員会で議論をしています。方針がまとまるまで再度このissueをopenとさせていただきます。

commented

技術委員会内で検討を行い、一旦VRM1.0-betaとして一切の正規化の制約を外した仕様を公開する方針となりました。
この正規化の取りやめは、回転だけでなく、ミラーを含むスケールについても対象となります。
これまでVRMのノードに課されていた一切のTransformに対する制約が外れる形となります。
「モデルがT-Poseとなるようにする」という制約は引き続き残ります。

経緯としましては、コミュニティから回転の正規化を外す要望が多い点を加味しての判断です。

正規化の取りやめに伴い、Humanoidに対して追加の情報が要求されるであろうという意見も承知ですが、
これをVRM1.0向けに完璧なHumanoidの仕様として公開するにはあまりにも時間が掛かると判断しており、
今後に長い時間をかけて仕様を試行錯誤する必要があると考えております。

スケールに関しても制約を外す点につきましては、以下の2点の理由があります:

  • すべてのTransformの制約を外すことによって、各アプリケーションでどのような問題が発生するかを調査したいため
  • UniVRMを含む各エクスポーターから正規化に関する責務を外すため
    • 副次作用として、UniVRMにおいてモデルをセットアップする際に2度のエクスポートをする必要も無くなります

インポーターの実装としては、これまでのVRMとの後方互換性を実現するため、
ランタイムインポート時に正規化を適用する機能が要求されると想定しています。
また、ランタイムインポート時の正規化を行うか否かは、アプリケーション開発者が使用用途に応じて自由に指定できることが求められると思います。

また、今後正規化の存在しないVRMを運用していく上で障害が大きすぎる場合に、
インポーター・エクスポーターのいずれかで正規化を行うことがConventionとして残る可能性はあるとは思いますので、
インポーター・エクスポーター共に、VRMの実装は正規化を行うことができる機能を提供することが推奨されます。

繰り返しになりますが、VRM1.0-betaに対して行われる変更です。
このあと、VRM1.0-betaの仕様に対してPull Requestが行われ、それがマージされた時点で効力を発揮します。
しばらくbetaで運用をいたしますので、その間に各実装・アプリケーションでどのような問題が発生したか、フィードバックをお願いします。
それらのフィードバックを加味した上で、VRM1.0の正式版としてリリースするに障害となると判断した項目について修正を行った上でVRM1.0をリリースする予定です。