지리정보 앵커를 사용하여 Android SDK (Kotlin/자바)에서 실제 콘텐츠 배치

지리적 앵커는 실제 세계에 3D 콘텐츠를 배치할 수 있는 앵커의 한 유형입니다.

지리정보 앵커 유형

지리적 앵커에는 세 가지 유형이 있으며 각각 고도를 다르게 처리합니다.

  1. WGS84 앵커:
    WGS84 앵커를 사용하면 지정된 위도, 경도, 고도에 3D 콘텐츠를 배치할 수 있습니다.

  2. 지형 앵커:
    지형 앵커를 사용하면 위도와 경도만 사용하여 해당 위치의 지형을 기준으로 콘텐츠를 배치할 수 있습니다. 고도는 VPS에서 알고 있는 지면 또는 바닥을 기준으로 결정됩니다.

  3. 루프톱 앵커:
    루프톱 앵커를 사용하면 해당 위치의 건물 옥상에 대한 높이와 함께 위도와 경도만 사용하여 콘텐츠를 배치할 수 있습니다. 고도는 거리 풍경 도형에 따라 건물 꼭대기를 기준으로 결정됩니다. 건물에 배치되지 않은 경우 기본적으로 지형 고도가 설정됩니다.

WGS84 지형 루프톱
수평 위치 위도, 경도 위도, 경도 위도, 경도
수직 위치 WGS84 고도 기준 Google 지도에서 결정된 지형 수준에 대한 상대 값 Google 지도에서 결정한 옥상 수준을 기준으로 합니다.
서버에서 해결해야 하나요? 아니요

기본 요건

계속하기 전에 Geospatial API를 사용 설정해야 합니다.

지리정보 앵커 배치

각 앵커 유형에는 이를 만드는 전용 API가 있습니다. 자세한 내용은 지리적 앵커 유형을 참고하세요.

히트 테스트에서 앵커 만들기

히트 테스트 결과에서 지리적 앵커를 만들 수도 있습니다. 히트 테스트의 포즈를 사용하고 GeospatialPose로 변환합니다. 이 앵커를 사용하여 설명된 3가지 앵커 유형 중 하나를 배치합니다.

AR 포즈에서 Geospatial Pose 가져오기

Earth.getGeospatialPose()는 AR 포즈를 지리적 포즈로 변환하여 위도와 경도를 결정하는 추가적인 방법을 제공합니다.

Geospatial Pose에서 AR Pose 가져오기

Earth.getPose()는 동쪽-남쪽 좌표계를 기준으로 지구에서 지정한 수평 위치, 고도, 사원수 회전을 GL 세계 좌표를 기준으로 AR 자세로 변환합니다.

사용 사례에 적합한 방법 선택

앵커를 만드는 각 메서드에는 고려해야 할 관련 장단점이 있습니다.

  • 거리 풍경 도형을 사용할 때는 히트 테스트를 사용하여 건물에 콘텐츠를 연결합니다.
  • WGS84 앵커보다 지형지물 또는 옥상 앵커를 사용하는 것이 좋습니다. 지형지물 또는 옥상 앵커는 Google 지도에서 결정된 고도 값을 사용하기 때문입니다.

위치의 위도와 경도 확인

위치의 위도와 경도를 계산하는 방법에는 세 가지가 있습니다.

  • Geospatial Creator를 사용하면 실제로 해당 위치를 방문하지 않고도 3D 콘텐츠로 세상을 보고 증강할 수 있습니다. 이렇게 하면 Unity 편집기에서 Google 지도를 사용하여 몰입형 3D 콘텐츠를 시각적으로 배치할 수 있습니다. 콘텐츠의 위도, 경도, 회전 및 고도가 자동으로 계산됩니다.
  • Google 지도 사용
  • Google 어스를 사용합니다. Google 지도 대신 Google 어스를 사용하여 이러한 좌표를 얻으면 최대 몇 미터의 오류가 발생할 수 있습니다.
  • 실제 위치로 이동

Google 지도 사용

