はじめに
今回は、 Unity 向けに ARKit と ARCore の機能をまとめた AR Foundation について調べました。
開発環境
- Unity 2019.1.1f1 (2019.1以降が推奨されています)
- AR Foundation 2.1.0
- ARCore XR Plugin 2.1.0
- ARKit XR Plugin 2.1.0
環境構築
1. 必要なパッケージをインストール
Unity2019.1では ARFoundation は preview となっているので、まず、 Package Manager -> Advanced で Show preview packages を選択して、一覧に表示する。
一覧の中から AR Foundation
、 ARCore XR Plugin
、 ARKit XR Plugin
の3つをインストールする。
2. Android の API レベルを変更
- Project Settings -> Player -> Identification -> Minimum API Level を 7.0以上に変更する。
3. Hierarchy に必要な GameObject を配置
初めから置かれている Main Camera を削除する。
GameObject -> XR -> AR Session Origin
と AR Session
をヒエラルキーに配置する。
以上で ARFoundation の開発環境が整いました
動かしてみる
ここからはサンプルプロジェクトのシーンを動かしながら、各機能を確認していきます。
平面検出
平面を検出する
GameObject -> XR -> AR Default Plane
を一旦ヒエラルキーに配置して、 Prefab 化する。
Prefab 化したら、ヒエラルキーから削除する。
AR Session Origin に AR Plane Manager
コンポーネントを追加する。
AR Plane Manager
コンポーネントの Plane Plefab
に Prefab 化した AR Default Plane
を設定する。
ここまでで、 Android 、 iOS 両方に対応した平面検出が実装できました。
実機にビルドして動かすとこんな感じです。
ここまで、コードを一切書かずに作ることができます。
- 検出速度、精度共に ARKit 、 ARCore と同じくらいの性能です。
- iOS 上で動かす時のみ ARKit 同様、検出した平面が存在しない方向を向いた後に平面に向き直ると、平面の場所が大きくズレてしまう事があります。
平面にオブジェクトを置く
平面に向けて Ray を飛ばすために、 AR Session Origin に AR Raycast Manager
コンポーネントを追加する。
var hits = new List<ARRaycastHit>(); if (_arRaycastManager.Raycast(touch.position, hits,TrackableType.PlaneWithinPolygon)) { Pose pose = hits[0].pose; // ここに生成、移動処理を書く }
AR Raycast Manager
コンポーネントの ARRaycastManager.Raycast
メソッド で、平面に向けて Ray を飛ばして、 List<ARRaycastHit>
を取得することができる。
ARRaycastHit
から Pose
を取得してオブジェクトを生成、移動させる。
出来上がったものがコチラです。
マーカー認識
AR Session Origin に AR Tracked Image Manager
コンポーネント を追加する。
Project ビューで右クリック -> Create -> XR -> Reference Image Library
を作成する。
Add Image ボタンをクリックして、項目を増やし、Reference Image Library
に認識したいマーカー画像を設定する。
※ Specify Size を有効にし、 Physical Size(物理サイズ)を設定すると、認識の精度が上がります。
AR Tracked Image Manager
コンポーネント に作成したReference Image Library
を設定する。
また、検出したマーカーの位置に3D モデル等を表示たい場合は、 Tracked Image Prefab
に任意の Prefab を設定する。
ARTrackedImageManager.trackedImagesChanged
に Event を登録する事で、ARTrackedImagesChangedEventArgs
クラス が取得できる。ARTrackedImagesChangedEventArgs
クラス には、「新たに検出したマーカー」、「情報が更新されたマーカー」、「検出されなくなったマーカー」の情報 (ARTrackedImage
クラス) がadded
、updated
、removed
にそれぞれ格納されている。
ARTrackedImage
クラス から取得した値をもとに3D モデル等の Scale を調整する。
- Position 、 Rotation は
AR Session Origin
コンポーネント が検出したマーカーに合わせて自動で調整しているようです。
- 移動するマーカーには対応していませんでした。
- マーカーの移動後の再認識までの速度は iOS の方が早いようです。
顔認識
AR Session Origin
に AR Face Manager
コンポーネント を追加する。
AR Face Manager
コンポーネント の Face Prefab
に、顔に重ねて表示する Prefab を設定する。
- iOS 、 Android どちらも同じプロジェクトでビルド出来るが、 Rig は ARKit と同様のものを取得しているので、 Android では顔の位置と向きのみのトラッキングとなり、 Rig は動かない。
UnityEngine.XR.ARCore.ARCoreFaceSubsystem.GetRegionPoses
関数 で、 ARKit の Rig を ARCore の Rig に変換する事が可能。(変換可能なのは ARCore に定義されている額2点と鼻1点の合計3点のみ)
Environment Probes
AR Session Origin
に AR Environment Probe Manager
コンポーネント を追加する。
周囲の風景を写したい GameObject のマテリアルの Metallic
、 Smoothness
を1に設定する。(この2つの値で見た目の調整ができる)
- iOS のみ対応
- カメラに映った物を映し出すので、本来映らないものまで映り込んでしまう。
(赤枠で囲んだ部分に本来写らないはずのコップやキーボードが写っています。)
- ARKit の同機能とほぼ同じ。
World Map
ARKit 、 ARCore 共に似たような機能はあるが AR Foundation
では iOS のみ対応
ARKitSessionSubsystem sessionSubsystem = (ARKitSessionSubsystem)m_ARSession.subsystem; ARWorldMapRequest request = sessionSubsystem.GetARWorldMapAsync(); // World Map を取得 ARWorldMap worldMap = request.GetWorldMap(); // シリアライズ化 var data = worldMap.Serialize(Allocator.Temp);
UnityEngine.XR.ARKit.ARKitSessionSubsystem
から WorldMap
を取得し、シリアライズ化する事で、 AR 空間の情報を保存する事ができる。
// デシリアライズ if (ARWorldMap.TryDeserialize(data, out worldMap)) { data.Dispose(); } // World Map を適用 sessionSubsystem.ApplyWorldMap(worldMap);
byte 配列で保存してある World Map
をデシリアライズする事で、同じ AR 空間を再現する事ができる。
- 検出した平面や、生成した GameObject の位置、向き、大きさを再現する事ができています。
- ロード用のメソッドを呼んでから、再現されるまで、3秒程かかります。
おわりに
- Unity Technologies が作っているだけあって、 UnityEditor 上で簡単に AR が実装できました。
- ただ、 ARFoundation を使用して iOS 向けに作ったプロジェクトを、そのまま Android 向けにビルドすると動作に差が生まれることがありそうです。