ついにでたOculusQuestハンドトラッキング×VRMでアバター空間満喫してみた

## はじめに

楽しみに待っていたOculusQuest ハンドトラッキング対応版 OculusIntegrationが公開されたので、
早速どんなのか試してみました。
とにかく早くQuest触りたいという欲求に忠実に品質度外視・検証速度重視/QCDのD偏重した記録をまとめたものになります。
それゆえに内容は今後修正・加筆されることがあります。

ちなみに今年後半のOculusQuestはアップデート盛りだくさんで凄かったです。
新規のデバイスでなく、既存の発売済みデバイスへのソフトウェアアップデートで配布されてしまうあたりFacebook/Oculusの開発力に好感。
あとはコンテンツさえ拡充されれば..。

* OculusLink: QuestをPCに接続することでPCのハイエンドVRコンテンツが遊べるようになった。これでどのHMD買うか悩まずQuest買っておけばだいたいOKになった
* HandTracking: コントローラなしでも手で操作が可能になった（現状はSDK配布されたばかりで各種アプリの対応待ち）より手軽にVR使いできるようになった
* BeatSaberに360モードがついて四方八方から飛来するノートで遊べるようになった.とても楽しい。
* Amazon prime videoアプリリリース
* DMMアプリリリース

## 検証環境

* Unity 2018.4.14f1
* macOS Catalina Version 10.15
* Oculus Quest Version12
* Oculus Integration Version12.0
* FinalIK Version19.0

## 組込手順(概要)

* OculusDeveloper公式のハンドトラッキングのページにそって組み込みする
    https://developer.oculus.com/documentation/quest/latest/concepts/unity-handtracking/
    (Google翻訳使うとだいたい読める日本語になるので手順に沿って進める)
* OVRCameraRigのCenterEyeAnchor,LeftHandAnchor,RightHandAnchorをFinalIKのSpine/HeadTarget,Spine/Pelvis,LeftArm/Target,RightArm/Targetにそれぞれ設定
* OVRLipSyncをVRMのVRM Blend Shape ProxyのA,I,U,E,Oに接続
* 不定期に瞬きするスクリプトをVRM Blend Shape ProxyのBLINKに接続
* OVRHandの指状態を手に反映
    * ここだけズルした。VRMの手boneのスケールを0にして非表示にしつつ、OVRHandの手をそのまま使用。

## 作例

VRMモデル動かしてみた。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Unity/Oculus IntegrationにHandTrackingきたので早速×VRMで試してみた。外部コントローラなしのQuestのみで指５指動く破壊力スゴイ <a href="https://twitter.com/hashtag/Oculus?src=hash&amp;ref_src=twsrc%5Etfw">#Oculus</a> <a href="https://twitter.com/hashtag/Handstracking?src=hash&amp;ref_src=twsrc%5Etfw">#Handstracking</a> <a href="https://t.co/I6w7iw3idK">pic.twitter.com/I6w7iw3idK</a></p>&mdash; sa_w_ara△ (@sa_w_ara) <a href="https://twitter.com/sa_w_ara/status/1208056085879222272?ref_src=twsrc%5Etfw">December 20, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">左指ピンチでメニュー表示ON/OFFするとこんな感じ <a href="https://t.co/IAjcJvBJNd">pic.twitter.com/IAjcJvBJNd</a></p>&mdash; sa_w_ara△ (@sa_w_ara) <a href="https://twitter.com/sa_w_ara/status/1208067189955149825?ref_src=twsrc%5Etfw">December 20, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

* OVRHand Behaviourから取得できる各種値をそのままデバッグ表示してみた
    * 各指の閉じ具合、各指のピンチon/off、精度High/Lowが取得できる。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">OVRHandクラスで取得される各種値の確認。<br>FingerIsPinching, GetFingerPinchStreangthはよく使いそう。 <a href="https://t.co/uSNSkBAcg8">pic.twitter.com/uSNSkBAcg8</a></p>&mdash; sa_w_ara△ (@sa_w_ara) <a href="https://twitter.com/sa_w_ara/status/1208208844016340992?ref_src=twsrc%5Etfw">December 21, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

