Mapeamento espacial no Unity

O mapeamento espacial permite-lhe obter malhas triangulares que representam as superfícies do mundo em torno de um dispositivo HoloLens. Pode utilizar dados de superfície para colocação, oclusão e análise de salas para dar aos seus projetos do Unity uma dose extra de imersão.

O Unity inclui suporte total para mapeamento espacial, que é exposto aos programadores das seguintes formas:

  1. Componentes de mapeamento espacial disponíveis no MixedRealityToolkit, que fornecem um caminho conveniente e rápido para começar a utilizar o mapeamento espacial
  2. APIs de mapeamento espacial de nível inferior, que fornecem controlo total e permitem uma personalização específica da aplicação mais sofisticada

Para utilizar o mapeamento espacial na sua aplicação, a capacidade SpatialPerception tem de ser definida no appxManifest.

Suporte de dispositivos

Funcionalidade HoloLens (primeira geração) HoloLens 2 Auscultadores envolventes
Mapeamento espacial ✔️ ✔️

Definir a capacidade SpatialPerception

Para que uma aplicação consuma dados de mapeamento espacial, a capacidade SpatialPerception tem de estar ativada.

Como ativar a capacidade SpatialPerception:

  1. No Editor do Unity, abra o painel "Definições do Leitor" (Editar > Leitor de Definições > do Projeto)
  2. Selecione no separador "Loja Windows"
  3. Expanda "Definições de Publicação" e verifique a capacidade "SpatialPerception" na lista "Capacidades"

Nota

Se já exportou o seu projeto do Unity para uma solução do Visual Studio, terá de exportar para uma nova pasta ou definir manualmente esta capacidade no AppxManifest no Visual Studio.

O mapeamento espacial também requer um MaxVersionTested de, pelo menos, 10.0.10586.0:

  1. No Visual Studio, clique com o botão direito do rato em Package.appxmanifest no Explorador de Soluções e selecione Ver Código
  2. Localize a linha que especifica TargetDeviceFamily e altereMaxVersionTested="10.0.10240.0" para MaxVersionTested="10.0.10586.0"
  3. Guarde o Package.appxmanifest.

Como adicionar mapeamento no Unity

Sistema de sensibilização espacial

No MRTK, veja o guia de introdução da sensibilização espacial para obter informações sobre a criação de vários observadores de malha espacial.

Para obter informações sobre observadores no dispositivo, veja o guia Configurar observadores de malha para dispositivos .

Para obter informações sobre a compreensão de cenas de observadores, veja o Guia de observadores de compreensão de cenas .

Análise de malha de nível superior: Compreensão Espacial

Atenção

A Compreensão Espacial foi preterida a favor da Compreensão da Cena.

O MixedRealityToolkit é uma coleção de código utilitário para desenvolvimento holográfico criado com base nas APIs holográficas do Unity.

Compreensão Espacial

Ao colocar hologramas no mundo físico, muitas vezes é desejável ir além da malha do mapeamento espacial e dos planos de superfície. Quando a colocação é feita processualmente, é desejável um nível mais elevado de compreensão ambiental. Geralmente, isto requer tomar decisões sobre o que é chão, teto e paredes. Também tem a capacidade de otimizar para um conjunto de restrições de colocação para determinar as melhores localizações físicas para objetos holográficos.

Durante o desenvolvimento de Young Conker e Fragments, a Asobo Studios enfrentou este problema de frente ao desenvolver um solver de sala. Cada um destes jogos tinha necessidades específicas do jogo, mas partilhavam a tecnologia de compreensão espacial principal. A biblioteca HoloToolkit.SpatialUnderstanding encapsula esta tecnologia, permitindo-lhe encontrar rapidamente espaços vazios nas paredes, colocar objetos no teto, identificar colocados para o caráter se sentar e uma miríade de outras consultas de compreensão espacial.

Todo o código fonte está incluído, o que lhe permite personalizá-lo de acordo com as suas necessidades e partilhar as suas melhorias com a comunidade. O código para o solver C++ foi moldado numa dll UWP e exposto ao Unity com uma queda na pré-base contida no MixedRealityToolkit.

Compreender Módulos

