Android XR

Unity를 사용하여 Android XR에서 공간 콘텐츠 설계하기

스피커

  • 레바나 첸

    소프트웨어 엔지니어

  • 제니퍼 스탠턴

    선임 소프트웨어 엔지니어

정보

강연 소개

Unity와 Android XR을 사용하여 실제 세계에 3D 환경을 고정하고 유지하는 방법을 알아보세요. 이 세션에서는 Spatial Entities 프레임워크, Cloud Anchors, Geospatial API를 다루며, 실제 배치를 기억하고 세션 간에 멀티 사용자 공간 데이터를 동기화하는 컨텍스트 인식 앱을 빌드하는 방법을 보여줍니다.

공간 엔티티의 아키텍처를 살펴보고 디지털 애셋을 실제 객체, 표면, 지리적 위치에 고정하는 방법을 알아봅니다. 공간 매핑, 세션 간 콘텐츠 유지, 공유 및 위치 인식 AR/XR 환경 제작을 위한 실용적인 Unity 워크플로를 배울 수 있습니다.

요약

  • AR Foundation은 OpenXR 확장 프로그램을 사용하여 XR 플랫폼 전반에 통합 스크립팅 API를 제공합니다. 이를 통해 평면 및 메시와 같은 실제 지형지물을 표현하고 쿼리할 수 있습니다.
  • 영구 앵커는 세션 및 기기 다시 시작 전반에서 디지털 콘텐츠를 안정화합니다. TrySaveAnchorAsync를 사용하여 유지하고 직렬화된 GUID를 사용하여 다시 로드할 수 있습니다.
  • Spatial Entities 프레임워크는 추적 가능한 확장 프로그램을 대체하여 경계 상자 및 마커와 같은 항목을 추적하기 위한 크로스 플랫폼 지원과 더 나은 성능을 제공합니다.
  • Android XR 권한이 부여될 때까지 관리자 구성요소를 사용 중지해야 합니다. 이렇게 하면 민감한 환경 데이터에 대한 제한된 액세스로 인해 발생하는 하위 시스템 초기화 실패를 방지할 수 있습니다.
  • Geospatial API는 Google Cloud ARCore 서비스와 VPS를 사용하여 실제 야외 위치에 콘텐츠를 배치합니다.

리소스

Gemini Notebook

주제

  • android-xr
  • Unity
  • ar-foundation
  • openxr
  • spatial-entities
  • 앵커 광고
  • geospatial-api

스크립트

소개 및 AR Foundation 개요

  • 좋습니다.

  • 환영합니다, 여러분.

  • Unity를 사용한 Android XR의 공간 콘텐츠 설계 과정에 오신 것을 환영합니다.

  • 제 이름은 제니퍼 스탠튼입니다.

  • Unity에서 선임 소프트웨어 엔지니어로 일하고 있습니다.

  • 저는 XR 기능팀에서 AR Foundation과 Unity에 증강 현실 및 혼합 현실을 구현하는 관련 패키지를 담당하고 있습니다.

  • 저는 Google XR 게임 엔진팀의 레바나 첸입니다.

  • Google팀은 향상된 XR 기능과 원활한 게임 엔진 통합을 제공하는 플러그인과 패키지를 제공하는 데 주력했습니다.

  • 개발자 여러분이 가장 많이 언급하는 어려움 중 하나는 동일한 코드베이스로 여러 XR 플랫폼을 지원하는 데 많은 노력이 필요하다는 점입니다.

  • AR Foundation은 Unity의 고수준 확장 현실 API 레이어입니다.

  • 주요 코드 재작성 없이 여러 XR 플랫폼을 타겟팅할 수 있고, 여러 코드베이스를 관리할 필요가 없으며, 모든 플랫폼에서 AR 기능을 위한 하나의 스크립팅 API를 제공합니다.

  • 이를 통해 개발자는 앱, 게임 또는 경험을 만드는 데 집중할 수 있으며 Unity가 플랫폼 세부정보를 처리합니다.

  • AR Foundation은 OpenXR 확장 프로그램을 통해 이를 달성합니다.

  • 이는 Khronos Group에서 정의한 개방형 업계 표준 API 집합입니다.

  • OpenXR은 공간 엔티티도 만들었습니다.

  • 디지털 콘텐츠가 실제 세계를 표현하고 상호작용하기 위한 프레임워크입니다.

  • 공간 엔티티는 실제 요소를 디지털 객체로 표현하는 통합 모델을 제공합니다.

  • XR 경험을 만들기 위한 여정을 시작할 수 있도록 AR Foundation에 관한 최신 정보를 공유해 드리겠습니다.

  • 다음으로, 디지털 콘텐츠가 실제 세계와 상호작용할 수 있도록 AR Foundation의 기능을 살펴보겠습니다.

  • 그런 다음 패키지 및 기타 종속 항목을 포함한 프로젝트 설정을 살펴보고 프로젝트에 구현할 수 있도록 하겠습니다.

  • 그런 다음 Android XR만의 고유한 기능을 살펴보며 마무리하겠습니다.