* おまけ: ハンドトラッキングしつつ自前計算Particleやってみたもの, 5000triangles
    * 全指個別で粒子触れるようにしたかったのだけれど、全く速度意識しないと重くて使い物にならなかったので省略。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Quest HandTracking×パーティクル楽しい<br>人差指に退ける力・中指に引き寄せる力持ってみたのだけど、<br>VRだと粒子移動に奥行き知覚がついて空間の開放感がとても良い <a href="https://twitter.com/hashtag/OculusQuest?src=hash&amp;ref_src=twsrc%5Etfw">#OculusQuest</a> <a href="https://twitter.com/hashtag/HandTracking?src=hash&amp;ref_src=twsrc%5Etfw">#HandTracking</a> <a href="https://t.co/IyxEC7ZnQY">pic.twitter.com/IyxEC7ZnQY</a></p>&mdash; sa_w_ara△ (@sa_w_ara) <a href="https://twitter.com/sa_w_ara/status/1208280988490448896?ref_src=twsrc%5Etfw">December 21, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

* おまけ: さらに360天球画像をパーティクルに投影してみた版
    * こっちのshaderを若干修正して天球をパーティクル群にリアルタイム投影 https://github.com/uisawara/unity-projection360

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Demo/Quest×HandTracking×360Particleをupdateで重力追加. 流れて弾けて静止する横浜空間360 <a href="https://twitter.com/hashtag/vrm?src=hash&amp;ref_src=twsrc%5Etfw">#vrm</a> <a href="https://twitter.com/hashtag/%E3%82%BB%E3%82%B7%E3%83%AB%E5%A4%89%E8%BA%AB?src=hash&amp;ref_src=twsrc%5Etfw">#セシル変身</a> <a href="https://twitter.com/hashtag/theta360?src=hash&amp;ref_src=twsrc%5Etfw">#theta360</a> <a href="https://twitter.com/hashtag/quest?src=hash&amp;ref_src=twsrc%5Etfw">#quest</a> <a href="https://twitter.com/hashtag/unity?src=hash&amp;ref_src=twsrc%5Etfw">#unity</a> <a href="https://t.co/jTBCCC5gPL">pic.twitter.com/jTBCCC5gPL</a></p>&mdash; sa_w_ara△ (@sa_w_ara) <a href="https://twitter.com/sa_w_ara/status/1209136250029928448?ref_src=twsrc%5Etfw">December 23, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

* UIのポインターはOculusのリファレンスにあるようにOVRHand.PointerPoseを使うのが適切だと思うのだけれど、このデモでは挙動をしる目的で自前算出している。
    * 指boneのtransform.positinoを使って算出、具体的にはindex2,thumb2の中点からindexTip,thumbTipのworldPosition中点を通るベクトル方向

* おまけ: ユーザーの手に追従するショートカット・リングの実装例
    * 左手にショートカット・リングが追従する。
        * 左手の座標に直接連動させると、手のちょっとした震えがそのままメニューに反映されてしまい、狙ったボタンが押しにくいのが課題に。
        * 手の位置とリングの現在位置に余裕を持たせておいて、ある程度手が移動しない限りはワールド位置上に固定されるようにすると選択が直感的に行える。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">作り中の Demo/Quest Handtracking に コマンドメニュー内のコマンドを直起動できるようになるショートカット・リング付けた<br>よく使うコマンドはワンタップ実行！<br>前から扇状展開されるUI作りたかったのでよき :D <a href="https://twitter.com/hashtag/Unity?src=hash&amp;ref_src=twsrc%5Etfw">#Unity</a> <a href="https://twitter.com/hashtag/OculusQuest?src=hash&amp;ref_src=twsrc%5Etfw">#OculusQuest</a> <a href="https://twitter.com/hashtag/handtracking?src=hash&amp;ref_src=twsrc%5Etfw">#handtracking</a> <a href="https://twitter.com/hashtag/UI?src=hash&amp;ref_src=twsrc%5Etfw">#UI</a> <a href="https://twitter.com/hashtag/%E3%82%BB%E3%82%B7%E3%83%AB%E5%A4%89%E8%BA%AB?src=hash&amp;ref_src=twsrc%5Etfw">#セシル変身</a> <a href="https://t.co/bhHSrXjoe5">pic.twitter.com/bhHSrXjoe5</a></p>&mdash; sa_w_ara△ (@sa_w_ara) <a href="https://twitter.com/sa_w_ara/status/1209793695341395970?ref_src=twsrc%5Etfw">December 25, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

