地理空間アンカーを使用して Unity に現実世界のコンテンツを配置する

地理空間アンカーは、現実世界に 3D コンテンツを配置するためのアンカーの一種です。

地理空間アンカーの種類

地理空間アンカーには 3 つのタイプがあり、それぞれ高度の処理方法が異なります。

  1. WGS84 アンカー:
    WGS84 アンカーを使用すると、任意の緯度、経度、高度に 3D コンテンツを配置できます。

  2. 地形アンカー:
    地形アンカーを使用すると、緯度と経度のみを使用して、その位置の地形を基準とした高さでコンテンツを配置できます。高度は、VPS によって知られているように、地面または床からの相対高度として決定されます。

  3. 屋上アンカー:
    屋上アンカーを使用すると、緯度と経度のみを使用して、その位置にある建物の屋上からの相対的な高さを使用してコンテンツを配置できます。表示高度は、ストリートビュー ジオメトリで知られている建物の上面からの相対位置になります。建物に配置されていない場合は、デフォルトで地形高度に設定されます。

WGS84 地形 屋上
水平方向 緯度、経度 緯度、経度 緯度、経度
縦向きの位置 WGS84 の高度に相対 Google マップによって決定された地形のレベルとの比較 Google マップによって決定される屋上の階数に対する相対値
サーバーで解決する必要があるか? ×

前提条件

続行する前に、Geospatial API が有効になっていることを確認してください。

地理空間アンカーを配置する

各アンカータイプには、アンカーを作成するための専用の API があります。詳細については、地理空間アンカーのタイプをご覧ください。

ヒットテストからアンカーを作成する

ヒットテストの結果から地理空間アンカーを作成することもできます。ヒットテストのポーズを使用して、GeospatialPose に変換します。ここで説明する 3 種類のアンカーのいずれかの配置に使用します。

AR ポーズから地理空間ポーズを取得する

AREarthManager.Convert(Pose) を使用すると、AR ポーズを地理空間ポーズに変換して、緯度と経度を特定することもできます。

地理空間ポーズから AR ポーズを取得する

AREarthManager.Convert(GeospatialPose) は、地球上で指定された水平位置、高度、四元数の回転を、東と南の座標フレームに関する GL ワールド座標に関する AR ポーズに変換します。

ユースケースに適した方法を選択する

アンカーを作成する各方法には、注意すべきトレードオフがあります。

  • 街並みのジオメトリを使用する場合は、ヒットテストを使用して建物にコンテンツを添付します。
  • WGS84 アンカーよりも地形アンカーまたは屋上アンカーが推奨されます。これらのアンカーは Google マップで決定される標高値を使用するためです。

場所の緯度と経度を特定する

場所の緯度と経度は、次の 3 つの方法で計算できます。

  • Geospatial Creator を使用すると、実際にその場所に行くことなく、3D コンテンツで世界を表示して拡張できます。これにより、Unity エディタの Google マップを使用して、臨場感あふれる 3D コンテンツを視覚的に配置できるようになります。コンテンツの緯度、経度、回転、高度は自動的に計算されます。
  • Googleマップの使用
  • Google Earth を利用する。なお、Google マップではなく Google Earth を使用してこれらの座標を取得すると、最大数メートルの誤差が生じます。
  • 物理的な場所に移動する

Googleマップの使用

Google マップを使用して場所の緯度と経度を取得するには:

  1. パソコンで Google マップにアクセスします。

  2. [レイヤ] > [その他] に移動します。

  3. [地図タイプ] を [航空写真] に変更し、画面左下の [地球表示] チェックボックスをオフにします。

    これにより、2D 視点が強制的に適用され、角度をつけた 3D ビューで起こり得るエラーがなくなります。

  4. 地図上で場所を右クリックし、経度または緯度を選択してクリップボードにコピーします。

Google Earth を使用する

UI で場所をクリックし、目印の詳細からデータを読み取ることで、Google Earth から場所の緯度と経度を計算できます。

Google Earth を使って場所の緯度と経度を取得するには:

  1. パソコンで Google Earth にアクセスします。

  2. ハンバーガー メニュー に移動し、[地図のスタイル] を選択します。

  3. [建物の 3D 表示] スイッチをオフにします。

  4. [建物の 3D 表示] スイッチをオフに切り替えたら、ピンアイコン をクリックして、選択した場所に目印を追加します。

  5. 目印を含めるプロジェクトを指定し、[保存] をクリックします。

  6. 目印の [タイトル] フィールドに、目印の名前を入力します。

  7. プロジェクト ペインの戻る矢印 をクリックし、 [その他の操作] メニューを選択します。

  8. メニューから [KML ファイルとしてエクスポート] を選択します。

