Closed12

PLATEAUとGeospatial APIを連携させる

Ekito|駅人Ekito|駅人

はじめに

建物の3Dモデルと実際の建物が重なって表示されるARアプリを作成する。
UnityにPLATEAUの3D都市モデル(今回は新宿エリア)をインポートし、そのモデルが実際の場所に表示されるように位置合わせをしていく(AndroidでもiOSでビルドできるように設定していく)。

Ekito|駅人Ekito|駅人

UnityプロジェクトにARCore Extensionsをインストール

  1. ARテンプレートを使って新規プロジェクトを作成
  2. Package ManagerからGoogle ARCore Extensions for AR Foundationをインストール
  3. ARCore ExtensionsのGeospatial Sampleをインポート
  4. Project Settings -> XR Plug-in Managementで、ARCoreとARKitを有効にする
  5. XR Plug-in Management -> ARCore Extensions -> Authentication StrategyをAPI Keyにし、先ほど取得したAPIキーを貼り付ける
  6. iOS Support EnabledとGeospatialにチェックを入れる
  7. Player Settings -> Other Settings -> Location Usage Descriptionにメッセージ(例: "Required for AR.")を入れる
  8. Assets/Samples/ARCore Extensions/1.35.0/Geospatial Sample/Configurations/GeospatialConfigのGeospatial ModeをEnabledにする

参考:
https://zenn.dev/tkada/articles/04b44474149130#unityの準備

Ekito|駅人Ekito|駅人

Unityシーンの作成

  1. シーンにAR Session Origin・AR Sessionを追加
  2. AR Session OriginにAR Anchor Manager・AR Earth Managerをアタッチ
  3. シーンにAR Core Extensionsを追加し、Inspectorで以下のように登録
    • Session -> AR Session
    • Session Origin -> AR Session Origin
    • Camera Manager -> AR Camera
    • AR Core Extensions Configuration -> GeospatialConfig
Ekito|駅人Ekito|駅人

PLATEAUの3D都市モデルをUnityにインポート

  1. PLATEAUオープンデータポータルサイトから東京23区の3D都市モデル(FBXファイル)をダウンロード
  2. 使いたい区域のFBXファイルを選んで、Unityにインポート(今回自分は新宿駅周辺のファイルを使いたいので以下のファイルをインポート)
    • 53394525_bldg_6677.fbx
    • 53394526_bldg_6677.fbx
    • 53394535_bldg_6677.fbx
  3. ProjectでFBXファイルを選択し、InspectorでConvert Unitsのチェックを外す→モデルのサイズが実寸大になる
Ekito|駅人Ekito|駅人

3D都市モデルの原点を設定

そのままだとモデルの原点がモデル自体から遠く離れた位置にある(下図において、原点は右上にあるMove Toolの位置)。この位置は平面直角9系の原点の位置と一致する。

そこで、モデルの近くに新しい原点を設定する。今回自分は、東京都庁第一本庁舎を新しい原点に設定する。

  1. 新しい原点にしたい場所の緯度経度をGoogleマップで取得
    • 東京都庁第一本庁舎の緯度: 35.689614031723785
    • 東京都庁第一本庁舎の経度: 139.69351102588638
  2. 以下のサイトで、新しい原点の緯度経度を平面直角座標系での値に変換
    (入力単位を十進法度単位に設定すること)
    https://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/bl2xyf.html
    • 東京都庁第一本庁舎のX座標: -34426.7729 m
    • 東京都庁第一本庁舎のY座標: -12654.8489 m
  3. 以下のサイトで、新しい原点のジオイド高を計算
    (入力単位を十進法度単位に設定すること)
    https://vldb.gsi.go.jp/sokuchi/surveycalc/geoid/calcgh/calcframe.html
    • 東京都庁第一本庁舎のジオイド高: 36.9982 m
  4. Unityシーン上で、新しい原点となる空のゲームオブジェクト(以下Originとする)を作成し、そのPositionを(x, y, z) = (-平面直角座標系でのY座標, 0, -平面直角座標系でのX座標)に設定する
    • OriginのPosition(自分の場合): (x, y, z) = (12654.8489, 0, 34426.7729)
  5. モデルすべてをOriginの子に設定する
  6. OriginのPositionを(x, y, z) = (0, 0, 0)にする
  7. Originをプレファブ化
Ekito|駅人Ekito|駅人

Geospatial APIを使って3D都市モデルを表示

以下の処理を行うスクリプトを作成し、Unityシーンのゲームオブジェクトにアタッチする

  1. トラッキング結果を取得し、精度を確認する
  2. 精度が良い場合、先ほど設定した原点の位置にアンカーを設置する(このときアンカーの高さは、先ほど計算した原点のジオイド高からスマートフォンの高さとして例えば1.5m引いた値を設定する)
  3. アンカーの場所で、先ほどプレファブ化したOrigin(モデル含む)からオブジェクトを生成(インスタンス化)する
VpsController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using Google.XR.ARCoreExtensions;

public class VpsController : MonoBehaviour
{
    [SerializeField] private AREarthManager earthManager;       // InspectorでAR Session Originを登録
    [SerializeField] private ARAnchorManager aRAnchorManager;   // InspectorでAR Session Originを登録
    [SerializeField] private GameObject plateauPrefab;          // InspectorでOriginを登録
    private GameObject plateauObject;
    public double headingThreshold = 25;
    public double horizontalThreshold = 20;

