気ままにI/O

プログラムとかものづくりのインプット・アウトプットのためのブログ。

XR Interaction Toolkitに挑戦!VIVE FlowやPICO4向けに拡張してみた



こんにちは、ながみねです。とってもとっても久しぶりにブログというものを書いています。書き出し方をまったく覚えていないけどこんなんでよかったっけ?

 

さて、この記事は「Qiita x エンジニアカフェ共催!今年がんばった技術(と好きなラーメンと麺のかたさ) Advent Calendar 2022」の14日目の記事です。

qiita.com

 

好きな食べ物はラーメンです

好きなラーメンは豚骨なんですが、先日福岡空港のラーメン滑走路でたまたま入ってみた「Noodle Laboratory 金斗雲」というお店の味噌ラーメンが美味しかったです。

ラーメン滑走路は10軒ほどのラーメン屋さんが並んでいる飲食街のこと。福岡には他にもラーメンスタジアムとか博多めん街道とか、ラーメン屋が集まってるところが割と近い範囲に複数あるんだけど、他の地域にもこういうのあるんだろうか?

 

www.fukuoka-airport.jp

canalcity.co.jp

www.jrhakatacity.com

 

XR Interaction Toolkitとは

というわけでここから本題。

XR Interaction ToolkitはUnity公式が提供しているXR開発用のパッケージです。

長いので以降はXRIと表記します。

docs.unity3d.com

 

MetaQuestやVIVEシリーズなどの各種VRバイスには、それぞれ開発用のSDKが提供されています。VR開発の際は対象デバイスごとにカメラや入力、操作といった処理をそれぞれ実装する必要がありました。

XRIはVRに必要な基本的な機能を抽象化してデバイスごとの差異を吸収してくれます。XRIを利用することで、クロスプラットフォームVRアプリを開発することができます。

XRIの基本的な使い方、どういった機能があるかは、以下のブログがとても参考になります。

framesynthesis.jp

gaprot.jp

xrdnk.hateblo.jp

 

Device-basedとAction-basedの入力

XRIの入力制御コンポーネントには、Device-basedとAction-basedという2種類があります。

Device-basedではデバイスからの入力値を直接参照し、Action-basedはInput Systemというデバイスからの入力をActionという形式に抽象化する仕組みを使って入力値を参照します。

Action-basedを利用するとボタンのカスタマイズやクロスプラットフォーム対応が容易になるため、ドキュメントではAction-basedの使用が推奨されています。

docs.unity3d.com

It is recommended that you use the Action-based variant instead of the Device-based variant to take advantage of the benefits that the Input System package provides. For example, it separates the logical inputs from the physical inputs, and users can create and switch between customized action maps, bind multiple cross-platform controller inputs to a single semantic action, and use event callbacks of input actions. Some features of the XR Interaction Toolkit package, such as the XR Device Simulator, are only supported when using input actions.

 

Device-basedのコントローラー設定。参照するボタンを直接設定しています。

 

Action-basedのコントローラー設定。InputSystemのActionを設定しています。

 

この記事では、XRIのAction-basedに対応していない入力をXRI対応させるために試してみたことについて書いてみます。

仕組みを調べつつ試行錯誤しながら最終的に上手くいった方法なので、もっと良いやり方があったり正確じゃない部分があるかもしれません。

参考にしたドキュメントや記事は最後にまとめて記載するのでより詳しく知りたい方はそちらも見てみてください。

 

①VIVE FlowのAction-based対応

VIVE Flowとは

VIVE FlowはHTCが開発・販売するVRバイスで、小型軽量のためメガネのように装着することができます。

https://www.vive.com/jp/product/vive-flow/overview/

 

一般的なVRバイスとは異なり手の動きをトラッキングするハンドコントローラーは付属せず、スマートフォンか専用の3DoFコントローラーを接続してポインター入力で操作します。

https://htcvive.jp/item/99H12271-00.html

VIVE FlowのXRI対応状況

VIVE Flowアプリの開発にはVIVE Wave XR Pluginを利用します。

VIVE Wave XR Pluginには機能拡張用のEssenceパッケージが用意されており、XRIを使うためにはこちらも一緒にインポートします。

ただし、Essenceパッケージを追加するだけでは不十分で、ProjectSettingsのWaveXR > Essenceの設定でXRIを有効化するとようやく使えるようになります。

 

XRIが動作するサンプルシーンが追加されているので、コントローラーの設定がどうなっているか確認してみます。XRControllerがDevice-basedになっており、現状のSDKではAction-basedには対応していないようです。



Action-basedに対応する

Action-basedに対応するというのは、デバイスからの入力をInputSystemで扱えるようにするということです。そのためInputSystemの仕組みを理解する必要があるのですが、これがなかなか奥深く大変でした。自分の理解もまだ浅いこともありここでは詳細には書かないですが、興味があれば公式ドキュメントのArchitectureの項目を読み込んでみることをオススメします。

docs.unity3d.com

 

