※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
MikuMikuMoving v1.2.8.0で公式にPerception Neuron対応されましたので、
現在こちらのプラグインを使用する必要はありません
※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※※
#Perception Neuronとは
中国Noitom社によって開発された低価格モーションキャプチャシステム。公式サイトはこちら。最大32個のNeuronと呼ばれる9軸センサモジュール(3軸ジャイロ+3軸加速度+3軸磁気)を用いて指先を含めた全身のキャプチャが可能。Axis Neuronという専用ソフトからTCP/UDPによるBVHフォーマットのモーションデータを送信することができます。システム一式(Neuron32個付属)で$1,499。Axis NeuronはこちらからFree版をダウンロードすることができます。Perception Neuronを持っていなくてもサンプルのモーションデータを再生させることができます。
#MMM(MikuMikuMoving)とは
Mogg氏により開発されているMikuMikuDance(MMD)互換ソフト。公式サイトはこちら。MMD用のモデルやモーションデータが使用可能。プラグイン機能を持っており、ユーザーが自由に機能を追加することができます。
今回、Axis Neuronから送信されたモーションデータをMMMで受信し、MMDモデルにモーションを反映させるプラグインを作成しました。実装した内容について書きたいと思います。
#MMMのプラグイン開発
MMMのプラグインの開発に必要なソフトウェアと環境は以下になります。
開発に必要なソフトウェア
・Microsoft Visual C# 2010 Express もしくはこれに準じるソフトウェア必要環境
・Windows XP以降
・.NET Framework 2.0
・DirectX 9.0c Runtime 2010 Feb以降 (SDKは必要ありません)
※MMM公式サイト 17.プラグインより引用
プラグインには、ボタンを押したときに実行されるコマンドプラグインと、毎フレーム実行される常駐型プラグインの2種類のタイプがあります。これらのプラグインでは、モデル、アクセサリ、エフェクト、カメラやライトに対して、キーフレーム操作やモーション操作等が可能です。プラグイン開発については詳しくはMMM公式サイトの17.プラグインをご確認ください。今回は常駐型プラグインで開発しました。
#モーションデータの受信
##NeuronDataReaderライブラリ
Axis NeuronはTCP/UDPによるBVHフォーマットのモーションデータを送信することができます。NeuronDataReaderというC/C++用ライブラリが提供されており、これを利用することでBVHデータの受信を簡単に行うことができます。
※画像は「Neuron Data Reader Runtime API Documentation」1.1. NeuronDataReader frameworkより引用
##BVHデータフォーマット
毎フレームのデータにはBVHヘッダとBVHモーションデータが含まれます。BVHヘッダには、位置情報の有り無しやリファレンスデータの有り無しなどの基本的な情報が含まれます。
// Header format of BVH data
typedef struct _BvhDataHeader
{
uint16_t Token1; // Package start token: 0xDDFF
DATA_VER DataVersion; // Version of community data format. e.g.: 1.0.0.2
uint16_t DataCount; // Values count
uint8_t WithDisp; // With/out displacement
uint8_t WithReference; // With/out reference bone data at first
uint32_t AvatarIndex; // Avatar index
uint8_t AvatarName[32]; // Avatar name
uint32_t FrameIndex; // Frame data index
uint32_t Reserved; // Reserved, only enable this package has 64bytes length
uint32_t Reserved1; // Reserved, only enable this package has 64bytes length
uint32_t Reserved2; // Reserved, only enable this package has 64bytes length
uint16_t Token2; // Package end token: 0xEEFF
}BvhDataHeader;
※コードは「Neuron Data Reader Runtime API Documentation」2.1.4. Header of BVH data streamより引用
BVHモーションデータは連続したfloat値になっています。リファレンスデータ有りの場合、最初の6つのfloat値はリファレンスボーンの位置と回転になります。位置情報有りの場合、すべてのボーンが6つのfloat値(位置xyzの3つと回転xyzの3つ)を持ちます。位置情報無しの場合、ルートボーンのみ6つのfloat値を持ち(位置xyzの3つと回転xyzの3つ)、それ以外のボーンは3つのfloat値を持ちます(回転xyzの3つ)。
※画像は「PERCEPTION NEURON UNITY HANDBOOK」APPENDIX D: BINARY DATA SEQUENCEより引用
回転の順序はYXZです(Axis Neuron側の設定で変更可能)。
また、ボーンの階層構造は「Neuron Data Reader Runtime API Documentation」Appendix B: BVH header templateに載っています。
##C#でNeuronDataReaderを利用する
公式サイトからダウンロードできるNeuronDataReader SDKにはdemo_csというC#で開発されたサンプルプロジェクトがついてきます。このサンプルプロジェクトにはNeuronDataReaderWraperというC#からNeuronDataReaderを使うためのラッパクラスが含まれています。C#でNeuronDataReaderを利用する場合はこちらを使わせてもらいましょう。
また、公式サイトからダウンロードできるUnity IntegrationというUnity向けのパッケージに含まれているNeuronActor.csやNeuronTransformsInstance.cs、NeuronAnimatorInstance.csに、受信したデータから各ボーンの位置と回転を取り出したり、取り出した位置と回転を受信側のモデルのボーンに適用する処理が書かれています。こちらも参考になるかと思います(位置と回転はUnityの座標系に合わせて変換されているので注意してください)。
#それぞれの座標系
##Perception Neuronで使用される座標系
xの正方向が右、yの正方向が上、zの正方向が手前の右手系です。
※画像は「Neuron Data Reader Runtime API Documentation」Appendix D: Skelton Graphより引用
##MMMで使用される座標系
xの正方向が右、yの正方向が上、zの正方向が奥の左手系です。
MMMの画面上の座標軸はzが手前に伸びてますがマイナス方向なので注意です。
##座標系の変換
以上より、位置に関してはzの値をマイナスに、回転に関してはxとyの回転を逆に回転させることで、Perception Neuron側の値をMMM側の座標系に合わせる事ができます。
#Perception NeuronとMMMのボーンの対応
Perception NeuronとMMMのボーンは以下のように対応させました。MMDモデルのボーン構造はモデルによって違いがあったりしますが、標準モデルをベースに対応付けています。Perception Neuronからは各ボーンの位置と回転が得られますが、Hipsの位置をセンターの位置に使用している以外は、位置は使わず回転のみ使っています。上半身については、Perception Neuron側のSpine、Spine1、Spine2、Spine3の四つのボーンの回転を合計した値をMMM側の上半身ボーンに使っています。上半身2を持っているモデルの場合は、Spineを上半身、Spine1、Spine2、Spine3の合計を上半身2に使う等すると良いかと思います。また、RightHandIndex1とRightInHandIndexの回転を合計した値を右人指1ボーンに使っています。他の指も同様です。(このあたりはUnity IntegrationのNeuronAnimatorInstance.csのやり方を参考にしました)
Perception Neuronのボーン | MMMのボーン |
---|---|
Hips(位置) | センター(位置) |
Hips | 下半身 |
RightUpLeg | 右足 |
RightLeg | 右ひざ |
RightFoot | 右足首 |
LeftUpLeg | 左足 |
LeftLeg | 左ひざ |
LeftFoot | 左足首 |
Spine+Spine1+Spine2+Spine3 | 上半身 |
Neck | 首 |
Head | 頭 |
RightShoulder | 右肩 |
RightArm | 右腕 |
RightForeArm | 右ひじ |
RightHand | 右手首 |
RightHandThumb1 | 右親指1 |
RightHandThumb2+RightHandThumb3 | 右親指2 |
RightHandIndex1+RightInHandIndex | 右人指1 |
RightHandIndex2 | 右人指2 |
RightHandIndex3 | 右人指3 |
RightHandMiddle1+RightInHandMiddle | 右中指1 |
RightHandMiddle2 | 右中指2 |
RightHandMiddle3 | 右中指3 |
RightHandRing1+RightInHandRing | 右薬指1 |
RightHandRing2 | 右薬指2 |
RightHandRing3 | 右薬指3 |
RightHandPinky1+RightInHandPinky | 右小指1 |
RightHandPinky2 | 右小指2 |
RightHandPinky3 | 右小指3 |
LeftShoulder | 左肩 |
LeftArm | 左腕 |
LeftForeArm | 左ひじ |
LeftHand | 左手首 |
LeftHandThumb1 | 左親指1 |
LeftHandThumb2+LeftHandThumb3 | 左親指2 |
LeftHandIndex1+LeftInHandIndex | 左人指1 |
LeftHandIndex2 | 左人指2 |
LeftHandIndex3 | 左人指3 |
LeftHandMiddle1+LeftInHandMiddle | 左中指1 |
LeftHandMiddle2 | 左中指2 |
LeftHandMiddle3 | 左中指3 |
LeftHandRing1+LeftInHandRing | 左薬指1 |
LeftHandRing2 | 左薬指2 |
LeftHandRing3 | 左薬指3 |
LeftHandPinky1+LeftInHandPinky | 左小指1 |
LeftHandPinky2 | 左小指2 |
LeftHandPinky3 | 左小指3 |
#Perception NeuronのモーションをMMMに適用させる
Perceoption NeuronのモーションをMMMに適用させるために、プラグイン内で以下のような処理を行っています。
##デフォルトポーズの違い
Perception NeuronのデフォルトポーズはTスタンスですが、MMMのデフォルトポーズはAスタンスです。MMM側でTスタンス用のモーションデータを使えるように、モデルのポーズをAスタンスからTスタンスに変換します。肩、腕のボーンが地面と平行になるようにz軸回転させます。
はじめに、肩ボーンの、グローバル座標系のx軸に対する角度を求めます。次に、腕ボーンの、肩ボーンに対する角度を求めます。肩と腕それぞれ求めた角度分戻すとTスタンスになります。
また、親指が、Perception Neuron側では閉じているのに対し、MMM側では開いています。
Tスタンスに変換後、グローバル座標系のx軸に沿うように親指1ボーンをY軸回転させて、親指を閉じさせます。
##ボーンの回転軸の違い
Axis Neuronから送信されるモーションデータの回転角はボーンのローカル座標系における回転角ですが、MMM側で回転を指定する際にはデフォルトポーズ時におけるMMMのグローバル座標系に一致したローカル座標系で指定することになります。
そのため、肩、腕、ひじ、手首、指についてはAxis Neuron側の回転をかける際に、Axis Neuron側とMMM側で回転軸が合うようにボーンを回転させておく必要があります。Tスタンスへの変換と合わせると、行列の計算は以下のようになります。
Tスタンスへの回転行列×「回転軸を合わせるための回転行列」の逆行列×肩、腕、ひじ、手首、指ボーンの回転行列×回転軸を合わせるための回転行列
親指1ボーンの場合は、上記計算に親指を閉じさせる回転を加えます。
親指を閉じる回転×Tスタンスへの回転行列×「回転軸を合わせるための回転行列」の逆行列×親指1ボーンの回転行列×回転軸を合わせるための回転行列
##Perception NeuronのSpineボーンとMMMの上半身ボーンの違い
Perception NeuronのSpineボーンはHipsボーンの子ですが、MMMの上半身ボーンは下半身ボーンと親子関係にありません。MMM側で、Hipsボーンの回転を上半身ボーンにも適用させる必要があります。行列の計算は以下のようになります。
Hipsボーンの回転行列×上半身ボーンの回転行列
##センター位置のリセット
直立時にセンターボーンのY座標が0になるように、直立時のPerception NeuronのHipsボーンのY座標の値をオフセット値として使っています。
#プラグインの使い方
今回作成したプラグイン(NeuronReceiver.dll)をこちらで配布しています。Perception Neuronの公式サイトからAxis NeuronとNeuronDataReader SDKをダウンロードしてください。MMMはこちらからダウンロードできます。(上で説明した内容をプラグインの内部で処理しています。このプラグインを使うにあたって、モデルのTスタンスへの修正などは必要ありません)
##Axis Neuronの設定
File->Settings->Broadcastingを選びます。TCPを選択し、BVHにチェックをいれてください。Portは7001にしておきます。
File->Settings->Output Formatを選びます。Frequency reducingは1、Block typeはBinary、RotationはYXZを選んでください。
##プラグインの導入
NeuronDataReader SDKのWindows\lib\x64\NeuronDataReader.dllをMMMの実行ファイルと同じフォルダなどパスの通っている場所にコピーしてください(32bit版のMMMを使用する場合はWindows\lib\x86\NeuronDataReader.dllを使用してください)。
Pulginsフォルダにプラグイン以外のdllを入れるとMMM起動時に以下のエラーになってしまうので注意してください。(対処法をサメジ部長 (@samezi)さんに教えていただきました。ありがとうございました)
NeuronReceiver.dllをMMMのPluginsフォルダにコピーしてください。
MMMを起動してください。起動時に以下のエラーが出ることがあるようです。その場合は、こちらの対処方法を参考にしてください。
NeuronReceiverプラグインのConnectボタンを押します。
Axis Neuronへの接続が成功すると以下のダイアログが表示されます。
接続に失敗すると以下のダイアログが表示されます。Axis Neuronが起動しているか確認してください。起動している場合は、Broadcastingの設定内容を確認してください。
直立時に、NeuronReceiverプラグインのセンター位置リセットボタンを押してください。モデルが接地します。
MMDモデルの体型によっては、移動時に足が滑ったり、しゃがんだときに地面に潜り込むことがあります。NeuronReceiverプラグインのY軸スケール係数とXZ平面スケール係数を調整することで、ある程度改善させることができます。
こんな感じで動かすことができます。
ヤバイめっさ楽しい pic.twitter.com/MBzfSeNtGE
— aimino (@minoaimino) 2015, 12月 21
##モーションの記録
はじめに、0フレーム目に足のIKを切った状態をキーフレーム登録しておきます。NeuronReceiverプラグインのRecordボタンを押すと、モーションの記録を開始します。
タイムラインにキーフレームが登録されます。キーフレームは全フレームに登録されます。
記録したモーションをMMM上で再生する場合は、NeuronReceiverのプラグインを無効化してから再生してください。また、PCの性能にもよりますが、大量にキーフレームを登録しているためか500~600フレームを超えたあたりからフレームレートが低下し、キーフレーム登録が飛び飛びになってしまうことが確認されています。
##記録したモーションをMMDで使う
VMDフォーマットで保存します。
##記録したモーションをUnityで使う
Noraさん(@Stereoarts)のMMD4Mechanimを使うことで、UnityでMMDモデルやMMDで作成したモーションを使うことができます。MMD4MechanimはStereoarts Homepageからダウンロードすることができます。詳しい使い方はこちらに載っています。
#課題
腰の位置を基準として各ボーンに回転の値をセットしているだけなので、Perception Neuron側とMMM側とでモデルの体型が異なる場合、足先の位置が一致しません。そのため屈伸や前屈のような本来足先が動かない動作で、足先が動いてしまうことがあります。
Axis NeuronとUnityのサンプルとMMMで比較。足先が動いちゃうのどうにかしたいな。。 pic.twitter.com/sSBu6XT3Pb
— aimino (@minoaimino) 2015, 12月 12
また、Axis NeuronとUnity Integrationのサンプルとで比較すると、Unityの方では足先が動いてしまっているので、体型の違い以外にも何か原因があるのかもしれません。(Axis Neuronでは足の接地判定を行って足を固定している?)
Axis NeuronとUnityのサンプルの足の動き比較 pic.twitter.com/rqL33H9D5k
— aimino (@minoaimino) 2015, 12月 11