* ハンドトラッキングからのオブジェクト生成
    * 両手指でピンチインしてできた領域からボックス生成する。
    * 生成したオブジェクトはそれぞれの手で持てる。
    * 現実空間と違って好きなだけ散らかせるので楽しい！

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">Hello 2020.迎春で新年初デモ〜<br>Quest / HandTracking / 両手指ピンチからのCube生成 -&gt; 掴み投げ -&gt; 棒打撃 <a href="https://twitter.com/hashtag/%E6%89%8B%E8%A3%BD?src=hash&amp;ref_src=twsrc%5Etfw">#手製</a> <a href="https://twitter.com/hashtag/unity?src=hash&amp;ref_src=twsrc%5Etfw">#unity</a> <a href="https://t.co/KF7h1AUr3S">pic.twitter.com/KF7h1AUr3S</a></p>&mdash; sa_w_ara△ (@sa_w_ara) <a href="https://twitter.com/sa_w_ara/status/1213610177380773888?ref_src=twsrc%5Etfw">January 4, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

## 所感

* 正面方向のハンドトラッキングの認識精度わりとよい
* とはいえ手・指を入力装置として使う場合のUIUXの考えかた自体が未成熟なのでそこを今後詰めていく。
    * ユーザーの意図的操作か・無意識動作かをうまく切り分ける仕組みは必要。
    * 機械学習をもとにした３Dアプリ開発として必要となる処理(ノイズ除去・スムージング・補正など)はQuestに限らず一般的な技法で使えるものがあると思うので、それを探すのもよさそう。
* 検知できる指の特性
    * 検収範囲： 手の認識される範囲が結構広め。下ろしたつもりの手指でピンチ操作した扱いになることがある。
        * 手の高さと組み合わせて、意図的に手を上げての操作のみ対象にしたほうがよいのかも。
        * 手が視界から外れるとトラッキング暴れる。
            * isTrackedプロパティで検出状態を確認すること。
    * 検知できる指ジェスチャ:
        * 右手指で2進数作ると2,6あたりの認識が苦手っぽい。（普通にやる姿勢ではないだろうから実用充分）
        * 人差し指中指・薬指小指でVの字作るのはできた。
    * 安定した動きにするにはユーザー側がなるだけ視界中央よりで手を使うように意識して動かないといけなかった。
        * UI等の触る対象の提示位置で、それとなく中央で手を動かすようにユーザーを誘導。

## 課題別の対策例

* GetFingersIsPinchingメソッドで各指のピンチ状態が取得されるが、頭部・手の位置次第で状態が瞬間falseになったりする
    * 対策案
        * メニュー開く操作等は、ピンチ状態が一定時間(0.5secあたり)継続したら実行されるようにする
* ハンドトラッキングがロストすると、アバターの手が空中静止しちゃう
    * 対策案
        * ロスト時はIKControllerを自前制御する
        * FinalIK/VRIKにはIKControllerに対するPositionWeight, RotationWeightのパラメータがあるので、ロスト時はweightを0にしてIK追従を辞め、例えばアイドルモーションにする。
* 手指の座標がぷるぷるする・座標が飛ぶ
    * 前数フレームの座標変化をもとに、なんらかの時間軸ノイズ除去フィルタを入れる。
        * 手っ取り早いのは単純移動平均しちゃう(前数フレーム間の座標値を足してデータ件数で割る), 座標移動が異常に大きかったらそれを無視して前フレームの座標を使う,もしくは前々フレーム以前の位置からの移動量を使った予想未来位置を使う

## 謝辞

お世話になった方々

* 3Dアバター: セシル変身
    * https://twitter.com/cecilproject
* @divideby_zero さん記事 / OculusQuest ハンドトラッキングSDKから、指Boneの情報を取得し分析する
    * 手Boneの指とID対応の図解がわかりやすくて素敵便利
    * https://qiita.com/divideby_zero/items/4949fadb2c60f810b3aa
* オオノマサキさん記事: 【低遅延・高画質】scrcpy+adbでOculusQuestのプレイ画面をMacにミラーリングする方法
    * https://note.com/masaki_ponpoko/n/nc759f6499c79?fbclid=IwAR37p-ibwNf7wrpCBtSLevW3uV3i5Gr6VcDyAIOUosD5FoW30KY5j3z8U_4