주요 AR Foundation 기능

  • AR Foundation이 실제 세계를 디지털로 표현하는 방법 중 하나는 평면입니다.

  • 이러한 평면은 바닥, 벽, 천장, 테이블, 소파와 같은 실제 환경의 평평한 표면입니다.

  • 메시는 실제 세계의 기하학을 나타내는 3D 구조입니다.

  • 그리고 실제 세계와 상호작용하는 방법인 레이 캐스트도 있습니다.

  • 이러한 요소는 광선이 디지털 콘텐츠와 교차하는 위치를 결정합니다.

  • 레이저 포인터라고 생각하면 됩니다. 레이저 포인터가 비추는 위치에 대한 정보를 얻을 수 있습니다.

  • 또한 공간에서 한 지점을 안정화하고 재배치하는 데 도움이 되는 앵커도 있습니다.

  • 이 프레젠테이션의 뒷부분에서 앵커에 대해 자세히 살펴보겠습니다.

  • 따라서 이 네 가지 모두 AR Foundation 기능으로 간주됩니다.

  • 이 슬라이드에서 언급되지 않은 몇 가지 기능도 있습니다.

  • 특히 평면과 앵커는 ARTrackable 클래스를 확장하고 관리자가 런타임에서 업데이트를 추적하므로 추적 가능한 것으로 간주됩니다.

  • 그럼 평면으로 가보겠습니다.

  • 평면의 경우 평면을 실시간으로 식별하는 ARPlane 구성요소가 있습니다.

  • 쿼리할 수 있는 시맨틱 라벨이 있으므로 평면이 바닥, 테이블, 벽 등인지 알 수 있습니다.

  • 평면에는 동적 경계도 있으므로 사용자가 실제 세계를 이동하면서 더 많은 표면을 볼 수 있게 되면 평면 경계가 실시간으로 동적으로 확장, 병합, 미세 조정됩니다.

  • 평면에 메시 콜라이더를 연결하여 물리를 처리할 수도 있습니다.

  • 여기에서 평면 감지의 예를 볼 수 있습니다.

  • Android XR 기기에서 실행되는 AR Foundation 샘플 레이 캐스트 장면입니다.

  • 실시간 동적 2D 다각형을 감지합니다.

  • 프로젝트 설정 가이드를 따르거나 샘플 또는 템플릿 중 하나를 사용하여 이 기능이 있는 앱을 빌드할 수 있습니다.

  • 다음은 메시입니다.

  • 메시는 환경의 3D 메시를 제공하는 AR 기능입니다.

  • 생성된 메시에 메시 콜라이더를 연결하면 디지털 콘텐츠가 실제 객체와 상호작용할 수 있습니다.

  • 메시를 사용하면 객체 오클루전을 효과적으로 구현할 수 있습니다.

  • 따라서 가상 객체가 실제 객체 뒤에 있는 것처럼 보일 수 있습니다.

  • 디지털 객체가 공간에서 이동할 수 있는 경우 동일한 도형을 사용하여 실제 객체를 피하는 경로를 찾을 수 있습니다.

  • 다음으로 레이 캐스팅이 있습니다.

  • 광선 투사는 복잡한 수학을 걱정할 필요 없이 3D 장면에 가상 광선을 투사하여 교차점을 찾습니다.

  • 투사된 광선은 환경 내의 여러 3D 객체와 교차할 수 있습니다.

  • 특정 유형의 디지털 콘텐츠만 적중하도록 광선 투사를 구성할 수 있습니다.

  • 예를 들어 평면과 충돌하지 않고 메시와만 교차하도록 구성할 수 있습니다.

