사례 연구 - HoloLens의 공간 매핑 기능 확장

Microsoft HoloLens 대한 첫 번째 앱을 만들 때 디바이스에서 공간 매핑의 경계를 얼마나 멀리 밀어붙일 수 있는지 확인하고자 했습니다. Microsoft Studios의 소프트웨어 엔지니어인 Jeff Evertt는 홀로그램이 사용자의 실제 환경에 배치되는 방식을 보다 세밀하게 제어할 필요 없이 새로운 기술이 어떻게 개발되었는지 설명합니다.

참고

HoloLens 2 환경 인식 애플리케이션을 직관적으로 개발할 수 있도록 설계된 구조화된 고급 환경 표현을 Mixed Reality 개발자에게 제공하는 새로운 Scene Understanding 런타임을 구현합니다.

비디오 보기

공간 매핑을 넘어

HoloLens의 첫 번째 게임 중 두 게임인 FragmentsYoung Conker에서 작업하는 동안 실제 세계에서 홀로그램의 절차적 배치를 수행할 때 사용자 환경에 대한 더 높은 수준의 이해가 필요하다는 것을 발견했습니다. 각 게임에는 고유한 배치 요구 사항이 있었습니다. 예를 들어 조각에서 바닥이나 테이블과 같은 다양한 표면을 구분하여 관련 위치에 단서를 배치할 수 있기를 원했습니다. 또한 소파나 의자와 같이 실물 크기의 홀로그램 캐릭터가 앉을 수 있는 표면을 식별할 수 있기를 원했습니다. 영 콘커에서 우리는 Conker와 그의 상대가 플레이어의 방에서 올려진 표면을 플랫폼으로 사용할 수 있기를 원했습니다.

이러한 게임의 개발 파트너인 Asobo Studios는 이 문제에 정면으로 직면하고 HoloLens의 공간 매핑 기능을 확장하는 기술을 만들었습니다. 이를 사용하여 플레이어의 방을 분석하고 벽, 테이블, 의자 및 바닥과 같은 표면을 식별할 수 있습니다. 또한 홀로그램 개체에 가장 적합한 배치를 결정하기 위해 제약 조건 집합에 대해 최적화할 수 있는 기능을 제공했습니다.

공간 이해 코드

Asobo의 원래 코드를 가져와서 이 기술을 캡슐화하는 라이브러리를 만들었습니다. Microsoft와 Asobo는 이제 이 코드를 오픈 소스로 제공하고 MixedRealityToolkit 에서 직접 프로젝트에 사용할 수 있도록 했습니다. 모든 소스 코드가 포함되어 있으므로 필요에 맞게 사용자 지정하고 개선 사항을 커뮤니티와 공유할 수 있습니다. C++ 솔버에 대한 코드는 UWP DLL로 래핑되고 MixedRealityToolkit 내에 포함된 드롭인 프리팹을 사용하여 Unity에 노출되었습니다.

Unity 샘플에는 벽에 빈 공간을 찾고, 천장이나 바닥의 큰 공간에 개체를 배치하고, 문자가 앉을 위치를 식별하고, 수많은 다른 공간 이해 쿼리를 식별할 수 있는 유용한 쿼리가 많이 포함되어 있습니다.

HoloLens에서 제공하는 공간 매핑 솔루션은 문제 공간의 전체 영역의 요구를 충족할 수 있을 만큼 일반적으로 설계되었지만 공간 이해 모듈은 두 개의 특정 게임의 요구를 지원하기 위해 빌드되었습니다. 따라서 솔루션은 특정 프로세스 및 가정 집합을 중심으로 구성됩니다.

  • 고정 크기 플레이스페이스: 사용자가 init 호출에서 최대 플레이스페이스 크기를 지정합니다.
  • 일회성 검사 프로세스: 프로세스에는 사용자가 작업하는 개별 검사 단계가 필요하며 플레이스페이스를 정의합니다. 쿼리 함수는 검사가 완료될 때까지 작동하지 않습니다.
  • 사용자 기반 플레이스페이스 "그리기": 스캔 단계에서 사용자는 플레이스페이스를 이동하고 둘러보며 포함해야 하는 영역을 효과적으로 그립니다. 생성된 메시는 이 단계에서 사용자 피드백을 제공하는 데 중요합니다.
  • 실내 집 또는 사무실 설정: 쿼리 함수는 직각으로 평평한 표면과 벽을 중심으로 설계되었습니다. 이는 소프트 제한 사항입니다. 그러나 검사 단계에서는 주 축과 부축을 따라 메시 공간 분할을 최적화하기 위해 기본 축 분석이 완료됩니다.

회의실 검사 프로세스

공간 이해 모듈을 로드할 때 가장 먼저 수행할 작업은 공간을 스캔하는 것이므로 바닥, 천장 및 벽과 같은 사용 가능한 모든 표면이 식별되고 레이블이 지정됩니다. 스캔 과정에서 방을 둘러보고 스캔에 포함해야 하는 영역을 "페인트"합니다.

이 단계에서 볼 수 있는 메시는 사용자에게 스캔 중인 방 부분을 알 수 있는 중요한 시각적 피드백 부분입니다. 공간 이해 모듈용 DLL은 내부적으로 플레이스페이스를 8cm 크기의 복셀 큐브 그리드로 저장합니다. 검사의 초기 부분에서는 기본 구성 요소 분석을 완료하여 방의 축을 확인합니다. 내부적으로 이러한 축에 맞춰 복셀 공간을 저장합니다. 메시는 복셀 볼륨에서 이소서페이스를 추출하여 약 1초마다 생성됩니다.

흰색의 공간 매핑 메시와 녹색의 이해 플레이스페이스 메시

흰색의 공간 매핑 메시와 녹색의 이해 플레이스페이스 메시

포함된 SpatialUnderstanding.cs 파일은 검사 단계 프로세스를 관리합니다. 다음 함수를 호출합니다.

  • SpatialUnderstanding_Init: 시작 시 한 번 호출합니다.
  • GeneratePlayspace_InitScan: 검사 단계가 시작되어야 했음을 나타냅니다.
  • GeneratePlayspace_UpdateScan_DynamicScan: 각 프레임을 호출하여 검사 프로세스를 업데이트합니다. 카메라 위치와 방향이 전달되며 위에서 설명한 플레이스페이스 그리기 프로세스에 사용됩니다.
  • GeneratePlayspace_RequestFinish: 플레이스페이스를 완료하기 위해 호출됩니다. 이렇게 하면 스캔 단계에서 "그린" 영역을 사용하여 플레이스페이스를 정의하고 잠급니다. 애플리케이션은 검사 단계 중에 통계를 쿼리하고 사용자 피드백을 제공하기 위해 사용자 지정 메시를 쿼리할 수 있습니다.
  • Import_UnderstandingMesh: 검사하는 동안 모듈에서 제공하고 이해 프리팹에 배치된 SpatialUnderstandingCustomMesh 동작은 프로세스에서 생성된 사용자 지정 메시를 주기적으로 쿼리합니다. 또한 검사가 완료된 후 다시 한 번 수행됩니다.

SpatialUnderstanding 동작에 의해 구동되는 스캔 흐름은 InitScan을 호출한 다음, 각 프레임을 UpdateScan합니다. 통계 쿼리가 적절한 검사를 보고하면 사용자는 Airtap을 호출하여 RequestFinish 를 호출하여 검사 단계의 끝을 나타낼 수 있습니다. UpdateScan 은 반환 값이 DLL이 처리를 완료했음을 나타내기 전까지 계속 호출됩니다.

쿼리

검사가 완료되면 인터페이스에서 세 가지 유형의 쿼리에 액세스할 수 있습니다.

  • 토폴로지 쿼리: 스캔한 회의실의 토폴로지를 기반으로 하는 빠른 쿼리입니다.
  • 셰이프 쿼리: 토폴로지 쿼리의 결과를 활용하여 정의한 사용자 지정 셰이프와 일치하는 가로 표면을 찾습니다.
  • 개체 배치 쿼리: 개체에 대한 규칙 및 제약 조건 집합에 따라 가장 적합한 위치를 찾는 더 복잡한 쿼리입니다.

세 가지 기본 쿼리 외에도 태그가 지정된 표면 유형을 검색하는 데 사용할 수 있는 레이캐스팅 인터페이스가 있으며 사용자 지정 방수 공간 메시를 복사할 수 있습니다.

토폴로지 쿼리

DLL 내에서 토폴로지 관리자는 환경의 레이블 지정을 처리합니다. 위에서 설명한 것처럼 대부분의 데이터는 복셀 볼륨 내에 포함된 서펠 내에 저장됩니다. 또한 PlaySpaceInfos 구조체는 월드 맞춤(아래 세부 정보), 바닥 및 천장 높이를 포함하여 플레이스페이스에 대한 정보를 저장하는 데 사용됩니다.

추론은 바닥, 천장 및 벽을 결정하는 데 사용됩니다. 예를 들어 표면적이 1m2보다 큰 가장 크고 가장 낮은 가로 표면은 바닥으로 간주됩니다. 검사 프로세스 중에 카메라 경로도 이 프로세스에 사용됩니다.

토폴로지 관리자가 노출하는 쿼리의 하위 집합은 DLL을 통해 노출됩니다. 노출된 토폴로지 쿼리는 다음과 같습니다.

  • QueryTopology_FindPositionsOnWalls
  • QueryTopology_FindLargePositionsOnWalls
  • QueryTopology_FindLargestWall
  • QueryTopology_FindPositionsOnFloor
  • QueryTopology_FindLargestPositionsOnFloor
  • QueryTopology_FindPositionsSittable

각 쿼리에는 쿼리 유형과 관련된 매개 변수 집합이 있습니다. 다음 예제에서 사용자는 원하는 볼륨의 최소 높이 & 너비, 바닥 위의 최소 배치 높이 및 볼륨 앞의 최소 간격을 지정합니다. 모든 측정값은 미터 단위입니다.

EXTERN_C __declspec(dllexport) int QueryTopology_FindPositionsOnWalls(
          _In_ float minHeightOfWallSpace,
          _In_ float minWidthOfWallSpace,
          _In_ float minHeightAboveFloor,
          _In_ float minFacingClearance,
          _In_ int locationCount,
          _Inout_ Dll_Interface::TopologyResult* locationData)

이러한 각 쿼리는 토폴로지Result 구조체의 미리 할당된 배열을 사용합니다. locationCount 매개 변수는 전달된 배열의 길이를 지정합니다. 반환 값은 반환된 위치 수를 보고합니다. 이 숫자는 전달된 locationCount 매개 변수보다 크지 않습니다.

TopologyResult에는 반환된 볼륨의 가운데 위치, 방향(예: 일반) 및 찾은 공간의 차원이 포함됩니다.

struct TopologyResult
     {
          DirectX::XMFLOAT3 position;
          DirectX::XMFLOAT3 normal;
          float width;
          float length;
     };

Unity 샘플에서 이러한 각 쿼리는 가상 UI 패널의 단추에 연결됩니다. 샘플은 이러한 각 쿼리에 대한 매개 변수를 적절한 값으로 하드 코드합니다. 자세한 예제는 샘플 코드의 SpaceVisualizer.cs 를 참조하세요.

셰이프 쿼리

DLL 내에서 셰이프 분석기(ShapeAnalyzer_W)는 토폴로지 분석기를 사용하여 사용자가 정의한 사용자 지정 셰이프와 일치합니다. Unity 샘플에는 셰이프 탭의 쿼리 메뉴에 표시되는 미리 정의된 셰이프 집합이 있습니다.

셰이프 분석은 가로 표면에서만 작동합니다. 예를 들어 소파는 평평한 좌석 표면과 소파 뒤쪽의 평평한 위쪽으로 정의됩니다. 셰이프 쿼리는 두 표면이 정렬되고 연결된 특정 크기, 높이 및 가로 세로 범위의 두 표면을 찾습니다. API 용어를 사용하면 소파 시트와 소파 뒷면의 위쪽이 셰이프 구성 요소이며 맞춤 요구 사항은 셰이프 구성 요소 제약 조건입니다.

"sittable" 개체에 대한 Unity 샘플(ShapeDefinition.cs)에 정의된 예제 쿼리는 다음과 같습니다.

shapeComponents = new List<ShapeComponent>()
     {
          new ShapeComponent(
               new List<ShapeComponentConstraint>()
               {
                    ShapeComponentConstraint.Create_SurfaceHeight_Between(0.2f, 0.6f),
                    ShapeComponentConstraint.Create_SurfaceCount_Min(1),
                    ShapeComponentConstraint.Create_SurfaceArea_Min(0.035f),
               }),
     };
     AddShape("Sittable", shapeComponents);

각 셰이프 쿼리는 구성 요소 제약 조건 집합과 구성 요소 간의 종속성을 나열하는 셰이프 제약 조건 집합이 있는 셰이프 구성 요소 집합에 의해 정의됩니다. 이 예제에서는 단일 구성 요소 정의에 3개의 제약 조건과 구성 요소 간에 셰이프 제약 조건이 없습니다(구성 요소가 하나만 있으므로).

반면 소파 셰이프에는 두 개의 셰이프 구성 요소와 4개의 셰이프 제약 조건이 있습니다. 구성 요소는 사용자의 구성 요소 목록(이 예제에서는 0 및 1)의 인덱스로 식별됩니다.

shapeConstraints = new List<ShapeConstraint>()
        {
              ShapeConstraint.Create_RectanglesSameLength(0, 1, 0.6f),
              ShapeConstraint.Create_RectanglesParallel(0, 1),
              ShapeConstraint.Create_RectanglesAligned(0, 1, 0.3f),
              ShapeConstraint.Create_AtBackOf(1, 0),
        };