KLM ファイルは、以下のようにカンマで区切られた <coordinates> タグで目印の緯度、経度、高度を報告します。

<coordinates>-122.0755182435043,37.41347299422944,7.420342565583832</coordinates>

<LookAt> タグの緯度と経度は、場所ではなくカメラの位置を指定するもので使用しないでください。

物理的な場所に移動する

特定の場所の高度は、実際にその場所に行って現地を観察することで算出できます。

回転四元数を取得する

GeospatialPose.EunRotation は、地理空間ポーズから向きを抽出し、ターゲットから東北(EUN)座標系にベクトルを変換する回転行列を表す四元数を出力します。X+ は東、Y+ は重力から上方、Z+ は北を指しています。

WGS84 アンカー

WGS84 アンカーは、任意の緯度、経度、高度に 3D コンテンツを配置できるアンカーの一種です。現実世界に配置するには、ポーズと向きに依存します。位置は緯度、経度、高度で構成され、WGS84 座標系で指定されます。向きは四元数回転で構成されます。

標高は、基準となる WGS84 楕円体からの高度(メートル単位)で報告されます。そのため、地面の高度はゼロではありません。アプリは、作成されたアンカーごとにこれらの座標を提供する役割を担います。

現実世界に WGS84 アンカーを配置する

場所の高度を特定する

アンカーを配置する場所の表示高度を決定する方法はいくつかあります。

  • アンカーの位置が物理的にユーザーの近くにある場合は、ユーザーのデバイスと同じ高度を使用できます。
  • 緯度と経度を取得したら、Elevation API を使用して、EGM96 の仕様に基づいて高度を取得します。GeospatialPose 高度と比較するには、Maps API EGM96 高度を WGS84 に変換する必要があります。コマンドラインと HTML インターフェースの両方がある GeoidEval をご覧ください。Maps API は、最初から WGS84 仕様に従って緯度と経度をレポートします。
  • 場所の緯度、経度、高度は Google Earth から取得できます。これにより、最大数メートルの誤差範囲が得られます。KML ファイルでは、<LookAt> タグではなく<coordinates> タグに含まれる緯度、経度、高度を使用します。
  • 既存のアンカーが近くにあり、かつ急な傾斜にない場合は、Maps API などの別のソースを使用せずに、カメラの GeospatialPose の高度を使用できることがあります。

アンカーを作成する

緯度、経度、高度、回転の四元数を取得したら、ARAnchorManagerExtensions.AddAnchor() を使用して、指定した地理座標にコンテンツを固定します。

if (earthTrackingState == TrackingState.Tracking)
{
  var anchor =
      AnchorManager.AddAnchor(
          latitude,
          longitude,
          altitude,
          quaternion);
  var anchoredAsset = Instantiate(GeospatialAssetPrefab, anchor.transform);
}

地形用アンカー

地形アンカーは、アンカーの一種で、緯度と経度のみを使用して AR オブジェクトを配置し、VPS からの情報を活用して、地面からの正確な標高を見つけます。

必要な標高を入力するのではなく、地形より高い高度を指定します。ゼロの場合、アンカーは地形と同じ高さになります。

飛行機探索モードを設定する

プレーンの検出はオプションであり、アンカーを利用するのに必須ではありません。水平プレーンのみが使用されることに注意してください。水平面は、地面に地形アンカーを動的に配置する場合に便利です。

地形アンカーは HorizontalHorizontal | Vertical の影響を受けます。

[Detection Mode] プルダウン メニューを使用して検出モードを設定します。

新しい Async API を使用して地形アンカーを作成する

地形アンカーを作成して配置するには、ARAnchorManagerExtensions.resolveAnchorOnTerrainAsync() を呼び出します。

アンカーはすぐに使用できる状態ではなく、解決する必要があります。問題が解決すると、ResolveAnchorOnTerrainPromise に表示されるようになります。

public GameObject TerrainAnchorPrefab;

public void Update()
{
    ResolveAnchorOnTerrainPromise terrainPromise =
        AnchorManager.ResolveAnchorOnTerrainAsync(
            latitude, longitude, altitudeAboveTerrain, eunRotation);

    // The anchor will need to be resolved.
    StartCoroutine(CheckTerrainPromise(terrainPromise));
}

