見出し画像

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ボタンで実行。

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ボタンで実行。

4. 関連



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