Existem três interfaces primárias expostas pelo módulo: topologia para consultas espaciais e de superfície simples, forma para deteção de objetos e o solver de colocação de objetos para colocação baseada em restrições de conjuntos de objetos. Cada um destes é descrito abaixo. Além das três interfaces primárias do módulo, pode ser utilizada uma interface de fundição de raios para obter tipos de superfície etiquetados e uma malha de área de reprodução estanque personalizada pode ser copiada.

Ray Casting

Após a conclusão da análise da sala, as etiquetas são geradas internamente para superfícies como o chão, o tecto e as paredes. A PlayspaceRaycast função faz um raio e devolve se o raio colidir com uma superfície conhecida e, em caso afirmativo, informações sobre essa superfície na forma de um 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, o raycast é calculado em relação à representação voxel em cubo de 8 cm calculada da área de reprodução. Cada voxel contém um conjunto de elementos de superfície com dados de topologia processados (também conhecidos como surfels). As ondas contidas na célula voxel interseccionada são comparadas e a melhor correspondência utilizada para procurar as informações de topologia. Estes dados de topologia contêm a etiquetagem devolvida na forma do enum "SurfaceTypes", bem como a área da superfície da superfície interseccionada.

No exemplo unity, o cursor lança um raio em cada moldura. Primeiro, contra os colisores do Unity. Em segundo lugar, contra a representação mundial do módulo understanding. E, por fim, novamente elementos da IU. Nesta aplicação, a IU obtém prioridade, a seguir o resultado de compreensão e, por último, os colisões do Unity. O SurfaceType é reportado como texto junto ao cursor.

O tipo de Surface está etiquetado junto ao cursor
O tipo de Surface está etiquetado junto ao cursor

Consultas de Topologia

Na DLL, o gestor de topologia processa a etiquetagem do ambiente. Conforme mencionado acima, grande parte dos dados são armazenados em surfels, contidos num volume voxel. Além disso, a estrutura "PlaySpaceInfos" é utilizada para armazenar informações sobre a área de jogos, incluindo o alinhamento mundial (mais detalhes sobre este aspeto abaixo), o piso e a altura do teto. A heurística é utilizada para determinar o chão, o tecto e as paredes. Por exemplo, a superfície horizontal maior e mais baixa com uma área de superfície superior a 1 m2 é considerada o piso.

Nota

O caminho da câmara durante o processo de análise também é utilizado neste processo.

Um subconjunto das consultas expostas pelo gestor de Topologia é exposto através do dll. As consultas de topologia expostas são as seguintes.

QueryTopology_FindPositionsOnWalls
QueryTopology_FindLargePositionsOnWalls
QueryTopology_FindLargestWall
QueryTopology_FindPositionsOnFloor
QueryTopology_FindLargestPositionsOnFloor
QueryTopology_FindPositionsSittable

Cada uma das consultas tem um conjunto de parâmetros, específico do tipo de consulta. No exemplo seguinte, o utilizador especifica a altura mínima & largura do volume pretendido, a altura mínima de colocação acima do piso e a quantidade mínima de desalfandegamento à frente do volume. Todas as medições estão em 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 uma destas consultas utiliza uma matriz pré-alocada de estruturas "TopologyResult". O parâmetro "locationCount" especifica o comprimento da matriz transmitida. O valor devolvido comunica o número de localizações devolvidas. Este número nunca é maior do que o transmitido no parâmetro "locationCount".

A "TopologyResult" contém a posição central do volume devolvido, a direção voltada (ou seja, normal) e as dimensões do espaço encontrado.

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

Nota

No exemplo do Unity, cada uma destas consultas está ligada a um botão no painel de IU virtual. O exemplo codifica os parâmetros de cada uma destas consultas para valores razoáveis. Veja SpaceVisualizer.cs no código de exemplo para obter mais exemplos.

Consultas de Forma

No dll, o analisador de formas ("ShapeAnalyzer_W") utiliza o analisador de topologia para corresponder às formas personalizadas definidas pelo utilizador. O exemplo do Unity define um conjunto de formas e expõe os resultados através do menu de consulta na aplicação, no separador forma. A intenção é que o utilizador possa definir as suas próprias consultas de forma de objeto e utilizá-las, conforme necessário pela sua aplicação.

