Caso práctico: expansión de las funcionalidades de asignación espacial de HoloLens

Al crear nuestras primeras aplicaciones para Microsoft HoloLens, nos dieron ganas de ver hasta qué punto podríamos insertar los límites de la asignación espacial en el dispositivo. Jeff Evertt, ingeniero de software de Microsoft Studios, explica cómo se desarrolló una nueva tecnología debido a la necesidad de tener más control sobre cómo se colocan los hologramas en el entorno real de un usuario.

Nota

HoloLens 2 implementa un nuevo entorno de ejecución de Scene Understanding, que proporciona Mixed Reality desarrolladores con una representación estructurada de entorno de alto nivel diseñada para que el desarrollo de aplicaciones con reconocimiento ambiental sea intuitivo.

Visualización del vídeo

Más allá de la asignación espacial

Mientras estábamos trabajando en Fragmentos y Young Conker, dos de los primeros juegos para HoloLens, descubrimos que cuando hacíamos la colocación de procedimientos de hologramas en el mundo físico, necesitábamos un nivel más alto de comprensión sobre el entorno del usuario. Cada juego tenía sus propias necesidades de colocación específicas: en Fragmentos, por ejemplo, queríamos poder distinguir entre diferentes superficies, como la planta o una mesa, para colocar pistas en ubicaciones relevantes. También queríamos poder identificar superficies en las que los caracteres holográficos de tamaño de vida podían sentarse, como un sofá o una silla. En Young Conker, queríamos que Conker y sus oponentes pudieran usar superficies elevadas en la sala de un jugador como plataformas.

Asobo Studios, nuestro socio de desarrollo para estos juegos, enfrentó este problema y creó una tecnología que amplía las capacidades de asignación espacial de HoloLens. Con esto, podríamos analizar la sala de un jugador e identificar superficies como paredes, tablas, sillas y suelos. También nos dio la capacidad de optimizar con un conjunto de restricciones para determinar la mejor ubicación para los objetos holográficos.

El código de comprensión espacial

Tomamos el código original de Asobo y creamos una biblioteca que encapsula esta tecnología. Microsoft y Asobo ya han abierto este código y lo han puesto a disposición de MixedRealityToolkit para que lo use en sus propios proyectos. Se incluye todo el código fuente, lo que le permite personalizarlo a sus necesidades y compartir sus mejoras con la comunidad. El código del solucionador de C++ se ha ajustado en un archivo DLL de UWP y se ha expuesto a Unity con un objeto prefabricado desplegable incluido en MixedRealityToolkit.

Hay muchas consultas útiles incluidas en el ejemplo de Unity que le permitirán encontrar espacios vacíos en las paredes, colocar objetos en el techo o en espacios grandes en el suelo, identificar lugares para sentarse y una gran cantidad de otras consultas de comprensión espacial.

Aunque la solución de asignación espacial proporcionada por HoloLens está diseñada para ser lo suficientemente genérica como para satisfacer las necesidades de toda la gama de espacios problemáticos, el módulo de comprensión espacial se creó para admitir las necesidades de dos juegos específicos. Por lo tanto, su solución se estructura en torno a un proceso específico y conjunto de suposiciones:

  • Espacio de reproducción de tamaño fijo: el usuario especifica el tamaño máximo del espacio de reproducción en la llamada init.
  • Proceso de examen único: el proceso requiere una fase de examen discreta en la que el usuario recorre y define el espacio de reproducción. Las funciones de consulta no funcionarán hasta después de que se haya finalizado el examen.
  • Espacio de reproducción controlado por el usuario "pintura": durante la fase de examen, el usuario se mueve y mira alrededor del espacio de reproducción, pintando eficazmente las áreas que se deben incluir. La malla generada es importante para proporcionar comentarios de los usuarios durante esta fase.
  • Configuración interior de la casa u oficina: las funciones de consulta están diseñadas alrededor de superficies planas y paredes en ángulos rectos. Se trata de una limitación temporal. Sin embargo, durante la fase de examen, se completa un análisis del eje principal para optimizar la teselación de malla a lo largo del eje principal y secundario.

Proceso de examen de salas