Google 지도를 사용하여 위치의 위도와 경도를 가져오려면 다음 단계를 따르세요.

  1. 데스크톱 컴퓨터에서 Google 지도로 이동합니다.

  2. 레이어 > 더보기로 이동합니다.

  3. 지도 유형위성으로 변경하고 화면 왼쪽 하단의 지구 보기 체크박스를 선택 해제합니다.

    이렇게 하면 2D 관점을 강제로 부여하고 각도 3D 보기에서 발생할 수 있는 오류를 제거할 수 있습니다.

  4. 지도에서 위치를 마우스 오른쪽 버튼으로 클릭하고 경도/위도를 선택하여 클립보드에 복사합니다.

Google 어스 사용하기

UI에서 위치를 클릭하고 장소표시 세부정보에서 데이터를 읽으면 Google 어스에서 위치의 위도와 경도를 계산할 수 있습니다.

Google 어스를 사용하여 위치의 위도와 경도를 가져오는 방법은 다음과 같습니다.

  1. 데스크톱 컴퓨터에서 Google 어스로 이동합니다.

  2. 햄버거 메뉴 로 이동하여 지도 스타일을 선택합니다.

  3. 3D 빌딩 스위치를 끕니다.

  4. 3D 빌딩 스위치가 꺼지면 핀 아이콘 을 클릭하여 선택한 위치에 위치표시를 추가합니다.

  5. 위치표시가 포함될 프로젝트를 지정하고 저장을 클릭합니다.

  6. 위치표시의 제목 필드에 위치표시의 이름을 입력합니다.

  7. 프로젝트 창에서 뒤로 화살표 를 클릭하고 작업 더보기 메뉴를 선택합니다.

  8. 메뉴에서 KML 파일로 내보내기를 선택합니다.

KLM 파일은 다음과 같이 <coordinates> 태그에서 쉼표로 구분된 위치표시의 위도, 경도, 고도를 보고합니다.

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

위치가 아닌 카메라 위치를 지정하는 <LookAt> 태그의 위도와 경도는 사용하지 마세요.

실제 위치로 이동

위치로 직접 이동하여 현지 관측을 통해 위치의 고도를 계산할 수 있습니다.

회전 쿼터니언 가져오기

GeospatialPose.getEastUpSouthQuaternion()는 Geospatial Pose에서 방향을 추출하고 벡터를 타겟에서 동쪽-위쪽-남쪽 (EUS) 좌표계로 변환하는 회전 행렬을 나타내는 쿼터니언을 출력합니다. X+는 동쪽을, Y+는 위쪽을, Z+는 남쪽을 가리킵니다. 값은 {x, y, z, w} 순서로 작성됩니다.

WGS84 앵커

WGS84 앵커는 지정된 위도, 경도, 고도에 3D 콘텐츠를 배치할 수 있는 앵커 유형입니다. 실제 세상에 배치하기 위해 자세와 방향을 사용합니다. 위치는 WGS84 좌표계에 지정된 위도, 경도, 고도로 구성됩니다. 방향은 쿼터니언 회전으로 구성됩니다.

고도는 참조 WGS84 타원체 위의 미터(m)로 보고되므로 지상 고도는 0이 아닙니다. 앱은 생성된 각 앵커에 이러한 좌표를 제공해야 합니다.

실제 세계에 WGS84 앵커 배치

위치의 고도 결정

앵커를 배치하기 위해 위치의 고도를 확인하는 방법에는 다음과 같은 몇 가지가 있습니다.

  • 앵커 위치가 사용자와 실제로 가까운 경우 사용자 기기의 고도와 유사한 고도를 사용할 수 있습니다.
  • 위도와 경도가 있으면 Elevation API를 사용하여 EGM96 사양을 기반으로 고도를 가져옵니다. GeospatialPose 고도와 비교하려면 Maps API EGM96 고도를 WGS84로 변환해야 합니다. 명령줄과 HTML 인터페이스가 모두 있는 GeoidEval을 참고하세요. Maps API는 기본적으로 WGS84 사양에 따라 위도와 경도를 보고합니다.
  • Google 어스에서 위치의 위도, 경도, 고도를 가져올 수 있습니다. 이렇게 하면 최대 몇 미터의 오류 허용 범위가 생깁니다. KML 파일에서 <LookAt> 태그가 아닌 <coordinates> 태그의 위도, 경도, 고도를 사용합니다.
  • 기존 앵커가 가까이 있고 가파른 경사가 아닌 경우 지도 API와 같은 다른 소스를 사용하지 않고도 카메라의 GeospatialPose에서 고도를 사용할 수 있습니다.

