Mapeamento espacial em Unity
O mapeamento espacial permite recuperar malhas triangulares que representam as superfícies do mundo em torno 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 Unity uma dose extra de imersão.
O Unity inclui suporte total para mapeamento espacial, que é exposto aos desenvolvedores das seguintes maneiras:
- Componentes de mapeamento espacial disponíveis no MixedRealityToolkit, que fornecem um caminho conveniente e rápido para começar a usar o mapeamento espacial
- APIs de mapeamento espacial de nível inferior, que fornecem controle total e permitem personalização mais sofisticada específica do aplicativo
Para usar o mapeamento espacial em seu aplicativo, o recurso SpatialPerception precisa ser definido em seu AppxManifest.
Suporte de dispositivos
Caraterística | HoloLens (primeira geração) | HoloLens 2 | Auriculares imersivos |
---|---|---|---|
Mapeamento espacial | ✔️ | ✔️ | ❌ |
Definindo o recurso SpatialPerception
Para que um aplicativo consuma dados de mapeamento espacial, o recurso SpatialPerception deve ser habilitado.
Como ativar a capacidade SpatialPerception:
- No Editor Unity, abra o painel "Configurações do Player" (Editar > Player de Configurações > do Projeto)
- Selecione no separador "Loja Windows"
- Expanda "Configurações de publicação" e verifique o recurso "SpatialPerception" na lista "Recursos"
Nota
Se você já exportou seu projeto Unity para uma solução do Visual Studio, precisará exportar para uma nova pasta ou definir manualmente esse recurso no AppxManifest no Visual Studio.
O mapeamento espacial também requer um MaxVersionTested de pelo menos 10.0.10586.0:
- No Visual Studio, clique com o botão direito do mouse em Package.appxmanifest no Gerenciador de Soluções e selecione Exibir Código
- Encontre a linha que especifica TargetDeviceFamily e altere MaxVersionTested="10.0.10240.0" para MaxVersionTested="10.0.10586.0"
- Salve o Package.appxmanifest.
Como adicionar mapeamento no Unity
Sistema de consciência espacial
No MRTK, veja o Guia de introdução à consciência espacial para obter informações sobre a configuração de vários observadores de malha espacial.
Para obter informações sobre observadores no dispositivo, consulte o guia Configurando observadores de malha para dispositivo .
Para obter informações sobre observadores que compreendem a cena, consulte o Guia do observador de compreensão de cena.
Análise de malha de nível superior: compreensão espacial
Atenção
A Compreensão Espacial foi preterida em favor da Compreensão de Cena.
O MixedRealityToolkit é uma coleção de código utilitário para desenvolvimento holográfico construído 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. Isso geralmente requer a tomada de decisões sobre o que é piso, teto e paredes. Você também tem a capacidade de otimizar contra um conjunto de restrições de posicionamento para determinar os melhores locais físicos para objetos holográficos.
Durante o desenvolvimento de Young Conker e Fragments, a Asobo Studios enfrentou esse problema de frente desenvolvendo um solucionador de salas. Cada um desses jogos tinha necessidades específicas do jogo, mas eles compartilhavam a tecnologia central de compreensão espacial. A biblioteca HoloToolkit.SpatialUnderstanding encapsula essa tecnologia, permitindo que você encontre rapidamente espaços vazios nas paredes, coloque objetos no teto, identifique o local para o personagem se sentar e uma infinidade de outras consultas de compreensão espacial.
Todo o código-fonte está incluído, permitindo que você o personalize de acordo com 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 gota no pré-fabricado contido no MixedRealityToolkit.
Noções básicas sobre módulos
Há três interfaces principais expostas pelo módulo: topologia para consultas simples de superfície e espaço, forma para deteção de objetos e o solucionador de posicionamento de objetos para posicionamento baseado em restrições de conjuntos de objetos. Cada um deles é descrito abaixo. Além das três interfaces de módulo primário, uma interface de fundição de raios pode ser usada para recuperar tipos de superfície marcados e uma malha de espaço de jogo impermeável personalizada pode ser copiada.
Fundição de Raios
Após a conclusão da varredura da sala, as etiquetas são geradas internamente para superfícies como piso, teto e paredes. A PlayspaceRaycast
função pega um raio e retorna 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 contra a representação computada de 8 cm de voxel cúbico do espaço de jogo. 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 intersectada são comparados e a melhor correspondência usada para procurar as informações de topologia. Esses dados de topologia contêm a rotulagem retornada na forma do enum "SurfaceTypes", bem como a área de superfície da superfície intersectada.
No exemplo Unity, o cursor projeta um raio em cada quadro. Primeiro, contra os colisores de Unity. Em segundo lugar, contra a representação do mundo do módulo de compreensão. E, finalmente, novamente elementos da interface do usuário. Neste aplicativo, a interface do usuário obtém prioridade, em seguida o resultado de compreensão e, por último, os colisores do Unity. O SurfaceType é reportado como texto junto ao cursor.
O tipo de superfície é rotulado ao lado do cursor
Consultas de topologia
Dentro da DLL, o gerenciador de topologia lida com a rotulagem do ambiente. Como mencionado acima, grande parte dos dados é armazenada dentro de surfels, contidos dentro de um volume de voxel. Além disso, a estrutura "PlaySpaceInfos" é usada para armazenar informações sobre o espaço de jogo, incluindo o alinhamento do mundo (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 mais de 1 m2 de área de superfície é considerada o piso.
Nota
O caminho da câmera durante o processo de digitalização também é usado neste processo.
Um subconjunto das consultas expostas pelo gerenciador de topologia são expostas 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 altura mínima e a largura do volume desejado, a altura mínima de colocação acima do piso e a quantidade mínima de folga na frente do volume. Todas as medidas sã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 de retorno informa 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 oposta (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 Unity, cada uma dessas consultas é vinculada a um botão no painel da interface do usuário virtual. O exemplo codifica os parâmetros para 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 formas ("ShapeAnalyzer_W") usa o analisador de topologia para corresponder às formas personalizadas definidas pelo usuário. O exemplo 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 para seu aplicativo.
A análise de formas funciona apenas em superfícies horizontais. Um sofá, por exemplo, é definido pela superfície plana do assento e pela parte superior plana do encosto do sofá. A consulta de forma procura duas superfícies de um tamanho, altura e intervalo de aspetos específicos, com as duas superfícies alinhadas e ligadas. Usando a terminologia APIs, o assento do sofá e a parte traseira são componentes de forma e os requisitos de alinhamento são restrições de componentes de forma.
Um exemplo de consulta definido no exemplo Unity (ShapeDefinition.cs), para objetos "sittable" é o 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).
Em contraste, a forma do sofá tem dois componentes de forma e quatro restrições de forma. Os componentes são identificados por seu í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 de 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 componentes e formas pode ser encontrada em "SpatialUnderstandingDll.cs" dentro das estruturas "ShapeComponentConstraint" e "ShapeConstraint".
A forma do retângulo é encontrada nesta superfície
Solucionador de posicionamento de objetos
O solucionador de posicionamento de objetos pode ser usado para identificar locais ideais na sala física para colocar seus objetos. O solucionador encontrará o melhor local de ajuste dadas 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 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 usa um nome de objeto, 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 seguinte função. estático público ObjectPlacementDefinition Create_OnFloor(Vetor3 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 colocação que satisfaçam o tipo e as regras são entã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áticas fornecidas. Um exemplo de regra e função de construção de restrição é fornecido 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 de objeto abaixo está procurando um lugar para colocar um cubo de meio metro na borda de uma superfície, longe de outros objetos previamente 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, uma estrutura "ObjectPlacementResult" contendo a posição, as dimensões e a orientação do posicionamento será retornada. Além disso, o posicionamento é adicionado à lista interna de objetos inseridos da dll. As consultas de posicionamento subsequentes levarão esse objeto em consideração. O arquivo "LevelSolver.cs" no exemplo Unity contém mais consultas de exemplo.
Figura 3: As caixas azuis como o resultado de três consultas de lugar no chão com regras de posição longe da câmera
Ao resolver a localização de posicionamento de vários objetos necessários para um cenário de nível ou aplicação, primeiro resolva objetos indispensáveis e grandes, a fim de maximizar a probabilidade de que um espaço possa ser encontrado. A ordem de colocação é importante. Se não for possível encontrar posicionamentos de objetos, tente configurações menos restritas. Ter um conjunto de configurações de fallback é fundamental para dar suporte à funcionalidade em muitas configurações de sala.
Processo de digitalização da sala
Enquanto a solução de mapeamento espacial fornecida pelo HoloLens é 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 construído 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 a seguir.
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" do espaço de jogo orientado pelo usuário – Durante a fase de digitalização, o usuário se move e olha ao redor do ritmo de jogo, efetivamente pintando as áreas, que devem ser incluídas. A malha gerada é importante para fornecer feedback do usuário durante esta fase. Configuração interna em casa ou no escritório – As funções de consulta são projetadas em torno de superfícies planas e paredes em ângulos retos. Esta é uma limitação suave. No entanto, durante a fase de digitalização, uma análise do eixo primário é concluída para otimizar a tesselação da malha ao longo dos eixos principal e menor. O arquivo SpatialUnderstanding.cs incluído gerencia o processo de fase de digitalização. Ele 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 varredura, impulsionado pelo comportamento "SpatialUnderstanding" chama InitScan e, em seguida, UpdateScan cada quadro. Quando a consulta de estatísticas relata uma cobertura razoável, o usuário tem permissão para tocar no ar para chamar RequestFinish para indicar o final da fase de digitalização. UpdateScan continua a ser chamado até que seu valor de retorno indique que a dll concluiu o processamento.
Noções básicas sobre malha
A dll de compreensão armazena internamente o espaço de reprodução como uma grade de cubos voxel de 8 cm de tamanho. Durante a parte inicial da digitalização, uma análise de componentes primários é 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 isosuperfície do volume de voxel.
Malha gerada produzida a partir do volume de voxel
Resolução de Problemas
- Certifique-se de ter definido o recurso SpatialPerception
- Quando o rastreamento for perdido, o próximo evento OnSurfaceChanged removerá todas as malhas.
Mapeamento espacial no kit de ferramentas de realidade mista
Para obter mais informações sobre como usar o mapeamento espacial com o Mixed Reality Toolkit, consulte a seção de reconhecimento espacial dos documentos MRTK.
Próximo ponto de verificação de desenvolvimento
Se você está seguindo a jornada de desenvolvimento Unity que estabelecemos, você está no meio da exploração dos blocos de construção principais do MRTK. A partir daqui, você pode continuar para o próximo bloco de construção:
Ou pule para os recursos e APIs da plataforma de Realidade Mista:
Você sempre pode voltar aos pontos de verificação de desenvolvimento Unity a qualquer momento.