Mapeamento espacial no Unity

O mapeamento espacial permite recuperar malhas de triângulo que representam as superfícies no mundo ao redor de um dispositivo HoloLens. Você pode usar dados de superfície para posicionamento, oclusão e análise de sala para dar aos seus projetos do Unity uma dose extra de imersão.

O Unity inclui suporte completo para mapeamento espacial, que é exposto aos desenvolvedores das seguintes maneiras:

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

Para usar o mapeamento espacial em seu aplicativo, a funcionalidade SpatialPerception precisa ser definida em seu AppxManifest.

Suporte a dispositivos

Recurso HoloLens (primeira geração) HoloLens 2 Headsets imersivos
mapeamento espacial ✔️ ✔️

Definindo a funcionalidade SpatialPerception

Para que um aplicativo consuma dados de mapeamento espacial, a funcionalidade SpatialPerception deve ser habilitada.

Como habilitar a funcionalidade SpatialPerception:

  1. No Editor do Unity, abra o painel "Player Configurações" (Editar > Project Configurações > Player)
  2. Selecione na guia "Windows Store"
  3. Expanda "Publicando Configurações" e verifique a funcionalidade "SpatialPerception" na lista "Funcionalidades"

Observação

Se você já exportou seu projeto do Unity para uma solução Visual Studio, será necessário exportar para uma nova pasta ou definir manualmente essa funcionalidade no AppxManifest em Visual Studio.

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

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

Como adicionar mapeamento no Unity

Sistema de conscientização espacial

No MRTK, veja o guia de introdução à conscientização espacial para obter informações sobre como configurar vários observadores de malha espacial.

Para obter informações sobre observadores no dispositivo, examine os observadores de malha configurando para o guia do dispositivo.

Para obter informações sobre observadores de compreensão de cena, veja o guia de observador de compreensão de cena .

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

Cuidado

A Compreensão Espacial foi preterida em favor do Reconhecimento de Cena.

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

Compreensão espacial

Ao colocar hologramas no mundo físico, muitas vezes é desejável ir além dos planos de malha e superfície do mapeamento espacial. Quando o posicionamento é feito de forma processual, um nível mais alto de compreensão ambiental é desejável. Isso geralmente requer tomar decisões sobre o que é piso, teto e paredes. Você também tem a capacidade de otimizar em relação a um conjunto de restrições de posicionamento para determinar os melhores locais físicos para objetos holográficos.

Durante o desenvolvimento do Young Conker and Fragments, o Asobo Studios enfrentou esse problema de frente desenvolvendo um solucionador de sala. Cada um desses jogos tinha necessidades específicas do jogo, mas eles compartilhavam a tecnologia de compreensão espacial principal. A biblioteca HoloToolkit.SpatialUnderstanding encapsula essa tecnologia, permitindo que você encontre rapidamente espaços vazios nas paredes, coloque objetos no teto, identifique colocados para o caractere sentar e uma miríade de outras consultas de compreensão espacial.

Todo o código-fonte está incluído, permitindo que você o personalize para suas necessidades e compartilhe suas melhorias com a comunidade. O código para o solucionador C++ foi encapsulado em uma dll UWP e exposto ao Unity com uma queda no pré-fabricado contido no MixedRealityToolkit.

Noções básicas sobre módulos

Há três interfaces primárias expostas pelo módulo: topologia para consultas espaciais e de superfície simples, forma para detecção de objetos e o solucionador de posicionamento de objeto para posicionamento baseado em restrições de conjuntos de objetos. Veja a descrição das duas maneiras abaixo. Além das três interfaces do módulo primário, uma interface de conversão de raios pode ser usada para recuperar tipos de superfície marcados e uma malha de playspace personalizada com luz d'água pode ser copiada.

Ray Casting

Depois que a verificação da sala for concluída, os rótulos serão gerados internamente para superfícies como piso, teto e paredes. A PlayspaceRaycast função usa um raio e retorna se o raio colidir com uma superfície conhecida e, em caso afirmativamente, 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 computada de voxel em cubo de 8 cm do playspace. Cada voxel contém um conjunto de elementos de superfície com dados de topologia processados (também conhecidos como surfels). Os surfels contidos na célula voxel interseccionada são comparados e a melhor correspondência usada para pesquisar as informações de topologia. Esses dados de topologia contêm a rotulagem retornada na forma da enumeração "SurfaceTypes", bem como a área de superfície da superfície interseccionada.

