見出し画像

VRM Animation (.vrma)をUnity上で簡単に生成できるようにした話

この記事はクラスターAdvent Calandar 2023の2日目の記事です。

1日目の記事はhtomineさんの clusterとPM でした!clusterにおけるPMチームの変遷やプロダクトのカバー範囲の広がりが感じ取れますね。

本記事では、clusterのアバターデータのフォーマットであるVRMに関連した技術・実装寄りの話題を紹介します。先立って注意ですが、この記事は現時点のclusterの機能とは関係しない話題ですので、そこだけご注意ください。

記事の作成背景として、個人開発しているVRM向けソフトのVMagicMirror(VMM)があります。VMMに先日導入したVRM Animation読み込み機能をより便利に使えるようにするために最近やっていた活動を紹介したい…というのが記事作成のきっかけになっています。


では本題へ。

VRM Animationとは?

VRM Animationとは、3DアバターデータのフォーマットであるVRMに関連した規格です。

この記事まで辿り着く人だと知っている人も多そうですが、VRMは3Dのアバターモデルそのものを定義するデータフォーマットであり、次のような情報を含みます。

  • メッシュ

  • テクスチャ、マテリアル

  • 人体ベースのボーン情報

  • 表情、揺れもの設定

  • ライセンスに関する情報

これに対して、VRM Animation (.vrma)はボーンや表情の動きかたを扱うデータです。既存で類似した用途に使えるデータフォーマットの例はこんな感じでしょうか。

VRM AnimationはVRMと同じく、glTF というオープンな3Dデータの規格に基づいて定義されています。現在VRM Animationは正式版に向けて仕様の調整がされている段階ですが、後述する通り現時点でも使うことができます。


今のUniVRM (v0.115.0)で出来ること

本記事の時点でのUniVRM (v0.115.0)は、VRM Animationについて以下のような機能をサポートしています。

  • 実行時のVRM Animationファイル(.vrma)の読み込み

  • Editor上での、 .bvh ファイルから .vrma へのファイル変換

  • Editorおよびアプリ実行時に、 .vrma の読み書きに使える処理

このサポート内容を見て「あれ、Animation Clipからの .vrma 変換機能ってないの?BVHだけ?」と思った人はカンが良いです。私も完全に同じことを思いました。

BVH Toolsを使えばAnimation ClipをBVHに変換し、それをさらに .vrma に変換…みたいな変換経路も組めるんですが、ぶっちゃけ面倒ですし、この方法だと変換過程で指のモーションが欠落してしまう問題も確認しています。これだと実用レベルとは言い難いです。

そのような背景も踏まえつつ、Animation Clipからの .vrma 変換については「UniVRMに機能として欲しい!」という要望も上げたりはしたんですが、まあ待たずに作っちゃっても良いよね…という見通しもありました。

そこで実際に作ったものがコチラです。

このプロジェクトを導入すると、Unity Editor上で

  • AnimationClipの右クリック

  • 専用ウィンドウでアバターとAnimationClipを指定して「Export」を実行

といった操作で .vrma ファイルが生成できるようになります。ラクそうですね。

AnimationClipを右クリックして変換
専用のウィンドウでアバター + Clipを指定して変換

くわしい使い方はGitHubのほうを見てもらうとして、以降では細かめの実装のことを書いていきます。


実装 (基本編):実はBVHファイル変換のマネで行ける

そもそもの取っ掛かりとして「.vrmaファイルの出力ってそもそも何をすればいいのか分からん…」と迷いそうですが、 .bvh ファイルを .vrmaファイルに変換するUniVRMコードを見るとかなりの事が分かります。

VRMAnimationMenu.cs

このコードでは、根本的には次のような処理が走っています。

  • BvhImporterContextを使い、BVHファイルに含まれる人体骨格からアバター相当のAnimatorを復元

  • BvhImporterContextを使い、BVHファイルからモーション部分のAnimationおよびAnimationClipを生成

  • BVHファイル上に載っているFPSの情報に基づいた秒数ごとの区切りで、Animation.Sample()をバシバシ呼び出してアバターの姿勢を変更

  • 姿勢を1回更新するたびに、その時点でのアバター姿勢を .vrma 用のデータとして書き足す

ここでBvhImporterContextがやっている細かい処理はいったん忘れてよいことにすると、BVHの部分は単にUnity上のAnimatorやAnimationClipに差し替えても問題なさそうに見えてきます。

