LoginSignup
54
51

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-03-18

概要

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

VRIK設定

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

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

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

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です。

54
51
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
54
51