Al cargar el módulo de comprensión espacial, lo primero que hará es examinar el espacio, por lo que todas las superficies utilizables, como el piso, el techo y las paredes, se identifican y etiquetan. Durante el proceso de digitalización, busca alrededor de la sala y "pinta" las áreas que deben incluirse en el examen.

La malla que se ve durante esta fase es una parte importante de los comentarios visuales que permite a los usuarios saber qué partes de la sala se están examinando. El archivo DLL del módulo de comprensión espacial almacena internamente el espacio de reproducción como una cuadrícula de cubos de vóxel de tamaño de 8 cm. Durante la parte inicial del examen, se completa un análisis de componentes principal para determinar los ejes de la sala. Internamente, almacena su espacio de vóxel alineado con estos ejes. Una malla se genera aproximadamente cada segundo mediante la extracción del isosurface del volumen de vóxel.

Malla de asignación espacial en blanco y comprensión de la malla de espacio de reproducción en verde

Malla de asignación espacial en blanco y comprensión de la malla de espacio de reproducción en verde

El archivo SpatialUnderstanding.cs incluido administra el proceso de fase de examen. Llama a las siguientes funciones:

  • SpatialUnderstanding_Init: se llama una vez al principio.
  • GeneratePlayspace_InitScan: indica que debe comenzar la fase de examen.
  • GeneratePlayspace_UpdateScan_DynamicScan: se llama a cada fotograma para actualizar el proceso de examen. La posición y la orientación de la cámara se pasan y se usan para el proceso de pintura del espacio de reproducción, descrito anteriormente.
  • GeneratePlayspace_RequestFinish: se llama para finalizar el espacio de reproducción. Esto usará las áreas "pintadas" durante la fase de examen para definir y bloquear el espacio de reproducción. La aplicación puede consultar estadísticas durante la fase de examen, así como consultar la malla personalizada para proporcionar comentarios del usuario.
  • Import_UnderstandingMesh: durante el examen, el comportamiento SpatialUnderstandingCustomMesh proporcionado por el módulo y colocado en el objeto prefabricado de comprensión consultará periódicamente la malla personalizada generada por el proceso. Además, esto se realiza una vez más después de finalizar el examen.

El flujo de examen, controlado por el comportamiento SpatialUnderstanding , llama a InitScan y, a continuación, UpdateScan cada fotograma. Cuando la consulta de estadísticas informa de una cobertura razonable, el usuario puede realizar la operación airtap para llamar a RequestFinish para indicar el final de la fase de examen. Se sigue llamando a UpdateScan hasta que el valor devuelto indica que el archivo DLL ha completado el procesamiento.

Las consultas

Una vez completado el examen, podrá acceder a tres tipos diferentes de consultas en la interfaz:

  • Consultas de topología: se trata de consultas rápidas basadas en la topología de la sala escaneada.
  • Consultas de formas: usan los resultados de las consultas de topología para buscar superficies horizontales que sean una buena coincidencia con las formas personalizadas que defina.
  • Consultas de selección de ubicación de objetos: son consultas más complejas que buscan la ubicación más adecuada en función de un conjunto de reglas y restricciones para el objeto.

Además de las tres consultas principales, hay una interfaz de raycasting que se puede usar para recuperar tipos de superficie etiquetados y se puede copiar una malla de sala hermética personalizada.

Consultas de topología

Dentro del archivo DLL, el administrador de topologías controla el etiquetado del entorno. Como se mencionó anteriormente, gran parte de los datos se almacenan en surfels, que se encuentran dentro de un volumen de vóxel. Además, la estructura PlaySpaceInfos se usa para almacenar información sobre el espacio de reproducción, incluida la alineación del mundo (más detalles sobre esto a continuación), el piso y el alto del techo.

La heurística se utiliza para determinar el suelo, el techo y las paredes. Por ejemplo, la superficie horizontal más grande y más baja con más de 1 m2 superficie se considera el suelo. Tenga en cuenta que la ruta de acceso de la cámara durante el proceso de examen también se usa en este proceso.

Un subconjunto de las consultas expuestas por el administrador de topologías se expone a través del archivo DLL. Las consultas de topología expuestas son las siguientes:

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