사용자 지정 셰이프 정의를 쉽게 만들 수 있는 래퍼 함수는 Unity 모듈에 제공됩니다. 구성 요소 및 셰이프 제약 조건의 전체 목록은 ShapeComponentConstraintShapeConstraint 구조체 내의 SpatialUnderstandingDll.cs에서 찾을 수 있습니다.

파란색 사각형은 의자 셰이프 쿼리의 결과를 강조 표시합니다.

파란색 사각형은 의자 셰이프 쿼리의 결과를 강조 표시합니다.

개체 배치 솔버

개체 배치 쿼리를 사용하여 개체를 배치할 실제 공간의 이상적인 위치를 식별할 수 있습니다. 솔버가 개체 규칙 및 제약 조건을 고려할 때 가장 적합한 위치를 찾습니다. 또한 개체 쿼리는 Solver_RemoveObject 또는 Solver_RemoveAllObjects 호출을 통해 개체가 제거될 때까지 유지되므로 제한된 다중 개체 배치가 가능합니다.

개체 배치 쿼리는 매개 변수가 있는 배치 형식, 규칙 목록 및 제약 조건 목록의 세 부분으로 구성됩니다. 쿼리를 실행하려면 다음 API를 사용합니다.

public static int Solver_PlaceObject(
                [In] string objectName,
                [In] IntPtr placementDefinition,	// ObjectPlacementDefinition
                [In] int placementRuleCount,
                [In] IntPtr placementRules,     	// ObjectPlacementRule
                [In] int constraintCount,
                [In] IntPtr placementConstraints,	// ObjectPlacementConstraint
                [Out] IntPtr placementResult)

이 함수는 개체 이름, 배치 정의 및 규칙 및 제약 조건 목록을 사용합니다. C# 래퍼는 규칙 및 제약 조건 생성을 쉽게 만드는 생성 도우미 기능을 제공합니다. 배치 정의에는 쿼리 형식, 즉 다음 중 하나가 포함됩니다.

public enum PlacementType
                {
                    Place_OnFloor,
                    Place_OnWall,
                    Place_OnCeiling,
                    Place_OnShape,
                    Place_OnEdge,
                    Place_OnFloorAndCeiling,
                    Place_RandomInAir,
                    Place_InMidAir,
                    Place_UnderFurnitureEdge,
                };

각 배치 형식에는 형식에 고유한 매개 변수 집합이 있습니다. ObjectPlacementDefinition 구조에는 이러한 정의를 만들기 위한 정적 도우미 함수 집합이 포함되어 있습니다. 예를 들어 개체를 바닥에 배치할 위치를 찾으려면 다음 함수를 사용할 수 있습니다.

public static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims)

배치 유형 외에도 규칙 및 제약 조건 집합을 제공할 수 있습니다. 규칙을 위반할 수 없습니다. 형식 및 규칙을 충족하는 가능한 배치 위치는 제약 조건 집합에 대해 최적화되어 최적의 배치 위치를 선택합니다. 제공된 정적 생성 함수에서 각 규칙 및 제약 조건을 만들 수 있습니다. 예제 규칙 및 제약 조건 생성 함수는 아래에 제공됩니다.

public static ObjectPlacementRule Create_AwayFromPosition(
                    Vector3 position, float minDistance)
               public static ObjectPlacementConstraint Create_NearPoint(
                    Vector3 position, float minDistance = 0.0f, float maxDistance = 0.0f)

아래 개체 배치 쿼리는 반미터 큐브를 표면 가장자리에 놓고 이전에 배치한 다른 개체와 회의실 중앙 근처에 배치할 위치를 찾고 있습니다.

List<ObjectPlacementRule> rules = 
          new List<ObjectPlacementRule>() {
               ObjectPlacementRule.Create_AwayFromOtherObjects(1.0f),
          };

     List<ObjectPlacementConstraint> constraints = 
          new List<ObjectPlacementConstraint> {
               ObjectPlacementConstraint.Create_NearCenter(),
          };

     Solver_PlaceObject(
          “MyCustomObject”,
          new ObjectPlacementDefinition.Create_OnEdge(
          new Vector3(0.25f, 0.25f, 0.25f), 
          new Vector3(0.25f, 0.25f, 0.25f)),
          rules.Count,
          UnderstandingDLL.PinObject(rules.ToArray()),
          constraints.Count,
          UnderstandingDLL.PinObject(constraints.ToArray()),
          UnderstandingDLL.GetStaticObjectPlacementResultPtr());