バイスの入力をInputSystemで扱うために以下の手順を踏みました。

  1. バイスから流れてくるデータの構造を確認する
  2. 1のデータ構造に合わせたクラスを定義する
  3. アプリ起動時に2のクラスをInputSystemに登録する
1. デバイスから流れてくるデータの構造を確認する

バイスが接続されると、Unityに対してデバイス固有のデータ構造でデータを流すような仕組みになっているようです。

バイスのデータ構造はInputSystemで取得できるので、今回はJSON形式にしてログ出力することで確認しました。

データ構造を確認するためのコード。

gist.github.com

 

出力したVIVE Flowのコントローラーのデータ構造。inputFeaturesの部分でどういった入力が取得できるかが分かります。

gist.github.com

 

2. 1のデータ構造に合わせたクラスを定義する

以下のようなクラスを定義しました。

  • 1のinputFeaturesに対応するInputControlのプロパティを定義していく
  • aliaceの部分でinputFeaturesのnameを指定
  • プロパティ名はInputSystemのActionを作成する際のパス名になる(重要)

gist.github.com

 

3. アプリ起動時に2のクラスをInputSystemに登録する

起動時の早いタイミングで登録するため、2のクラスの静的コンストラクタでクラスを登録、コンストラクタが必ず実行されるようにRuntimeInitializeOnLoadMethodの空メソッドを用意しています。

WithInterace(), WithProduct()はデバイスデータとクラスをマッチングするための条件付けで、VIVE Flowのコントローラーが接続されたときにこのクラスが使われるように設定しています。

gist.github.com

動作確認

まずはInputSystemへの登録がうまくいっているかを見てみます。

InputDebugウィンドウを開くと、クラス定義したWave Controllerの情報が追加されていることが分かります。これでInputSystemがこのデータ構造を知っている状態になりました。



ここまで来ればシーンをAction-basedが動くように整えてあげればOKです。

まずは、InputActionManagerを追加してInputActionが有効になるように設定します。

今回はXRIのサンプルに含まれるデフォルトアセットを使用しています。(デフォルトアセットが使えるようにクラス定義のところでプロパティ名を合わせていた)



続いてコントローラーのオブジェクトをAction-basedに置き換えます。ここでもXRIのデフォルトのInputActionを設定してます。



この状態でビルドすると、無事にAction-basedのXRIが動作していることが確認できます。

youtu.be

これでVIVE FlowのAction-based対応は完成です。

バイスのクラス定義の全体は以下のGistにあります。記事ではコントローラーのみ言及しましたが、コードではHMDにも対応しています。

VIVE FlowのInputControlLayout定義 · GitHub

 

②PICO 4のハンドトラッキング対応

PICO 4とは

PICO 4はPico Technologyが開発・販売するVRヘッドセットです。2022年10月に発売されたばかりの新しいデバイスで、比較的小型軽量、高画質、安価といった特徴があります。

https://www.amazon.co.jp/dp/B0BDYFB176?utm_source=official

PICO 4のXRI対応状況

PICO 4のアプリ開発にはPICO Integrationを使います。PICO IntegrationはXRIのAction-basedにも対応しており、ドキュメントのQuickstartでもXRI (Action-based) を使う形で記載されています。

Quickstart - Create a simple XR scene

 

PICO Integrationのパッケージ内を見てみると、VIVE Flowの場合に自作したデバイス構造を登録するためのクラス定義があることが分かります。



というわけで、コントローラーを使う分にはデフォルトのままでもAction-basedのXRIが動作します。

そこで今度は、PICO 4で利用できるハンドトラッキング操作をXRIに対応させることにチャレンジしてみました。

ハンドトラッキングを扱う方法はSDKで提供されており、ドキュメントにも記載されています。

Hand tracking

 

画像のようにプレハブを配置するだけでハンドモデルの表示とトラッキング状態の適用は簡単にできるようになっています。



しかし、トラッキング情報の反映はPXR_Handコンポーネントで処理されていますが、見た目の反映とトラッキング情報にアクセスするためのプロパティを公開しているだけです。

このままではオブジェクトとのインタラクション等は発生しないので、PXR_Handのプロパティを参照して独自にインタラクションを実装する必要があります。ここからXRIで扱えるように対応していきます。

XRI(Action-based)に対応させる

VIVE Flowのほうにも書いたように、XRI (Action-based)に対応するというのは、デバイスからの入力をInputSystemで扱えるようにするということです。

VIVE Flowのほうでやったのは、「デバイスから届くデータをInputSystemで扱えるようにデータの型を登録する」という作業でした。

今回の場合、ハンドトラッキングの情報はデバイスとして扱われていないので、「デバイスから届くデータ」自体がありません。

そこで、ハンドトラッキング情報を「カスタムデバイス」として扱い、スクリプトから入力データを発行する、ということをしていきます。

InputSystemのドキュメントのCreating custom Devicesの項目で詳細が解説されています。

