Пример. Расширение возможностей пространственного сопоставления HoloLens

Создавая наши первые приложения для Microsoft HoloLens, мы хотели бы увидеть, насколько далеко мы можем раздвинуть границы пространственного сопоставления на устройстве. Джефф Эвертт (Jeff Evertt), инженер по программному обеспечению в Microsoft Studios, объясняет, как была разработана новая технология из-за необходимости более высокого контроля над тем, как голограммы размещаются в реальной среде пользователя.

Примечание

HoloLens 2 реализует новую среду выполнения осмысления сцены, которая предоставляет разработчикам Смешанная реальность структурированное высокоуровневое представление среды, предназначенное для интуитивно понятной разработки приложений с учетом окружающей среды.

Просмотреть видео

Помимо пространственного сопоставления

В то время как мы работали над Fragments и Young Conker, двумя из первых игр для HoloLens, мы обнаружили, что, когда мы делали процедурное размещение голограмм в физическом мире, нам нужен более высокий уровень понимания среды пользователя. У каждой игры были свои особые потребности в размещении: например, в фрагментах мы хотели иметь возможность различать различные поверхности, такие как пол или стол, чтобы разместить подсказки в соответствующих местах. Мы также хотели иметь возможность идентифицировать поверхности, на которые могли бы сидеть голографические символы в натуральная величину, например диван или стул. В Янг Конкер, мы хотели, чтобы Конкер и его противники могли использовать поднятые поверхности в комнате игрока в качестве платформ.

Asobo Studios, наш партнер по разработке этих игр, столкнулся с этой проблемой и создал технологию, которая расширяет возможности пространственного сопоставления HoloLens. Используя это, мы могли бы проанализировать комнату игрока и определить поверхности, такие как стены, столы, стулья и полы. Он также дал нам возможность оптимизировать набор ограничений, чтобы определить наилучшее размещение голографических объектов.

Код пространственного распознавания

Мы взяли исходный код Асобо и создали библиотеку, которая инкапсулирует эту технологию. Корпорация Майкрософт и Asobo открыли этот код и сделали его доступным в MixedRealityToolkit для использования в собственных проектах. Весь исходный код включен, что позволяет настроить его в соответствии со своими потребностями и поделиться улучшениями с сообществом. Код для решателя C++ был упакован в библиотеку DLL UWP и предоставлен Unity с раскрывающимся заготовкой, содержащейся в MixedRealityToolkit.

В пример Unity входит множество полезных запросов, которые позволяют находить пустые пространства на стенах, размещать объекты на потолке или на больших пространствах на полу, определять места для размещения персонажей и множество других запросов пространственного понимания.

Хотя решение пространственного сопоставления, предоставляемое HoloLens, является достаточно универсальным для удовлетворения потребностей всей гаммы проблемных пространств, модуль пространственного распознавания был создан для поддержки потребностей двух конкретных игр. Таким образом, его решение структурировано на основе определенного процесса и набора предположений:

  • Игровое пространство фиксированного размера: пользователь указывает максимальный размер пространства воспроизведения в вызове инициализации.
  • Процесс одноразового сканирования. Для этого процесса требуется дискретный этап сканирования, на котором пользователь выполняет обход, определяя пространство воспроизведения. Функции запросов не будут функционировать до тех пор, пока проверка не будет завершена.
  • Рисование пространства воспроизведения, управляемого пользователем: на этапе сканирования пользователь перемещается и просматривает пространство воспроизведения, эффективно рисуя области, которые должны быть включены. Созданная сетка важна для предоставления отзывов пользователей на этом этапе.
  • В помещении для дома или офиса: функции запроса разрабатываются вокруг плоских поверхностей и стен под прямым углом. Это мягкое ограничение. Однако на этапе сканирования выполняется анализ основной оси для оптимизации тесселяции сетки вдоль основной и вспомогательной осей.

Процесс сканирования помещений

При загрузке модуля пространственного распознавания первое, что вы сделаете, — это сканировать пространство, чтобы все пригодные для использования поверхности, такие как пол, потолок и стены, были идентифицированы и помечены. В процессе сканирования вы просматриваете свою комнату и "рисуете" области, которые должны быть включены в сканирование.

