ARグレイマンを歩かせてみよう!! UE4-ARCoreのTipsと、AR Shadowについて

#はじめに
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">ARグレイマン動いた!!😆<br>デプスカメラが無いPixel3だけどそれなりにオクルージョンも取れてる。<br>AR Shadowは、ScreenSpacePassthroughCameraマテリアルで実装出来たけど、Litに影を落としてるので、よく見ると板ポリが見えちゃう。これは回避策あるのかなぁ🤔<a href="https://twitter.com/hashtag/UE4?src=hash&amp;ref_src=twsrc%5Etfw">#UE4</a> <a href="https://twitter.com/hashtag/UE4Study?src=hash&amp;ref_src=twsrc%5Etfw">#UE4Study</a> <a href="https://twitter.com/hashtag/ARCore?src=hash&amp;ref_src=twsrc%5Etfw">#ARCore</a> <a href="https://twitter.com/hashtag/AR?src=hash&amp;ref_src=twsrc%5Etfw">#AR</a> <a href="https://t.co/75C8nBGiSD">pic.twitter.com/75C8nBGiSD</a></p>&mdash; LazyKozy 2nd (@LazyKozy) <a href="https://twitter.com/LazyKozy/status/1341581660890955776?ref_src=twsrc%5Etfw">December 23, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

いきなりネタバレですが:stuck_out_tongue:、上のツイートはARグレイマンをUniversal CityWalk Hollywoodで歩かせた時のムービーです。
世の中的にはモバイルARと言えば、LiDARが載ってるiOS端末でARKit!!、というのが主流な気がしますが、GoogleのARCoreも負けてはいません。
私は二年落ちのGoogle Pixel3を使っているので、UE4-ARCoreにチャレンジしてみたいと思います。

#開発環境
- UE4.26.0
- デバイス Pixel3

#UE4のARCoreサポート状況
ARCoreが発表され始めた頃は、GoogleがUE4へのインテグレートを行ってたのですが（https://github.com/google-ar/arcore-unreal-sdk）、
最近は更新もぱったり止まってしまっていました。
そんな中2019年12月にARCoreがDepth APIのリリースを発表、Unityのサポートも名言、UE4に関しては全く言及なし、という事で、ああこれはもう先が無いかな～、と正直思ってたんですが、なんとなんと!! 4.26でARCore 1.8とDepth APIのサポートをEpic自らインテグレートしてくれました!! (https://docs.unrealengine.com/en-US/WhatsNew/Builds/ReleaseNotes/4_26/index.html)
DepthはARにとって非常に大きな要素です。レンダリングのコア部分に踏み込めるEpicがインテグレートしてくれた意味は大きいと思います。

#HandheldARサンプルでDepth APIを有効にする
とりあえずUE4でARと言えば、ここから始まるHandheldARサンプル。
デフォルトではDepth機能が無効化されているので、これをオンにしてみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/34854ff9-8766-ec69-e547-e62aab98976e.png)
- `Use Scene Depth for Occlusion`=ON
- `Enable Session Tracking Feature`=`Scene Depth`にセット
そしてAndroidビルドを作るだけです。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">UE4.26で、ARCoreのデプスとれた!!😆<br>D_ARSessionConfigで、UseSceneDepthForOcclusion=On、EnabledSessionTrackingFeature=SceneDepth。エンジンコード読まなきゃわからんわ😅<br>デプスカメラの無いPixel3でこれなら上出来かな。しかし半透明マテリアルの丸影には反映されて無いなぁ🤔<a href="https://twitter.com/hashtag/UE4?src=hash&amp;ref_src=twsrc%5Etfw">#UE4</a> <a href="https://twitter.com/hashtag/UE4Study?src=hash&amp;ref_src=twsrc%5Etfw">#UE4Study</a> <a href="https://t.co/2e5igt20X4">pic.twitter.com/2e5igt20X4</a></p>&mdash; LazyKozy (@LazyKozy) <a href="https://twitter.com/LazyKozy/status/1312954614287749121?ref_src=twsrc%5Etfw">October 5, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

結果はご覧の通り。椅子の後ろのオブジェはちゃんとオクルージョンされているのがわかります。ただし半透明マテリアルにはオクルージョンが効いてないんですよね…。ちゃんとDepth Testはオンにしてるのですが…。ちゃんとエンジンソースを読めばわかるかもしれませんが理由は不明です。

#HandheldARサンプルにグレイマンを入れ込む
これは色々なやり方があると思いますが、今回はタッチした場所に歩くようにしたかったので、TopDown C++サンプルから、AssetsとCodeをマイグレートする方法で行いました。C++プロジェクトでなくBPプロジェクトを使っても問題ないと思います。
Assetのオペレーションで細かい部分を書くと大変なので端折りますが:sweat:、大雑把にやった事としては、以下の通りです。

- Codeをマイグレート。ContentはMannequin,TopDownCPP以下のMapをマイグレート。
- BP_ARPawnをReparentして、TopDownのPawnの子供にする。
- BP_ARPlayerControllerをReparentして、TopDownのPlayerControllerの子供にする。
- TopDownExampleMapの余分な壁などを削除、`DefaultGameMap`にセット。
- Map上にCameraActorを配置。`Auto Activate for Player`=Player0をセット。

