Unityで認識した平面をポータル化して異世界を覗く【ARFoundation編】

##壁の向こうの異世界

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">8時間で終わらなかったので追加作業<br>ARFoundationとステンシルバッファ何となく理解した <a href="https://twitter.com/hashtag/hachitai?src=hash&amp;ref_src=twsrc%5Etfw">#hachitai</a> <a href="https://t.co/9mTfRCuDJQ">pic.twitter.com/9mTfRCuDJQ</a></p>&mdash; 1pp0 (@1pp0qq1) <a href="https://twitter.com/1pp0qq1/status/1142818086912815104?ref_src=twsrc%5Etfw">June 23, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


##はじめに
本記事はUnityのARFoundationを使用して、現実空間で認識された平面越しに異世界（AR空間）を表示する仕組みの備忘録になります。

現実空間に空いた穴越しに別の世界が見えたり、その中に入り込んだりできるAR表現はポータル表現と呼ばれています。
ポータルの実現方法としては、どこでもドアのようなアーチ型のオブジェクトを現実とAR空間の境界にするものや、認識した画像マーカーの奥にAR空間を表示するものなどがあります。
参考：[UnityとARKitで異世界への扉(ポータル)を作る方法](https://medium.com/@ykobayashi/unity%E3%81%A8arkit%E3%81%A7%E7%95%B0%E4%B8%96%E7%95%8C%E3%81%B8%E3%81%AE%E6%89%89-%E3%83%9D%E3%83%BC%E3%82%BF%E3%83%AB-%E3%82%92%E4%BD%9C%E3%82%8B%E6%96%B9%E6%B3%95-b2bd4e45ad84)

本記事で紹介するのは、あらかじめ用意した画像マーカーなどではなく、認識された平面領域全てを異世界を覗くポータルにする方法になります。
要素技術は、**ARFoundation**による平面認識と、**ステンシルバッファ**を用いた描画処理ですが、今回はARFoundationの導入から平面認識まで実装し、最後にポータル化の前段階として現実空間にAR空間を重畳して表示する所まで説明します。

平面の認識はUnity-ARKit-Pluginを使ったことはありましたが、ARFoundationは今回が初めての利用でしたのでインストールから説明していきます。ARKit3の機能がARFoundationで利用できるようになっている事もあり、今後はARFoundationを使っていくのが良さそうです。

##準備
###環境
・Unity 2019.1.5f1
・ARFoundation 2.1.3
・ARKit XR Plugin 2.1.1
・Xcode 10.3
・iOS 12.3.1 (iPnone7)


###ARFoundationの導入
Unity-ARKit-PluginはAssetStoreないしは[bitbucket](https://bitbucket.org/Unity-Technologies/unity-arkit-plugin/src/default/)からダウンロードして導入していましたが、ARFoundationはUnityのPackage Managerから追加します。

1. Unityで新規プロジェクトを作成
2. `Window -> Package Manager`からPackagesウィンドウを開く
<img width="779" alt="package manager" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/05897568-f602-496e-1aeb-163e822548b9.png">

3. 「ar」で検索し、`AR Foundation`及び`ARKit XR Plugin`(Androidの場合は`ARKit Core Plugin`)をインストール
<img width="701" alt="ar" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/ffbfdbeb-deb3-9d65-53f7-5ad16728d9f0.png">

以上でARFoundationの導入は完了です！

## 実装
### 平面領域の可視化
それではARFoundationで現実空間の平面領域を認識し可視化していきます。
単に可視化するだけならコンポーネントの追加だけで良いため、非常に簡単に実現することができます。

1. *MainScene*を作成して*Main Camera*を削除
2. Hierarchyを右クリックして*AR Session Origine*及び*AR Sessoin*を作成
<img width="386" alt="ar session" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/c822c7a6-d057-d33e-b8c6-8dd77bb8b90a.png">
3. 同様に*AR Dafault Plane*を作成し、Prefab化してHierarchyから削除
<img width="476" alt="AR Dafault Plane" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/c97f7fe1-70e9-2c04-4281-0e2000389969.png">
4. *AR Session Origine*にAdd Componentから*AR Plane Manager*を追加
<img width="337" alt="AR Plane Manager" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/ca915860-8137-27d3-93f9-02d54f5c7fec.png">
5. *AR Plane Manager*の*PlanePrefab*にPrefab化した*AR Dafault Plane*をアタッチ
6. *DetectionMode*を*Everything*にして水平面及び垂直面の両方を検出
<img width="337" alt="AR Dafault Plane" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/87e8f6c2-03ba-d393-d5c4-dc886e43683a.png">

以上で平面の認識と可視化は完了です。手順は下記レポートを参考にさせていただきました。
参考：[【VR/ARイベント】「Unity AR Foundation」講座の参加レポート](http://blog.lab7.biz/archives/17909106.html)

一旦ここまででビルドして動作を確認しましょう。

###ビルド設定
`Edit > Project Settings > Player > iOS > Other Settings`の`Requires ARKit support`にチェックを入れると、カメラ使用のための`Camera Usage Description`に自動で文言が入力されます。
また、ARKitはiOS11からサポートのため、`Target minimum iOS Version`を`11.0`以降にします。
これでビルドしたところ下記のエラーが出たため`Architecture`を`Universal`から`ARM64`に変更しました。

```
BuildFailedException: ARKit XR Plugin only supports the ARM64 architecture. See Player Settings > Other Settings > Architecture.
```
最終的な設定は下記の通りです。
<img width="490" alt="Architecture" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/256e0fe3-d8b3-2304-fbd1-86dbc8bfd756.png">

ビルドして辺りを見渡すと、下の動画のように認識された平面領域が表示されます！
![default.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/7cb51666-1fe3-1e23-29ec-70dc91266382.gif)

ここまでノーコーディングで実装することができるため、ARFoundationは非常に使い勝手が良いと思いました。
平面認識の`AR Plane Manager`の他にも、特徴点を表示する`AR Point Cloud Manager`や、画像トラッキングを行う`AR Tracked Image Manager`などもあるので、用途に応じて試してみてください。

### 異世界の作成と表示
ステンシルバッファを用いて平面越しにAR空間を表示する前段階として、現実空間にAR空間を重畳して表示します。
冒頭の動画ではアセットストアの[Nature Starter Kit 2](https://assetstore.unity.com/packages/3d/environments/nature-starter-kit-2-52977)のDemoシーンのオブジェクトを使用しました。
下記の手順では、現実空間のみを表示するカメラと、AR空間のみを表示するカメラをそれぞれ用意し、二つの映像を重ねて表示する事で、現実空間にAR空間を重畳します。

1. 空のGameObject *ARWorldParent*を作成し、平面越しに表示したいオブジェクトを*ARWorldParent*の子として配置してAR空間を作成
<img width="742" alt="ar world parent" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/eaee3c79-9bd8-1d8a-8cc9-4cfe46ddf974.png">

2. AR空間用のレイヤー*ARWorld*を追加
<img width="360" alt="layer" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/7626bdab-0381-2fdf-7672-c9ab57798c50.png">

3. *ARWorldParent*のレイヤーを追加した*ARWorld*に変更
<img width="631" alt="スクリーンショット 2019-09-01 18.07.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/e44c8667-9cab-aff8-1648-0d3cc6353472.png">
変更時に下記のようなダイアログが表示されるので、Yesを選択して*ARWorldParent*以下の全てのオブジェクトのレイヤーも*ARWorld*に変更します。
<img width="694" alt="alert" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/af9f9d85-06e0-91f1-1047-70a3017ff13b.png">

4. カメラの追加と設定
*AR Camera*の子として新しくカメラ*Wolrd Camera*を追加し、*Clear Flags*を*Depth Only*、*Culling Mask*を*ARWorld*に変更します。
<img width="613" alt="スクリーンショット 2019-09-01 18.27.02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/0c3c850a-bd7c-6cc7-7c33-95ea868b6c0c.png">
また、*AR Camera*の*Culling Mask*は逆に*ARWorld*を外します。
<img width="614" alt="AR Camera" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/8bfbd002-15a9-58bb-3c39-ac9f90a9051c.png">

これで1で作成したAR空間のオブジェクトは*AR Camera*には映らず、*World Camera*にのみ描画されるようになるため、*AR Camera*の映像の上に*Wolrd Camera*の映像を重畳する事で、現実空間にAR空間を重ねて表示する事ができます。
また、*AR Camera*にはカメラ（スマートフォン）の6DOF（並進・回転）姿勢が自動で反映されます。*Wolrd Camera*は親である*AR Camera*の姿勢に追従するため、下の動画のようにスマートフォンの移動に伴って、AR空間内のカメラも移動します。
![move.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174948/f5140835-b460-2e59-4724-3750ff569261.gif)

この状態では最初からAR空間全体が現実空間に表示されているため、異世界を覗くようなポータル表現にはまだなっていません。
次回はステンシルバッファを用いてARFoundationで認識した平面の領域のみAR空間が表示されるよう、平面領域をポータル化する方法について説明します！

「[Unityで認識した平面をポータル化して異世界を覗く【ステンシルバッファ編】](https://qiita.com/ippo/items/7a3ddcf688ce5e86c347)」をお待ちください！