VRMファイルをUnityで読み込む2つの方法

# はじめに
UnityでVRMファイルを読み込む2種類の方法をまとめました。
参考の記事を探してみても、半年前の記事がUniVRMのバージョンが上がり使えなくなっていたり、あまり参考にできなかったため、今の最新版のUniVRM(`v0.98.0_2f6b`)でも使えるように備忘録程度に残しておきます。
※本投稿では、著作権等に配慮しスクショ等は載せておりません。

（初投稿なので誤字脱字等あるかもしれませんがご容赦ください。）

# 目次
1. 事前準備
2. ローカル上のファイル読み込み
3. サーバー上のファイル読み込み
4. まとめ

# 1. 事前準備
この記事を見られる方はUnityは既にインストールしてあるものとしてUnityのインストールの手順は割愛させて頂きます。
今回利用したUnityのバージョンは`2021.3.1f1`です。4/13にUnity2021LTSがリリースされたため、早速使ってみました。

1. UniVRMの導入
    1. [UniVRMのリリースページ](https://github.com/vrm-c/UniVRM/releases)から、4/30現在で最新のバージョン`UniVRM_Samples-0.98.0_2f6b.unitypackage`をダウンロードします。
    2. UnityEditorを開き、メニューバーの`Assets/Import Package/Custom Package...`を選択し、1.でダウンロードしたパッケージをインポートします。特に細かいこだわり等なければ何も考えずに全てインポートしてしまっていいと思います。※今回私も何も考えずに全てインポートしました。

2. UniTaskの導入
※スキップ可。await/asyncを使いたいって方はこちらを参考にしてください。本投稿ではawait/asyncを使っていますが、Unityのコルーチンで行っても問題ありません。
        1. [UniTaskのリリースページ](https://github.com/Cysharp/UniTask/releases)から、最新のバージョン`UniTask.2.3.1.unitypackage`をダウンロードします。
        2. UnityEditorを開き、メニューバーの`Assets/Import Package/Custom Package...`を選択し、1.でダウンロードしたパッケージをインポートします。

3. VRMファイルの用意
こちら、読み込みができているかどうかの動作確認用になります。
    1. 自作のものか購入したものをローカルのどこかに格納します。(Assets直下でも問題ありません)
    2. Microsoft AzureのBlob Storageでもその他AWSなど別のサービスでもいいため、`.vrm`のファイルをサーバー上にアップします。（https://○○.blob.core.windows.net/××/fileName.vrm の形式で読み込めるようになっていれば問題ありません。WebGLで読み込む場合、格納場所によってはCORSエラー等で読み込めない場合がありますのでご注意ください。）

# 2. ローカル上のファイル読み込み
コードがこちら↓
```C#
using Cysharp.Threading.Tasks;
using UniGLTF;
using UnityEngine;
using UnityEngine.Networking;
using VRM;
using VRMShaders;

namespace VRMTest
{
    public class VRMFileLoader : MonoBehaviour
    {
        /// <summary>
        /// ローカルのものを見に行くかどうか
        /// </summary>
        [SerializeField] private bool isLoal;

        /// <summary>
        /// 読み込みたいファイル名
        /// </summary>
        [SerializeField] private string fileName;

        private async void Start()
        {
            if (isLoal) await LoadLocalVrmFile(fileName);
        }

        /// <summary>
        /// ローカルファイルの読み込み
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private async UniTask LoadLocalVrmFile(string fileName)
        {
            // 指定したPathにある.vrmのファイルをGLTF形式にパースする
            // パースもDisposeしないとメモリリークしてしまうためusingを使用
            using var gltfData = new AutoGltfFileParser($"Assets/VRMAvatar/{fileName}.vrm").Parse();
            var vrm = new VRMData(gltfData);

            // Disposeしないとメモリリークしてしまうためusingを使用
            using var context = new VRMImporterContext(vrm);
            var instance = await context.LoadAsync(new RuntimeOnlyAwaitCaller());
            // シーン上にオブジェクト(メッシュ)表示
            instance.ShowMeshes();
        }
    }
}

```
## 注意点
1. パースしているところ、`VRMImporterContext`のところ、この2カ所でDisposeをしていますが、それぞれDisposeしてやらないとメモリリークが起きてしまいます。
2. `instance.ShowMeshes()`を呼んでやらないと、シーン上に読み込みはされるものの、表示がされないません。

# 3. サーバー上のファイル読み込み
コードがこちら↓
UnityWebRequest.Get()引数を事前準備でサーバーにあげたファイルのURLを渡してあげてください。
```C#
using Cysharp.Threading.Tasks;
using UniGLTF;
using UnityEngine;
using UnityEngine.Networking;
using VRM;
using VRMShaders;

namespace VRMTest
{
    public class VRMFileLoader : MonoBehaviour
    {
        /// <summary>
        /// 読み込みたいファイル名
        /// </summary>
        [SerializeField] private string fileName;

        private async void Start()
        {
            await LoadServerVrmFile(fileName);
        }

        /// <summary>
        /// サーバー上のファイルの読み込み
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        private async UniTask LoadServerVrmFile(string fileName)
        {
            using var webRequest = UnityWebRequest.Get($"https://〇〇.blob.core.windows.net/××/{fileName}.vrm");
            await webRequest.SendWebRequest();

            // WebRequestで取得した.vrmのByteデータをGLTF形式にパースする
            // パースもDisposeしないとメモリリークしてしまうためusingを使用
            using var gltfData = new GlbBinaryParser(webRequest.downloadHandler.data, "").Parse();
            var vrm = new VRMData(gltfData);

            // Disposeしないとメモリリークしてしまうためusingを使用
            using var context = new VRMImporterContext(vrm);
            var instance = await context.LoadAsync(new RuntimeOnlyAwaitCaller());
            // シーン上にオブジェクト表示
            instance.ShowMeshes();
        }
    }
}

```
## 注意点
1. パースしているところ、`VRMImporterContext`のところ、この2カ所でDisposeをしていますが、それぞれDisposeしてやらないとメモリリークが起きてしまいます。
2. `LoadAsync`で`new RuntimeOnlyAwaitCaller`を渡していますが、WebGLで使う場合、`new ImmediateCaller`を渡してやらないと読み込みが終わりません。※WebGLはマルチスレッド対応していないため
3. `instance.ShowMeshes()`を呼んでやらないと、シーン上に読み込みはされるものの、表示がされないません。

# まとめ
載せられる画像がなくすみません。。事前準備ができていれば、お手元でもご確認頂けるかと思いますので是非上記コードを試してみてください。改善できる部分等あればご指摘頂ければと思いますので、お気軽にコメントください。
余談ですがStartでも`async`が使えます。

ここまでご覧頂きありがとうございました。