Cada una de las consultas tiene un conjunto de parámetros, específicos del tipo de consulta. En el ejemplo siguiente, el usuario especifica el alto mínimo & ancho del volumen deseado, el alto de colocación mínimo por encima del piso y la cantidad mínima de espacio delante del volumen. Todas las medidas están en metros.

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)

Cada una de estas consultas toma una matriz asignada previamente de estructuras TopologyResult . El parámetro locationCount especifica la longitud de la matriz pasada. El valor devuelto informa del número de ubicaciones devueltas. Este número nunca es mayor que el parámetro locationCount pasado.

TopologyResult contiene la posición central del volumen devuelto, la dirección orientada (es decir, normal) y las dimensiones del espacio encontrado.

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

Tenga en cuenta que en el ejemplo de Unity, cada una de estas consultas está vinculada a un botón en el panel de la interfaz de usuario virtual. El ejemplo codifica de forma rígida los parámetros de cada una de estas consultas en valores razonables. Consulte SpaceVisualizer.cs en el código de ejemplo para obtener más ejemplos.

Consultas de formas

Dentro del archivo DLL, el analizador de formas (ShapeAnalyzer_W) usa el analizador de topología para buscar coincidencias con las formas personalizadas definidas por el usuario. El ejemplo de Unity tiene un conjunto predefinido de formas que se muestran en el menú de consulta, en la pestaña forma.

Tenga en cuenta que el análisis de formas solo funciona en superficies horizontales. Un sofá, por ejemplo, está definido por la superficie del asiento plano y la parte superior plana del sofá atrás. La consulta de formas busca dos superficies de un tamaño, alto y rango de aspecto específicos, con las dos superficies alineadas y conectadas. Con la terminología de las API, el asiento del sofá y la parte superior de la parte posterior del sofá son componentes de forma y los requisitos de alineación son restricciones de componentes de forma.

Una consulta de ejemplo definida en el ejemplo de Unity (ShapeDefinition.cs), para los objetos "sittable" es la siguiente:

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

Cada consulta de formas se define mediante un conjunto de componentes de forma, cada uno con un conjunto de restricciones de componentes y un conjunto de restricciones de formas que enumera las dependencias entre los componentes. En este ejemplo se incluyen tres restricciones en una única definición de componente y ninguna restricción de forma entre componentes (ya que solo hay un componente).

En cambio, la forma del sofá tiene dos componentes de forma y cuatro restricciones de forma. Tenga en cuenta que los componentes se identifican mediante su índice en la lista de componentes del usuario (0 y 1 en este ejemplo).

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

Las funciones contenedoras se proporcionan en el módulo de Unity para facilitar la creación de definiciones de formas personalizadas. La lista completa de restricciones de componentes y formas se puede encontrar en SpatialUnderstandingDll.cs dentro de las estructuras ShapeComponentConstraint y ShapeConstraint .

El rectángulo azul resalta los resultados de la consulta de forma de silla.

El rectángulo azul resalta los resultados de la consulta de forma de silla.

Solucionador de ubicación de objetos

Las consultas de selección de ubicación de objetos se pueden usar para identificar ubicaciones ideales en la sala física para colocar los objetos. El solucionador encontrará la ubicación más adecuada según las restricciones y las reglas de objeto. Además, las consultas de objeto se conservan hasta que el objeto se quita con Solver_RemoveObject o Solver_RemoveAllObjects llamadas, lo que permite la selección restringida de ubicación de varios objetos.

Las consultas de selección de ubicación de objetos constan de tres partes: tipo de selección de ubicación con parámetros, una lista de reglas y una lista de restricciones. Para ejecutar una consulta, use la API siguiente:

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)

Esta función toma un nombre de objeto, una definición de ubicación y una lista de reglas y restricciones. Los contenedores de C# proporcionan funciones auxiliares de construcción para facilitar la construcción de reglas y restricciones. La definición de ubicación contiene el tipo de consulta , es decir, uno de los siguientes:

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

Cada uno de los tipos de selección de ubicación tiene un conjunto de parámetros únicos para el tipo. La estructura ObjectPlacementDefinition contiene un conjunto de funciones auxiliares estáticas para crear estas definiciones. Por ejemplo, para buscar un lugar para colocar un objeto en el suelo, puede usar la siguiente función:

