見出し画像

Google ARCore Geospatial API で Hello World アプリを構築する

はじめに

Google ARCore Geospatial API を使った簡易なデモを作成します。


OnePlanet XR について

https://1planet.co.jp/xrconsulting.html

このブログ記事は OnePlanet XR によるものです。
OnePlanet XR は、AR/MR/VPS技術に専門特化したコンサルティングサービスです。豊富な実績を元に、AR/MR技術を活用した新たな事業の立ち上げ支援や、社内業務のデジタル化/DX推進など、貴社の必要とするイノベーションを実現いたします。

ご相談から受け付けております。ご興味ございましたらお問い合わせください。


Google ARCore Geospatial API について

Google ARCore Geospatial API は、Google ストリートビューすべての場所に3Dコンテンツを配置することができ、大規模な AR体験を作成することができるテクノジーです。スマホのセンサーとGPSを使用し、スマホのカメラで映し出された情報から検出。その情報からGoogleのVPSテクノロジーが提供するローカライズ モデルと照合し、ユーザーのスマホの正確な位置を特定します。


Google ARCore Geospatial API 動作サポートしているスマホデバイス

Android、iOSといったスマートフォンに対応していますが、リリースしている全機種に対応しているわけではございません。サポートしているスマートフォンについて、以下のページでご確認ください。


作成するアプリ

タップしてモデルを配置。(Google ARCore Geospatial APIを使い、緯度、経度、高度の情報をローカルに保存。)
アプリケーションを再起動すると、Google ARCore Geospatial APIのVPS機能が働き、タップした場所にモデルが再配置するアプリケーションを作成します。

開発環境 / 動作環境

Unity Editor 2020.3.28f1
AR Foundation 4.2.2
ARCore XR Plugin 4.2.2
ARKit XR Plugin 4.2.2
XCode 14.1
CocoaPods 1.11.3
iPhone 12 Pro (iOS 15.6)


準備

以下の記事を参考にGoogle ARCore Geospatial API の サンプルを動作するところまで進めます。

上記を進めていく上でトラブルが発生した場合、トラブルの解決方法についても記載しておりますが、解決しない場合は以下も参考にしていただければと思います。

XCode、CocoaPodsのバーションが低い場合、実行時にエラーが発生し原因究明が困難です。また、AR Foundation と Google ARCore Geospatial API のバージョンによって、カメラ映像の描画にバグがあります。開発環境 / 動作環境に記している内容であれば問題なく動作すると思います。


開発

AR Session Origin と AR Session を追加

新規でSceneを作成し、Main Cameraを削除。その後、ヒエラルキー上で右クリックでメニューを表示。AR Session Origin と AR Sessionを選択すると、ヒエラルキー上にAR Session OriginとAR SessionのGame Objectが生成されます。

AR Session Origin に コンポーネント を追加

AR Session Origin に AR Earth ManagerAR Anchor Manager を追加します。

ARCore Extensions の 作成

ヒエラルキー上でGame Objectを作成し、名前を「ARCore Extensions」とします。ARCore ExtensionsにAR Core Extensionsのスクリプトを追加。
Session、SessionOrigin、Camera Manager に ヒエラルキーのあるAR Session、AR Session Origin、AR Cameraを設定します。

AR Core Extensions Config には、Assets/Samples/ARCore Extensions/バージョン番号/Geospatial Sample/Configurations/GeospatialConfig.assetを設置します。

Geospatial Example の作成

新規でGame Objectを作成。名前をGeospatial Exampleに変更します。
GeospatialExampleを新規スクリプトを作成し、Geospatial Exampleに追加します。GeospatialExampleのコードは以下になります。

