UnityとARKitで異世界への扉(ポータル)を作る方法

AR Ojisan
11 min readNov 26, 2017

--

【初の技術ネタ】

どうも、思いの外更新が持続できていて、自分でも驚いている小林@ARおじさんです。さて今回はARKitがベータ版で出始めたころに世間を騒がせたデモのカラクリについてご紹介します。

そのデモがこちら。

普通の道のど真ん中に突如扉が現れて、向こう側には街並みとはかけ離れたジャングルの景色が、、、。そして扉を潜るとそこには一面ジャングルの景色が広がっています。扉を振り返るとその中には先ほどいた街並みの景色が!

紹介された当初は度肝を抜かれたと同時に「これ、どうやってやってるんだろう」と頭を捻ったのを覚えています。当時はまだUnityすらも初心者だったため、ほぼ未知の世界でしたが、githubでこれと同じことを実現したものを見つけたので、今回はその仕組みを説明してみたいと思います。

githubページはこちらになります。

【何が起こっているのか】

さて今回のこのデモ、大きく二つの状況と一つのイベントに分けて説明します。

二つの状況というのは

  • ユーザ(カメラ)がポータルを潜る前
  • ユーザ(カメラ)がポータルを潜った後

そしてイベントというのは「ユーザ(カメラ)がポータルを潜るイベント」です。なお、ここで今回言っている「ポータル」というのは異世界と現実世界をつなぐ扉のことを指します。

【シーンをみてみる】

さて早速上のgithubリンクからソースコードをダウンロードしてUnityで開いてみるとMainSceneというのが確認できます。このシーンで着目すべきは

  • Main Cameraのinspector
  • ARWorldの中身

の二点です。この二つに着目しながらシーンの中身がどうなっているのか確認してみましょう。

【状況1:ユーザ(カメラ)がポータルを潜る前】

まず最初の状況です。

シーンにはMain Cameraがあり、Portalがあり、そのさきにARWorldという異世界が広がっています。先ほどデモ動画を確認いただくか、今回のこのソースを実際にiPhone上で実行していただくとわかるのですが、Main Cameraが移動する(つまりユーザ自身が移動する)とPortalの穴から見えるAR Worldの見え方も連動して動いています。しかし、Portalを潜らずにPortalの奥に回り込んでもそこには何もありません。さてこれはどうなっているのでしょうか?

このカラクリを理解するにはまず LayerとCulling Maskの関係性、それからRender Textureについて理解する必要があります。

Layerというのはシーン内の各オブジェクトをグルーピングしているもので、このLayerを設定することでオブジェクトの描画のオンオフを簡単に切り替えることが可能となります。今回のシーンでみてみるとAR World下にあるPortalをクリックしてInspectorを確認するとLayerは「Portal」となっています。Portal直下のオブジェクトも全てLayerは「Portal」です。逆にARWorld内のそれ以外のオブジェクトを確認するとLayerは「ARWorld」です。

そしてこの描画のオンオフを切り替えるのがカメラの中に用意されている「Culling Mask」というものです。Main CameraインスペクタのCameraコンポーネント内に「Culling Mask」という項目があります。

上から2番目

このプルダウンを選択するとMain Cameraが描画するLayerがなんなのかがわかります。

チェックがついているのが描画するLayer

チェックしてみるとどうやらMain CameraはARWorldは描画しないと設定されているようです。しかし妙です。実際にデモ動画やプレイしてみるとARWorldに設定されているオブジェクトたちは描画されています。

実はPortalCamというのがシーン内に用意されていて、ここにもカメラコンポーネントが仕込んであります。

このPortalCamを選択し、インスペクタを確認すると今度はCulling MaskがARWorldを描画する代わりにPortalLayerを描画しないようになっています。

なるほど、つまりこのPortalCamから見えたARWorldの世界が実際のデモ動画の中で見えていたわけです。ちなみにPortalCamはportal.csというスクリプトのUpdate関数で常にMainCameraとの位置と向きの同期をとっています。しかし今度は「PortalCamの描画はどのようにMainCamの中で描画されているのか?」という疑問が浮かびます。そこで登場するのが今度はRender Textureというものです。これはカメラの描画をテキスチャーとしてオブジェクトに貼り付けることが可能です。実はポータルの入り口にはこのRender Textureが貼り付けられていて、このRender TextureがPortalCamが描画する映像を映し出し、それをMainCameraがユーザに対して描画しているということです。

