イケメンテックラボでエンジニアをしている寺林です。
Apple社が出しているARKitをUnityで使う方法を紹介します!
ARKitとは?
ARKit(エーアールキット)はApple社が提供しているARライブラリです。
特徴としてARCoreと同じように平面検知や画像を使ったマーカーARに加え、ARKit2ではAR空間を他の人と共有したり、現実の物体をオブジェクトとして認識させトラッキングさせたりといったこともできます。
一番身近なところだとiPhoneに今入っている計測アプリがARKitで出来ています。
iPhoneを持ってる人は一度は触ったことがあるんじゃないでしょうか?


Appleは最近かなりARを推しており、これからがとても楽しみなライブラリの一つです。
今回はこのARKitで開発をするためのSDKを用意しサンプルをビルドしてみましょう。
必要なもの・環境
ARKitはApple社が提供しているため、(当然といえば当然なのですが)iOSデバイスを用意する必要があります。
Appleの拡張現実のページによるとARKit2に対応しているのは以下の端末です。iOS12をインストールしている必要があります。

また今回の記事ではUnity2018.3.xを使います。UnityでiOSビルドできる環境が既にある方向けの記事とさせていただきます。
ARKitのUnitySDKを用意する
ARKitはARCoreと違い、Unity用のSDKはAppleは作っていません。
UnityTechnologyが作っているのでそちらを利用します。
BitBucketにリポジトリがあるのでこちらにアクセスします
ダウンロードをクリックし、

リポジトリをダウンロードする をクリックしプロジェクトを落としましょう。

ダウンロードしたらプロジェクトをUnityで開きましょう!
サンプルシーンをビルドしてみる
まずはサンプルシーンを開きましょう。
Assets/UnityARKitPlugin/Examples/ のなかにサンプルシーンが大量に入っています。

ARKitの機能/バージョン別でサンプルが別れています。
今回は一番ベースとなるサンプルのUnityARKitSceneを開きましょう

ダブルクリックでシーンを開きます。

ビルドをしてみる
なにやら色々ありますがとりあえず一回ビルドをしてみましょう。
BuildSettingsを開き、iOSにSwitchPlatformをします

次にScene in Buildの中にあるUnityARKitPlugin/Examples/UnityARKitScene/UnityARKitScene にチェックを入れ、BuildAndRunをクリック、XCodeのプロジェクトの出力先を決めてビルドしましょう

ビルドが終わったらXCodeが立ち上がるので実機を繋いでデバイスを選択します

Projectの設定からBundleIdentifierを固有のものにしてTeamを選択しビルドしましょう!
サンプルシーンで遊んでみる
ビルドが終わるとアプリが実行されます。
まずはじめにカメラの権限を要求してくるのでOKを押しましょう

カメラが起動し平面を検知した場合その部分に青色の枠が表示されます。
この枠内がARKitが平面として識別している範囲になります。

枠内をタップするとキューブが表示されます。
ドラッグしてやると平面上を移動させることもできます。

ちゃんとARとして出ているので当然ですが横からもみることが出来ます。

とてもシンプルなサンプルですがオブジェクトの配置した際の精度の高さなどは体験できると思います。
コードを見てみる
さてこのシンプルなサンプルがどういう仕組みになっているのか軽く見てみましょう。
シーンのHierarchyを見てみるとこのようになっています。

この中で大事なものは
・GeneratePlanes(平面を表示する)
・ARCameraManager(ARKitの設定などをしてくれる)
・HitCubeParent/HitCube(タップしたところにキューブを置く処理)
の3つです。
GeneratePlanes
GeneratePlanesでは平面検知時に表示される青色の枠のオブジェクトを生成してくれます。

UnityARGeneratePlaneというスクリプトの中でのその処理をしています。
PlanePrefabには表示させたいオブジェクトを入れます。
サンプルではdebugPlanePrefabというprefabが入っています。

これを表示して、平面のサイズに応じて大きさを変えてやっているという感じです。結構シンプルな作りです。
ARCameraManager
ARCameraManagerにはUnityARCameraManagerというスクリプトが入っています。
このスクリプトではARKitのセッション情報の設定などをしています。ARCoreではSessionConfigという外部ファイルに設定をして読み込む形でしたがARKitではそのままインスペクタで設定します。