Сетка, видимая на этом этапе, является важной частью визуальной обратной связи, которая позволяет пользователям узнать, какие части комнаты сканируются. Библиотека DLL для модуля пространственного распознавания внутренне хранит пространство воспроизведения в виде сетки объемных кубов размером 8 см. На начальной части сканирования выполняется анализ основного компонента для определения осей комнаты. Внутренне он сохраняет пространство voxel, выровненное по этим осям. Сетка создается примерно каждую секунду путем извлечения изоповерхия из тома voxel.

Сетка пространственного сопоставления в белом цвете и сетка пространства воспроизведения в зеленом

Сетка пространственного сопоставления в белом цвете и сетка пространства воспроизведения в зеленом

Включенный файл SpatialUnderstanding.cs управляет процессом этапа сканирования. Он вызывает следующие функции:

  • SpatialUnderstanding_Init: вызывается один раз в начале.
  • GeneratePlayspace_InitScan: указывает, что этап сканирования должен начаться.
  • GeneratePlayspace_UpdateScan_DynamicScan. Вызывает каждый кадр для обновления процесса сканирования. Положение и ориентация камеры передаются и используются для процесса рисования пространства воспроизведения, описанного выше.
  • GeneratePlayspace_RequestFinish: вызывается для завершения игрового пространства. При этом будут использоваться области, "окрашенные" на этапе сканирования, чтобы определить и заблокировать пространство воспроизведения. Приложение может запрашивать статистику на этапе сканирования, а также запрашивать настраиваемую сетку для предоставления отзывов пользователей.
  • Import_UnderstandingMesh. Во время сканирования поведение SpatialUnderstandingCustomMesh , предоставляемое модулем и помещенное в заготовку осмысления, будет периодически запрашивать пользовательскую сетку, созданную процессом. Кроме того, это делается еще раз после завершения сканирования.

Поток сканирования, управляемый поведением SpatialUnderstanding , вызывает InitScan, а затем UpdateScan each frame. Когда запрос статистики сообщает о разумном объеме, пользователь может перенашивать вызов RequestFinish , чтобы указать конец этапа сканирования. UpdateScan продолжает вызываться до тех пор, пока возвращаемое значение не указывает на завершение обработки библиотеки DLL.

Запросы

После завершения сканирования вы сможете получить доступ к трем различным типам запросов в интерфейсе:

  • Запросы топологии. Это быстрые запросы, основанные на топологии сканируемой комнаты.
  • Запросы фигур. Они используют результаты запросов топологии для поиска горизонтальных поверхностей, которые хорошо соответствуют пользовательским фигурам, которые вы определяете.
  • Запросы на размещение объектов. Это более сложные запросы, которые находят наиболее подходящее расположение на основе набора правил и ограничений для объекта.

В дополнение к трем основным запросам существует интерфейс raycasting, который можно использовать для получения помеченных типов поверхностей, а также для копирования пользовательской сетки помещений.

Запросы топологии

В библиотеке DLL диспетчер топологий обрабатывает метки среды. Как упоминалось выше, большая часть данных хранится в серферах, которые содержатся в томе voxel. Кроме того, структура PlaySpaceInfos используется для хранения сведений о пространстве воспроизведения, включая выравнивание мира (подробнее об этом ниже), пол и высоту потолка.

Эвристика используется для определения пола, потолка и стен. Например, самая большая и самая низкая горизонтальная поверхность с площадью поверхности более 1 м2 считается полом. Обратите внимание, что путь к камере во время сканирования также используется в этом процессе.

Подмножество запросов, предоставляемых диспетчером топологий, предоставляется через библиотеку 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)

Каждый из этих запросов принимает предварительно выделенный массив структур TopologyResult . Параметр locationCount указывает длину переданного массива. Возвращаемое значение сообщает количество возвращаемых расположений. Это число никогда не больше переданного параметра locationCount .

TopologyResult содержит центральное положение возвращаемого тома, направление облицовки (т. е. нормальное) и размеры найденного пространства.

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

Обратите внимание, что в примере Unity каждый из этих запросов связан с кнопкой на панели виртуального пользовательского интерфейса. Пример жестко задает параметры для каждого из этих запросов с разумными значениями. Дополнительные примеры см. в файле SpaceVisualizer.cs в примере кода.

