REALITYにAR機能を実装してみた話
はじめまして、Unityエンジニアのyaegakiです。
自由研究でREALITYアプリにAR機能を実装したときに意外と苦労したのでその話をご紹介します。
注意:この記事はデモとしてAR機能を実装した話であり、将来REALITYアプリにAR機能が実装されることを約束するものではありません。
実装する機能について
REALITYアプリにはさまざまな機能があるので一口にAR機能を実装するといってもどこに実装するかを考える必要があります。
候補としては配信や視聴、アバターカメラなどがありますが今回は視聴時にAR機能を使って配信者を目の前に召喚するという機能を実装することにしました。
前準備
REALITYではアバター表示に関する部分はUnityで実装されているのでAR機能もUnityで実装することになります。
今回は実装のためにAR Foundationを使用することにしました。
使用したUnityとパッケージのバージョンは以下の通りです。
・Unity 2018.4.27
・AR Foundation 1.5.0-preview.7
・ARCore XR Plugin 2.2.0-preview.6
・ARKit XR Plugin 2.2.0-preview.6
通常のアプリの場合Unity2018でAR Foundationを使用するのはおすすめしません。
Preview版パッケージであること、Android11対応ビルドが大変であることがその理由です。
Android11対応ビルドについては後述します。
AR機能を実装する
ここからは実際にAR機能の実装について記載していきます。
UnityエディタでシーンにAR SessionとAR Session Origin オブジェクトを配置します。
このふたつは右クリックメニューから作成することができます。
AR機能をオン/オフするためにはAR SessionやAR Session Originオブジェクトのアクティブ状態を切り替える必要があります。なのでヒエラルキーのトップに直接配置するのではなく親オブジェクトを作成して子として追加しておいた方が後々便利です。
AR Session Originオブジェクトに AR Plane Manager と AR Raycast Manager を追加します。AR Plane Managerは検知した平面の可視化、AR Raycast Managerは平面との当たり判定取得に使用します。
AR Plane ManagerにはPlane Prefabを設定する必要があります。Plane Prefabは平面の可視化時に使用されるPrefabです。
右クリックメニューの XR > AR Default Planeで作成したオブジェクトをPrefab化してそれを設定しましょう。
AR Session Originオブジェクトの配下に物理カメラから取り込んだ画像を表示する用のカメラが存在するので必要に応じてdepthの調整などを行います。
ここまででオブジェクトの配置は終了です。
デフォルトではAR機能をオフにするためにオブジェクトを非アクティブにしておきましょう。
次はコードを実装していきます。
大まかな流れは以下の通りです。
1. ARオブジェクトのアクティブ化
2. アバター配置位置決定
3. アバター配置
シーンにはARオブジェクトを非アクティブ状態で配置しています。
ボタンを押した際にARオブジェクトをアクティブ状態にするようにします。
// ARオブジェクトをシーンから取得
var ar = GameObject.Find("AROrigin");
// アクティブ化
ar.gameObject.SetActive(true);
// SessionとSessionOriginの取得
// 雑に取得していますがちゃんとやる場合はSerializeFieldなどに設定しましょう;)
var session = ar.GetComponentInChildren<ARSession>(true);
var sessionOrigin = ar.GetComponentInChildren<ARSessionOrigin>(true);
session.Reset();
AR Plane Managerの設定が正しくできている場合、以下のように認識された平面が可視化されます。
認識された平面をタップすることで配置位置を決定できるように実装します。
これを実現するためには ARRaycastManager を使用します。
使い方は簡単でタップした位置を渡してRaycastメソッドを呼ぶだけです。
// 事前にARRaycastManagerを取得しておく
var raycastManager = sessionOrigin.GetComponent<ARRaycastManager>();
// 以下毎フレーム行う処理
// タッチ位置
// ちゃんとやるならInput.touchesを使う
var touchPos = Input.mousePosition;
// ヒット情報を格納するリスト
var hits = new List<ARRaycastHit>();
var hit = raycastManager.Raycast(touchPos, hits, TrackableType.PlaneWithinPolygon);
if (!hit)
{
return;
}
// ヒットした位置
var hitPos = hits[0].pose.position;
配置場所が決定したら可視化された平面の非表示と実際の配置を行います。
// 位置を決定したらもう平面が見えてる必要はないので非表示にする
var planeManger = sessionOrigin.GetComponent<ARPlaneManager>();
planeManger.enabled = false;
foreach (var plane in planeManger.trackables)
{
plane.gameObject.SetActive(false);
}
// アバターの位置を変更
avatarWindow.position = hitPos;
// そのまま配置するとアバターが正面を向いてない可能性があるので調整する
var arCamera = sessionOrigin.transform.Find("AR Camera");
var look = arCamera.position - avatarWindow.position;
look.y = 0;
avatarWindow.rotation = Quaternion.LookRotation(-look, Vector3.up);
これで実装は終了です。
ここまでは意外と簡単ですがここから地獄のビルド編が始まります。
Androidビルド前知識
一般的なUnity製アプリではUnityから出力したプロジェクトをビルドすればそのまま実際のアプリになりますが、REALITYアプリでは異なります。
REALITYアプリではUnityから出力したプロジェクトを加工してモジュール化したものをAndroidプロジェクトで使用するという形になっています。
Unity as a Library を知っている人はそれで想像していただけるとわかりやすいかもしれません。(公式のUnity as a Libraryを使ってるわけではなくREALITY独自で実装しています。)
Androidビルド
一般的なアプリでもREALITYアプリでもまずはUnityでビルド(プロジェクトのエクスポート)を行います。
ビルド前にMultithread Renderingをオフ、MinSDKを7以上に設定します。
Unityでビルドしたものをそのまま実際のアプリにする場合かつAndroid11以上に対応するためにはgradleの設定を行う必要があります。
詳細は以下のページをご覧ください。
gradleの設定を行う場合、Android Gradle Plugin 3.6.0以上にすることが求められますが、ARCore XR Plugin 2.2.0-preview.6の場合はビルドに失敗します。
解決方法は違うバージョンを使うかaarファイルを加工する必要があります。
違うバージョンを使用するためにはそもそもUnityのバージョンを2019以上にする必要があります。
これがUnity2018でAR Foundationの使用をお勧めしない理由です。
aarファイルを加工する方法については後述します。
REALITYアプリの場合はUnityのgradle設定は使用しないので特に設定せずプロジェクトのエクスポートを行います。
エクスポートしたプロジェクトに含まれる以下のファイルをAndroidプロジェクトにコピーします。
AndroidCameraPlugin.aar
arcore_client.aar
unityandroidpermissions.aar
ARPresto.aar
UnityARCore.aar
コピーしたライブラリが含まれるようにgradleファイルを編集します。
implementation files("libs/arcore_client.aar", "libs/unityandroidpermissions.aar", "libs/AndroidCameraPlugin.aar")
ARPresto.aarとUnityARCore.aar についてはそのまま追加するとビルドに失敗します。
失敗する理由はaarファイル内にAndroidManifest.xmlなどが含まれていないためです。
エラーメッセージがめちゃくちゃわかりづらいので注意しましょう。
// エラーメッセージ
Execution failed for task ':javaPreCompileDebug'.
> Could not resolve all files for configuration ':debugCompileClasspath'.
> Failed to transform artifact 'ARPresto.aar (:ARPresto:)' to match attributes {artifactType=android-classes, org.gradle.usage=java-api}.
> Execution failed for AarToClassTransform: /Users/XXX/Documents/arsampleapp/Temp/gradleOut/libs/ARPresto.aar.
> entry
> Failed to transform artifact 'UnityARCore.aar (:UnityARCore:)' to match attributes {artifactType=android-classes, org.gradle.usage=java-api}.
> Execution failed for AarToClassTransform: /Users/XXX/Documents/arsampleapp/Temp/gradleOut/libs/UnityARCore.aar.
> entry
このふたつの中で実際に必要なのは内部のネイティブライブラリ(.so)だけなのでunzipして中身だけ適切なディレクトリに配置すればビルドできるようになります。
Proguardを有効にする場合は以下の設定を追加します。
-keep class com.google.ar.** { *; }
-keep class com.google.vr.** { *; }
-keep class com.unity3d.plugin.** { *; }
AndroidManifestにAR Coreを使用するための設定を記載します。
<!-- AR機能はOptional -->
<meta-data
android:name="com.google.ar.core"
android:value="optional"
/>
<!-- Android11対応 -->
<queries>
<package android:name="com.google.ar.core">
</queries>
これでAndroidビルド設定は終わりです。
iOSビルド前知識
iOSもAndroidと同様で特殊な構成になっています。
iOSビルド
まずはUnityでビルドを行います。
ビルド前にMinOSをiOS11以上、Camera Usage Descriptionの設定を行います。
ビルドして生成された以下のファイルをiOSのプロジェクトにコピーします。
com.unity.xr.arextensions/Runtime/iOS/*.a
com.unity.xr.arextensions/Runtime/iOS/*.m
UnitySubsystemsManifest.json
UnitySubsystemsManifest.jsonは正しいパス(Data/UnitySubsystems/Runtime/UnitySubsystemsManifest.json)に配置しないと動作しないので注意が必要です。
iOSのプロジェクトのinfo.plistにarkitを追加します。
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>arkit</string>
</array>
Build SettingsからArchitectures を arm64 に変更します。
これでiOSビルド設定は終わりです。
まとめ
AR機能を実装すること自体は比較的簡単なので試してみたら面白いと思います。REALITYアプリでは特殊な構成ゆえにビルドに苦労しましたがこの記事を見ればもう迷わなくて済みますね。