設定項目はこのような感じになっています。
・Camera MainCameraを指定します。必ず設定する必要があります。
・Start Alignment これはちょっと難しいんですがAR空間の向き、座標系をどう決めるか、みたいな設定(のはず…)です。 UnityAR Aligment Gravity And Headingにするとデバイスのセンサーを使ってx軸、z軸を経度緯度の方向と一致させることが出来ます。 こういった設定はARCoreにはなかった気がするので現実と向きを合わせたりするにはARKitは向いてるのだと思います。
・Plane Detection 垂直面を認識させるかとかそういった設定です。
・GetPoint Cloud 特徴点(サンプルで言うところの黄色い点たち)を取得するかどうか
・Enable Light Estimation ARKitの機能の一つ、環境光推定を使うかどうか。部屋が暗い時にDirectionalLightのパラメータを暗めに設定してくれる機能です。
・Enable Auto Focus オートフォーカスを入れるかどうか。
・EnvironmentTexturing ARKit固有の機能である環境マッピングを利用するかどうかを選択します。有効にする場合、追加でコード書く必要がありますが今回は触れません
・DetectionImages 画像を使ったマーカーARをする際に使う画像の情報を入れます。
・DetectionObjects ARKitのオブジェクトトラッキング機能を使う場合に使用します。オブジェクトのデータを入れます。
HitCubeParent/HitCube
このオブジェクトにはUnityARHitTestExampleというスクリプトが入っています。
名前でなんとなくわかると思いますがこれが画面をタップした際に平面にキューブを移動させる処理が書かれているクラスになります。

実際にタップしたら〜の処理の部分はUpdate()の中、46行目からです
var touch = Input.GetTouch(0); if (touch.phase == TouchPhase.Began || touch.phase == TouchPhase.Moved) { var screenPosition = Camera.main.ScreenToViewportPoint(touch.position); ARPoint point = new ARPoint { x = screenPosition.x, y = screenPosition.y };
まずはじめにタッチされたら座標をビューポート座標に変換し、
ARPoint型変数を作り入れてやる必要があります。これはARKitのヒットテスト関数がARPoint型を必要とするからです。(なんでVector2とかじゃダメなんでしょうね…)
// prioritize reults types ARHitTestResultType[] resultTypes = { //ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingGeometry, ARHitTestResultType.ARHitTestResultTypeExistingPlaneUsingExtent, // if you want to use infinite planes use this: //ARHitTestResultType.ARHitTestResultTypeExistingPlane, //ARHitTestResultType.ARHitTestResultTypeEstimatedHorizontalPlane, //ARHitTestResultType.ARHitTestResultTypeEstimatedVerticalPlane, //ARHitTestResultType.ARHitTestResultTypeFeaturePoint };
次にヒットテストで命中させる物の種類を決めます。
サンプルでは「ARHitTestResultTypeExistingPlaneUsingExtent」を指定しています。これは平面のサイズを考慮した現実世界に出している平面の上に命中すると言う設定です。
基本的にこれで問題ないです。他のオプション使うことはあんま無いような気がします
一覧はAppleのリファレンスに載っているので木になる方は参考にしてみてください。

foreach (ARHitTestResultType resultType in resultTypes) { if (HitTestWithResultType (point, resultType)) { return; } }
そしてその設定したタイプの数だけヒットテストを実行します。
HitTestWithResultTypeはスクリプト12行目から書かれています。
bool HitTestWithResultType (ARPoint point, ARHitTestResultType resultTypes) { ListhitResults = UnityARSessionNativeInterface.GetARSessionNativeInterface ().HitTest (point, resultTypes); if (hitResults.Count > 0) { foreach (var hitResult in hitResults) { Debug.Log ("Got hit!"); m_HitTransform.position = UnityARMatrixOps.GetPosition (hitResult.worldTransform); m_HitTransform.rotation = UnityARMatrixOps.GetRotation (hitResult.worldTransform); Debug.Log (string.Format ("x:{0:0.######} y:{1:0.######} z:{2:0.######}", m_HitTransform.position.x, m_HitTransform.position.y, m_HitTransform.position.z)); return true; } } return false; }
HitTest関数自体はUnityARSessionNativeInterfaceが持っています。ARPointとタイプを与えてやると結果を配列で返します。(配列0番目が一番手前の平面情報が入ってるはず)
ARCoreの時はHitTestの結果をそのままposition,rotationに代入できたのですがARKitの場合は一手間必要です。
hitResultのメンバーにworldTransformと言うMatrix4x4型の変数がいます。このマトリクスをUnityARMatrixOps.GetPosition(GetRotation)関数を使ってUnityのワールド座標に変換してやる必要があります。(ちょっとめんどくさいですね。)
以上がサンプルの簡単な説明になります。
まとめ
ARKitのサンプルを触ってみました。
ARCoreと同じように手軽にリッチなAR体験を作ることができます。 ARKitには他のSDKには無い機能(環境マッピングや物体のトラッキングなど)があるので独自の表現が出来そうですね。
ARKItもまだまだ今後アップデートしていくと思われるので今のうちから触っておいても損はしないと思いますので興味ある方は是非試してみてはいかがでしょうか。
次回以降も新しい技術や検証をしていきますので次回も是非ご覧ください!