패키지 및 종속 항목 설정

  • 이제 관련 패키지를 살펴보겠습니다.

  • 이러한 패키지는 모두 함께 작동하여 서로 통신하고 완전한 XR 환경을 만듭니다.

  • 앞서 언급했듯이 AR Foundation은 평면 및 앵커와 같은 기능을 처리합니다.

  • OpenXR 플러그인은 다양한 XR 플랫폼에서 네이티브 API를 처리하므로 기기에서 OpenXR을 사용하는 경우 이 패키지를 사용하는 것이 좋습니다.

  • OpenXR 플러그인은 선택적으로 OpenXR Spatial Entities 패키지와 페어링하여 Spatial Entities 확장 프로그램에 구체적으로 액세스할 수 있습니다. 이에 대해서는 나중에 자세히 설명하겠습니다.

  • XR 헤드셋, 유선 XR 글라스와 같은 Android XR 기기를 구체적으로 타겟팅하는 경우 Unity OpenXR Android XR 패키지가 필요합니다.

  • 이 모든 작업은 플러그인 패턴을 따르므로 코드에 미치는 영향은 최소화됩니다.

  • 일부 기능은 일부 AR Foundation 패키지에서 사용할 수 없습니다.

  • 예를 들어 이 차트를 보면 Unity OpenXR Spatial Entities 패키지에서는 현재 메시를 사용할 수 없습니다.

  • XR Plug-in Management를 사용하면 프로젝트에서 선택한 OpenXR 기능을 관리할 수 있습니다.

  • Unity 편집기에서 Edit(수정), Project Settings(프로젝트 설정)로 이동하면 왼쪽 사이드바에 XR Plug-in Management(XR 플러그인 관리)가 표시됩니다.

  • 여기에서 OpenXR 기능 및 기능 그룹을 찾을 수 있으므로 빌드에 포함할 기능 및 기능 그룹을 선택하거나 선택 해제할 수 있습니다.

  • 예를 들어 이 스크린샷에서는 Android XR OpenXR 패키지와 함께 제공되는 모든 항목을 확인할 수 있습니다.

  • 그런 다음 원하는 경우 공간 항목과 조합하여 사용할 수 있습니다.

  • 여기에 패키지 관리자 창이 있습니다.

  • 일반적인 XR 프로젝트 설정에서 볼 수 있는 몇 가지 패키지를 강조 표시했습니다.

  • 보시다시피 앞서 언급한 AR Foundation이 있습니다.

  • OpenXR 플러그인도 있습니다.

  • Android XR 패키지가 있습니다.

  • 여러 플랫폼으로 컴파일되는 프로젝트를 만들 수 있으므로 다른 플랫폼의 몇 가지 다른 프로젝트도 강조 표시했습니다.

  • 오른쪽 하단에는 다른 패키지도 있습니다.

  • OpenXR 기기와 호환되는 Composition Layers, XR 관련 작업에 매우 유용한 Core Utils, 그리고 입력 처리에 필수적인 XR Hands와 XR Interaction Toolkit이 있습니다.

  • 다음은 프로젝트 검증입니다.

  • 이 역시 XR 플러그인 관리의 일부입니다.

  • 프로젝트 검증을 통해 중복된 기능은 물론 프로젝트를 실행하기 위해 수정해야 하는 다른 문제를 감지할 수 있습니다.

  • 하지만 이 스크린샷에서는 중복 특성을 강조 표시했습니다.

  • 기본적으로 앵커, 평면, 레이캐스트와 같은 두 가지 동일한 기능이 있습니다.

  • 사용자가 여러 패키지에서 이러한 기능의 중복을 포함하도록 선택했으며, 사용자에게 둘 중 하나를 선택하라는 메시지가 표시됩니다.