Запросы фигуры

Внутри библиотеки DLL анализатор фигур (ShapeAnalyzer_W) использует анализатор топологии для сопоставления с пользовательскими фигурами, определенными пользователем. Пример Unity содержит предварительно определенный набор фигур, которые отображаются в меню запроса на вкладке фигур.

Обратите внимание, что анализ фигур работает только на горизонтальных поверхностях. Например, диван определяется плоской поверхностью сиденья и плоской верхней части дивана назад. Запрос на фигуру ищет две поверхности определенного размера, высоты и диапазона аспектов, при этом две поверхности выровнены и соединены друг с другом. Используя терминологию API, сиденье дивана и верхняя часть задней части дивана являются компонентами фигуры, а требования к выравниванию — ограничениями компонентов фигуры.

Пример запроса, определенный в примере Unity (ShapeDefinition.cs) для объектов sittable, выглядит следующим образом:

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);

Каждый запрос к фигуре определяется набором компонентов фигуры, каждый из которых имеет набор ограничений компонентов и набор ограничений фигуры, которые перечисляют зависимости между компонентами. Этот пример включает три ограничения в одном определении компонента и не ограничивает фигуру между компонентами (так как существует только один компонент).

В отличие от этого, фигура дивана имеет два компонента фигуры и четыре ограничения фигуры. Обратите внимание, что компоненты идентифицируются по индексу в списке компонентов пользователя (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 для простого создания пользовательских определений фигур. Полный список ограничений компонентов и фигур можно найти в spatialUnderstandingDll.cs в структурах ShapeComponentConstraint и ShapeConstraint .

Синий прямоугольник выделяет результаты запроса фигуры стула.

Синий прямоугольник выделяет результаты запроса фигуры стула.

Решатель размещения объектов

Запросы на размещение объектов можно использовать для определения идеальных расположений в физической комнате для размещения объектов. Решатель найдет оптимальное расположение с учетом правил и ограничений объекта. Кроме того, запросы объектов сохраняются до тех пор, пока объект не будет удален с помощью 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. Последующие запросы на размещение будут учитывать этот объект. Файл LevelSolver.cs в примере Unity содержит дополнительные примеры запросов.

Синие рамки показывают результат трех запросов Place On Floor с правилами

Синие рамки показывают результат трех запросов 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;
     };

Внутренне луч вычисляется на основе вычисленного 8-кубового представления пространства воспроизведения. Каждый voxel содержит набор элементов поверхности с обработанными данными топологии (также известные как серферы). Серферы, содержащиеся в пересекающейся ячейке voxel, сравниваются и лучшее соответствие, используемое для поиска сведений о топологии. Эти данные топологии содержат метки, возвращаемые в виде перечисления SurfaceTypes , а также поверхность пересекающейся поверхности.

В примере Unity курсор приводит луч к каждому кадру. Во-первых, против коллайдеров Unity; во-вторых, по отношению к представлению в мире модуля понимания; и, наконец, с элементами пользовательского интерфейса. В этом приложении пользовательский интерфейс получает приоритет, затем результат понимания и, наконец, коллайдеры Unity. SurfaceType отображается как текст рядом с курсором.

Отчеты о результатах raycast пересечения с полом.

Отчеты о результатах raycast пересечения с полом.

Получите код

Код с открытым кодом доступен в MixedRealityToolkit. Если вы используете код в проекте, сообщите нам на форумах разработчиков HoloLens . Мы не можем ждать, чтобы увидеть, что вы делаете с ним!

Об авторе

Джефф Эвертт (Jeff Evertt), руководитель программной инженерии корпорации Майкрософт Джефф Эвертт (Jeff Evertt ) — ведущий разработчик программного обеспечения, который работал над HoloLens с первых дней, от инкубации до опытных разработок. До HoloLens он работал над Xbox Kinect и в игровой индустрии на различных платформах и играх. Джефф увлечен робототехникой, графикой и вещами с кричащие огни, которые идут звуковой сигнал. Он любит изучать новые вещи и работать над программным обеспечением, оборудованием, и особенно в пространстве, где они пересекаются.

См. также статью