docs.unity3d.com

 

ハンドトラッキング情報をカスタムデバイスとして扱うために下記の手順を踏みました。

 

  1. バイスの入力状態を表す構造体を定義する
  2. 1の入力状態に合わせたクラスを定義する
  3. アプリ起動時に2のクラスをInputSystemに登録する
  4. 任意のタイミングで2のデバイスをInputSystemに追加する
  5. ハンドトラッキング情報をもとにInputSystemにデータを発行する
 
1. デバイスの入力状態を表す構造体を定義する

カスタムデバイスの入力状態は構造体として表されます。

コントローラー入力と同等に扱えるようにフィールド名や種類を定義しています。今回はハンドトラッキングでの選択操作をgripとして扱います。

メモリ上でのフィールドのレイアウトを明確にするためoffset設定されているのがポイントです。

gist.github.com

 
2. 1の入力状態に合わせたクラスを定義する

ここは基本的にはVIVE Flowのときと同じです。

1の構造体とフィールド名を合わせているのと、grip系以外は継承元のXRControllerの定義をそのまま使うので、シンプルな状態になっています。

gist.github.com

 

3. アプリ起動時に2のクラスをInputSystemに登録する

ここもほとんどVIVE Flowと同じです。デバイス追加は次の手順で明示的に行うので、デバイス情報とのマッチング条件は不要になっています。

gist.github.com

 

4. 任意のタイミングで2のデバイスをInputSystemに追加する

通常のコントローラー等のデバイスの場合は、デバイスが認識された時点でデバイス情報をもとにInputSystemが内部でデバイスクラスのインスタンスを追加します。

ハンドトラッキングはデバイスとして認識されないため、明示的にインスタンスを追加する必要があります。

今回はハンドトラッキングバイスを制御するためのコンポーネントを作成し、OnEnableのタイミングで追加するようにしています。

gist.github.com

 

5. ハンドトラッキング情報をもとにInputSystemにデータを発行する

ここまでで、InputSystemがハンドトラッキングの入力状態を受け取る準備ができました。

最後に、PXR_Handから取得したハンドトラッキング情報をもとにHandDeviceStateのインスタンスを用意してInputSytemにデータを発行する処理をHandDeviceSupportに追加します。

これでInputSystem内で適切にデータが処理されてInputActionが更新されXRIまで届くようになります。

gist.github.com

 

動作確認

まずはInputDebugウィンドウでデバイスが登録されているかを確認します。画像のように、クラス定義した通りの情報が表示されています。Formatの値がPCHDとなっており、入力状態を表すHandDeviceStateとの紐付けもうまくいっているようです。



続いて、シーンの適当なオブジェクトにHandDeviceSupportコンポーネントを追加してPXR_Handを設定し、XRIのポインタ操作ができるようにRay Interactor (Action-based)も配置しました。Ray InteractorのInputActionはXRIのデフォルトアセットを設定しています。



これで準備完了なのでビルドして試してみます。

ハンドトラッキングでXRIのRayInteractorが動作していることが確認できました。

youtu.be

 

これでハンドトラッキングのXRI対応が完成です。

ただ、この状態でハンドコントローラーが起動してトラッキング範囲内にあると、コントローラーとハンドトラッキングの入力が競合してレイの表示位置がおかしくなることがあります。

ハンドトラッキングの入力だけ使用したい場合は、XRIのAction設定をデフォルトのInputActionからハンドトラッキング専用のAction設定に変更する必要がありそうです。

ちなみにPICO 4のパススルー機能と組み合わせると、自分の手でオブジェクトを操作するような体験もできます。

 

 

まとめ

長い道のりだった。。。。

今回はVIVE FlowとPICO 4でXR Interaction Toolkitを使いやすくするために拡張してみた話を書きました。

ポイントとしては、以下のようなところかなと思います。

  • XR Interaction ToolkitにはDevice-basedとAction-basedがあり、Action-basedのほうが柔軟性・拡張性があり推奨されている
  • Action-basedはInputSystemの仕組みを利用しているため、深掘りするためにはInputSystemの理解が必要
  • InputSystemのデバイス登録まわりを理解すれば、XRI未対応デバイスやカスタムデバイスでもXRIに対応させることができる

InputSystemをまだあまり使ったことがなく、InputActionで入力を抽象化できるんだよねーくらいの浅い理解から出発したので、デバイスに近い部分がどういった仕組みになっているのかの理解が深まったのは勉強になりました。マルチデバイス対応やパフォーマンス(GCなど)を考慮してこういった作りになってるのかなと想像しています。

理解が深まったとはいえ、細部についてはフワッとした理解のまま進めた部分も多いため、記事中のコードは詳細説明を省いている部分が多いです。一応そのまま動くように書いているつもりですが、試してみてうまくいかないところがあったらゴメンなさい。

XR領域はどんどん新しいデバイスが出てくるので、またおもしろデバイスを入手したらXRI対応を試してみようと思います!

それでは!

参考