로컬 앵커 및 지속성

  • 이제 로컬 앵커와 지속성에 대해 알아보겠습니다.

  • 앵커는 AR 세션의 추적 상태 변경과 관계없이 디지털 콘텐츠가 환경의 원하는 위치에 유지되도록 합니다.

  • AR 플랫폼은 시간이 지남에 따라 앵커의 포즈를 자동으로 업데이트하여 일관된 위치에 유지되도록 합니다. 앵커는 앵커에서 멀어졌다가 다시 돌아올 때 콘텐츠가 제자리에 유지되도록 하는 가장 좋은 방법입니다.

  • 사용자가 앱을 닫았다가 나중에 다시 시작하여 별도의 세션에서 다시 돌아오는 경우는 어떨까요?

  • 영구 앵커를 사용하면 AR 세션에 앵커를 저장한 후 후속 세션에서 로드할 수 있습니다.

  • 앱 수명 주기 및 기기 다시 시작 후에도 유지됩니다.

  • 여기에는 AR Foundation 샘플 프로젝트의 영구 앵커 샘플이 나와 있습니다.

  • 샘플 장면에는 앵커 저장 및 로드와 같이 해당 플랫폼에서 지원되는 모든 기능이 포함된 메뉴가 있습니다.

  • 이제 영구 앵커의 수명 주기에 대해 알아보겠습니다.

  • 먼저 로컬에서 앵커를 만듭니다. 아직 어디에도 저장되지 않고 영구 스토리지에도 저장되지 않은 로컬 앵커입니다.

  • 그런 다음 스토리지에 유지합니다.

  • 그런 다음 앱을 다시 시작하고 저장소에서 로드할 수 있습니다.

  • Android XR을 비롯한 일부 플랫폼에서는 앵커 삭제 또는 지속 해제도 지원합니다.

  • 공간 엔티티 제공업체는 저장된 모든 앵커를 자동으로 로드하는 기능을 지원하므로 ID별로 각 앵커를 수동으로 요청할 필요가 없습니다.

  • XR 플러그인 관리 설정에서 Spatial Entities: Anchors 기능의 Automatically Load Saved Anchors 옵션을 선택할 수 있습니다.

  • 앵커를 저장하는 데는 몇 줄의 코드만 있으면 됩니다.

  • 먼저 Unity 편집기에서 시작하여 XR Origin 게임 객체에 AR Anchor Manager 구성요소를 추가하여 장면이 준비되었는지 확인합니다.

  • 관리자는 런타임에서 추적된 앵커로 장면을 업데이트합니다.

  • 이제 코드를 살펴보겠습니다.

  • 앵커를 추가하려면 TryAddAnchorAsync를 호출하고 앵커를 배치할 위치의 포즈를 전달하면 됩니다.

    async void CreateAnchorAsync(ARAnchorManager manager, Pose pose) {
        var result = await manager.TryAddAnchorAsync(pose);
        if (result.status.IsError()) {
            // handle error
            return;
        }
        var anchor = result.value;
    }
  • 다음으로 앵커 삭제입니다.

    void RemoveAnchor(ARAnchorManager manager, ARAnchor anchor) {
        var result = manager.TryRemoveAnchor(anchor.trackableId);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    }
    
    void DestroyAnchorComponent(ARAnchor anchor) {
        Destroy(anchor);
    }
  • 마찬가지로 코드 한 줄만 있으면 됩니다.

  • TryRemoveAnchor를 호출하고 앵커의 추적 가능한 ID를 전달합니다.

  • 앵커가 생성될 때 이미 추적 가능한 ID가 부여되었습니다.

  • 그리고 TrySaveAnchorAsync가 있습니다.

    async void SaveAnchorAsync(ARAnchorManager manager, ARAnchor anchor) {
        var result = await manager.TrySaveAnchorAsync(anchor);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    
        // Save this value, then return it as an input parameter
        SerializableGuid guid = result.value;
    }
  • AR 앵커를 입력으로 전달합니다.

  • 이 함수는 영구 스토리지에서 앵커를 식별하는 SerializableGuid를 반환합니다.

  • 나중에 앵커를 로드할 때 GUID를 사용할 수 있습니다.

  • 이제 앵커 로드에 대해 알아보겠습니다.

    async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid) {
        var result = await manager.TryLoadAnchorAsync(guid);
        if (result.status.IsError()) {
            // handle error
            return;
        }
    
        ARAnchor anchor = result.value;
    }
  • 여기서 호출할 함수는 TryLoadAnchorAsync이며, 이전에 직렬화된 GUID를 전달합니다.

  • 결과로 AR 앵커를 반환합니다.

  • 이제 마치겠습니다.

  • 이러한 함수를 사용하면 로컬 앵커와 영구 앵커를 모두 쉽게 관리할 수 있습니다.

  • 따라서 앵커 추적은 대부분의 AR 플랫폼에서 상당히 비용이 많이 드는 작업입니다.

  • 앵커를 사용할 때는 다음 권장사항을 고려하세요. 여러 앵커를 서로 너무 가깝게 만들지 말고, 다른 앵커를 만드는 대신 근처에 있는 앵커를 사용할 수 있는지 고려하고, 더 이상 사용하지 않는 앵커는 삭제하세요. 마지막으로, 앵커의 원하는 포즈를 계산하는 데 도움이 되는 다른 AR 기능을 사용 설정하는 경우 앵커가 성공적으로 생성되면 해당 기능을 사용 중지할 수 있는지 고려하세요.

  • 이제 레바나에게 넘기겠습니다.

Unity용 Android XR SDK

  • 제니퍼님, 감사합니다.

  • 다음으로 Unity에서 Android XR 여정에 Google이 어떻게 기여하고 있는지 살펴보겠습니다.

  • 따라서 Google은 지금까지 제니퍼가 소개한 Unity OpenXR 플러그인 외에도 Android XR 플랫폼에서 사용할 수 있는 추가 기능을 갖춘 Unity용 Android XR SDK를 제공합니다.

  • 이 SDK에는 다양한 기능과 패키지가 포함되어 있으며, 이 세션에서는 Unity용 Android XR 확장 프로그램 패키지에 중점을 두겠습니다.

  • 따라서 OpenXR Android XR 및 OpenXR Spatial Entities와 마찬가지로 Unity용 Android XR 확장 프로그램 패키지는 실제로 OpenXR 제공업체입니다.

  • 이 패키지의 아키텍처는 다음과 같으며, 병렬로 또는 독립적으로 작동할 수 있습니다.

  • 따라서 다른 모든 OpenXR 제공업체와 마찬가지로 Google의 패키지는 표준 OpenXR 인터페이스를 기반으로 직접 빌드됩니다.

  • 여기에서는 다른 모든 제공업체와 비교하여 다양한 기능을 보여줍니다.

  • 따라서 언제, 왜 다른 옵션보다 특정 옵션을 선택해야 하는지 결정하는 데 도움이 될 수 있습니다.

  • 이 표에 표시된 추적 가능 항목과 공간 항목 마커에 관해 궁금한 점이 있을 수 있습니다.

  • 참고로, 이전에 Android XR에서 앱과 환경을 빌드한 적이 있는 경우, Google은 추적 가능한 확장 프로그램을 통해 이 기능을 제공했으며, 이 기능은 Android XR에서만 작동했습니다.

  • 그리고 나중에 제니퍼가 언급했듯이 OpenXR 오픈 그룹으로 작업한 후 오른쪽에 Spatial Entities Extension을 출시하고 일관된 크로스 플랫폼 환경을 제공합니다.

  • 현재는 Spatial Entities 프레임워크를 위해 추적 가능한 확장 프로그램을 지원 중단하고 있습니다.

  • 후자가 성능이 훨씬 더 좋을 뿐만 아니라 크로스 플랫폼 지원도 제공하기 때문입니다.

