UniVRM
UniVRM copied to clipboard
Update When Offscreenが無効なため、アバターの部位が消えることがある => rootBone を推定して設定するオプションを追加する
環境情報
- UniVRM version:
0.89.0
- Unity version:
Unity-2019.4
- OS:
Windows 10
不具合の内容
VRMアバターのSkinned Mesh RendererのUpdate When Offscreenが無効の状態でインポートされるため、VRoidの顔の部位などが消えることがある。
不具合の再現方法
Animatorでアニメーションさせた状態で部位分けされているVRMをカメラで撮影する。
スクリーンショット
Unityのドキュメントに対策として
https://docs.unity3d.com/ja/2020.3/Manual/class-SkinnedMeshRenderer.html このような場合は、以下のいずれかの解決策を試して問題を解決できます。 ...
- Bounds (境界) を修正してメッシュの可能なバウンディングボリュームに適合するように修正します。 ...
- Update When Offscreen をスキンに対して有効にし、常にスキンメッシュをレンダリングします。
とあります。Update When Offscreenはパフォーマンスが下がるのでBoundsを調整して対応したいことが多いと思うのですが、VRMはBounds情報を持たないため自動計算するしか無く、その場合SkinnedMeshRendererが分離している場合Boundsが独立して計算されるため、このような「カメラにモデルを近づけると一部のパーツだけが消える」という事象が発生すると理解しています。(認識が誤っていたら申し訳ないです)
VRMロード時に「個別に消えると表示がおかしくなるSkinnedMeshRendererのグループのバウンディングボックスの範囲を同じにする」という処理ができれば良いと思うのですが「個別に消えると表示がおかしくなるSkinnedMeshRendererのグループ」はどうやって検出するのか問題など議論の余地はたくさんありますね・・・。
本事象では髪が消えて胴体が残っています。Unityはバウンディングボックス計算にアニメーション情報を用いるらしいので、Humanoidアニメーションが有効な胴体のメッシュのバウンディングボックスが大きく計算され、アニメーションの無い髪は小さく計算されていて、その大きさの違いによりカメラを近づけると髪が先に消えるけど顔と胴体は残る、という事象に思えます。
とりあえず、すべての SkinnedMeshRenderer の Update When Offscreen を有効にするのが回避策で まとめて設定する関数があります。
- https://vrm-c.github.io/UniVRM/ja/api/0_77_runtime_import.html?highlight=offscreen などを参考にしてください(UniVRMのバージョンにより関数の生えている場所が変わっている)
問題は、SkinnedMeshRenderer
を設定せずに適切にカリングされるようにすることで
メッシュの構成によっては起こります。
下のスクリーンショットの白い箱が SkinnedMeshRenderer
が OFF のときの可視判定です。
![](https://user-images.githubusercontent.com/68057/162668486-197b9a56-2e60-4e17-a637-feaddbce10be.jpg)
これは初期化したときに Mesh の BoundBox + SkinnedMeshRenderer.rootBone
で作られて、
以降フレームが進んで Mesh が変形しても形は追随しません。位置は回転は追随します。
で、間違った SkinnedMeshRenderer.rootBone を設定すると下のようになります。
![](https://user-images.githubusercontent.com/68057/162668922-3bcf4d3a-7669-4f85-bea6-860aedba968a.jpg)
当たり判定と、メッシュの場所が一致しない可能性があります。 例えば、SkinnedMeshRenderer.rootBone に root 位置の transform が指定されていると 足元にカリング判定のボックスが出るかもしれない。
SkinnedMeshRenderer.rootBone には、gltf.skin.skeleton を対応させている(いた?)のですが、 これは gltf と unity とで意味合いの異なる別の概念なのでよろしくない。
https://www.khronos.org/registry/glTF/specs/2.0/glTF-2.0.html#schema-reference-skin
ちなみに rootBone を null にするとバウンディングボックスが足元に行きました。この状態になっていそうな気がします。
Unity においては、SkinnedMeshRenderer.rootBone に SkinnedMeshRenderer.bones から一番根元になるボーンを選ぶのが妥当なような気がしているので 、そのようなインポートオプションを追加しようかと思いました。 デフォルトの動作を変えると、今までうまくいっていたのが逆にうまくいかなくなる場合もあり得るので、 オプションで明示したときのインポート動作を変えれるようにしてみます。
SkinnedMeshRenderer.rootBone
がややこしくて、
- boundingBox の基準位置の変更
- boneWeight 無い頂点の bone になる?(顔などモーフのために SkinndMeshRendererを使う場合にある)
の2つの機能が兼用で、エッジケースがあります。
ランタイムインポートした際にアプリ開発者側で調整を加えないといけないのがVRMの理念とずれている気がします。
対策法が複数あるのは小さい話題です。
「人型のキャラクターや3Dアバター」において細かいモデルデータの差違を吸収・統一しアプリケーション側の取り扱いを簡単にする
v0.95で実装されたVrmUtilityによりアプリ開発者側での調整が減ったと思われるのでCloseとします。 https://vrm-c.github.io/UniVRM/ja/api/0_95_highlevel.html