はじめに
前回の投稿から約1ヶ月ほど空きました。
何をしていたかと言えば、両手もモデルを使えるように試行錯誤していました。
MediaPipeのそもそもの精度の問題なのか、どうしても納得いく制御ができず🥺
ある程度の形になったところで区切ることにしました。
未だ課題はありますが、こういう方法もある、というところで残そうと思います。
Python
こちらから変更なし
Blender
作成したモデルはこちら。
基本構造は前回同様ですが、両手のボーンを変更しました。具体的には、手首から各指に繋がる骨を削除しました。
こちらは前回作成したモデルです。手首から指へのボーンがありますね。
なぜ削除したかというと、このボーンと指のボーンに26°ほどの角度が付いているのが不都合だからです。
Unityスクリプトでは、掌と指の角度で回転量を算出するのですが、もともと角度が付いてしまっていると、この計算が難しくなります。
さらに、Unityと軸を合わせるため、以下の操作をしました。
1. -y方向を正面とする
2. オブジェクトモードで、x軸を-y方向へ90°回転
3. 編集モードで、x軸をy方向へ90°回転
見た目は元の状態に戻っていますが、実際には90°下向きになっています。
Unity
スクリプト
MediaPipe管理
こちらから変更なし
UDP管理
こちらから変更なし
顔パーツ管理
今回はNoseMove()での、顔の回転制御を変更しました。
private void NoseMove(PartsData_face face)
{
Transform Nose_Transform = Nose.transform;
Vector3 Nose_pos = Nose_Transform.localPosition;
Nose_pos.x = Nose_pos_base.x + ((face.nose[0]*6f) * (1f));
Nose_pos.y = Nose_pos_base.y + ((face.nose[1]*3f) * (-1f));
Nose_pos.z = Nose_pos_base.z + ((face.nose[2]*100f) * (-1f));
Nose_Transform.localPosition = Nose_pos;
Vector3 top = new Vector3(face.top[0], face.top[1]*(-1f), (face.top[2]*(-1f)));
Vector3 right = new Vector3(face.right[0], face.right[1]*(-1f), face.right[2]*(-1f));
Vector3 left = new Vector3(face.left[0], face.left[1]*(-1f), face.left[2]*(-1f));
Vector3 bottom = new Vector3(face.bottom[0], face.bottom[1]*(-1f), (face.bottom[2]*(-1f)));
Vector3 center_right_left = Vector3.Lerp(right, left, 0.5f);
facedir1.transform.position = center_right_left;
facedir1.transform.LookAt(left, top-bottom);
facedir2.transform.LookAt(facedir1.transform.position, top-bottom);
Nose_Transform.eulerAngles = facedir2.transform.eulerAngles;
}
顔の回転制御を正確に計算することは難しいので、Vector3.LookAt()を使用します。
facedir1・facedir2という2つのオブジェクトを用意し、facedir2はfacedir1の子オブジェクトとして設定します。
こうすることで、facedir2は顔全体の回転を表すことができるようになります。
右手パーツ管理
ランドマーク同士を接続するオブジェクトを生成する処理はコメントアウトで残しています。実際には HandPosition()・HandAngle()・FingerAngle() を使用し、手のモデルの制御を行います。
private void HandPosition(GameObject[] hand_objs, Vector3[] pos_list)
{
Transform transform = hand_objs[(int)RightHandLandmarkName.Wrist].transform;
Vector3 pos = pos_list[(int)RightHandLandmarkName.Wrist];
pos.x = (pos_list[(int)RightHandLandmarkName.Wrist].x * 12f) + (-3f);
pos.y = (pos_list[(int)RightHandLandmarkName.Wrist].y * 12f) + (8f);
pos.z = (pos_list[(int)RightHandLandmarkName.Thumb_Cmc].z * 20f);
transform.position = pos;
}
手全体の移動を制御します。今回はMediaPipe側でhand_landmarksを使用しています。xとyは移動可能ですが、z軸は変化しないため、zのみThumb_Cmcを使用します。
左手パーツ管理
基本構造は右手パーツ管理と同様です。
エディタ設定
オブジェクトがzプラス方向を正面としているので、カメラはそれと正対するように配置します。
右手の割り当てはこうなっています。手首から第1関節に伸びるボーンはモデルにありませんが、スクリプトには存在するので、ダミーオブジェクトを割り当てています。
動作確認
ある程度動いてはいますが、手全体の回転によって、関節の角度がおかしくなることがあります。
ともあれ、それなりに動かせるようにはなりました😁
さいごに
力技で実装した部分が多々ありますが、ある程度アバターとして動かせるようになってよかったです。
ここからどう改善していくか、これから検討していきます。
それでは、今回はここまで。ありがとうございました😊