성공하면 배치 위치, 차원 및 방향을 포함하는 ObjectPlacementResult 구조체가 반환됩니다. 또한 배치는 DLL의 배치된 개체의 내부 목록에 추가됩니다. 후속 배치 쿼리는 이 개체를 고려합니다. Unity 샘플 의 LevelSolver.cs 파일에는 더 많은 예제 쿼리가 포함되어 있습니다.

파란색 상자는

파란색 상자는 "카메라 위치에서 벗어나기" 규칙이 있는 세 개의 Place On Floor 쿼리의 결과를 표시합니다.

팁:

  • 수준 또는 애플리케이션 시나리오에 필요한 여러 개체의 배치 위치를 해결하는 경우 먼저 필수 및 큰 개체를 해결하여 공간을 찾을 수 있는 확률을 최대화합니다.
  • 배치 순서가 중요합니다. 개체 배치를 찾을 수 없는 경우 덜 제한된 구성을 시도합니다. 대체 구성 집합을 갖는 것은 여러 회의실 구성에서 기능을 지원하는 데 중요합니다.

광선 캐스팅

세 가지 기본 쿼리 외에도 광선 캐스팅 인터페이스를 사용하여 태그가 지정된 표면 유형을 검색할 수 있으며, 회의실을 스캔하고 완료한 후 바닥, 천장 및 벽과 같은 표면에 대해 내부적으로 레이블이 생성되는 사용자 지정 방수 플레이스페이스 메시를 복사할 수 있습니다. PlayspaceRaycast 함수는 광선을 사용하고 광선이 알려진 표면과 충돌하는 경우 를 반환하고, 그렇다면 RaycastResult의 형태로 해당 표면에 대한 정보를 반환합니다.

struct RaycastResult
     {
          enum SurfaceTypes
          {
               Invalid,	// No intersection
               Other,
               Floor,
               FloorLike,         // Not part of the floor topology, 
                                  //     but close to the floor and looks like the floor
               Platform,          // Horizontal platform between the ground and 
                                  //     the ceiling
               Ceiling,
               WallExternal,
               WallLike,          // Not part of the external wall surface, 
                                  //     but vertical surface that looks like a 
                                  //	wall structure
               };
               SurfaceTypes SurfaceType;
               float SurfaceArea;	// Zero if unknown 
                                        //	(i.e. if not part of the topology analysis)
               DirectX::XMFLOAT3 IntersectPoint;
               DirectX::XMFLOAT3 IntersectNormal;
     };

내부적으로 레이캐스트는 플레이스페이스의 계산된 8cm 큐브 복셀 표현에 대해 계산됩니다. 각 복셀에는 처리된 토폴로지 데이터(서펠이라고도 함)가 있는 표면 요소 집합이 포함되어 있습니다. 교차된 복셀 셀 내에 포함된 서펠을 비교하고 토폴로지 정보를 조회하는 데 가장 적합합니다. 이 토폴로지 데이터에는 SurfaceTypes 열거형 형식으로 반환된 레이블 지정과 교차된 표면의 표면적이 포함됩니다.

Unity 샘플에서 커서는 프레임마다 광선을 투사합니다. 먼저 Unity의 충돌자에 대해 둘째, 이해 모듈의 월드 표현과 반대입니다. 마지막으로 UI 요소에 대해 을 선택합니다. 이 애플리케이션에서 UI는 우선 순위를 가져온 다음, 결과를 이해하고 마지막으로 Unity의 충돌기를 가져옵니다. SurfaceType은 커서 옆에 있는 텍스트로 보고됩니다.

레이캐스트 결과 보고가 바닥과 교차합니다.

레이캐스트 결과 보고가 바닥과 교차합니다.

코드 가져오기

오픈 소스 코드는 MixedRealityToolkit에서 사용할 수 있습니다. 프로젝트에서 코드를 사용하는 경우 HoloLens 개발자 포럼 에 알려주세요. 우리는 당신이 그것으로 무엇을보고 기다릴 수 없어!

작성자 정보

Jeff Evertt, Microsoft의 소프트웨어 엔지니어링 책임자 Jeff Evertt 는 초기부터 인큐베이션부터 개발 경험까지 HoloLens에서 근무한 소프트웨어 엔지니어링 책임자입니다. HoloLens 이전에는 Xbox Kinect 및 게임 업계에서 다양한 플랫폼과 게임에서 근무했습니다. Jeff는 로봇 공학, 그래픽 및 경고음이 울리는 화려한 조명이 있는 것들에 열정적입니다. 그는 새로운 것을 배우고 소프트웨어, 하드웨어, 특히 두 가지가 교차하는 공간에서 작업하는 것을 즐깁니다.

참고 항목