using Google.XR.ARCoreExtensions;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class GeospatialExample : MonoBehaviour
{
    [SerializeField]
    private ARAnchorManager arAnchorManager;
    [SerializeField]
    private AREarthManager arEarthManager;
    [SerializeField]
    private GameObject arObject;
    [SerializeField]
    private GameObject button;
    
    private double latitude = -1;
    private double longitude = -1;
    private double altitude = -1;

    private const double VERTICAL_THRESHOLD = 25;
    private const double HOLIZONTAL_THRESHOLD = 25;

    private ARGeospatialAnchor anchor;

    private bool isCreateAnchor = false;

    // Start is called before the first frame update
    void Start()
    {
        var la = PlayerPrefs.GetString("latitude", "");
        var lg = PlayerPrefs.GetString("longitude", "");
        var al = PlayerPrefs.GetString("altitude", "");

        if (la == "")
        {
            return;
        }
        latitude = double.Parse(la);
        longitude = double.Parse(lg);
        altitude = double.Parse(al);
    }

    // Update is called once per frame
    void Update()
    {
        if (IsSkip())
        {
            return;
        }
        Adjust();
    }

    bool IsSkip()
    {
        if (Application.isEditor)
        {
            return true;
        }
        
        if (ARSession.state != ARSessionState.SessionTracking)
        {
            return true;
        }
        if (arEarthManager.IsGeospatialModeSupported(
            GeospatialMode.Enabled) == FeatureSupported.Unsupported)
        {
            return true;
        }
        if (!IsHighAccuracyDeviceEarthPosition())
        {
            return true;
        }
        if (!IsExistGeoSpatialAnchor())
        {
            return true;
        }
        return false;
    }

    bool IsHighAccuracyDeviceEarthPosition()
    {
        if (!IsEarthTracking())
        {
            return false;
        }
        var pose = arEarthManager.CameraGeospatialPose;
        var verticalAccuracy = pose.VerticalAccuracy;
        var horizontalAccuracy = pose.HorizontalAccuracy;

        if (verticalAccuracy > VERTICAL_THRESHOLD && 
            horizontalAccuracy > HOLIZONTAL_THRESHOLD)
        {
            return false;
        }
        return true;
    }

    bool IsExistGeoSpatialAnchor()
    {
        if (!IsEarthTracking())
        {
            return false;
        }
        button.SetActive(true);
        if (latitude == -1 && longitude == -1 && altitude == -1)
        {
            return false;
        }

        if (!isCreateAnchor)
        {
            anchor = null;
            var offsetRotation = Quaternion.AngleAxis(100f, Vector3.up);
            var pose = arEarthManager.CameraGeospatialPose;
            anchor = arAnchorManager.AddAnchor(latitude, longitude, altitude, offsetRotation);
        }
        isCreateAnchor = anchor != null;
        return isCreateAnchor;
    }

    void Adjust()
    {
        arObject.SetActive(true);
        arObject.transform.SetPositionAndRotation(
            anchor.transform.position,
            anchor.transform.rotation);
        
        PlayerPrefs.SetString("latitude", latitude.ToString());
        PlayerPrefs.SetString("longitude", longitude.ToString());
        PlayerPrefs.SetString("altitude", altitude.ToString());
        PlayerPrefs.Save();
    }

    public void UpdatePosition()
    {
        if (IsHighAccuracyDeviceEarthPosition())
        {
            isCreateAnchor = false;
            var pose = arEarthManager.CameraGeospatialPose;
            latitude = pose.Latitude;
            longitude = pose.Longitude;
            altitude = pose.Altitude;
        }
    }

    private bool IsEarthTracking()
    {
        return arEarthManager.EarthTrackingState == TrackingState.Tracking;
    }
}

モデルを配置するボタン

カメラのポジションに3Dモデルを配置するためのボタンを作成します。

ボタンのOn Clickには、先程作成したのUpdatePositionメソッドを設定します。

ボタンを非アクティブ状態にします。(Geospatial APIが使用できる状態になったタイミングでGeospatial Exampleがアクティブ状態に設定します。)

モデルの用意

今回はUnity Asset Store にある以下のモデルを使用しました。

今回は地球が周るアニメーションを実施するため、親のGame Objectを作成し、地球のモデルは子オブジェクトとします。

地球のモデルのスケールを0.02に変更します。周るアニメーションのスクリプト、Spin Freeを地球モデルに追加します。

地球のモデルにAudio Sourceを設定していますが、必須ではないため説明は割愛します。このデモでは空間オーディオの設定を行っています。(リスナーはAR Cameraに設定。)空間オーディオに設定方法について、以下の記事が参考になります。

地球の親モデルを非アクティブ状態にします。

Geospatial Exampleのフィールド設定

AR Anchor Manager と AR Earth Managerは、AR Session Originのを使用します。最後に作成したボタンと地球の親オブジェクトも設定します。


実行

起動時にGeospatial APIの初期化処理が行われ、Geospatial APIが使用可能な状態になったタイミングでボタンはアクティブ状態に変わります。
ボタンをタップすると地球がカメラ位置に配置されます。(配置したタイミングで緯度、経度、高度が保存されます。)
アプリを再度、起動すると保存した緯度、経度、高度を取得し、以前に配置した地球が同じ場所に再配置されます。


OnePlanet Tech Magazine

Magic Leap 、スマホAR(Niantic Lightship ARDKやWebAR)といったAR技術全般をブログマガジンを連載しています。

OnePlanet XR

https://1planet.co.jp/xrconsulting.html

AR/MR/VPS技術に専門特化したコンサルティングサービス

お問い合わせ先

https://1planet.co.jp/xrconsulting.html#op_form