【Unity】VisionPro 上でAROcculusionの実現方法【VisionPro】

# TL;DR
- `AR/Basic Occulusion` Shader を使えば実現できます
- ~~[公式ドキュメント](https://docs.unity3d.com/Packages/com.unity.polyspatial.visionos@1.1/manual/ShaderGraph.html) 通り、やっぱりできなかった~~
    - ~~URP のBlend mode が "Alpha", "PreMultiply", "Additive" の3種しか対応してないため~~
- ~~今後のサポート次第では可能になるかも~~
- ~~現状はUnityにヒアリング中~~
    - [VisionOS用Discussionフォーラム](https://discussions.unity.com/t/does-polyspatial-plan-to-support-blend-zero-for-arocculusion/352025)

# 検証環境

| 項目 | バージョン |
| --- | --- |
| Unity | Unity2022.3.20.f1 |
| PolySpatial | 1.1.6 |
| VisionOS | 1.0.3 |
| URP | 14.0.10 |


# 目的

VisionPro はARanchorの性能やMesh 検知の精度が非常に高いので、これを用いて
AROcculusion を行って、Unity空間上で生成したObjectを現実の物体の背後にいたら隠れるようにしたかった。

# AROcculusion の方法

一般的なARのOcculusionの実現は大きく分けて2通りあります

1. DepthMap を用いて該当Objectの描画判定を行うかどうか判断するロジックを入れて実現
1. 現実世界のObject をMesh 情報として受け取り、そのMeshを透明で描画&その背後のObject は透明で描画させる
1の方法はAR系では王道であるが、VisionPro は現在のカメラ映像はおろか、深度情報もプライバシー保護の観点から取得不可能。

一方、ARMesh についてはARFundationのコンポーネントでもよく使う [`ARPlaneManager`](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/api/UnityEngine.XR.ARFoundation.ARPlaneManager.html) であったり [`ARMeshManager`](https://docs.unity3d.com/Packages/com.unity.xr.arfoundation@5.1/api/UnityEngine.XR.ARFoundation.ARMeshManager.html) 経由で情報を取得可能です。

これはVisionOS側 (≠ ARKit ) のAPIがMesh情報を返してくれるので、これをPolyspatial側がラップしてARFoundation経由で使えるという仕組みです（スマホARとかやってた人なら、基本セットのアレね　という感じです）

## Meshの背後を透明にするShader

透明Object の描画なのでTransparent で `Blend zero zero` とやってしまうことで、自身も透明、背後Object も透明とさせることができるので、これでそのままARの映像を一番奥に描画してもらえれば実現できます。

ただ、このやりかたはOpaqueはどうすんの？　という問題があります。

そこで @aa_debdeb さんが紹介している `Opaqueよりも一歩早めに透明ピクセルを描画する` というやり方です

https://qiita.com/aa_debdeb/items/d39b0ea8cbfcdc464c02

簡単にいうと、DepthMap はとれないけどMesh があるならそれの深度情報を先に書き込んでしまえということですね。


### VisionPro への導入

PolySpatial <= 1.0.0当時はShaderGraph Only でしたが v1.1.Xになってからは、一部カスタムShader(URP) もサポートされるようになりました。

そこで上記記事はもともとURPで書いてあることからそのまま実行してみました。


### 結果

会社のオフィスの壁で試してみたのですが、アプリ起動前はこの見た目が

![IMG_0005.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65257/4dc8640a-cf55-bbe7-43d3-35cb63fee340.png)

壁や天井などMesh を検知して透明で描画するはずの部分がこんな感じで真っ黒になってしまいました。

![IMG_0004.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65257/e2606d77-6ec0-f484-e481-697af616be14.png)


Shader はInspector ではちゃんと透明になっていたため、やっぱり `Blend zero` が未対応だからこのような結果になっていると思われます

## Unity に問い合わせてみたら
Unity 中の人からの返答をもらいました。
結論としては `AR/Basic Occulusion` Shader を利用することで解決ということです。

### 再実験

AR Occulusion できてる！！

<iframe width="560" height="315" src="https://www.youtube.com/embed/E2fXi06gU3s?si=ykNwHEL9aCyNxaN2" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

## Apple のShader/Materialについて
### 基本知識
Apple の現在のMaterial は [こちら](https://developer.apple.com/documentation/shadergraph/realitykit/unlit-surface-(realitykit)) のドキュメントにもある通り、 `Unlit` , `PBR` , `Occulusion` , `Shadow Receiving Occlusion` の4種が主なMaterialです。

PolySpatialでは上記のいずれかにUnityのMaterial(Shader) をマッピングするそうです。

また、先の実験で `Blend mode "Zero"` は現状Apple側はサポートしてないため、透過を扱うには "Alpha" のモードで値を0にする必要があるとのことです.(もちろんRenderQueue はTransparent）

余談ではありますが、現状のPolySpatial 1.1.x系だと、 `頂点を操作するようなShader` については `ShaderGraph製のみサポート` とのことで、手書きのCustomShaderは頂点を変更するようなものでなければ一部利用可能とのことです。

参照： [UnityのPolySpatialのフォーラムでUnity公式からの回答より](
https://discussions.unity.com/t/does-polyspatial-plan-to-support-blend-zero-for-arocculusion/352025/3)

### AR/Basic Occulusion Shaderについて

今回の `AR/Basic Occulusion` Shader はAppleのMaterial でいうところの `Occulusion` にマッピングされることから、今回のAR Occulusionが利用可能とのことです。



# まとめ

- PolySpatial でAR Occulusionをする場合は `AR/Basic Occulusion` Shader を用いましょう
- Swift UI で実現する場合は `Occulusion` , `Shadow Receiving Occlusion` Materialを設定すれば実現できるようです