Oculus Questのハンドトラッキングを雑にまとめる
ハンドトラッキングがSDKで使用できるようになったので自分用メモということで簡単にまとめていこう。7割程度、自分用メモ
まずどこを見ればよいのか?
とりあえずこのページを読んでおけば一通りの設定はできそう developer.oculus.com
最短の設定
AndroidにSwitch Platform、Oculus Integrationをインポート済み前提
- OVRCameraRigをHierarchyに入れる
- OVR Managerの Hand Tracking Supportを「Controllers and Hands」か「Hands Only」に変更する
- LeftHandAnchor、RightHandAnchor直下にOVRHandPrefabを追加
- RightHandに設定したOVR Hand、OVR Skelton、OVR Meshをそれぞれ「Hand Right」に設定する
- ビルド
OVRHandPrefabの構成
OVR Skeleton
ボーン情報やジェスチャの検出、衝突判定まわりが含まれている。
- Skelton Type:右手か左手か
- Update Root Pose:OVR Camera Rigの下層に配置する場合チェックを付けない。OVRHandPrefab単体で使用する場合チェックをつける。
- Update Root Scale:トラッキングしている手のサイズを反映するかどうか(あんまり差がわからなかった)
- Enable Physics Capsules:物理挙動、衝突を有効にする
OVR Mesh
描画に使用するメッシュの情報。中身を見るとOVRPluginからメッシュを引っ張ってきてるみたい。
OVR Mesh Renderer
OVR SkeletonとOVR Meshから返されたデータを組み合わせて、手のアニメーション化された3Dモデルを生成します。(原文翻訳まま)
Inspectorに出ている値、特に設定しない場合同一オブジェクトからGetComponentして取得している。
if (_ovrMesh == null) { _ovrMesh = GetComponent<OVRMesh>(); } if (_ovrSkeleton == null) { _ovrSkeleton = GetComponent<OVRSkeleton>(); }
OVR Skeleton Renderer
ボーンを描画する。使用する際にはOVR Mesh Rendererのチェックを外しておいた方が良くみえる。下図は左がOVR Skeleton Rendererを有効にした状態。
ボーンID
OVRSkelton.cs内でBoneIDが以下のように定義されている。
public enum BoneId { Invalid = OVRPlugin.BoneId.Invalid, Hand_Start = OVRPlugin.BoneId.Hand_Start, Hand_WristRoot = OVRPlugin.BoneId.Hand_WristRoot, // root frame of the hand, where the wrist is located Hand_ForearmStub = OVRPlugin.BoneId.Hand_ForearmStub, // frame for user's forearm Hand_Thumb0 = OVRPlugin.BoneId.Hand_Thumb0, // thumb trapezium bone Hand_Thumb1 = OVRPlugin.BoneId.Hand_Thumb1, // thumb metacarpal bone Hand_Thumb2 = OVRPlugin.BoneId.Hand_Thumb2, // thumb proximal phalange bone Hand_Thumb3 = OVRPlugin.BoneId.Hand_Thumb3, // thumb distal phalange bone Hand_Index1 = OVRPlugin.BoneId.Hand_Index1, // index proximal phalange bone Hand_Index2 = OVRPlugin.BoneId.Hand_Index2, // index intermediate phalange bone Hand_Index3 = OVRPlugin.BoneId.Hand_Index3, // index distal phalange bone Hand_Middle1 = OVRPlugin.BoneId.Hand_Middle1, // middle proximal phalange bone Hand_Middle2 = OVRPlugin.BoneId.Hand_Middle2, // middle intermediate phalange bone Hand_Middle3 = OVRPlugin.BoneId.Hand_Middle3, // middle distal phalange bone Hand_Ring1 = OVRPlugin.BoneId.Hand_Ring1, // ring proximal phalange bone Hand_Ring2 = OVRPlugin.BoneId.Hand_Ring2, // ring intermediate phalange bone Hand_Ring3 = OVRPlugin.BoneId.Hand_Ring3, // ring distal phalange bone Hand_Pinky0 = OVRPlugin.BoneId.Hand_Pinky0, // pinky metacarpal bone Hand_Pinky1 = OVRPlugin.BoneId.Hand_Pinky1, // pinky proximal phalange bone Hand_Pinky2 = OVRPlugin.BoneId.Hand_Pinky2, // pinky intermediate phalange bone Hand_Pinky3 = OVRPlugin.BoneId.Hand_Pinky3, // pinky distal phalange bone Hand_MaxSkinnable = OVRPlugin.BoneId.Hand_MaxSkinnable, // Bone tips are position only. They are not used for skinning but are useful for hit-testing. // NOTE: Hand_ThumbTip == Hand_MaxSkinnable since the extended tips need to be contiguous Hand_ThumbTip = OVRPlugin.BoneId.Hand_ThumbTip, // tip of the thumb Hand_IndexTip = OVRPlugin.BoneId.Hand_IndexTip, // tip of the index finger Hand_MiddleTip = OVRPlugin.BoneId.Hand_MiddleTip, // tip of the middle finger Hand_RingTip = OVRPlugin.BoneId.Hand_RingTip, // tip of the ring finger Hand_PinkyTip = OVRPlugin.BoneId.Hand_PinkyTip, // tip of the pinky Hand_End = OVRPlugin.BoneId.Hand_End, // add new bones here Max = OVRPlugin.BoneId.Max }
OVRSkelton.csではボーンに関するメソッドが用意されている。試しに以下のようなスクリプトを作ってGetCurrentStartBoneID、_skeleton.GetCurrentEndBoneId、GetCurrentNumBones、GetCurrentNumSkinnableBonesを表示してみる。
using UnityEngine; using UnityEngine.UI; public class SkeletonInfo : MonoBehaviour { [SerializeField]OVRSkeleton _skeleton; Text _info; // Start is called before the first frame update void Start() { _info = GetComponent<Text>(); _info.text += $"StartBoneId : {_skeleton.GetCurrentStartBoneId()}\n"; _info.text += $"EndBoneId : {_skeleton.GetCurrentEndBoneId()}\n"; _info.text += $"CurrentNumBones : {_skeleton.GetCurrentNumBones()}\n"; _info.text += $"CurrentNumSkinnableBones : {_skeleton.GetCurrentNumSkinnableBones()}\n"; } }
GetCurrentStartBoneID、_skeleton.GetCurrentEndBoneIdはどちらもBone IDのHand_StartとHand_Endが取得できるよう。GetCurrentNumBonesではスケルトン内のボーン数が、GetCurrentNumSkinnableBonesでは指先の~Tipを除外した数が取得できる
手がトラッキングできてるかどうか
OVRHandのIsTrackedで手がトラッキングできているかをBool値で取得できる。HandConfidenceはトラッキングされた手の信頼度をTrackingConfidence型で取得できる。値はLowかHighのどちらか。 以下のようなスクリプトを作成し、動かしながら挙動を確認してみる
using UnityEngine; using UnityEngine.UI; public class HandInfo : MonoBehaviour { [SerializeField]OVRHand _hand; Text _info; // Start is called before the first frame update void Start() { _info = GetComponent<Text>(); } // Update is called once per frame void Update() { _info.text = ""; _info.text += $"IsTracked : {_hand.IsTracked}\n"; _info.text += $"HandConfidence : {_hand.HandConfidence}"; } }
両手が近くても描画したい!
どうやら「IsTrackedがtrueでもTrackingConfidenceがfalseだと手が描画されない」っていうのがデフォルトっぽい。これを修正したい場合、OVRHand.csの中でHandConfidenceを判定している部分を書き換えるとIsTrackedのみでも手が描画されるようになった。
OVRMeshRenderer.MeshRendererData OVRMeshRenderer.IOVRMeshRendererDataProvider.GetMeshRendererData() { var data = new OVRMeshRenderer.MeshRendererData(); data.IsDataValid = _isInitialized; if (_isInitialized) { // ↓ここの判定を書き換える data.IsDataHighConfidence = IsTracked;// && HandConfidence == TrackingConfidence.High; } return data; }
とりあえずここまで。