    void Update()
    {
        // トラッキングが出来ていない場合は何もしない
        if (earthManager.EarthTrackingState != TrackingState.Tracking)
        {
            return;
        }
        // トラッキング結果を取得
        GeospatialPose pose = earthManager.CameraGeospatialPose;
        // トラッキング精度がthresholdより良い(値が小さい)場合
        if (pose.HeadingAccuracy < headingThreshold && pose.HorizontalAccuracy < horizontalThreshold)
        {
            // 初めてトラッキング精度が良くなったタイミングでオブジェクト生成
            if (plateauObject == null)
            {
                PlacePlateau();
            }
        }
    }

    public void PlacePlateau()
    {
        // 東京都庁第一本庁舎にアンカーを設置
        var anchor = aRAnchorManager.AddAnchor(
            35.689614031723785,     // アンカーの緯度
            139.69351102588638,     // アンカーの経度
            36.9982 - 1.5,          // アンカーのジオイド高 - スマートフォンの高さ(地面から1.5mと想定) 
            Quaternion.identity
        );
        // アンカーの場所でオブジェクト生成
        plateauObject = Instantiate(plateauPrefab, anchor.transform);
    }
}
Ekito|駅人Ekito|駅人

結果

無事、建物の3Dモデルと実際の建物が重なって表示されました!(下の写真では、トラッキング精度が良くなったときにメッセージを表示する処理も加えています)

Ekito|駅人Ekito|駅人

Record & Playback APIを使用

Record & Playback APIを使用して、一度現地でカメラに映る周囲の様子を動画撮影しておけば、再び現地に行かずともその動画を使ってARのテストができる(ただしAndroidのみ)。

参考:
https://zenn.dev/drumath2237/articles/c6c0241b9ba2ae
こちらの記事の方法だと、テストに使える動画が直前に撮影したもののみだったので、少しコードを変えてみた。
以前に撮影した動画を使いたい場合、Inspectorでその動画の撮影日時をyyyyMMddHHmmss形式で入力すれば、直前に撮影した動画ではなく入力した日時に撮影した動画を使えるようにした。

public string timeOfVideo = "";     // 以前撮影した動画でplaybackしたい場合、
                                    // その動画の撮影日時をyyyyMMddHHmmss形式でInspectorに入力

public void TogglePlayback()
{
    // 略
    if (timeOfVideo == "")    // 直前に撮影した動画でplaybackしたい場合
    {
        if (!File.Exists(path))
        {
            OnSendMessage?.Invoke($"Cannot find file path {path}.");
            return;
        }
        subsystem.StartPlayback(path);
        OnPlaybackStatusChanged?.Invoke(true);
        OnSendMessage?.Invoke("Start playback.");
    }
    else    // 以前撮影した動画でplaybackしたい場合
    {
        string path1 = Path.Combine(Application.persistentDataPath, $"arcore-session{timeOfVideo}.mp4");
        if (!File.Exists(path1))
        {
            OnSendMessage?.Invoke($"Cannot find file path {path1}.");
            return;
        }
        subsystem.StartPlayback(path1);
        OnPlaybackStatusChanged?.Invoke(true);
        OnSendMessage?.Invoke("Start playback.");            
    }
}
まっさんまっさん

こんにちは!
記事の内容はとても参考になりました!

質問なのですが、私は建物のオブジェクトの代わりに仮想地形を現実風景の地面の上にかぶせて表示させようとしていますが
上手く表示されません
地形アンカーのパラメータなどを見返しましたが特に不具合は見つけられませんでした

表示させるオブジェクトには描画できる最大のサイズなどは決まっているのでしょうか?
因みに使用したファイル形式は.objです。

それとも単純にうまくトラッキングができないだけなんでしょうか・・・

Ekito|駅人Ekito|駅人

コメントありがとうございます!
AR CameraのCameraコンポーネントのClipping Planesで描画範囲を指定することができます。
Farに入れた値より遠い距離にあるオブジェクトは表示されません。

ただ、近くにあるオブジェクトも表示されていない場合は、トラッキングが出来ていないかもしれません。トラッキング出来ているかどうか、トラッキング精度が良くなったタイミングがあったかどうかをデバッグしてみるのはいかがでしょうか?

VpsController.cs
    void Update()
    {
        // トラッキングが出来ていない場合は何もしない
        if (earthManager.EarthTrackingState != TrackingState.Tracking)
        {
            Debug.Log("Not tracked.");  // トラッキング出来ていないとき表示
            return;
        }
        // トラッキング結果を取得
        GeospatialPose pose = earthManager.CameraGeospatialPose;
        // トラッキング精度がthresholdより良い(値が小さい)場合
        if (pose.HeadingAccuracy < headingThreshold && pose.HorizontalAccuracy < horizontalThreshold)
        {
            // 初めてトラッキング精度が良くなったタイミングでオブジェクト生成
            if (plateauObject == null)
            {
                Debug.Log("High accuracy.");  // 初めてトラッキング精度が良くなったとき表示
                PlacePlateau();
            }
        }
    }
このスクラップは2022/12/16にクローズされました