Motion Diffusion Model で生成したモーションをUnityで再生する
Motion Diffusion Model で生成したモーションをUnityで再生する方法をまとめました。
前回
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. 関連
この記事が気に入ったらサポートをしてみませんか?