Unity
VRM

UnityでVRMファイルをインポートして使うときの覚え書き

はじめに

2018年4月に、ドワンゴが「VRM」という新しい3Dアバターフォーマットを公開しました。
今回はこのVRMをUnity上で利用する方法についてのメモ書きです。
(随時追加します。)

なお、この記事は「VRMファイルをUnity上でどう扱うか」についての記事です。
3DモデルをVRMファイルに変換する話はしません。

一緒にみると良いかもしれない資料

以前作った、VRのアバターをARで操作するシステム(twitter)について解説した資料です。
この資料でも触れているVRMについての情報をこの記事は含んでいます。

使い方メモ

0.UniVRMの各種使い方など

まずは公式ドキュメントを読みましょう。
下の方にBlendShape(表情変更)のやり方についての説明とかあります。

1. UniVRMの導入

UnityでVRMを利用する場合はUniVRMを用いることになるでしょう。
公式でサポートされているVRMインポータ/エクスポータです。

導入方法は簡単で、releaseページから最新のunitypackageをダウンロードして導入すればOKです。
なお、2018年6月時点ではUniVRMは週に1~2回の頻度でアップデートが実行されています。
アップデートのたびに微妙に仕様が変更されたり、バグって壊れたりすることがあるので小まめにバージョンを確認することを推奨します。

2. VRMモデルのインポート方法

VRMファイルをUnityで利用する方法は2パターンあります。

  • Prefab化する
  • ランタイムロードする

Prefab化する方法

VRMファイルをUnityの/Assets以下に配置した場合、自動的にUniVRMがインポートしてPrefabに変換してくれます。
ビルドにVRMモデルを埋め込んでしまう場合はこのPrefabを直接使う方法が楽です。

image.png

ランタイムロードする方法

VRMをスクリプトからランタイムロードすることで、動的にVRMモデルをGameObjectとしてシーン上に出現させることができます。
ランタイムロードする場合は、VRMファイルをStreamingAssetsなどに配置するとよいでしょう。

一番単純なロード方法
void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    // Actionコールバックで生成されたGameObjectが返される
    VRMImporter.LoadVrmAsync(path, gameObject =>
    {
        gameObject.transform.position = new Vector3(1, 1, 1);
    });
}

なお、UniVRMがv0.37以降であればasync/awaitを用いてロード待ちをすることができます。

async/await
async void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    // awaitでロード待機
    var gameObject = await VRMImporter.LoadVrmAsync(path);

    gameObject.transform.position = new Vector3(1, 1, 1);
}

おすすめのロード方法

WWWを使って先にbyte[]ファイルを読み込んでしまい、それをVRMImporter.LoadVrmAsyncに渡す方法を推奨します。
理由は次の2点です。

  • VRMImporter.LoadVrmAsyncが内部でFile.ReadAllBytesを使用しており、これが同期読み込みのため重い(v0.38時点)
  • StreamingAssets以下のファイルはAndroid実機の場合はWWW経由じゃないとアクセスできない
WWWを使ったロード方法
IEnumerator LoadVrmCoroutine(string path, Action<GameObject> onLoaded)
{
    var www = new WWW(path);
    yield return www;
    VRMImporter.LoadVrmAsync(www.bytes, onLoaded);
}

void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    StartCoroutine(LoadVrmCoroutine(path, go =>
    {
        go.transform.position = new Vector3(1, 1, 1);
    }));
}

ちなみに、UniRxを導入してasync/awaitと組み合わせるとこう書けます。最高では?

UniRx+async/await+WWW
async void Start()
{
    var path = Application.streamingAssetsPath + "/" + "AliciaSolid.vrm";

    var www = new WWW(path);

    await www;

    var go = await VRMImporter.LoadVrmAsync(www.bytes);

    go.transform.position = new Vector3(1, 1, 1);
}

3.指の曲げ伸ばし

いろいろ手法があると思うけど、自分はこのスクリプトをAddComponentして使っています。

使い方
// 対象アバターのGameObjectのrootにアタッチ
var fingerController = gameObject.AddComponent<FingerController>();

// 左手全部曲げる
fingerController.FingerRotation(FingerController.FingerType.LeftAll, 1.0f);

// 右手の人差し指だけ伸ばしてあと全部曲げる
fingerController.FingerRotation(FingerController.FingerType.RightAll, 1.0f);
fingerController.FingerRotation(FingerController.FingerType.RightIndex, 0.0f);

1.gif
両手全部曲げ伸ばししてる画像(Lerp入れるとなめらかになってよさそう)

とりあえずここまで

発見があれば追記します