공간 엔티티로 빌드하기

  • 그런 다음 공간 항목을 자세히 살펴보겠습니다.

  • 제니퍼가 언급한 평면, 광선 투사, 앵커 외에도 마커와 같은 공간 엔터티 제공업체에서 지원하는 이 기능을 통해 표준 2D 바코드를 감지 및 디코딩하고 3D 공간에서 위치를 추적할 수 있으며, 이미지는 알려진 2D 그래픽을 인식하고 3D 공간에서 추적할 수 있습니다.

  • 그런 다음 키보드, 노트북, 마우스와 같은 실제 객체를 식별하고 3D 포즈에서 경계 상자를 추적하는 경계 상자가 있습니다.

  • 다음 예시에서는 Google의 확장 프로그램 패키지에서 제공하는 경계 상자 기능을 구성합니다.

  • 이 뷰는 모든 OpenXR 기능에서 거의 동일합니다.

  • 따라서 먼저 Android XR 플랫폼으로 전환한 다음 OpenXR 제공업체를 사용 설정하고 빌드하려는 OpenXR 기능을 선택하면 됩니다. 예를 들어 여기에는 공간 엔티티 경계 상자 기능이 표시되어 있습니다. 그런 다음 프로젝트 유효성 검사 문제를 해결합니다.

  • 이를 통해 특정 기능에 필요한 모든 종속 항목을 찾은 다음 여러 기능이 동일한 하위 시스템에 기여하여 발생하는 충돌을 해결할 수 있습니다.

  • 예를 들어 여기에서 앵커, 평면 또는 경계 상자를 사용하려면 하나의 기능만 선택하면 됩니다.

  • 마지막으로 샘플 장면 또는 테스트 장면을 빌드하면 됩니다.

  • 런타임에 추적 가능한 객체에 액세스하려면 AR 경계 상자 관리자 구성요소를 추가하면 됩니다. AR 앵커 관리자와 유사한 구성요소이며 AR Foundation 인터페이스를 사용하여 액세스할 수 있습니다.

  • 또는 확장 프로그램 패키지에서 제공하는 공간 항목 샘플을 템플릿으로 가져와 시작할 수도 있습니다.

  • Android XR에서 AR Foundation 기능을 사용할 때의 주요 차이점은 다음과 같습니다.

  • 사용자 주변 환경을 이해하려면 앱에서 주변 환경에 관한 민감한 정보에 액세스해야 하는 경우가 많습니다.

  • 사용자를 보호하기 위해 Android XR은 시스템, 즉 권한 뒤에 있는 데이터 액세스를 제한합니다.

  • 카메라 액세스나 위치 액세스와 같은 일반적인 Android 권한 호출에 대해서는 이미 잘 알고 계실 것입니다.

  • 여기서 권장사항은 오른쪽에서 볼 수 있듯이 에디터에서 또는 Awake 이벤트와 같은 스크립트에서 시작 시 관리자 구성요소를 사용 중지하는 것입니다.

    void Awake() {
        planeManager = GetComponent<ARPlaneManager>();
        planeManager.enabled = false;
        boundingBoxManager = GetComponent<ARBoundingBoxManager>();
        boundingBoxManager.enabled = false;
        permissionUtil = GetComponent<AndroidXRPermissionUtil>();
    }
    
    void OnEnable() {
        StartCoroutine(WaitForPermissions());
    }
  • 필요한 권한을 모두 부여한 후에는 여기에서 WaitForPermissions 도우미가 하는 것처럼 스크립트에서 관리자를 사용 설정할 수 있습니다.

    IEnumerator WaitForPermissions() {
        planeManager.enabled = false;
        boundingBoxManager.enabled = false;
    
        yield return new WaitUntil(() =>
            permissionUtil.AllPermissionGranted());
    
        // Enable after granted permissions and
        // restart subsystems.
        planeManager.enabled = true;
        boundingBoxManager.enabled = true;
    
        // Save permission status.
        permissionGranted = true;
    }
  • 이렇게 하면 권한 부족으로 인한 하위 시스템 초기화 실패를 방지할 수 있습니다.

  • 이것은 특별한 기능입니다.

  • 제니퍼가 AR Foundation을 사용한 앵커에 관해 설명했듯이, Android XR에서 앵커 기능을 확장하기 위해 Google 패키지에서는 공유 가능한 앵커라는 새로운 기능도 제공합니다.

  • 이 기능은 AR Foundation 앵커 기능이 현재 지원하는 기능을 복제하며, 지리 공간 앵커와 같은 몇 가지 추가 기능도 제공합니다.

  • 짐작하시겠지만 앵커는 가상 세계와 실제 세계를 연결하는 실제 다리입니다.

  • 제니퍼가 생성, 저장, 로드하는 과정을 안내해 드렸지만, 이 모든 과정은 사용자의 거실과 같은 로컬 공간에서 이루어졌습니다.

  • 하지만 다른 국가 및 지역은 어떨까요?

  • 그리고 어떻게 서로 공유할 수 있을까요?