앵커 만들기

위도, 경도, 고도, 회전 쿼터니언이 있으면 Earth.createAnchor()를 사용하여 지정한 지리적 좌표에 콘텐츠를 고정합니다.

자바

if (earth != null && earth.getTrackingState() == TrackingState.TRACKING) {
  Anchor anchor =
    earth.createAnchor(
      /* Location values */
      latitude,
      longitude,
      altitude,
      /* Rotational pose values */
      qx,
      qy,
      qz,
      qw);

  // Attach content to the anchor specified by geodetic location and pose.
}

Kotlin

if (earth.trackingState == TrackingState.TRACKING) {
  val anchor =
    earth.createAnchor(
      /* Location values */
      latitude,
      longitude,
      altitude,
      /* Rotational pose values */
      qx,
      qy,
      qz,
      qw
    )

  // Attach content to the anchor specified by geodetic location and pose.
}

지형 앵커

지형 앵커는 VPS의 정보를 활용하여 지상에서 정확한 고도를 찾고 위도와 경도만 사용하여 AR 객체를 배치할 수 있는 앵커 유형입니다.

원하는 고도를 입력하는 대신 지형 위에서의 고도를 제공합니다. 이 값이 0이면 앵커가 지형과 같은 높이가 됩니다.

비행기 찾기 모드 설정

평면 찾기는 선택사항이며 앵커를 활용하는 데 필요하지 않습니다. 수평 평면만 사용됩니다. 수평 평면은 지형 앵커를 지면에 동적으로 정렬하는 데 도움이 됩니다.

Config.PlaneFindingMode를 사용하여 앱에서 비행기를 감지하는 방법을 선택합니다.

새로운 Async API를 사용하여 지형 앵커 만들기

지형 앵커를 만들어 배치하려면 Earth.resolveAnchorOnTerrainAsync()를 호출합니다.

앵커 광고는 즉시 준비되지 않으며 해결해야 합니다. 문제가 해결되면 ResolveAnchorOnTerrainFuture에서 사용할 수 있습니다.

자바

final ResolveAnchorOnTerrainFuture future =
  earth.resolveAnchorOnTerrainAsync(
    latitude,
    longitude,
    /* altitudeAboveTerrain= */ 0.0f,
    qx,
    qy,
    qz,
    qw,
    (anchor, state) -> {
      if (state == TerrainAnchorState.SUCCESS) {
        // do something with the anchor here
      } else {
        // the anchor failed to resolve
      }
    });

Kotlin

var future =
  earth.resolveAnchorOnTerrainAsync(
    latitude,
    longitude,
    altitudeAboveTerrain,
    qx,
    qy,
    qz,
    qw,
    { anchor, state ->
      if (state == TerrainAnchorState.SUCCESS) {
        // do something with the anchor here
      } else {
        // the anchor failed to resolve
      }
    }
  )

미래 상태 확인

Future에는 연결된 FutureState가 있습니다.

설명
FutureState.PENDING 작업이 아직 대기 중입니다.
FutureState.DONE 작업이 완료되고 결과를 사용할 수 있습니다.
FutureState.CANCELLED 작업이 취소되었습니다.

Future 결과의 지형 앵커 상태 확인

Anchor.TerrainAnchorState는 비동기 작업에 속하며 최종 Future 결과의 일부입니다.

자바

switch (terrainAnchorState) {
  case SUCCESS:
    // A resolving task for this Terrain anchor has finished successfully.
    break;
  case ERROR_UNSUPPORTED_LOCATION:
    // The requested anchor is in a location that isn't supported by the Geospatial API.
    break;
  case ERROR_NOT_AUTHORIZED:
    // An error occurred while authorizing your app with the ARCore API. See
    // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.TerrainAnchorState#error_not_authorized
    // for troubleshooting steps.
    break;
  case ERROR_INTERNAL:
    // The Terrain anchor could not be resolved due to an internal error.
    break;
  default:
    // not reachable
    break;
}