#AR Shadowについて調べてみた
ARでは影は非常に重要な要素の一つです。影の有無で、存在感が全く変わってきます。
HandheldARでは、プリミティブなオブジェクトしかないので丸影を使っていますが、やはりグレイマンからは、ちゃんとCast Shadowを落としたい所です。
しかし調査し始めて、これがかなり難しそうな事に気付きました:frowning2:
まず情報が少ない。情報があったとしてもかなり古くてそのまま使えないものがほとんどでした。
https://youtu.be/Z6FAl6v0ax0
[![](http://img.youtube.com/vi/Z6FAl6v0ax0/0.jpg)](http://www.youtube.com/watch?v=Z6FAl6v0ax0)

そんな中では、このムービーが一番有益だったと思います。が、このムービーも4.17でARKitを使っているので、そのまま持ってくることは出来ませんでした。

#AR Shadowのマテリアル発見!!
色々調べた末に、何とEngineの中に使えるサンプルマテリアルがある事に気付きました。灯台下暗し!:stuck_out_tongue:
**\Engine\Plugins\Runtime\AR\ARUtilities\Content\Materials**
この中の**M_ScreenSpacePassthroughCamera**が今回使用するマテリアルです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/44db11be-a989-6e38-7e83-2035604480f3.png)

#ScreenSpacePassthroughCameraマテリアルを使ったAR Shadowの実装
*（最初ツイートした時にはEngine改造が必要だと書きましたが、4.26正式版でもう一度トライしてみたらEngine改造無しで出来ました!!:grinning:）*
このマテリアルをそのまま使う事は出来なかったので、以下の様な変更を行いました。
1. /Engine以下のファイルを変更するのは、あまり好ましくないので、まずM_ScreenSpacePassthroughCameraを自分のContentディレクトリにCopyします。
2. M_ScreenSpacePassthroughCameraを変更します。
  - `ShadingModel`を`DefaultLit`に変更。
  - 最終OutputをEmissiveColorから`BaseColor`に繋ぎなおす。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/6453262e-f241-b19f-8492-4752d287f5c7.png)
3. M_ScreenSpacePassthroughCameraからMaterialInstanceを作成して(MI_ScreenSpacePassthroughCamera)、変更する。
  - `UseExternalTexture`と、`ExternalCameraTexture`をOn。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/eb86534d-4d5d-1769-f53e-debd2fd1137f.png)
4. BP_ARPawnにShadowレシーバー用のShadowMeshを作って、MI_ScreenSpacePassthroughCameraをアサイン。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/9bd843e5-2a5e-6a9a-dea3-a8503ddad165.png)
5. BP_ARPawnのBlueprintを変更。
  - MaterialInstanceDynamic型のVariable、`ShadowMaterial`を作成。
  - 以下のロジックを追加。TickでGetARTextureで取得したCameraImageを、TextureParameterValue`ExternalCameraTexture`に更新させるのがポイント。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/891792/3035ad0b-f7c1-8f78-881a-28635ee8dbe2.png)

#動いた!! けどいくつか問題点が…
これで最初のツイートで見せた、ARShadow付きの歩くグレイマンが動きました!!
別テイクのムービーも載せておきます。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">これは別テイクで、オクルージョンの失敗例😅<br>手前の柵はいい感じに取れてるんだけど、左の看板はごちゃごちゃした背景も看板として誤認識してるっぽい。<br>まあ単眼カメラのPixel3の限界かなぁ。LiDARが載せてくれれば、全然余裕で取れると思うんだけど😭<a href="https://twitter.com/hashtag/UE4?src=hash&amp;ref_src=twsrc%5Etfw">#UE4</a> <a href="https://twitter.com/hashtag/UE4Study?src=hash&amp;ref_src=twsrc%5Etfw">#UE4Study</a> <a href="https://twitter.com/hashtag/ARCore?src=hash&amp;ref_src=twsrc%5Etfw">#ARCore</a> <a href="https://twitter.com/hashtag/AR?src=hash&amp;ref_src=twsrc%5Etfw">#AR</a> <a href="https://t.co/BBk3BrNlbq">pic.twitter.com/BBk3BrNlbq</a></p>&mdash; LazyKozy 2nd (@LazyKozy) <a href="https://twitter.com/LazyKozy/status/1341585088941707264?ref_src=twsrc%5Etfw">December 23, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

しかしムービーを見てもらえればわかると思いますが、Litマテリアルはライティングの影響を受けてしまっているので、よく見るとShadowMeshが見えてしまいます。これの解決法も現時点ではわかりません。誰か知ってる方がいましたら教えて下さい…:sweat:
*（前述のチュートリアルムービーでは、`CastModulatedShadows`を使えばUnlitマテリアルにも影が落とせると言及されていたのでテストしてみましたが、動きませんでした。）*

あとオクルージョンが上手く判定出来ないケースもやはり多いですね。下の柵は割といい感じに取れているのですが、背景がごちゃごちゃしてると誤認識してしまう事がままあります。まあこれはGoogleが頑張ってもらうのを待つか、LiDARが載ってるデバイスを買うしかなさそうですね。

#今後の予定？
せっかくUE4-ARCoreの基本がわかってきたので、時間をみつけて今後も何かしたいと思っています。
VRマルチプレイヤーFPSはさんざん作ってきたので、ARマルチプレイヤーFPSを作ってみたいのですが…、まずもう一台ARCoreが動く端末を手に入れないといけませんね:sweat:

ここまで読んでいただいてありがとうございました。UE4-ARCoreをやってる人はかなり少数派だと思うのですが、がんばっていきましょう～。