Unityでシースルー機能を使い3Dモデルを動かしたい
UnityとMiragesolo with Daydream を同期し、シースルー機能を使用してMR空間を作成しました。
そこに3Dモデルを配置し、3Dモデルを躍らせるscriptをアタッチしたのですが
Unityの再生ボタンでは3Dモデルは問題なく動作するのに、Miragesoloへbuild and runすると動かなくなります。
##3Dモデルを動かす方法
https://qiita.com/kenkra/items/7b5634ff7f8c6bf0257a 参考にしたサイト
openposeと3d-pose-baselineを使用し、実際に人物が映っている動画から関節座標を読み取り、その関節座標をテキスト化したpos.txtを作成します。
その関節座標を3Dモデルの関節に反映して動いてもらうためのscriptを作成しました。
そのscriptを対象の3Dモデルにアタッチすればモデルがその通りに動くようになります。
アニメーションコントローラーは使いません。
##シースルー機能を使う方法
https://qiita.com/OKsaiyowa/items/406ff18f863d7bd3e6e0 参考にしたサイト
GoogleVRをダウンロードし、最初からMR空間を再現できます。
発生している問題・エラーメッセージ
scriptのコードのエラー文は出ていません。ですが、pos.txtを読み込むscriptのコードをはります。
該当のソースコード
C#
1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using System.IO; 5using System; 6 7// pos.txtのデータ 8// https://github.com/miu200521358/3d-pose-baseline-vmd/blob/master/doc/Output.md 9// 0 :Hip 10// 1 :RHip 11// 2 :RKnee 12// 3 :RFoot 13// 4 :LHip 14// 5 :LKnee 15// 6 :LFoot 16// 7 :Spine 17// 8 :Thorax 18// 9 :Neck/Nose 19// 10:Head 20// 11:LShoulder 21// 12:LElbow 22// 13:LWrist 23// 14:RShoulder 24// 15:RElbow 25// 16:RWrist 26 27public class Pos_txt_Reader : MonoBehaviour 28{ 29 float scale_ratio = 0.001f; // pos.txtとUnityモデルのスケール比率 30 // pos.txtの単位はmmでUnityはmのため、0.001に近い値を指定。モデルの大きさによって調整する 31 float heal_position = 0.05f; // 足の沈みの補正値(単位:m)。プラス値で体全体が上へ移動する 32 float head_angle = 15f; // 顔の向きの調整 顔を15度上げる 33 34 public String pos_filename; // pos.txtのファイル名 35 public Boolean debug_cube; // デバッグ用Cubeの表示フラグ 36 public int start_frame; // 開始フレーム 37 public String end_frame; // 終了フレーム 38 float play_time; // 再生時間 39 Transform[] bone_t; // モデルのボーンのTransform 40 Transform[] cube_t; // デバック表示用のCubeのTransform 41 Vector3 init_position; // 初期のセンターの位置 42 Quaternion[] init_rot; // 初期の回転値 43 Quaternion[] init_inv; // 初期のボーンの方向から計算されるクオータニオンのInverse 44 List<Vector3[]> pos; // pos.txtのデータを保持するコンテナ 45 int[] bones = new int[10] { 1, 2, 4, 5, 7, 8, 11, 12, 14, 15 }; // 親ボーン 46 int[] child_bones = new int[10] { 2, 3, 5, 6, 8, 10, 12, 13, 15, 16 }; // bonesに対応する子ボーン 47 int bone_num = 17; 48 Animator anim; 49 int s_frame; 50 int e_frame; 51 52 // pos.txtのデータを読み込み、リストで返す 53 List<Vector3[]> ReadPosData(string filename) { 54 List<Vector3[]> data = new List<Vector3[]>(); 55 56 List<string> lines = new List<string>(); 57 StreamReader sr = new StreamReader(filename); 58 while (!sr.EndOfStream) { 59 lines.Add(sr.ReadLine()); 60 } 61 sr.Close(); 62 63 foreach (string line in lines) { 64 string line2 = line.Replace(",", ""); 65 string[] str = line2.Split(new string[] { " " }, System.StringSplitOptions.RemoveEmptyEntries); // スペースで分割し、空の文字列は削除 66 67 Vector3[] vs = new Vector3[bone_num]; 68 for (int i = 0; i < str.Length; i += 4) { 69 vs[(int)(i/4)] = new Vector3(-float.Parse(str[i + 1]), float.Parse(str[i + 3]), -float.Parse(str[i + 2])); 70 } 71 data.Add(vs); 72 } 73 return data; 74 } 75 76 // BoneTransformの取得。回転の初期値を取得 77 void GetInitInfo() 78 { 79 bone_t = new Transform[bone_num]; 80 init_rot = new Quaternion[bone_num]; 81 init_inv = new Quaternion[bone_num]; 82 83 bone_t[0] = anim.GetBoneTransform(HumanBodyBones.Hips); 84 bone_t[1] = anim.GetBoneTransform(HumanBodyBones.RightUpperLeg); 85 bone_t[2] = anim.GetBoneTransform(HumanBodyBones.RightLowerLeg); 86 bone_t[3] = anim.GetBoneTransform(HumanBodyBones.RightFoot); 87 bone_t[4] = anim.GetBoneTransform(HumanBodyBones.LeftUpperLeg); 88 bone_t[5] = anim.GetBoneTransform(HumanBodyBones.LeftLowerLeg); 89 bone_t[6] = anim.GetBoneTransform(HumanBodyBones.LeftFoot); 90 bone_t[7] = anim.GetBoneTransform(HumanBodyBones.Spine); 91 bone_t[8] = anim.GetBoneTransform(HumanBodyBones.Neck); 92 bone_t[10] = anim.GetBoneTransform(HumanBodyBones.Head); 93 bone_t[11] = anim.GetBoneTransform(HumanBodyBones.LeftUpperArm); 94 bone_t[12] = anim.GetBoneTransform(HumanBodyBones.LeftLowerArm); 95 bone_t[13] = anim.GetBoneTransform(HumanBodyBones.LeftHand); 96 bone_t[14] = anim.GetBoneTransform(HumanBodyBones.RightUpperArm); 97 bone_t[15] = anim.GetBoneTransform(HumanBodyBones.RightLowerArm); 98 bone_t[16] = anim.GetBoneTransform(HumanBodyBones.RightHand); 99 100 // Spine,LHip,RHipで三角形を作ってそれを前方向とする。 101 Vector3 init_forward = TriangleNormal(bone_t[7].position, bone_t[4].position, bone_t[1].position); 102 init_inv[0] = Quaternion.Inverse(Quaternion.LookRotation(init_forward)); 103 104 init_position = bone_t[0].position; 105 init_rot[0] = bone_t[0].rotation; 106 for (int i = 0; i < bones.Length; i++) { 107 int b = bones[i]; 108 int cb = child_bones[i]; 109 110 // 対象モデルの回転の初期値 111 init_rot[b] = bone_t[b].rotation; 112 // 初期のボーンの方向から計算されるクオータニオン 113 init_inv[b] = Quaternion.Inverse(Quaternion.LookRotation(bone_t[b].position - bone_t[cb].position,init_forward)); 114 } 115 } 116 117 // 指定の3点でできる三角形に直交する長さ1のベクトルを返す 118 Vector3 TriangleNormal(Vector3 a, Vector3 b, Vector3 c) 119 { 120 Vector3 d1 = a - b; 121 Vector3 d2 = a - c; 122 123 Vector3 dd = Vector3.Cross(d1, d2); 124 dd.Normalize(); 125 126 return dd; 127 } 128 129 // デバック用cubeを生成する。生成済みの場合は位置を更新する 130 void UpdateCube(int frame) 131 { 132 if (cube_t == null) { 133 // 初期化して、cubeを生成する 134 cube_t = new Transform[bone_num]; 135 136 for (int i = 0; i < bone_num; i++) { 137 Transform t = GameObject.CreatePrimitive(PrimitiveType.Cube).transform; 138 t.transform.parent = this.transform; 139 t.localPosition = pos[frame][i] * scale_ratio; 140 t.name = i.ToString(); 141 t.localScale = new Vector3(0.05f, 0.05f, 0.05f); 142 cube_t[i] = t; 143 144 Destroy(t.GetComponent<BoxCollider>()); 145 } 146 } 147 else { 148 // モデルと重ならないように少しずらして表示 149 Vector3 offset = new Vector3(1.2f, 0, 0); 150 151 // 初期化済みの場合は、cubeの位置を更新する 152 for (int i = 0; i < bone_num; i++) { 153 cube_t[i].localPosition = pos[frame][i] * scale_ratio + new Vector3(0, heal_position, 0) + offset; 154 } 155 } 156 } 157 158 void Start() 159 { 160 anim = GetComponent<Animator>(); 161 play_time = 0; 162 if (System.IO.File.Exists (pos_filename) == false) { 163 Debug.Log("<color=blue>Error! Pos file not found(" + pos_filename + "). Check Pos_filename in Inspector.</color>"); 164 } 165 pos = ReadPosData(pos_filename); 166 GetInitInfo(); 167 if (pos != null) { 168 // inspectorで指定した開始フレーム、終了フレーム番号をセット 169 if (start_frame >= 0 && start_frame < pos.Count) { 170 s_frame = start_frame; 171 } else { 172 s_frame = 0; 173 } 174 int ef; 175 if (int.TryParse(end_frame, out ef)) { 176 if (ef >= s_frame && ef < pos.Count) { 177 e_frame = ef; 178 } else { 179 e_frame = pos.Count - 1; 180 } 181 } else { 182 e_frame = pos.Count - 1; 183 } 184 Debug.Log("End Frame:" + e_frame.ToString()); 185 } 186 } 187 188 void Update() 189 { 190 if (pos == null) { 191 return; 192 } 193 play_time += Time.deltaTime; 194 195 int frame = s_frame + (int)(play_time * 30.0f); // pos.txtは30fpsを想定 196 if (frame > e_frame) { 197 play_time = 0; // 繰り返す 198 frame = s_frame; 199 } 200 201 if (debug_cube) { 202 UpdateCube(frame); // デバッグ用Cubeを表示する 203 } 204 205 Vector3[] now_pos = pos[frame]; 206 207 // センターの移動と回転 208 Vector3 pos_forward = TriangleNormal(now_pos[7], now_pos[4], now_pos[1]); 209 bone_t[0].position = now_pos[0] * scale_ratio + new Vector3(init_position.x, heal_position, init_position.z); 210 bone_t[0].rotation = Quaternion.LookRotation(pos_forward) * init_inv[0] * init_rot[0]; 211 212 // 各ボーンの回転 213 for (int i = 0; i < bones.Length; i++) { 214 int b = bones[i]; 215 int cb = child_bones[i]; 216 bone_t[b].rotation = Quaternion.LookRotation(now_pos[b] - now_pos[cb], pos_forward) * init_inv[b] * init_rot[b]; 217 } 218 219 // 顔の向きを上げる調整。両肩を結ぶ線を軸として回転 220 bone_t[8].rotation = Quaternion.AngleAxis(head_angle, bone_t[11].position - bone_t[14].position) * bone_t[8].rotation; 221 } 222}
試したこと
キューブをx軸方向に動かすプログラムはMiragesoloにbuild and runしても動きました。
pos.txtを読み込んで3Dモデルを動かすことだけができません。
補足情報(FW/ツールのバージョンなど)
Unity2018.3.14f1(64-bit)
あなたの回答
tips
プレビュー