Geospatial API

  • 여기서 Geospatial API가 사용됩니다.

  • 좋아하는 서점이나 큰 공원의 만남의 장소와 같은 실제 위치를 기반으로 지리 공간 앵커를 만들 수 있습니다.

  • 따라서 Geospatial API는 GPS와 같은 가상 위치추적 시스템인 VPS를 통해 정확한 지리적 위치 추적기를 제공할 수 있지만, 실외 스트리트 뷰 데이터를 사용하여 훨씬 더 정확한 추적 결과를 제공합니다.

  • 이전에 ARCore의 Geospatial 기능을 사용해 본 적이 있다면 이 뷰가 익숙할 수도 있습니다.

  • 따라서 Android XR에서 Geospatial API에 액세스하려면 먼저 Google Cloud Platform에서 호스팅되는 백엔드에 연결해야 합니다.

  • Geospatial 백엔드는 실제로 Google Cloud의 ARCore 서비스로 구동되므로 동일한 단계를 따라 ARCore API 액세스로 Google Cloud 프로젝트를 구성할 수 있습니다.

  • 이렇게 하면 확장 프로그램 패키지에서 제공하는 Android XR Google Cloud 기능을 사용할 수 있습니다.

  • Geospatial API를 사용 설정하고 Google Cloud 프로젝트에서 API 키를 가져온 후에는 이 기능을 사용 설정하고 여기에 표시된 OpenXR 설정 메뉴를 통해 인증을 구성할 수 있습니다. 또는 TrySetAuthenticationAsync라는 스크립팅 API 호출을 통해 수동으로 구성할 수도 있습니다.

    async Task TrySetAuthentication() {
        googleCloudFeature =
            OpenXRSettings.Instance
                .GetFeature<XRGoogleCloudFeature>();
        var result = await googleCloudFeature
            .TrySetAuthenticationAsync(
                strategy, credential);
        if (result.IsSuccess()) {
            // Ready to use for geospatial tracker.
        } else if (result.IsUnsupported())  {
            // Google Cloud feature is not supported
        } else if (result.IsKeylessError()) {
            // Confirm if keyless has been set up properly.
        } else {
            // Handling other common OpenXR errors.
        }
    }
  • 여기서 주목할 점은 현재 Android XR 플랫폼은 API 키 인증만 지원하지만 향후 더 많은 사용자 인증 정보가 지원될 예정이라는 것입니다.

  • 따라서 인증을 완료하면 지리 공간 관리자를 사용하여 지리 공간 기능에 액세스할 수 있습니다.

  • 통합하기 전에 먼저 Google 지도에서 VPS 적용 범위를 확인하는 것이 좋습니다.

  • 따라서 이 기능을 사용할 때는 네트워킹 액세스 권한도 있어야 합니다.

  • XR Geospatial Manager는 AR Foundation에서 제공하는 AR Session 구성요소와 유사하게 작동합니다.

  • 따라서 시스템 지원 및 지리 공간 추적기의 추적 상태를 나타내는 정적 상태 속성이 함께 제공됩니다.

  • 다음과 같은 콜백으로 상태 변경 이벤트를 등록할 수 있습니다.

    void OnGeospatialStateChanged(XRGeospatialStateChangedEventArgs eventArgs) {
        bool isInitFailed = eventArgs.CurrState == XRGeospatialState.InitializationFailed;
        if (isInitFailed && XRGeospatialManager.InitializationResult.IsCloudAuthError()) {
            // Handle cloud authentication issues.
        } else if (isInitFailed) {
            // Check XRGeospatialManager.InitializationResult for other common OpenXR errors.
        } else {
            // Iterate runtime state change, and handle accordingly.
        }
    }
    
    private void OnEnable() {
        XRGeospatialManager.OnStateChanged += OnGeospatialStateChanged;
    }
  • Geospatial 상태가 실행 중으로 변경되면 이 관리자 인스턴스를 다음 모든 기능에 사용할 수 있습니다.

  • 먼저, 지정된 GPS 위치에서 VPS 사용 가능 여부를 확인하기 위해 Unity의 Location API를 사용하여 GPS 값을 가져온 다음 VPS 사용 가능 여부를 확인합니다.

    async Task CheckVPSAvailability(CancellationToken token) {
        var location = Input.location.lastData;
        try {
            var result = await geospatialManager.TryCheckVPSAvailabilityAsync(
                location.latitude, location.longitude);
            token.ThrowIfCancellationRequested();
            if (result.status.nativeStatusCode == (int)XrResultAndroid.GeospatialCoordinateInvalid) {
                // Invalid coordinates, try with another location.
            } else if (result.status.IsError()) {
                // Handle common OpenXR error: result.status.ToOpenXRStatus()
            }  else if (result.value == XRVPSAvailability.Available)  {
                // You may use this pose to attach geospatial anchors.
                var currentGeospatialPose = new XRGeospatialPose(location);
            }
        }
    }
  • 따라서 지도를 통해 VPS 지원을 확인하고 싶지 않은 경우 이 방법으로 대체할 수 있지만, 실제 GPS 상태를 확인하려면 야외로 나가야 합니다.

  • 그런 다음 TryLocate를 사용하여 Unity 포즈와 지리 공간 포즈 간에 포즈를 변환할 수도 있습니다.

    // Geospatial Pose -> Unity Pose
    var geospatialPose =
        new XRGeospatialPose(Input.location.lastData);
    if (geospatialManager.TryLocate(
            geospatialPose, out XRPoseResult result)) {
        if (result.IsPositionValid ||
            result.IsPositionTracked) {
            transform.position = result.Pose.position;
        }
        if (result.IsRotationValid ||
            result.IsRotationTracked) {
            transform.rotation = result.Pose.rotation;
        }
    }
    // Unity Pose -> Geospatial Pose
    Pose pose = new Pose(
        transform.position, transform.rotation);
    if (geospatialManager.TryLocate(
          pose, out XRGeospatialPoseResult result)) {
        if (result.IsPositionValid ||
            result.IsRotationValid) {
            // You may use for anchor attachment.
        }
    }
  • 그런 다음 VPS 확인 또는 포즈 변환의 결과를 가져와서 지정된 지리 공간 포즈 또는 알려진 표면 유형(예: 지형 또는 옥상)에 공간 앵커를 연결할 수 있습니다.

    async void PlaceAnchor(XRGeospatialPose pose) {
        try {
            Result<ARShareableAnchor> anchorResult;
            XRGeospatialSurfaceType typeToPlace = /* Choose your target type */;
            if (typeToPlace == XRGeospatialSurfaceType.Default) {
                anchorResult = geospatialManager.TryAttachGeospatialAnchor(pose);
            } else {
                pose.Altitude = 0;
                anchorResult = await geospatialManager.TryAttachSurfaceAnchorAsync(typeToPlace, pose);
            }
            if (anchorResult.status.IsSuccess()) {
                // ARShareableAnchorManager will track the anchor and update it.
            } else {
                // Handle OpenXR failures.
            }
        }
    }
  • 이 모든 것을 결합하면 실제 세계를 중심으로 한 진정으로 마법 같은 공간 경험을 구축할 수 있습니다.

  • 여기에서는 시청자를 실제 세계의 타겟팅된 장소로 안내하는 화살표를 배치합니다.

  • 좋습니다.

  • 따라서 Android XR SDK는 Google이 Unity 개발자에게 제공하는 유일한 패키지가 아닙니다.

  • 이제 Unity용 Google Android XR SDK에 Android XR 상호작용 프레임워크와 Android XR 오디오 패키지도 포함됩니다.

  • 또한 더 많은 기능 지원과 더 많은 공간 엔티티를 포함하여 Android XR SDK를 확장하게 되어 매우 기쁩니다.

