メインコンテンツへスキップ
見出し画像

Motion Diffusion Model で生成したモーションをUnityで再生する

    Motion Diffusion Model で生成したモーションをUnityで再生する方法をまとめました。

    ・Unity 2021.3.10f1

    前回

    1. Motion Diffusion Modelで生成したモーション

    前回、「Motion Diffusion Model」で「the person walked forward and is picking up his toolbox.」(人は前に歩き、ツールボックスを拾う)というプロンプトからモーション(results.npy)を生成し、(おまけで) result.jsonに変換しました。

    画像

    今回は、このモーションをUnityで再生します。

    2. Unityでのモーションの可視化

    はじめに、モーションのUnityでの可視化を行います。

    (1) 3DでUnityプロジェクトを作成。
    (1) results.jsonをAssets/Resourcesに配置。
    (2) 「LitJSON」をダウンロードし、srcフォルダをAssetsに配置。
    「LitJSON」は、JSONを読み込むためのライブラリになります。

    (3) Hierarchyウィンドウに空のGameObjectを生成し、「Main」という名前に変更し、スクリプト「Main.cs」を追加。

    ・Main.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using LitJson;
    
    public class Main : MonoBehaviour
    {
        GameObject[] points = new GameObject[22];
        int index = -1;
        JsonData jsonData;
    
        // スタート時に呼ばれる
        void Start()
        {
            // ポイント群の生成
            for (int i = 0; i < 22; i ++)
            {
                points[i] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                points[i].transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
            }
    
            // JSONの読み込み
            string str = Resources.Load<TextAsset>("results").ToString();
            this.jsonData = JsonMapper.ToObject(str);
            index = 0;
        }
    
        // 定期的に呼ばれる
        void FixedUpdate () {
            // indexのインクリメント
            if (index < 0) return;
            index = (index + 1) % 120;
    
            // ポイント群の更新
            for (int i = 0; i < 22; i++)
            {
                Vector3 pos = this.points[i].transform.position;
                pos.x = (float)(double)this.jsonData["motion"][0][i][0][this.index];
                pos.y = (float)(double)this.jsonData["motion"][0][i][1][this.index];
                pos.z = (float)(double)this.jsonData["motion"][0][i][2][this.index];
                this.points[i].transform.position = pos;
            }
        }
    }
    

    (4) Playボタンで実行。

    Motion Diffusion Model でテキストから生成したモーションをUnityで再生してみる。 pic.twitter.com/995a3rc72i

    — 布留川英一 / Hidekazu Furukawa (@npaka123) October 9, 2022

    3. VRMでのモーションの再生

    次に、VRMでモーションを再生します。

    (1) UniVRMのリポジトリから最新のunitypackageファイル(UniVRM-0.105.0_cd48.unitypackage)をダウンロード
    VRMを読み込むためのパッケージです。

    (2)Unityのメニュー「Assets → Import Package → Custom Package」 で先ほどダウンロードしたunitypackageファイルをインポート。

    (3) ニコニコ立体からVRMファイル(AliciaSolid.vrm)をダウンロードし、Assetsに追加。

    (3) AliciaSolidをシーンにドラッグ&ドロップ。
    (4) Main.csを以下のように編集。

    ・Main.cs

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using LitJson;
    
    public class Main : MonoBehaviour
    {
        GameObject[] points = new GameObject[22];
        int index = -1;
        JsonData jsonData;
    
        public GameObject model;
        Animator animator;
    
        // スタート時に呼ばれる
        void Start()
        {
            // ポイント群の生成
            for (int i = 0; i < 22; i ++)
            {
                points[i] = GameObject.CreatePrimitive(PrimitiveType.Sphere);
                points[i].transform.localScale = new Vector3(0.1f, 0.1f, 0.1f);
            }
    
            // アニメータの取得
            this.animator = this.model.GetComponent<Animator>();
    
            // JSONの読み込み
            string str = Resources.Load<TextAsset>("results").ToString();
            this.jsonData = JsonMapper.ToObject(str);
            index = 0;
        }
    
        // 定期的に呼ばれる
        void FixedUpdate () {
            // indexのインクリメント
            if (index < 0) return;
            index = (index + 1) % 120;
    
            // ポイント群の更新
            for (int i = 0; i < 22; i++)
            {
                Vector3 pos = this.points[i].transform.position;
                pos.x = (float)(double)this.jsonData["motion"][0][i][0][this.index];
                pos.y = (float)(double)this.jsonData["motion"][0][i][1][this.index];
                pos.z = (float)(double)this.jsonData["motion"][0][i][2][this.index];
                this.points[i].transform.position = pos;
            }
    
            //HipsのTransformの取得
            Transform hipsTransform = this.animator.GetBoneTransform(HumanBodyBones.Hips);
    
            // 左足の更新
            UpdateBoneRotate(HumanBodyBones.LeftUpperLeg, 2, 5, -hipsTransform.up);
            UpdateBoneRotate(HumanBodyBones.LeftLowerLeg, 5, 8, -hipsTransform.up);
            UpdateBoneRotate(HumanBodyBones.LeftFoot, 8, 11, hipsTransform.forward);
    
            // 右足の更新
            UpdateBoneRotate(HumanBodyBones.RightUpperLeg, 1, 4, -hipsTransform.up);
            UpdateBoneRotate(HumanBodyBones.RightLowerLeg, 4, 7, -hipsTransform.up);
            UpdateBoneRotate(HumanBodyBones.RightFoot, 7, 10, hipsTransform.forward);
    
            // 左手の更新
            UpdateBoneRotate(HumanBodyBones.LeftShoulder, 14, 17, -hipsTransform.right);
            UpdateBoneRotate(HumanBodyBones.LeftUpperArm, 17, 19, -hipsTransform.right);
            UpdateBoneRotate(HumanBodyBones.LeftLowerArm, 19, 21, -hipsTransform.right);
    
            // 右手の更新
            UpdateBoneRotate(HumanBodyBones.RightShoulder, 13, 16, hipsTransform.right);
            UpdateBoneRotate(HumanBodyBones.RightUpperArm, 16, 18, hipsTransform.right);
            UpdateBoneRotate(HumanBodyBones.RightLowerArm, 18, 20, hipsTransform.right);
    
            // 背骨の更新
            UpdateBoneRotate(HumanBodyBones.Neck, 12, 15, hipsTransform.up);
            UpdateBoneRotate(HumanBodyBones.Spine, 6, 3, -hipsTransform.up);
            UpdateBoneRotate(HumanBodyBones.UpperChest, 9, 6, -hipsTransform.up);
    
            // Hipsの位置の更新
            Vector3 p = hipsTransform.position;
            p.x = (float)(double)this.jsonData["motion"][0][0][0][this.index]+1;
            p.y = (float)(double)this.jsonData["motion"][0][0][1][this.index];
            p.z = (float)(double)this.jsonData["motion"][0][0][2][this.index];
            hipsTransform.position = p;
        }
    
        // ボーン向きの更新
        void UpdateBoneRotate(HumanBodyBones bone, int p1, int p2, Vector3 axis)
        {
            Vector3 v1 = new Vector3();
            v1.x = (float)(double)this.jsonData["motion"][0][p1][0][this.index];
            v1.y = (float)(double)this.jsonData["motion"][0][p1][1][this.index];
            v1.z = (float)(double)this.jsonData["motion"][0][p1][2][this.index];
    
            Vector3 v2 = new Vector3();
            v2.x = (float)(double)this.jsonData["motion"][0][p2][0][this.index];
            v2.y = (float)(double)this.jsonData["motion"][0][p2][1][this.index];
            v2.z = (float)(double)this.jsonData["motion"][0][p2][2][this.index];
    
            this.animator.GetBoneTransform(bone).rotation = Quaternion.FromToRotation(axis, v2-v1);
        }
    }


    画像

    (5) AliciaSolidをMainのmodelにドラッグ&ドロップ。

    画像

    (6) Playボタンで実行。

    Motion Diffusion Model のでテキストから生成したモーションをUnity x VRMで再生してみる。 pic.twitter.com/oLG6bKfbUN

    — 布留川英一 / Hidekazu Furukawa (@npaka123) October 9, 2022

    4. 関連



     
     

    npaka

     
     
    プログラマー。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガジェット。年2冊ペースで技術書を執筆。アニソン / カラオケ / ギター / 猫 twitter : @npaka123

    あなたへのおすすめ