A análise da forma funciona apenas em superfícies horizontais. Um sofá, por exemplo, é definido pela superfície plana do assento e pela parte superior plana do sofá para trás. A consulta da forma procura duas superfícies de tamanho, altura e intervalo de aspetos específicos, com as duas superfícies alinhadas e ligadas. Com a terminologia das APIs, o assento do sofá e a parte superior traseira são componentes da forma e os requisitos de alinhamento são restrições de componentes de forma.

Uma consulta de exemplo definida no exemplo do Unity (ShapeDefinition.cs), para objetos "sittable" é a seguinte.

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 forma é definida por um conjunto de componentes de forma, cada um com um conjunto de restrições de componentes e um conjunto de restrições de formas que listam dependências entre os componentes. Este exemplo inclui três restrições numa única definição de componente e nenhuma restrição de forma entre componentes (uma vez que existe apenas um componente).

Em contraste, a forma do sofá tem dois componentes de forma e quatro restrições de forma. Os componentes são identificados pelo respetivo índice na lista de componentes do utilizador (0 e 1 neste exemplo).

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

As funções wrapper são fornecidas no módulo unity para criar facilmente definições de formas personalizadas. A lista completa de restrições de componentes e formas pode ser encontrada em "SpatialUnderstandingDll.cs" nas estruturas "ShapeComponentConstraint" e "ShapeConstraint".

A forma do retângulo encontra-se nesta superfície
A forma do retângulo encontra-se nesta superfície

Solver de Colocação de Objetos

O solver de colocação de objetos pode ser utilizado para identificar as localizações ideais na sala física para colocar os objetos. O solver encontrará a melhor localização adequada, dadas as regras e restrições do objeto. Além disso, as consultas de objetos persistem até que o objeto seja removido com chamadas "Solver_RemoveObject" ou "Solver_RemoveAllObjects", permitindo a colocação restrita de vários objetos. As consultas de colocação de objetos consistem em três partes: tipo de colocação com parâmetros, uma lista de regras e uma lista de restrições. Para executar uma consulta, utilize a seguinte 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)

Esta função utiliza um nome de objeto, uma definição de colocação e uma lista de regras e restrições. Os wrappers C# fornecem funções auxiliares de construção para facilitar a construção de regras e restrições. A definição de colocação contém o tipo de consulta , ou seja, um dos seguintes.

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

Cada um dos tipos de colocação tem um conjunto de parâmetros exclusivos do tipo. A estrutura "ObjectPlacementDefinition" contém um conjunto de funções auxiliares estáticas para criar estas definições. Por exemplo, para encontrar um local para colocar um objeto no chão, pode utilizar a seguinte função. objectPlacementDefinition público estático Create_OnFloor(Vector3 halfDims) Além do tipo de colocação, pode fornecer um conjunto de regras e restrições. As regras não podem ser violadas. As possíveis localizações de colocação que satisfaçam o tipo e as regras são depois otimizadas para o conjunto de restrições para selecionar a localização de colocação ideal. Cada uma das regras e restrições pode ser criada pelas funções de criação estática fornecidas. Abaixo, é fornecida uma função de construção de regras e restrições de exemplo.

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

A consulta de colocação de objetos abaixo está à procura de um local para colocar um cubo de meio metro na borda de uma superfície, longe de outros objetos anteriormente colocados e perto do centro da 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());

Se for bem-sucedida, é devolvida uma estrutura "ObjectPlacementResult" que contém a posição de colocação, as dimensões e a orientação. Além disso, a colocação é adicionada à lista interna de objetos colocados da dll. As consultas de colocação subsequentes terão este objeto em conta. O ficheiro "LevelSolver.cs" no exemplo do Unity contém mais consultas de exemplo.

Resultados da colocação de objetos
Figura 3: As caixas azuis como o resultado de três casas em consultas no chão com regras de posição da câmara afastadas

