OculusQuestのハンドトラッキングについて色々調べてみた


初めに

先日OculusIntegrationのアップデートがされ、QuestのハンドトラッキングがUnityでも試せるようになったので、色々調べてみました。

Oculus公式も言ってますが、ハンドトラッキング機能は現在developer preview版となっているので、本リリースの時にはこの記事が役に立たなくなってる可能性もありますが悪しからず

検証環境

  • Unity2019.1.14f1
  • OculusIntegration ver 12.0
  • OculusQuest ver 12.0
  • Windows10

余談ですが、Integraitonはver1.43からver12.0に一気に上がりました。
おそらく、Questのバージョン番号と合わせるためかと

公式リファレンス

https://developer.oculus.com/documentation/quest/latest/concepts/unity-handtracking/
これみたら大体書いてあります。

以降、リファレンスの主だった部分をまとめつつ足りない部分を補ったり、試してみての所感を書いていきます。

公式サンプル

  • Oculus/SampleFramework/Usage/HandInteractionTrainScene
  • Oculus/VR/Scenes/HandTest

などのシーンは参考になると思うので、一度見ておくといいと思います。

また、デバック用にOVRHandTest.csが用意されており、今回紹介した諸々の値を見ることが出来ます。

とりあえず動かしてみる

OVRCameraRigHandAnchor以下にOVRHandPrefabを入れる

なお、OVRManager/HandTrackingSuppportControllersOnlyの時は動作しないので、切り替えてあげる必要があります(自分の環境ではサンプルのHandTestシーンではオフになってました)

OVRHandPrefabの各種設定

  • OVRHand
    • HandType:対応する手を設定
  • OVRMesh
    • MeshType:同上
  • OVRSkelton
    • SkeltonType:同上
    • UpdateRootPose:オンにするとOVRCameraRigとの位置の依存性が無くなる。普通に使う分にはオフのままでいいでしょう
    • UpdateRootScale:現実の手の大きさから、表示される手のモデルの大きさを補正する
    • EnablePhysicsCapsules:コライダーを手につける(Hand_Thumb1_CapsuleColliderとか、Hand_Pinky3_CapsuleColliderなど結構細かくコライダーが分けられている模様)
  • OVRSkeltonRenderer:デフォルトではオフになっているが、オンにすることで手の骨格が表示される

OVRHand

よく使いそうな関数、パラメータをピックアップしていきます

GetFingerIsPinching() と GetFingerPinchStrength()

読んで字のごとくですが、引数として渡した指がピンチしているか否かのbool値とfloat値を返してくれます。
引数には、Thumb, Index, Middle, Ring, Pinky が設定できる

var hand = GetComponent<OVRHand>();

bool isIndexPinching = hand.GetFingerIsPinching(OVRHand.HandFinger.Index);
float  middlePinchStrength = hand.GetFingerPinchStrength(OVRHand.HandFinger.Middle);

Thumb(親指)を指定した場合には、親指と任意の指においてのピンチを見るようです(おそらく親指の先端と一番近い指が選ばれている)

IsTracked

手のトラッキングがとれているかどうか

HandConfidence

トラッキングの信用性
HandConfidence.HighHandConfidence.Lowがあり、手を高速で動かしたり重ねたりするとLowになりました。

手が重なったときに消える件について

これはOculusホーム画面でも同様の処理が行われてましたが、両手が重なったときには手の表示が消えます。
実際には手の表示が消えていても手の座標等の情報自体は生きている(かなり汚いですが)ので、拍手とかの判定くらいには使えそうです。

OVRMeshRenderer.csにて、トラッキングの精度が下がってるとき(IsDataValid && IsDateHighConfidence == false)には手のメッシュを消すという処理が行われてようなので、これをごにょごにょすれば両手が重なったときにも表示を残せそうです。

・・・が、ハンドトラッキングがまだテスト版で、本来はアクセスすることを想定していない部分を弄ることになるのでよほどのことがない限りおすすめしません。

自分も試しにやってみたんですが、カメラ側の手はほとんどぶれませんが、隠れている方の手は結構飛ぶのでメッシュ消したくなるのもわかる感じでした。

各指のTransform情報は取れてるので、試しに各座標にSphereを生成したらこんな感じになりました。

用途によりますが、拍手くらいならとれそうな感じなので割と実用圏内くらいにはトラッキング出来てる気がします。

ハンドジェスチャーについて

例のOculusマークのハンドジェスチャーはOVRHand.csIsSystemGestureInProgressでとれるようです。
内部的には手のひらがカメラ側を向いたときに人差し指のピンチを見ているらしい。

基本的なハンドジェスチャーはOculus側で用意してくれてないかな~とか思ったのですが、自分の調べた範囲ではなさそうなので自作する必要がありそうです。
といってもあまり凝ったものでなければ、前述のGetFingerIsPinching()GetFingerPinchStrength()を使えば簡単に実装できそうです。

とりあえずグー、チョキ、パーを作ったので載せときます。閾値はかなり適当に決めたので調整しつつ使ってもらえればと思います。

public bool Guu
    {
        get
        {
            //薬指と小指は値が0になることが多いのであきらめた
            return (hand.GetFingerPinchStrength(OVRHand.HandFinger.Middle) >= 0.5f &&
                    hand.GetFingerPinchStrength(OVRHand.HandFinger.Index) >= 0.1f);
        }
    }

    public bool Choki
    {
        get
        {
            return ((hand.GetFingerPinchStrength(OVRHand.HandFinger.Pinky) >=0.05f) && (hand.GetFingerPinchStrength(OVRHand.HandFinger.Ring) >= 0.2f));
        }
    }

    public bool Paa
    {
        get
        {
            return (hand.GetFingerPinchStrength(OVRHand.HandFinger.Thumb) <= 0.01f);
        }
    }

void Update()
    {

        if (Guu)
        {
            Debug.Log("GUU");
        }
        else if (Choki)
        {
            Debug.Log("CHOKI");
        }
        else if (Paa)
        {
            Debug.Log("PAA");
        }
        else
        {
            Debug.Log("NO SIGN");
        }

    }

中指と人差し指は割ときれいな値をとれるんですが、薬指小指は若干精度悪い印象でした。

また、グーの判定ですが、実際にグーポーズすればわかると思いますが、指の先端ではなく各指の第1,2関節と親指の先端が交わるため、GetFingerPinchStrength()では満足な値が得られません。

なので、より精度を求める場合には、各指関節の値をとってそれと判定を行った方がいい気がします。(OVRSkelton.Bonesを使って指のボーン情報をとることが出来る)

今回は記事が長くなりそうなので、またの機会にハンドジェスチャーについては書こうと思います。

その他細かな点

  • ハンドトラッキングするためには、Questにビルドする前にQuestのホーム画面の設定で「手を使用」に切り変える必要がある。(アプリ内で切り替える方法があるかは現在未検証)
  • コントローラーと手を同時にトラックすることはできない(今のところは)