さわぐちFMオキーステーション

幸せの鐘が鳴り響き僕はただ嬉しいふりをする

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

ボーン情報やジェスチャの検出、衝突判定まわりが含まれている。

f:id:amidaMangrove:20191226004707p:plain

  • Skelton Type:右手か左手か
  • Update Root Pose:OVR Camera Rigの下層に配置する場合チェックを付けない。OVRHandPrefab単体で使用する場合チェックをつける。
  • Update Root Scale:トラッキングしている手のサイズを反映するかどうか(あんまり差がわからなかった)
  • Enable Physics Capsules:物理挙動、衝突を有効にする

OVR Mesh

描画に使用するメッシュの情報。中身を見るとOVRPluginからメッシュを引っ張ってきてるみたい。

f:id:amidaMangrove:20191226010449p:plain

OVR Mesh Renderer

OVR SkeletonとOVR Meshから返されたデータを組み合わせて、手のアニメーション化された3Dモデルを生成します。(原文翻訳まま)

f:id:amidaMangrove:20191226010547p:plain

Inspectorに出ている値、特に設定しない場合同一オブジェクトからGetComponentして取得している。

   if (_ovrMesh == null)
    {
        _ovrMesh = GetComponent<OVRMesh>();
    }

    if (_ovrSkeleton == null)
    {
        _ovrSkeleton = GetComponent<OVRSkeleton>();
    }

OVR Skeleton Renderer

ボーンを描画する。使用する際にはOVR Mesh Rendererのチェックを外しておいた方が良くみえる。下図は左がOVR Skeleton Rendererを有効にした状態。 f:id:amidaMangrove:20191227175729j:plain

ボーン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を除外した数が取得できる

f:id:amidaMangrove:20191227182931p:plain

手がトラッキングできてるかどうか

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}";
    }
}

f:id:amidaMangrove:20191227190645j:plain f:id:amidaMangrove:20191227190654j:plain

両手が近くても描画したい!

どうやら「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;
    }

f:id:amidaMangrove:20191230163327j:plain

とりあえずここまで。