始めに
前回、Vive pro eyeを簡単に試してみました。
今回は解説しきれなかったSDKに用意されているメソッドについてまとめようと思います。 バージョンはv2です。
途中、だらだら書いているので簡単に見たい方はまとめをご覧ください。
目次
- 始めに
- 目次
- SRanipal_Eye_v2.UpdateData(private)
- SRanipal_Eye_v2.GetVerboseData
- SRanipal_Eye_v2.GetEyeOpenness
- SRanipal_Eye_v2.TryGaze
- SRanipal_Eye_v2.GetGazeRay
- SRanipal_Eye_v2.Focus
- SRanipal_Eye_v2.GetPupilPosition
- SRanipal_Eye_v2.LaunchEyeCalibration
- まとめ
- 最後に
SRanipal_Eye_v2.UpdateData(private)
このメソッドはprivate
であり外部からは呼び出すことは出来ませんが、SRanipal_Eye_v2のメソッドからは頻繁に使われています。
このメソッドではSRanipal_Eye_API.GetEyeData_v2
を呼び出しトラッキング情報を取得します。また、1フレーム内で複数回トラッキング情報をAPIから取得しないようになっていおります。
private static bool UpdateData() { if (Time.frameCount == LastUpdateFrame) return LastUpdateResult == Error.WORK; else LastUpdateFrame = Time.frameCount; LastUpdateResult = SRanipal_Eye_API.GetEyeData_v2(ref EyeData_); return LastUpdateResult == Error.WORK; }
SRanipal_Eye_v2.GetVerboseData
こちらはVerboseData(左右それぞれの情報と両目合わせた情報)を取得することができます。コードを見るに返り値はtrue
で固定のようです。
こちらは2つ用意されており、1つは引数にEyeData_v2
を渡してそこから取得するものとUpdateData()
を呼び出してから取得するものがあります。
public static bool GetVerboseData(out VerboseData data, EyeData_v2 eye_data) { data = eye_data.verbose_data; return true; } public static bool GetVerboseData(out VerboseData data) { UpdateData(); return GetVerboseData(out data, EyeData_); }
SRanipal_Eye_v2.GetEyeOpenness
こちらは引数で右目、左目を指定してその目がどれだけ開いているかを取得します。取り出している値は
SingleEyeData.eye_openness
になります。こちらも返り値は必ずtrue
のようです。
こちらのメソッドも2つ用意されており、1つはEyeData_v2
を渡してそこから取得するものとUpdateData()
を呼び出してから取得するものです。
public static bool GetEyeOpenness(EyeIndex eye, out float openness, EyeData_v2 eye_data) { if (SRanipal_Eye_Framework.Status == SRanipal_Eye_Framework.FrameworkStatus.WORKING) { SingleEyeData eyeData = eye == EyeIndex.LEFT ? eye_data.verbose_data.left : eye_data.verbose_data.right; bool valid = eyeData.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_EYE_OPENNESS_VALIDITY); openness = valid ? eyeData.eye_openness : 0; } else { // If not support eye tracking, set default to open. openness = 1; } return true; } public static bool GetEyeOpenness(EyeIndex eye, out float openness) { UpdateData(); return GetEyeOpenness(eye, out openness, EyeData_); }
SRanipal_Eye_v2.TryGaze
こちらは引数でSingleEyeDataValidity
を渡して、指定されたデータが正しく取得できているかを確かめるメソッドです。左右それぞれの目と両目すべてが正しい場合にtrueを返します。
SingleEyeDataValidity
はenum型で、目の位置や方向などを指定できます。
こちらも1つはeye_data
から取得するものとUpdateData()
を呼び出すものの2種類あります。
public static bool TryGaze(SingleEyeDataValidity validity, out GazeIndex gazeIndex, EyeData_v2 eye_data) { bool[] valid = new bool[(int)GazeIndex.COMBINE + 1] { eye_data.verbose_data.left.GetValidity(validity), eye_data.verbose_data.right.GetValidity(validity), eye_data.verbose_data.combined.eye_data.GetValidity(validity)}; gazeIndex = GazeIndex.COMBINE; for (int i = (int)GazeIndex.COMBINE; i >= 0; --i) { if (valid[i]) { gazeIndex = (GazeIndex)i; return true; } } return false; } public static bool TryGaze(SingleEyeDataValidity validity, out GazeIndex gazeIndex) { UpdateData(); return TryGaze(validity, out gazeIndex, EyeData_); }
SRanipal_Eye_v2.GetGazeRay
このメソッドは目線の出発点と方向、もしくは目線を表すRayを求めるメソッドになります。
引数で右目、左目、両目を指定し、取得が成功すればtrueを返します。(API自体が動いていなかった場合もtrueを返す点に注意)
EyeData_v2
を引数に与えた場合はEyeData_v2
からRayを求め、そうでない場合はUpdateData()
を呼び出してから求めています。
// 出発点、方向を求める public static bool GetGazeRay(GazeIndex gazeIndex, out Vector3 origin, out Vector3 direction, EyeData_v2 eye_data); public static bool GetGazeRay(GazeIndex gazeIndex, out Vector3 origin, out Vector3 direction); // Rayを求める public static bool GetGazeRay(GazeIndex gazeIndex, out Ray ray, EyeData_v2 eye_data) public static bool GetGazeRay(GazeIndex gazeIndex, out Ray ray)
こちらのメソッドの処理を詳しく見ると、トラッキング情報のみでRayを求めていることがわかりました。 なので、こちらはHMDの位置や方向は考慮されていません。
origin = eyesData[(int)gazeIndex].gaze_origin_mm * 0.001f; direction = eyesData[(int)gazeIndex].gaze_direction_normalized; // トラッキング情報は右手座標, Unityは左手座標系なのでx方向を反転 origin.x *= -1; direction.x *= -1;
SRanipal_Eye_v2.Focus
こちらは実際にRayを飛ばして、その結果をFocusInfo
に保存するメソッドです。
このメソッドはオーバーロードを多用していますが、基本的にRayの距離、飛ばすRayの半径、Rayが当たるLayerを指定するか、EyeData_v2
を渡すかとなっており実装自体に差はありません。
public static bool Focus(GazeIndex index, out Ray ray, out FocusInfo focusInfo, float radius, float maxDistance, int focusableLayer, EyeData_v2 eye_data); public static bool Focus(GazeIndex index, out Ray ray, out FocusInfo focusInfo, float radius, float maxDistance, int focusableLayer); public static bool Focus(GazeIndex index, out Ray ray, out FocusInfo focusInfo, float radius, float maxDistance, EyeData_v2 eye_data) public static bool Focus(GazeIndex index, out Ray ray, out FocusInfo focusInfo, float radius, float maxDistance) public static bool Focus(GazeIndex index, out Ray ray, out FocusInfo focusInfo, float maxDistance, EyeData_v2 eye_data) public static bool Focus(GazeIndex index, out Ray ray, out FocusInfo focusInfo, EyeData_v2 eye_data) public static bool Focus(GazeIndex index, out Ray ray, out FocusInfo focusInfo)
実装としてはGetGazeRay
から目線の方向を求めて、HMDの位置と方向と組み合わせてRayを飛ばします。見ている個所を取得したい場合はGetGazeRay
ではなくこのFocus
を使うことになるでしょう。
Ray rayGlobal = new Ray(Camera.main.transform.position, Camera.main.transform.TransformDirection(ray.direction)); RaycastHit hit; if (radius == 0) valid = Physics.Raycast(rayGlobal, out hit, maxDistance, focusableLayer); else valid = Physics.SphereCast(rayGlobal, radius, out hit, maxDistance, focusableLayer); focusInfo = new FocusInfo { point = hit.point, normal = hit.normal, distance = hit.distance, collider = hit.collider, rigidbody = hit.rigidbody, transform = hit.transform };
SRanipal_Eye_v2.GetPupilPosition
こちらは目の位置を-1 ~ 1に正規化するメソッドです。 取得した結果ではx軸は右方向が正、y軸は上方向が正となります。 もともとのデータではy軸の方向が反転しているためこちらを呼び出して正規化した方がよいでしょう。
public static bool GetPupilPosition(EyeIndex eye, out Vector2 postion, EyeData_v2 eye_data) { bool valid = false; if (SRanipal_Eye_Framework.Status == SRanipal_Eye_Framework.FrameworkStatus.WORKING) { SingleEyeData eyeData = eye == EyeIndex.LEFT ? eye_data.verbose_data.left : eye_data.verbose_data.right; valid = eyeData.GetValidity(SingleEyeDataValidity.SINGLE_EYE_DATA_PUPIL_POSITION_IN_SENSOR_AREA_VALIDITY); postion = valid ? postion = new Vector2(eyeData.pupil_position_in_sensor_area.x * 2 - 1, eyeData.pupil_position_in_sensor_area.y * -2 + 1) : Vector2.zero; } else { // If not support eye tracking, set default in middle. postion = Vector2.zero; valid = true; } return valid; } public static bool GetPupilPosition(EyeIndex eye, out Vector2 postion) { UpdateData(); return GetPupilPosition(eye, out postion, EyeData_); }
SRanipal_Eye_v2.LaunchEyeCalibration
こちらはアイトラッキングのキャリブレーションを実行するメソッドになります。
public static bool LaunchEyeCalibration() { int result = SRanipal_Eye_API.LaunchEyeCalibration(IntPtr.Zero); return result == (int)Error.WORK; }
まとめ
SDKに用意されているメソッドは次のようにまとめられると思います。
データを取得する
関数名 | 解説 |
---|---|
GetVerboseData | トラッキング情報を取得 |
GetEyeOpenness | 目をどれぐらい開けているかを取得 |
GetPupilPosition | 目(瞳孔)の位置を取得 |
目線を求める
関数名 | 解説 |
---|---|
GetGazeRay | ローカル空間での目線を求める |
Focus | ワールド空間での目線を求める |
その他
関数名 | 解説 |
---|---|
TryGaze | トラッキング情報が正しく取得できているか |
LaunchEyeCalibration | アイトラッキングのキャリブレーションを始める |
最後に
メソッドの実装を見て、個人的にはSDKは使いやすいものではないと感じています。特に、メソッドの返り値がアイトラッキングが動作していない場合でもtrue
を返す点に違和感を感じます。
// GetGazeRay if (SRanipal_Eye_Framework.Status != SRanipal_Eye_Framework.FrameworkStatus.WORKING) { origin = Camera.main.transform.position; valid = true; // 動作していないのにtrueを返している }
ただし、トラッキング情報をどのように扱えばよいかの参考にはなったので一通り調べてみてよかったと感じています。
また何か興味がわけばもう少しVive pro eyeを触ってみようと思います。
追記
少し遊んでみました。