すなわち、以下のように変えても動くんでは…という期待が持てます。

  • Unity上で、あらかじめprefabになっているアバターのAnimatorを選ぶ

  • Unity上で、あらかじめ存在するAnimationClipを選ぶ

  • 30FPSなどの適当に決めた時間区切りに基づき、アバターに対してAnimationClipの姿勢をバシバシ適用していく

  • 適用した姿勢を .vrmaファイル用の出力結果として書き足す

実際のコード上ではAnimation.Sample()の代わりにAnimationClip.SampleAnimation()を使うなどのマイナーチェンジを加えていますが、ほぼそのままでうまく行っています。


ひとつ大きめのクセのあったポイントとして、アバターのAnimatorを含むオブジェクトにRendererや揺れもの設定が入っていると .vrma ファイルにそれらのメッシュ/テクスチャ/揺れもの情報なども埋め込まれてしまう…という問題がありました。

※この問題の原因は深追いしていませんが、たぶんUniVRM(かその基盤のUniGLTF)が気を利かせて「GameObjectの情報をなるべく保存して再現しやすくするぞ!」と頑張っちゃっているせいでそうなっています。

この問題を対策するため、実際には以下のような前処理もやっています。

  • 指定したアバターをObject.Instantiateで複製

  • 複製後、ボーンの骨格と関係ないコンポーネントをすべて削除

    • ここでSkinnedMeshRendererなど、描画に関係のある要素をすべて削除します。

  • さらに、Animatorで参照されているHumanoid骨格 (手足や指のボーン) を除いたボーンの情報も全て削除

    • 例えば、もとのアバターに揺れもののスカートや補助骨などのボーンがある場合、それらのボーンも全て削除

  • 上記の間引きを行ったアバターに対して、AnimationClipのモーションを適用していく


実装(応用編): アバターを用意せずに済ませたい!

以上までで一応コードとしては動くんですが、上記の手順では「何かのアバターを用意しないと .vrma ファイルが入手できない」という制約が生じます。

そもそも .vrmaの生成と適用で同一のアバターを使っておくとモーションの再現性が担保しやすく便利…という話もあるんですが、それでも「アバター無しでAnimationClip -> .vrma 変換ができると簡単!」という操作体験を何とかして提供したい気持ちがありました。

そこで、アバターの準備を省きたい人向けに、コードから動的に人型の骨格を生成するアプローチを考えます。

動的に人型骨格を生成するのはUnityのビルトイン関数のAvatarBuilder.BuildHumanAvatarを呼べば実現するんですが、これはこれでトラップが多少ありました。

  • HumanTraitの一部ボーン名とHumanBodyBonesの名前が微妙に異なっており、せっかく作った指ボーンがAnimatorから認識されない

    • 具体的には指ボーンが該当しています。

  • Hipsボーンをどういう状態にしておくのが正解かが、ドキュメントを見てもイマイチ分からない

双方とも本質的な問題ではないわりに面倒だったんですが、この辺の問題をクリアすることで人型モデル相当のボーンとAnimatorを持つオブジェクトが生成できるようになります。

苦労の痕跡についてはコードのうち ReferenceHumanoid.cs あたりをご覧下さい…。

で。

このAnimatorの生成が終わったあとは先ほど書いた基本編の処理に帰着できるため、アバターを用意せずに「Animation Clipの右クリックでvrmaファイルに変換」という体験が提供できる、ということになります。

再掲: AnimationClipを右クリックして変換する…だけのことが意外と難しい


なお、ここまでやってみたうえでの補足として、どうもコードからのアバター動的生成は本ケースとは相性が悪いのではと思っています。

単に「暗黙にデフォルトで参照されるアバターを持っていて、それとAnimationClipを組み合わせる」みたいな実装のほうが筋が良さそうです、はい…。


最後に

ホントは本記事で見せた実装をもうちょっとブラッシュアップしてUniVRMへのPRを出して「contributeしたぜイエーイ!」みたいな記事にしたかったんですが、私用やら何やらで間に合いませんでした。

まあこの記事を書いたあとの自分か、本記事を読んだ誰かがやってくれることに期待しましょう。


本記事は以上です。

クラスター Advent Calendar 2023の次(3日目)の記事は @toyakun さんの、clusterに適したゲームジャンル ~「アバターの日」イベントの事例より です!


この記事が気に入ったらサポートをしてみませんか?