요약 정리 및 리소스

  • 이제 세션이 거의 끝나갑니다.

  • 온라인에서 Unity의 로드맵을 확인하여 향후 계획을 확인할 수 있습니다.

  • 내년 초에 차세대 Unity인 Unity 7이 출시될 예정입니다.

  • 그동안 이 도구를 사용해 보고 혼합 현실 템플릿을 사용하여 프로젝트를 만들거나 GitHub에서 AR Foundation 샘플 프로젝트를 살펴보세요.

  • 또한 공간 엔티티 및 Unity 패키지 지원에 대해 자세히 알아보려면 Android 개발자 사이트의 페이지를 방문하세요.

  • 감사합니다.

  • Android XR에서 AR Foundation과 Spatial Entity를 사용하여 어떤 것을 빌드할 수 있을지 기대됩니다.

코드

코드 스니펫

'로컬 앵커 및 지속성'의 10:34에 있는 C# 스니펫
async void CreateAnchorAsync(ARAnchorManager manager, Pose pose) {
    var result = await manager.TryAddAnchorAsync(pose);
    if (result.status.IsError()) {
        // handle error
        return;
    }
    var anchor = result.value;
}
'로컬 앵커 및 지속성'의 10:43에 있는 C# 스니펫
void RemoveAnchor(ARAnchorManager manager, ARAnchor anchor) {
    var result = manager.TryRemoveAnchor(anchor.trackableId);
    if (result.status.IsError()) {
        // handle error
        return;
    }
}