No exemplo do Unity, o cursor converte um raio em cada quadro. Primeiro, contra os colisores do Unity. Em segundo lugar, em relação à representação mundial do módulo de compreensão. E, por fim, novamente elementos da interface do usuário. Neste aplicativo, a interface do usuário obtém prioridade, o resultado da compreensão e, por fim, os colisores do Unity. O SurfaceType é relatado como texto ao lado do cursor.

Surface type is labeled next to the cursor
O tipo de superfície é rotulado ao lado do cursor

Consultas de topologia

Dentro da DLL, o gerenciador de topologia manipula a rotulagem do ambiente. Conforme mencionado acima, grande parte dos dados é armazenada em surfels, contidos em um volume voxel. Além disso, a estrutura "PlaySpaceInfos" é usada para armazenar informações sobre o playspace, incluindo o alinhamento mundial (mais detalhes sobre isso abaixo), piso e altura do teto. A heurística é usada para determinar piso, teto e paredes. Por exemplo, a maior e menor superfície horizontal com área de superfície maior que 1 m2 é considerada o piso.

Observação

O caminho da câmera durante o processo de verificação também é usado nesse processo.

Um subconjunto das consultas expostas pelo gerenciador de Topologias é exposto por meio da 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íficos para o tipo de consulta. No exemplo a seguir, o usuário especifica a largura mínima de altura & do volume desejado, a altura mínima de posicionamento acima do piso e a quantidade mínima de liberação na frente do volume. Todas as medidas 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 dessas consultas usa uma matriz pré-alocada de estruturas "TopologyResult". O parâmetro "locationCount" especifica o comprimento da matriz passada. O valor retornado relata o número de locais retornados. Esse número nunca é maior do que o passado no parâmetro "locationCount".

O "TopologyResult" contém a posição central do volume retornado, 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;
};

Observação

No exemplo do Unity, cada uma dessas consultas é vinculada a um botão no painel de interface do usuário virtual. O exemplo codifica os parâmetros de cada uma dessas consultas para valores razoáveis. Consulte SpaceVisualizer.cs no código de exemplo para obter mais exemplos.

Consultas de forma

Na dll, o analisador de forma ("ShapeAnalyzer_W") usa o analisador de topologia para corresponder às formas personalizadas definidas pelo usuário. O exemplo do Unity define um conjunto de formas e expõe os resultados por meio do menu de consulta no aplicativo, dentro da guia forma. A intenção é que o usuário possa definir suas próprias consultas de forma de objeto e fazer uso delas, conforme necessário por seu aplicativo.

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

Uma consulta de exemplo definida no exemplo do Unity (ShapeDefinition.cs) para objetos "ittable" é 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 componente e um conjunto de restrições de forma que listam dependências entre os componentes. Este exemplo inclui três restrições em uma única definição de componente e nenhuma restrição de forma entre componentes (pois há apenas um componente).

Por outro lado, a forma do sofá tem dois componentes de forma e quatro restrições de forma. Os componentes são identificados pelo índice na lista de componentes do usuário (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 facilitar a criação de definições de forma personalizadas. A lista completa de restrições de componente e forma pode ser encontrada em "SpatialUnderstandingDll.cs" dentro das estruturas "ShapeComponentConstraint" e "ShapeConstraint".

Rectangle shape is found on this surface
A forma do retângulo é encontrada nesta superfície

Solucionador de posicionamento de objeto

O solucionador de posicionamento de objeto pode ser usado para identificar locais ideais na sala física para colocar seus objetos. O solucionador encontrará o melhor local adequado, considerando as regras e restrições do objeto. Além disso, as consultas de objeto persistem até que o objeto seja removido com chamadas "Solver_RemoveObject" ou "Solver_RemoveAllObjects", permitindo o posicionamento restrito de vários objetos. As consultas de posicionamento de objetos consistem em três partes: tipo de posicionamento com parâmetros, uma lista de regras e uma lista de restrições. Para executar uma consulta, use a API a seguir.

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)

