Help us understand the problem. What is going on with this article?

Unity、Oculus(HTC Vive)、VRIKで簡易モーションキャプチャを作った話

More than 3 years have passed since last update.

概要

Oculus TouchとVRIKで動かしたモデルのモーションを保存して再生できる機能を作ってみたので紹介します。自分が試したのはOculusですが、HTCViveでも同じ話だと思います。

VRIK設定

下記サイト等を参考にして設定してください。
http://qiita.com/halne369/items/daadf0e00330a1019830

モーションデータ取り扱い

Humanoidタイプのモデルのアニメーションを編集するには、AnimationClipを動的に生成するのではなくてHumanPoseHandlerクラスを使うといいらしい。

https://forum.unity3d.com/threads/is-there-any-method-to-manipulate-a-humanoid-muscle-by-api.354221/#post-2291395

HumanPhoseHandlerの使い方は以下の通り。

test.cs
HumanPose m_pose;

//ハンドラー取得
var handler = new HumanPoseHandler(m_animator.avatar, m_animator.transform);

//HumanPose取得
handler.GetHumanPose(ref m_pose)

//HumanPoseセット
handler.SetHumanPose(ref m_pose)

これだけ。

モーションデータ取得→保存

やり方は簡単で、毎フレームHumanPoseを取得してScriptableObjectに保存するだけ。

HumanPoses.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;

public class HumanPoses : ScriptableObject {

    [System.SerializableAttribute]

    public class SerializeHumanPose
    {
        public Vector3 bodyPosition;
        public Quaternion bodyRotation;
        public float[] muscles;
        public int frameCount;
    }

    public List<SerializeHumanPose> poses = new List<SerializeHumanPose>();

}
RecordHumanPose.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UniRx.Triggers;
using System;
using LitJson;
using System.IO;
using UnityEditor;

public class RecordHumanPose : MonoBehaviour {

    /// <summary>
    /// 対象のアニメーター
    /// </summary>
    [SerializeField] Animator m_animator;

    int m_frameCount;

    HumanPose m_pose;

    HumanPoseHandler m_handler;

    Action m_recordEndCB;


    /// <summary>
    /// 録画開始
    /// </summary>
    public void RecordStart()
    {

        m_frameCount = 0;

        HumanPoses humanposes = ScriptableObject.CreateInstance<HumanPoses>();

        var dispose = this.UpdateAsObservable().Subscribe(p =>
        {
            m_handler.GetHumanPose(ref m_pose);
            var seripose = new HumanPoses.SerializeHumanPose();
            seripose.bodyPosition = m_pose.bodyPosition;
            seripose.bodyRotation = m_pose.bodyRotation;
            seripose.frameCount = m_frameCount;
            seripose.muscles = new float[m_pose.muscles.Length];
            seripose.frameCount = m_frameCount;
            for (int i = 0; i < seripose.muscles.Length; i++)
            {
                seripose.muscles[i] = m_pose.muscles[i];
            }
            humanposes.poses.Add(seripose);
        }
        );

        var timeCountObservable = this.UpdateAsObservable().Subscribe(p => m_frameCount++);

        m_recordEndCB = () =>
        {
            string path = AssetDatabase.GenerateUniqueAssetPath("Assets/Resources/HumanPoses.asset");
            AssetDatabase.CreateAsset(humanposes, path);
            AssetDatabase.Refresh();
            m_frameCount = 0;
            timeCountObservable.Dispose();
            dispose.Dispose();
        };
    }


    /// <summary>
    /// 録画終了
    /// </summary>
    public void RecordEnd()
    {
        if (m_recordEndCB != null)
        {
            m_recordEndCB();
            m_recordEndCB = null;
        }

    }

    private void Awake()
    {
        m_handler = new HumanPoseHandler(m_animator.avatar, transform);
    }
}

モーションデータ再生

ScriptableObjectに保存したHumanPoseを読み取ってセットする。

PlayHumanPoses.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UniRx;
using UniRx.Triggers;

public class PlayHumanPoses : MonoBehaviour {

    [SerializeField]
    HumanPoses m_poses;

    [SerializeField]
    Animator m_animator;

    HumanPoseHandler m_handler;

    System.Action m_finishPlayCB;

    int m_index = 0;

    public void Play()
    {
        m_index = 0;
        var disposable = this.UpdateAsObservable().Where(p=>m_index < m_poses.poses.Count).Select(p => 1).Scan((now, count) =>
        {
            Debug.Log(now);
            return now + count;
        }).Subscribe(xx=>
        {
            DoSetHumanPose(xx);
        });

        m_finishPlayCB = () =>
        {
            disposable.Dispose();
            m_index = 0;
        };
    }


    public void Stop()
    {
        if (m_finishPlayCB != null)
        {
            m_finishPlayCB();
            m_finishPlayCB = null;
        }
    }


    void DoSetHumanPose(int frame)
    {
        if (m_poses.poses[m_index].frameCount == frame-1)
        {
            var pose = new HumanPose();
            pose.bodyPosition = m_poses.poses[m_index].bodyPosition;
            pose.bodyRotation = m_poses.poses[m_index].bodyRotation;
            pose.muscles = m_poses.poses[m_index].muscles;
            m_handler.SetHumanPose(ref pose);
            m_index++;
        }
    }

    // Use this for initialization
    void Awake () {
        m_handler = new HumanPoseHandler(m_animator.avatar, transform);
    }

}

最後に

後はOculusかHTCViveでVRIKをセットしたモデルを動かして上記方法でモーションを記録+再生をすればOKです。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした