OculusQuest2でパススルー機能を実装する

# はじめに
OculusIntegrationのV.31.0からパススルー機能が試験的に解放されたので内容について調べてみました。
以下、本ページでは公式の説明プラスアルファ不明点・説明不足部分等を加筆した内容になっています。

# パススルー機能について
- これまで使用できなかったQuest本体の全面に取り付けてられているカメラをアプリ上で活用できる機能です。
- ~~正式版は2021年にリリースされるそうで、Oculus Storeへのアプリ公開も正式版まではリリースできないとのことです。→リリース済~~
- ~~公式のドキュメントにはQuest2と明記されているため、おそらくQuest1には対応していない。~~と思っていましたが、Quest1でも動作するようです。

# パススルー機能でできること
- Questのカメラ（公式ではセンサーという表現をしている）で取得した映像をアプリと別レイヤーとしてレンダリングすることができる。
- OVRPassthroughLayerスクリプトのコントロールを使用して、パススルーの視覚スタイルをカスタマイズできる。
    - パススルーレイヤーのアルファを指定できる。
        - textureOpacity 
    - パススルー画像にエッジレンダリングフィルターを設定できる。
        - edgeRenderingEnabled
        - edgeColor
    - パススルー画像を色付けするためのカラーマップを定義し、コントラストと明るさの調整またはポスタリゼーション（量子化）効果を設定できる。
        - SetColorMap(Color[] values)
        - SetColorMapControls(float contrast, float brightness, float posterize, Gradient gradient)
- XR Compositorスタック内のオーバーレイやアンダーレイなどのVRアプリに対するパススルーレイヤーの配置と、ブレンド方法を指定できる。(複合レイヤーを使用)
- パススルーが画面のどこに表示されるかを指定できる。(アルファマスキングを使用)
- パススルーへの色付け、エッジの描画、不透明度・ポスタリゼーション（使用する色数を減らし、イラストのように表現すること）をカスタマイズできる。
    - エッジレンダリングにより、パススルー画像でソーベルフィルター※が有効になり、色付きのエッジが追加できる。<br>
      ※画像の平均化を行いながらエッジ強調を行い、ノイズを低減しながらエッジを強調すること
- パススルー画像が投影されるジオメトリを定義できる。(表面投影パススルー)

# パススルー機能ではできないこと
- ユーザーの物理的環境の画像や動画にアクセス、表示、または保存することはできない。
    - スクリーンショットしたファイルは破損ファイルとして扱われる。→OculusDeveloperHUBを使用すると撮影できました。
    - 録画した映像には、パススルー画像のみ表示されない。→OculusDeveloperHUB,SideQuestを使えば録画できます。
    - ミラーリングの場合、キャスト先はパススルー画像が表示されない。→SideQuestを使えばキャストできます。
- パススルーの深度に基づいてブレンドを指定する方法はありません。"現在は"という表現だったので、今後は深度も取れるようになるかも。
- カラー表示はできない。設定できそうな説明が公式にあったのですが、パススルー画像全体の色相を変更できるだけの様です。

# 実装手順
1. 実験モードの解放  
ADBをインストールしている状態で以下のADBコマンドを実行する。<br>
``adb shell setprop debug.oculus.experimentalEnabled 1``  
<font color="Red">**重要：コマンドはQuest2を再起動する度にこのコマンドを実行する必要がある。**</font>→現在は不要になりました。
1. Quest2のバージョンをV.31以降にアップデートする。
1. UnityでいつものQuest2を使用するための環境設定を行う。  
途中設定を変えるとアセットの再インポートが走るので、OculusIntegrationは設定の最後にインポートした方が良い。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/22e29ddc-72a9-a7cf-d9c2-83cc55f089cb.png)

1. OVRCameraRigを配置し、 OVRManagerの``Experimental Features Enabled`` と`` Passthrough Capability Enabled``のチェックを入れる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/b8c320a3-02e7-992f-41c4-850c618e3cb1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/d00259d3-a2af-ba46-4e1c-920c1003b10f.png)
以下の画面の様に``Experimental Features Enabled``が表示されない場合、Oculus XR Plugin パッケージが古いのでアップデートすると表示されるようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/c1509521-0ba4-3191-eecf-9477dc4bf613.png)
1. OVRPassthroughLayerをOVRcameraRigにアタッチする。

ここまで設定できれば、パススルー表示ができるようになっている。

# パススルー画面
### エッジ検出
エッジ部分は紫色に表示させていますが、ディスプレイや本のエッジ部分はそこそこ上手く取れていると思います。
~~少なくともデフォルト設定ではパススルーの映像を録画できないので、直接カメラを押し当てて撮影していますが、録画する方法があればコメントを頂けると助かります。~~
OculusDeveloperHUBやSideQuestを使えば録画・スクリーンショットを撮れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/531e822e-e6ca-d0dd-b74c-73277b7f7f8d.png)

<iframe width="560" height="315" src="https://www.youtube.com/embed/mxGxnm0maco" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

### オブジェクト表示
オーバーレイでパススルー画像をレンダリングしているので、配置したオブジェクトが裏側に映ってしまっています。
#### ダメな例
<iframe width="560" height="315" src="https://www.youtube.com/embed/_v-96UWpMRE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

アンダーレイにすると配置したオブジェクトが前面に描画されますが、デフォルトのままでアンダーレイに設定するとスカイボックスが邪魔してパススルー画像が描画されなくなります。
そこで、カメラのインスペクター画面からスカイボックスのアルファを0に変更するといい感じに描画されるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1336886/1b82e7d0-bc59-ecfc-ae33-808f258b86e6.png)

#### 修正後
<iframe width="560" height="315" src="https://www.youtube.com/embed/UlkwTRN0IaE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# サンプルシーン
サンプルシーンの動画を探してもなかったので、全サンプルシーンを動かして動画を撮ってみました。
結構な数があったので、別ページに掲載します。

https://qiita.com/kazuki_kuriyama/items/8e808556e1d7f79072a0

# 使用方法
1. [Projection Surface(投影面)]設定  
パススルー描画がautomatic environment depth reconstruction(自動環境深度再構成)を使用するか、ユーザー定義の面を使用するかを決定します。
1. [Compositing(合成)]セクションのコントロール  
OVRPassthroughLayerと同様に機能する。  
[Placement(配置)]設定に応じて、パススルーはオーバーレイまたはアンダーレイとして合成される。OVRPassthroughLayersを含む複数のレイヤーが存在する場合は、
[Composition Depth(構成の深さ)]設定を使用して、レイヤー間の順序を定義する。
1. OVRPassthroughLayerの複数のインスタンスをシーンに追加し、それぞれに独自の構成を設定を行う。※アクティブ可能なのは最大3インスタンス。


# パススルーが表示されない場合の確認事項
- すべての実験的なチェックボックスが選択されているか？
- ""adb shell setprop debug.oculus.experimentalEnabled 1""が実行されているか？  
``Oculus Utilities Plugin with OpenXR is being used, which is under experimental status``
- OVRPassthroughLayerで、Opacityに0より大きい値を設定しているか？
- カラーマップグラデーションを使用する場合、color stop alphaに0より大きい値を設定しているか？
- OVRPassthroughLayerの配置がアンダーレイに設定されている場合、アプリケーションが画面全体（アルファチャネル内）を覆っていないか？
- ProjectSettingsは以下の設定になっているか？
    - Script Backend：IL2CPPT
    - Target Architecture：ARM64のみON

# 参考

https://assetstore.unity.com/packages/tools/integration/oculus-integration-82022

https://developer.oculus.com/experimental/passthrough-api/