Kotlin

when (state) {
  TerrainAnchorState.SUCCESS -> {
    // A resolving task for this Terrain anchor has finished successfully.
  }
  TerrainAnchorState.ERROR_UNSUPPORTED_LOCATION -> {
    // The requested anchor is in a location that isn't supported by the Geospatial API.
  }
  TerrainAnchorState.ERROR_NOT_AUTHORIZED -> {
    // An error occurred while authorizing your app with the ARCore API. See
    // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.TerrainAnchorState#error_not_authorized
    // for troubleshooting steps.
  }
  TerrainAnchorState.ERROR_INTERNAL -> {
    // The Terrain anchor could not be resolved due to an internal error.
  }
  else -> {
    // Default.
  }
}

루프탑 앵커

루프톱 앵커 히어로

옥상 앵커는 앵커의 한 유형으로 위의 지형 앵커와 매우 유사합니다. 차이점은 지형 위의 고도가 아닌 옥상 위의 고도를 제공한다는 것입니다.

새 Async API를 사용하여 Rooftop 앵커 만들기

앵커는 즉시 준비되지 않으며 해결해야 합니다.

루프톱 앵커를 만들고 배치하려면 Earth.resolveAnchorOnRooftopAsync()를 호출합니다. 지형 앵커와 마찬가지로 Future의 FutureState에도 액세스할 수 있습니다. 그러면 Future 결과를 확인하여 Anchor.RooftopAnchorState에 액세스할 수 있습니다.

자바

final ResolveAnchorOnRooftopFuture future =
  earth.resolveAnchorOnRooftopAsync(
    latitude,
    longitude,
    /* altitudeAboveRooftop= */ 0.0f,
    qx,
    qy,
    qz,
    qw,
    (anchor, state) -> {
      if (state == RooftopAnchorState.SUCCESS) {
        // do something with the anchor here
      } else {
        // the anchor failed to resolve
      }
    });

Kotlin

var future =
  earth.resolveAnchorOnRooftopAsync(
    latitude,
    longitude,
    altitudeAboveRooftop,
    qx,
    qy,
    qz,
    qw,
    { anchor, state ->
      if (state == RooftopAnchorState.SUCCESS) {
        // do something with the anchor here
      } else {
        // the anchor failed to resolve
      }
    }
  )

미래 상태 확인

Future에는 연결된 FutureState가 있습니다(위 참고).

Future 결과의 Rooftop 앵커 상태 확인

Anchor.RooftopAnchorState는 비동기 작업에 속하며 최종 Future 결과의 일부입니다.

자바

switch (rooftopAnchorState) {
  case SUCCESS:
    // A resolving task for this Rooftop anchor has finished successfully.
    break;
  case ERROR_UNSUPPORTED_LOCATION:
    // The requested anchor is in a location that isn't supported by the Geospatial API.
    break;
  case ERROR_NOT_AUTHORIZED:
    // An error occurred while authorizing your app with the ARCore API.
    // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.RooftopAnchorState#error_not_authorized
    // for troubleshooting steps.
    break;
  case ERROR_INTERNAL:
    // The Rooftop anchor could not be resolved due to an internal error.
    break;
  default:
    // not reachable
    break;
}

Kotlin

when (state) {
  RooftopAnchorState.SUCCESS -> {
    // A resolving task for this Rooftop anchor has finished successfully.
  }
  RooftopAnchorState.ERROR_UNSUPPORTED_LOCATION -> {
    // The requested anchor is in a location that isn't supported by the Geospatial API.
  }
  RooftopAnchorState.ERROR_NOT_AUTHORIZED -> {
    // An error occurred while authorizing your app with the ARCore API. See
    // https://developers.google.com/ar/reference/java/com/google/ar/core/Anchor.RooftopAnchorState#error_not_authorized
    // for troubleshooting steps.
  }
  RooftopAnchorState.ERROR_INTERNAL -> {
    // The Rooftop anchor could not be resolved due to an internal error.
  }
  else -> {
    // Default.
  }
}

다음 단계