Essa função usa um nome de objeto, uma definição de posicionamento 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 posicionamento 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 posicionamento tem um conjunto de parâmetros exclusivos para o tipo. A estrutura "ObjectPlacementDefinition" contém um conjunto de funções auxiliares estáticas para criar essas definições. Por exemplo, para encontrar um lugar para colocar um objeto no chão, você pode usar a função a seguir. public static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims) Além do tipo de posicionamento, você pode fornecer um conjunto de regras e restrições. As regras não podem ser violadas. Os possíveis locais de posicionamento que atendem ao tipo e às regras são otimizados em relação ao conjunto de restrições para selecionar o local de posicionamento ideal. Cada uma das regras e restrições pode ser criada pelas funções de criação estática fornecidas. Uma regra de exemplo e uma função de construção de restrição são fornecidas abaixo.

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 posicionamento do objeto abaixo está procurando um lugar para colocar um cubo de meio medidor 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 tiver êxito, uma estrutura "ObjectPlacementResult" que contém a posição de posicionamento, dimensões e orientação será retornada. Além disso, o posicionamento é adicionado à lista interna de objetos colocados da dll. Consultas de posicionamento subsequentes levarão esse objeto em conta. O arquivo "LevelSolver.cs" no exemplo do Unity contém mais consultas de exemplo.

Results of object placement
Figura 3: As caixas azuis como o resultado de três lugares em consultas no chão com as regras de posição da câmera ausentes

Ao resolver o local de posicionamento de vários objetos necessários para um cenário de nível ou aplicativo, primeiro resolva objetos indispensáveis e grandes para maximizar a probabilidade de que um espaço possa ser encontrado. A ordem de posicionamento é importante. Se não for possível encontrar posicionamentos de objeto, tente configurações menos restritas. Ter um conjunto de configurações de fallback é fundamental para dar suporte à funcionalidade em várias configurações de sala.

Processo de verificação de sala

Embora a solução de mapeamento espacial fornecida pelo HoloLens seja projetada para ser genérica o suficiente para atender às necessidades de toda a gama de espaços problemáticos, o módulo de compreensão espacial foi criado para dar suporte às necessidades de dois jogos específicos. Sua solução é estruturada em torno de um processo específico e conjunto de suposições, 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.

Playspace controlado pelo usuário "pintura" – Durante a fase de verificação, o usuário move e olha em torno do ritmo de reprodução, pintando efetivamente as áreas, que devem ser incluídas. A malha gerada é importante para fornecer comentários do usuário durante essa fase. Instalação interna da casa ou do escritório – as funções de consulta são projetadas em torno de superfícies planas e paredes em ângulos retos. Essa é uma limitação suave. No entanto, durante a fase de verificação, uma análise do eixo primário é concluída para otimizar o mosaico de malha ao longo do eixo principal e secundário. O arquivo SpatialUnderstanding.cs incluído gerencia o processo de fase de verificação. Ele chama as funções a seguir.

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 verificação, controlado pelo comportamento "SpatialUnderstanding", chama InitScan e atualiza cada quadro. Quando a consulta de estatísticas relata uma cobertura razoável, o usuário tem permissão para fazer airtap para chamar RequestFinish para indicar o fim da fase de verificação. UpdateScan continua a ser chamado até que seu valor retornado indique que a dll concluiu o processamento.

Noções básicas sobre Mesh

A dll de compreensão armazena internamente o playspace como uma grade de cubos voxel de tamanho de 8 cm. Durante a parte inicial da verificação, uma análise de componente primária é concluída para determinar os eixos da sala. Internamente, armazena seu espaço voxel alinhado a esses eixos. Uma malha é gerada aproximadamente a cada segundo extraindo a isosurface do volume voxel.

Generated mesh produced from the voxel volume
Malha gerada produzida a partir do volume voxel

Solução de problemas

  • Verifique se você definiu a funcionalidade SpatialPerception
  • Quando o rastreamento for perdido, o próximo evento OnSurfaceChanged removerá todas as malhas.

Mapeamento espacial em Realidade Misturada Toolkit

Para obter mais informações sobre como usar o Mapeamento Espacial com Realidade Misturada Toolkit, consulte a seção de reconhecimento espacial dos documentos do MRTK.

Próximo ponto de verificação de desenvolvimento

Se você estiver seguindo a jornada de desenvolvimento do Unity que estabelecemos, você está no meio da exploração dos blocos de construção principais do MRTK. Deste ponto, você pode prosseguir para o próximo bloco de construção:

Ou vá diretamente para as funcionalidades e APIs da plataforma de Realidade Misturada:

Você sempre pode voltar para os pontos de verificação de desenvolvimento do Unity a qualquer momento.

Confira também