これによって現実空間にぽっかり穴が空いて、奥に異世界が見える。という演出が可能になります。ちなみにRender TextureにはPortal Shaderというものが適用されています。Shaderについては目下勉強中の僕なので(笑)あまり正確なことはわかりませんが、おそらく奥行きのある3D描画を平面の2D描画に変換しているのではないかと思っています(ここだけは自信がない)

何はともあれ、これによってPortalを潜る前の描画はできました。これは何もARKitだけではなくて、Kudanなどを使って写真の奥に奥行きある世界を映し出すなどの演出の際にも使えます。

状況1はまとめると下図のようになります。

【ユーザ(カメラ)がポータルを潜るイベント】

さて目の前に異世界への扉が開いていればその中に入ってみたくなるのが人間の性というものです。ではポータルをくぐった瞬間は一体何が起こるのでしょうか?次はこの部分を説明します。

シーンに設定されているMain Cameraのインスペクタを注意深くみてみるとなんとSphere ColliderとRigibodyが設定されていることがわかります。

つまりカメラ(ユーザ)が何かと衝突したことができるようになっています。ではこれは何と衝突したことを検知する為に設定されているのでしょうか?そう、Portalです。

Portalオブジェクトをクリックしてインスペクタを確認するとColliderとRigidbodyが設定されていることがわかります。

そしてこのPortalにはPortalCamとMainCameraの動きを同期させていたPortal.csもアタッチされています。実はこのスクリプトにはもう一つOnTriggerEnter関数が設定されています。

この関数内ではこのスクリプトをアタッチされているオブジェクトと「MainCamera」タグが設定されているオブジェクトが衝突するとif文の中が実行されます。そしてその中身は24行目のたった1行です。この1行はMain CameraのCulling maskの設定を変える式になっています。この式自体は少し見慣れないものかもしれませんが、Culling maskは各レイヤーがインデックスを持っていて、そのインデックス番目のビットを0か1にすることでオンオフを切り替えています。

LayerMask.NameToLayer(“ARWorld”)はレイヤー名からインデックスを取得し、シフト演算子でインデックス分だけ左にシフトしています。そしてそのビットとmainCameraのCulling Maskのビットを「^=」でXORしています。XORで処理しているのは再度ユーザがポータルを通って現実の世界に戻った時に、再びARWorldレイヤーの描画をオフにする為です。

すなわち24行目ではMain CameraのCulling Maskを編集し、ARWorldレイヤーを表示させるように設定しています。こうすると今までRender Textureごしからしか見えていなかったARWorld全体がMain Cameraで描画することが可能になります。

こうしてポータルをくぐった瞬間にARWorldのなかに入ったような演出をすることが可能となります。

【状況2: ユーザ(カメラ)がポータルを潜った後】

それでは最後にユーザがポータルを潜った後、どうなっているのかを説明します。先ほど紹介したようにPortalとMain Cameraの衝突を検知することでMain CameraはARWorldレイヤーを描画できます。これで一件落着のように思われますが、実はもう一つ重要なことがあります。それはユーザが後ろを振り返った時にPortalの窓からは現実世界が見えているということです。

この表現ができなければ、ユーザが現実世界とARWorldが繋がっているように感じることができません。この表現を行う上でキーとなるのはPortalオブジェクトの直下にある「MaskFace」です。

このMaskFaceのインスペクタを確認するとShaderに「DepthMask」というものが設定されていることがわかります。

それではDepthMaskの中身をみてみましょう。

DepthMask.shaderの中身は先ほどのに比べるとだいぶ短くなっています。まずTagsではレンダーキューをGeometryよりも一つ低い値にしてデフォルトの描画よりも優先順位をあげています。そしてZWriteをOnに設定することでこのシェダーが適用されたオブジェクトの後ろにあるものは描画されなくなり、ColorMaskを0に設定することでこのシェダー自体も何も描画されません。そうするとARWorldの中にぽっかり穴が空いたような描写になります。ただここもShaderについては目下勉強中の為(以下略

こうすることでMaskFaceの部分だけ ARWorldが表示されず、iPhoneカメラが捉えている現実世界が見えているというような仕組みになっています。

これによってARWorld内での挙動も実現することができます。

【色々な演出が可能】

このカラクリさえ取得してしまえば、色々な演出をARKitで行うことが可能になります。

例えばオフィスの向こうでサッカー場が広がっていたり

ちょっとホラーチックな世界を出したり

現実世界をちょっと楽しくすることができます。

こんなポータルを使った楽しいARの世界、あなたも作ってみてはいかがでしょうか?

それではまた次回!

--

--