Ao resolver a localização de colocação de múltiplos objetos necessários para um cenário de nível ou aplicação, resolva primeiro objetos indispensáveis e grandes para maximizar a probabilidade de um espaço ser encontrado. A ordem de colocação é importante. Se não for possível localizar colocações de objetos, experimente configurações menos restritas. Ter um conjunto de configurações de contingência é fundamental para suportar funcionalidades em muitas configurações de sala.

Processo de Análise de Salas

Embora a solução de mapeamento espacial fornecida pelo HoloLens tenha sido concebida para ser genérica o suficiente para satisfazer as necessidades de toda a gama de espaços problemáticos, o módulo de compreensão espacial foi criado para suportar as necessidades de dois jogos específicos. A sua solução está estruturada em torno de um processo específico e de um conjunto de pressupostos, resumidos abaixo.

Fixed size playspace – The user specifies the maximum playspace size in the init call.

One-time scan process –
    The process requires a discrete scanning phase where the user walks around,
    defining the playspace.
    Query functions will not function until after the scan has been finalized.

"pintura" da área de reprodução orientada pelo utilizador – durante a fase de análise, o utilizador move-se e olha em torno do ritmo das peças, pintando efetivamente as áreas, que devem ser incluídas. A malha gerada é importante para fornecer feedback dos utilizadores durante esta fase. Configuração de casa ou escritório interior – as funções de consulta são concebidas em torno de superfícies planas e paredes em ângulos rectos. Esta é uma limitação suave. No entanto, durante a fase de análise, é concluída uma análise do eixo primário para otimizar a tessellação de malha ao longo do eixo principal e secundário. O ficheiro spatialUnderstanding.cs incluído gere o processo de fase de análise. Chama as seguintes funções.

SpatialUnderstanding_Init – Called once at the start.

GeneratePlayspace_InitScan – Indicates that the scan phase should begin.

GeneratePlayspace_UpdateScan_DynamicScan –
    Called each frame to update the scanning process. The camera position and
    orientation is passed in and is used for the playspace painting process,
    described above.

GeneratePlayspace_RequestFinish –
    Called to finalize the playspace. This will use the areas “painted” during
    the scan phase to define and lock the playspace. The application can query
    statistics during the scanning phase as well as query the custom mesh for
    providing user feedback.

Import_UnderstandingMesh –
    During scanning, the “SpatialUnderstandingCustomMesh” behavior provided by
    the module and placed on the understanding prefab will periodically query the
    custom mesh generated by the process. In addition, this is done once more
    after scanning has been finalized.

O fluxo de análise, impulsionado pelo comportamento "SpatialUnderstanding" chama InitScan e, em seguida, UpdateScan cada frame. Quando a consulta de estatísticas comunica uma cobertura razoável, o utilizador tem permissão para executar o airtap para chamar RequestFinish para indicar o fim da fase de análise. UpdateScan continua a ser chamado até que o respetivo valor devolvido indique que o dll concluiu o processamento.

Compreender o Mesh

A compreensão dll armazena internamente a área de reprodução como uma grelha de cubos voxel de tamanho de 8 cm. Durante a parte inicial da análise, é concluída uma análise do componente principal para determinar os eixos da sala. Internamente, armazena o seu espaço voxel alinhado com estes eixos. Uma malha é gerada aproximadamente a cada segundo ao extrair o isosurface do volume voxel.

Malha gerada produzida a partir do volume voxel
Malha gerada produzida a partir do volume voxel

Resolução de problemas

  • Certifique-se de que definiu a capacidade SpatialPerception
  • Quando o controlo for perdido, o próximo evento OnSurfaceChanged removerá todas as malhas.

Mapeamento Espacial no Mixed Reality Toolkit

Para obter mais informações sobre como utilizar o Mapeamento Espacial com Mixed Reality Toolkit, veja a secção de deteção espacial dos documentos do MRTK.

Próximo Ponto de Verificação de Desenvolvimento

Se estás a seguir a jornada de desenvolvimento do Unity que demos, estás no meio de explorar os blocos modulares principais do MRTK. A partir daqui, pode continuar para o bloco modular seguinte:

Em alternativa, avance para Mixed Reality capacidades e APIs da plataforma:

Pode sempre voltar aos pontos de verificação de desenvolvimento do Unity em qualquer altura.

Ver também