public static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims)

Además del tipo de selección de ubicación, puede proporcionar un conjunto de reglas y restricciones. Las reglas no se pueden infringir. Las posibles ubicaciones de selección de ubicación que satisfacen el tipo y las reglas se optimizan con respecto al conjunto de restricciones para seleccionar la ubicación de ubicación óptima. Las funciones de creación estática proporcionadas pueden crear cada una de las reglas y restricciones. A continuación se proporciona una función de construcción de reglas y restricciones de ejemplo.

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

La consulta de colocación de objetos siguiente busca un lugar para colocar un cubo de medio metro en el borde de una superficie, lejos de otros objetos de posición anteriores y cerca del centro de la sala.

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

Si se ejecuta correctamente, se devuelve una estructura ObjectPlacementResult que contiene la posición de colocación, las dimensiones y la orientación. Además, la colocación se agrega a la lista interna de objetos colocados de la DLL. Las consultas de selección de ubicación posteriores tendrán en cuenta este objeto. El archivo LevelSolver.cs del ejemplo de Unity contiene más consultas de ejemplo.

Los cuadros azules muestran el resultado de tres consultas place on floor con reglas de

Los cuadros azules muestran el resultado de tres consultas place on floor con reglas de "distancia de la posición de la cámara".

Sugerencias:

  • Al resolver la ubicación de ubicación de varios objetos necesarios para un escenario de nivel o aplicación, primero resuelva objetos indispensables y grandes para maximizar la probabilidad de que se pueda encontrar un espacio.
  • El orden de selección de ubicación es importante. Si no se encuentran las ubicaciones de objetos, pruebe con menos configuraciones restringidas. Tener un conjunto de configuraciones de reserva es fundamental para admitir la funcionalidad en muchas configuraciones de sala.

Conversión de rayos

Además de las tres consultas principales, se puede usar una interfaz de conversión de rayos para recuperar tipos de superficie etiquetados y se puede copiar una malla de espacio de reproducción hermética personalizada Después de examinar y finalizar la sala, las etiquetas se generan internamente para superficies como el piso, el techo y las paredes. La función PlayspaceRaycast toma un rayo y devuelve si el rayo colisiona con una superficie conocida y, si es así, información sobre esa superficie en forma de 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;
     };

Internamente, el raycast se calcula con la representación de vóxel en cubo de 8cm calculada del espacio de reproducción. Cada vóxel contiene un conjunto de elementos de superficie con datos de topología procesados (también conocidos como surfels). Los surfeles contenidos en la celda de vóxel intersecdo se comparan y se comparan las mejores coincidencias usadas para buscar la información de topología. Estos datos de topología contienen el etiquetado devuelto en forma de la enumeración SurfaceTypes , así como el área expuesta de la superficie intersecda.

En el ejemplo de Unity, el cursor convierte un rayo cada fotograma. En primer lugar, contra los colisionadores de Unity; en segundo lugar, contra la representación mundial del módulo de comprensión; y, por último, en los elementos de la interfaz de usuario. En esta aplicación, la interfaz de usuario obtiene prioridad, el resultado de comprensión y, por último, los colisionadores de Unity. SurfaceType se notifica como texto junto al cursor.

Intersección de informes de resultados de Raycast con el piso.

Intersección de informes de resultados de Raycast con el piso.

Obtención del código

El código fuente abierto está disponible en MixedRealityToolkit. Háganoslo saber en los foros para desarrolladores de HoloLens si usa el código de un proyecto. ¡No podemos esperar a ver lo que haces con él!

Acerca del autor

Jeff Evertt, jefe de ingeniería de software en Microsoft Jeff Evertt es un jefe de ingeniería de software que ha trabajado en HoloLens desde los primeros días, desde la incubación hasta la experiencia en el desarrollo. Antes de HoloLens, trabajó en Xbox Kinect y en el sector de los juegos en una amplia variedad de plataformas y juegos. Jeff es apasionado de la robótica, los gráficos y las cosas con luces intermitentes que suenan. Disfruta aprendiendo cosas nuevas y trabajando en software, hardware, y especialmente en el espacio donde los dos se cruzan.

Consulta también