private IEnumerator CheckTerrainPromise(ResolveAnchorOnTerrainPromise promise)
{
    yield return promise;

    var result = promise.Result;
    if (result.TerrainAnchorState == TerrainAnchorState.Success &&
        result.Anchor != null)
    {
        // resolving anchor succeeded
        GameObject anchorGO = Instantiate(TerrainAnchorPrefab,
            result.Anchor.gameObject.transform);
        anchorGO.transform.parent = result.Anchor.gameObject.transform;
    }
    else
    {
       // resolving anchor failed
    }

    yield break;
}

Promise の状態の確認

Promise には PromiseState が関連付けられます。

状態 説明
Pending オペレーションはまだ保留中です。
Done オペレーションが完了し、結果が利用可能になります。
Cancelled 操作はキャンセルされました。

Promise の結果の Terrain アンカー状態を確認する

TerrainAnchorState は非同期オペレーションに属し、最終的な Promise の結果の一部です。

switch (result.TerrainAnchorState)
{
    case TerrainAnchorState.Success:
        // Anchor has successfully resolved
        break;
    case TerrainAnchorState.ErrorUnsupportedLocation:
        // The requested anchor is in a location that isn't supported by the Geospatial API.
        break;
    case TerrainAnchorState.ErrorNotAuthorized:
        // An error occurred while authorizing your app with the ARCore API. See
        // https://developers.google.com/ar/reference/unity-arf/namespace/Google/XR/ARCoreExtensions#terrainanchorstate_errornotauthorized
        // for troubleshooting steps.
        break;
    case TerrainAnchorState.ErrorInternal:
        // The Terrain anchor could not be resolved due to an internal error.
        break;
    default:
        break;
}

屋根アンカー

屋上アンカーのヒーロー

屋上アンカーはアンカーの一種で、上記の地形アンカーとよく似ています。違いは、地形の上方ではなく、屋根の上方の高度を指定することです。

新しい Async API を使用して屋上アンカーを作成する

アンカーはすぐに使用できる状態ではなく、解決する必要があります。

屋上アンカーを作成して配置するには、ARAnchorManagerExtensions.resolveAnchorOnRooftopAsync() を呼び出します。地形アンカーと同様に、Promise の PromiseState にもアクセスします。これで、Promise の結果を確認して RooftopAnchorState にアクセスできます。

public GameObject RooftopAnchorPrefab;

public void Update()
{
    ResolveAnchorOnRooftopPromise rooftopPromise =
        AnchorManager.ResolveAnchorOnRooftopAsync(
            latitude, longitude, altitudeAboveRooftop, eunRotation);

    // The anchor will need to be resolved.
    StartCoroutine(CheckRooftopPromise(rooftopPromise));
}

private IEnumerator CheckRooftopPromise(ResolveAnchorOnTerrainPromise promise)
{
    yield return promise;

    var result = promise.Result;
    if (result.RooftopAnchorState == RooftopAnchorState.Success &&
        result.Anchor != null)
    {
        // resolving anchor succeeded
        GameObject anchorGO = Instantiate(RooftopAnchorPrefab,
            result.Anchor.gameObject.transform);
        anchorGO.transform.parent = result.Anchor.gameObject.transform;
    }
    else
    {
       // resolving anchor failed
    }

    yield break;
}

Promise の状態の確認

Promise には PromiseState が関連付けられます。上記のをご覧ください。

Promise の結果の屋上アンカーの状態を確認する

RooftopAnchorState は非同期オペレーションに属し、最終的な Promise の結果の一部です。

switch (result.RooftopAnchorState)
{
    case TerrainAnchorState.Success:
        // Anchor has successfully resolved
        break;
    case RooftopAnchorState.ErrorUnsupportedLocation:
        // The requested anchor is in a location that isn't supported by the Geospatial API.
        break;
    case RooftopAnchorState.ErrorNotAuthorized:
        // An error occurred while authorizing your app with the ARCore API. See
        // https://developers.google.com/ar/reference/unity-arf/namespace/Google/XR/ARCoreExtensions#terrainanchorstate_errornotauthorized
        // for troubleshooting steps.
        break;
    case RooftopAnchorState.ErrorInternal:
        // The Rooftop anchor could not be resolved due to an internal error.
        break;
    default:
        break;
}

次のステップ