void DestroyAnchorComponent(ARAnchor anchor) {
    Destroy(anchor);
}
'로컬 앵커 및 지속성'의 11:00에 있는 C# 스니펫
async void SaveAnchorAsync(ARAnchorManager manager, ARAnchor anchor) {
    var result = await manager.TrySaveAnchorAsync(anchor);
    if (result.status.IsError()) {
        // handle error
        return;
    }

    // Save this value, then return it as an input parameter
    SerializableGuid guid = result.value;
}
'로컬 앵커 및 지속성'의 11:17에 나오는 C# 스니펫
async void LoadAnchorAsync(ARAnchorManager manager, SerializableGuid guid) {
    var result = await manager.TryLoadAnchorAsync(guid);
    if (result.status.IsError()) {
        // handle error
        return;
    }

    ARAnchor anchor = result.value;
}
'공간 항목으로 빌드하기'의 16:45에 나오는 C# 스니펫
void Awake() {
    planeManager = GetComponent<ARPlaneManager>();
    planeManager.enabled = false;
    boundingBoxManager = GetComponent<ARBoundingBoxManager>();
    boundingBoxManager.enabled = false;
    permissionUtil = GetComponent<AndroidXRPermissionUtil>();
}

void OnEnable() {
    StartCoroutine(WaitForPermissions());
}
'공간 항목으로 빌드하기'의 16:58에 나오는 C# 스니펫
IEnumerator WaitForPermissions() {
    planeManager.enabled = false;
    boundingBoxManager.enabled = false;

    yield return new WaitUntil(() =>
        permissionUtil.AllPermissionGranted());

    // Enable after granted permissions and
    // restart subsystems.
    planeManager.enabled = true;
    boundingBoxManager.enabled = true;

    // Save permission status.
    permissionGranted = true;
}
'Geospatial API'의 19:12에 있는 C# 스니펫
async Task TrySetAuthentication() {
    googleCloudFeature =
        OpenXRSettings.Instance
            .GetFeature<XRGoogleCloudFeature>();
    var result = await googleCloudFeature
        .TrySetAuthenticationAsync(
            strategy, credential);
    if (result.IsSuccess()) {
        // Ready to use for geospatial tracker.
    } else if (result.IsUnsupported())  {
        // Google Cloud feature is not supported
    } else if (result.IsKeylessError()) {
        // Confirm if keyless has been set up properly.
    } else {
        // Handling other common OpenXR errors.
    }
}
'Geospatial API'의 20:24에 나오는 C# 스니펫
void OnGeospatialStateChanged(XRGeospatialStateChangedEventArgs eventArgs) {
    bool isInitFailed = eventArgs.CurrState == XRGeospatialState.InitializationFailed;
    if (isInitFailed && XRGeospatialManager.InitializationResult.IsCloudAuthError()) {
        // Handle cloud authentication issues.
    } else if (isInitFailed) {
        // Check XRGeospatialManager.InitializationResult for other common OpenXR errors.
    } else {
        // Iterate runtime state change, and handle accordingly.
    }
}

private void OnEnable() {
    XRGeospatialManager.OnStateChanged += OnGeospatialStateChanged;
}
'Geospatial API'의 20:38에 나오는 C# 스니펫
async Task CheckVPSAvailability(CancellationToken token) {
    var location = Input.location.lastData;
    try {
        var result = await geospatialManager.TryCheckVPSAvailabilityAsync(
            location.latitude, location.longitude);
        token.ThrowIfCancellationRequested();
        if (result.status.nativeStatusCode == (int)XrResultAndroid.GeospatialCoordinateInvalid) {
            // Invalid coordinates, try with another location.
        } else if (result.status.IsError()) {
            // Handle common OpenXR error: result.status.ToOpenXRStatus()
        }  else if (result.value == XRVPSAvailability.Available)  {
            // You may use this pose to attach geospatial anchors.
            var currentGeospatialPose = new XRGeospatialPose(location);
        }
    }
}
'Geospatial API'의 21:03에 있는 C# 스니펫
// Geospatial Pose -> Unity Pose
var geospatialPose =
    new XRGeospatialPose(Input.location.lastData);
if (geospatialManager.TryLocate(
        geospatialPose, out XRPoseResult result)) {
    if (result.IsPositionValid ||
        result.IsPositionTracked) {
        transform.position = result.Pose.position;
    }
    if (result.IsRotationValid ||
        result.IsRotationTracked) {
        transform.rotation = result.Pose.rotation;
    }
}
'Geospatial API'의 21:03에 나오는 C# 스니펫
// Unity Pose -> Geospatial Pose
Pose pose = new Pose(
    transform.position, transform.rotation);
if (geospatialManager.TryLocate(
      pose, out XRGeospatialPoseResult result)) {
    if (result.IsPositionValid ||
        result.IsRotationValid) {
        // You may use for anchor attachment.
    }
}
'Geospatial API'의 21:11에 있는 C# 스니펫
async void PlaceAnchor(XRGeospatialPose pose) {
    try {
        Result<ARShareableAnchor> anchorResult;
        XRGeospatialSurfaceType typeToPlace = /* Choose your target type */;
        if (typeToPlace == XRGeospatialSurfaceType.Default) {
            anchorResult = geospatialManager.TryAttachGeospatialAnchor(pose);
        } else {
            pose.Altitude = 0;
            anchorResult = await geospatialManager.TryAttachSurfaceAnchorAsync(typeToPlace, pose);
        }
        if (anchorResult.status.IsSuccess()) {
            // ARShareableAnchorManager will track the anchor and update it.
        } else {
            // Handle OpenXR failures.
        }
    }
}