Mappage spatial dans Unity

Le mappage spatial vous permet de récupérer des maillages triangles qui représentent les surfaces du monde autour d’un appareil HoloLens. Vous pouvez utiliser des données de surface pour l’emplacement, l’occlusion et l’analyse de salle pour donner à vos projets Unity une dose supplémentaire d’immersion.

Unity inclut une prise en charge complète du mappage spatial, qui est exposé aux développeurs de la manière suivante :

  1. Composants de mappage spatial disponibles dans MixedRealityToolkit, qui fournissent un chemin pratique et rapide pour la prise en main du mappage spatial
  2. API de mappage spatial de niveau inférieur, qui fournissent un contrôle total et permettent une personnalisation plus sophistiquée de l’application

Pour utiliser le mappage spatial dans votre application, la fonctionnalité SpatialPerception doit être définie dans votre AppxManifest.

Prise en charge des appareils

Fonctionnalité HoloLens (première génération) HoloLens 2 Casques immersifs
Mappage spatial ✔️ ✔️

Définition de la fonctionnalité SpatialPerception

Pour qu’une application consomme des données de mappage spatial, la fonctionnalité SpatialPerception doit être activée.

Comment activer la fonctionnalité SpatialPerception :

  1. Dans l’Éditeur Unity, ouvrez le volet « Paramètres du lecteur » (Modifier > le lecteur des paramètres > du projet)
  2. Sélectionnez l’onglet « Windows Store »
  3. Développez « Paramètres de publication » et cochez la fonctionnalité « SpatialPerception » dans la liste « Fonctionnalités »

Remarque

Si vous avez déjà exporté votre projet Unity vers une solution Visual Studio, vous devez exporter vers un nouveau dossier ou définir manuellement cette fonctionnalité dans AppxManifest dans Visual Studio.

Le mappage spatial nécessite également un MaxVersionTested d’au moins 10.0.10586.0 :

  1. Dans Visual Studio, cliquez avec le bouton droit sur Package.appxmanifest dans le Explorateur de solutions, puis sélectionnez Afficher le code
  2. Recherchez la ligne spécifiant TargetDeviceFamily et remplacez MaxVersionTested="10.0.10240.0 » par MaxVersionTested="10.0.10586.0 »
  3. Enregistrez package.appxmanifest.

Comment ajouter un mappage dans Unity

Système de reconnaissance spatiale

Dans MRTK, consultez le guide de prise en main de la sensibilisation spatiale pour obtenir des informations sur la configuration de différents observateurs de maillage spatial.

Pour plus d’informations sur les observateurs sur l’appareil, consultez le guide de configuration des observateurs de maillage pour les appareils .

Pour plus d’informations sur les observateurs de compréhension des scènes, consultez le guide de l’observateur de compréhension de scène.

Analyse du maillage de niveau supérieur : Compréhension spatiale

Attention

La compréhension spatiale a été déconseillée en faveur de Scene Understanding.

MixedRealityToolkit est une collection de code utilitaire pour le développement holographique basé sur les API holographiques d’Unity.

Compréhension spatiale

Lorsque vous placez des hologrammes dans le monde physique, il est souvent souhaitable d’aller au-delà du maillage spatial et des plans de surface du mappage spatial. Lorsque le placement est effectué de façon procédurale, un niveau plus élevé de compréhension environnementale est souhaitable. Cela nécessite généralement de prendre des décisions sur ce qui est le plancher, le plafond et les murs. Vous avez également la possibilité d’optimiser par rapport à un ensemble de contraintes de placement pour déterminer les meilleurs emplacements physiques pour les objets holographiques.

Au cours du développement de Young Conker et Fragments, Asobo Studios a fait face à ce problème en développant un résolveur de salle. Chacun de ces jeux avait des besoins spécifiques aux jeux, mais ils partagent la technologie de compréhension spatiale principale. La bibliothèque HoloToolkit.SpatialUnderstanding encapsule cette technologie, ce qui vous permet de trouver rapidement des espaces vides sur les murs, de placer des objets sur le plafond, d’identifier placés pour le caractère à asseoir et d’une myriade d’autres requêtes de compréhension spatiale.

Tout le code source est inclus, ce qui vous permet de le personnaliser en fonction de vos besoins et de partager vos améliorations avec la communauté. Le code du solveur C++ a été encapsulé dans une dll UWP et exposé à Unity avec une goutte dans le préfab contenu dans MixedRealityToolkit.

Présentation des modules

Il existe trois interfaces principales exposées par le module : la topologie pour les requêtes spatiales et de surface simples, la forme pour la détection d’objets et le solveur de placement d’objets pour le placement basé sur la contrainte des jeux d’objets. Chacune est décrite ci-dessous. En plus des trois interfaces de module primaires, une interface de cast de rayons peut être utilisée pour récupérer des types de surface étiquetés et un maillage d’espace de jeu étanche personnalisé peut être copié.

Distribution de rayons

Une fois l’analyse de la pièce terminée, les étiquettes sont générées en interne pour les surfaces telles que le plancher, le plafond et les murs. La PlayspaceRaycast fonction prend un rayon et retourne si le rayon entre en collision avec une surface connue et, le cas échéant, des informations sur cette surface sous la forme d’un 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;
};

En interne, la raycast est calculée par rapport à la représentation voxel de 8 cm de cube calculée de l’espace de jeu. Chaque voxel contient un ensemble d’éléments de surface avec des données de topologie traitées (aka surfels). Les surfels contenus dans la cellule voxel croisée sont comparés et la meilleure correspondance utilisée pour rechercher les informations de topologie. Ces données de topologie contiennent l’étiquetage retourné sous la forme de l’énumération « SurfaceTypes », ainsi que la surface d’exposition de la surface croisée.

Dans l’exemple Unity, le curseur caste un rayon à chaque image. Tout d’abord, contre les collisionneurs d’Unity. Deuxièmement, contre la représentation mondiale du module de compréhension. Enfin, encore des éléments d’interface utilisateur. Dans cette application, l’interface utilisateur obtient la priorité, à côté du résultat de compréhension, et enfin, les collisionneurs d’Unity. SurfaceType est signalé en tant que texte en regard du curseur.

Le type surface est étiqueté en regard du curseur
Le type surface est étiqueté en regard du curseur

Requêtes de topologie

Dans la DLL, le gestionnaire de topologie gère l’étiquetage de l’environnement. Comme mentionné ci-dessus, la plupart des données sont stockées dans des surfels, contenues dans un volume voxel. En outre, la structure « PlaySpaceInfos » est utilisée pour stocker des informations sur l’espace de jeu, y compris l’alignement du monde (plus de détails sur ce point ci-dessous), le plancher et la hauteur du plafond. Les heuristiques sont utilisées pour déterminer le plancher, le plafond et les murs. Par exemple, la surface horizontale la plus grande et la plus basse avec une surface de surface supérieure à 1 m2 est considérée comme le plancher.

Remarque

Le chemin de l’appareil photo pendant le processus d’analyse est également utilisé dans ce processus.

Un sous-ensemble des requêtes exposées par le gestionnaire de topologies est exposé via la dll. Les requêtes de topologie exposées sont les suivantes.

QueryTopology_FindPositionsOnWalls
QueryTopology_FindLargePositionsOnWalls
QueryTopology_FindLargestWall
QueryTopology_FindPositionsOnFloor
QueryTopology_FindLargestPositionsOnFloor
QueryTopology_FindPositionsSittable

Chacune des requêtes a un ensemble de paramètres spécifiques au type de requête. Dans l’exemple suivant, l’utilisateur spécifie la hauteur minimale et la largeur du volume souhaité, la hauteur minimale de placement au-dessus du sol et la quantité minimale d’autorisation devant le volume. Toutes les mesures sont en mètres.

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)

Chacune de ces requêtes prend un tableau pré-alloué de structures « TopologyResult ». Le paramètre « locationCount » spécifie la longueur du tableau passé. La valeur de retour indique le nombre d’emplacements retournés. Ce nombre n’est jamais supérieur au paramètre « locationCount » passé.

La « TopologieResult » contient la position centrale du volume retourné, la direction orientée (c.-à-d. normale) et les dimensions de l’espace trouvé.

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

Remarque

Dans l’exemple Unity, chacune de ces requêtes est liée à un bouton dans le panneau d’interface utilisateur virtuelle. L’exemple code en dur les paramètres de chacune de ces requêtes à des valeurs raisonnables. Pour plus d’exemples, consultez SpaceVisualizer.cs dans l’exemple de code.

Requêtes de forme

Dans la dll, l’analyseur de formes (« ShapeAnalyzer_W ») utilise l’analyseur de topologie pour correspondre aux formes personnalisées définies par l’utilisateur. L’exemple Unity définit un ensemble de formes et expose les résultats via le menu de requête dans l’application, dans l’onglet forme. L’intention est que l’utilisateur peut définir ses propres requêtes de forme d’objet et les utiliser, selon les besoins de son application.

L’analyse des formes fonctionne uniquement sur des surfaces horizontales. Un canapé, par exemple, est défini par la surface de siège plat et le haut plat du canapé arrière. La requête de forme recherche deux surfaces d’une taille, d’une hauteur et d’une plage d’aspects spécifiques, avec les deux surfaces alignées et connectées. À l’aide de la terminologie des API, le siège de canapé et le haut arrière sont des composants de forme et les exigences d’alignement sont des contraintes de composants de forme.

Un exemple de requête défini dans l’exemple Unity (ShapeDefinition.cs), pour les objets « sittable » est le suivant.

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

Chaque requête de forme est définie par un ensemble de composants de forme, chacun avec un ensemble de contraintes de composant et un ensemble de contraintes de forme qui répertorient les dépendances entre les composants. Cet exemple inclut trois contraintes dans une définition de composant unique et aucune contrainte de forme entre les composants (car il n’existe qu’un seul composant).

En revanche, la forme de canapé a deux composants de forme et quatre contraintes de forme. Les composants sont identifiés par leur index dans la liste des composants de l’utilisateur (0 et 1 dans cet exemple).

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

Les fonctions wrapper sont fournies dans le module Unity pour faciliter la création de définitions de formes personnalisées. La liste complète des contraintes de composant et de forme se trouve dans « SpatialUnderstandingDll.cs » dans les structures « ShapeComponentConstraint » et « ShapeConstraint ».

La forme rectangle est trouvée sur cette surface
La forme rectangle est trouvée sur cette surface

Solveur de placement d’objets

Le solveur de placement d’objets peut être utilisé pour identifier les emplacements idéaux dans la salle physique pour placer vos objets. Le solveur trouvera le meilleur emplacement adapté en fonction des règles et contraintes d’objet. En outre, les requêtes d’objet persistent jusqu’à ce que l’objet soit supprimé avec des appels « Solver_RemoveObject » ou « Solver_RemoveAllObjects », ce qui permet un positionnement multi-objet limité. Les requêtes de placement d’objets se composent de trois parties : le type de placement avec des paramètres, une liste de règles et une liste de contraintes. Pour exécuter une requête, utilisez l’API suivante.

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)

Cette fonction prend un nom d’objet, une définition de placement et une liste de règles et de contraintes. Les wrappers C# fournissent des fonctions d’assistance de construction pour faciliter la construction des règles et des contraintes. La définition de placement contient le type de requête , c’est-à-dire l’un des éléments suivants.

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

Chacun des types de placement a un ensemble de paramètres propres au type. La structure « ObjectPlacementDefinition » contient un ensemble de fonctions d’assistance statiques pour la création de ces définitions. Par exemple, pour trouver un endroit où placer un objet sur le sol, vous pouvez utiliser la fonction suivante. public static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims) En plus du type de placement, vous pouvez fournir un ensemble de règles et de contraintes. Les règles ne peuvent pas être violées. Les emplacements de placement possibles qui répondent au type et aux règles sont ensuite optimisés par rapport à l’ensemble de contraintes afin de sélectionner l’emplacement de placement optimal. Chacune des règles et contraintes peut être créée par les fonctions de création statique fournies. Un exemple de fonction de construction de règle et de contrainte est fourni ci-dessous.

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 requête de placement d’objets ci-dessous recherche un endroit où placer un cube de demi-compteur sur le bord d’une surface, loin d’autres objets précédemment placés et près du centre de la salle.

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 elle réussit, une structure « ObjectPlacementResult » contenant la position de placement, les dimensions et l’orientation est retournée. En outre, le placement est ajouté à la liste interne des objets placés de la dll. Les requêtes de placement suivantes prennent en compte cet objet. Le fichier « LevelSolver.cs » dans l’exemple Unity contient d’autres exemples de requêtes.

Résultats de la sélection d’objets
Figure 3 : Boîtes bleues de la façon dont le résultat de trois endroits sur les requêtes de plancher avec des règles de position de caméra loin de la caméra

Lors de la résolution de l’emplacement de placement de plusieurs objets requis pour un scénario de niveau ou d’application, résolvez d’abord les objets indispensables et volumineux afin d’optimiser la probabilité qu’un espace soit trouvé. L’ordre de placement est important. Si les placements d’objets sont introuvables, essayez des configurations moins contraintes. L’utilisation d’un ensemble de configurations de secours est essentielle pour prendre en charge les fonctionnalités de nombreuses configurations de salle.

Processus d’analyse de salle

Bien que la solution de mappage spatial fournie par HoloLens soit conçue pour être suffisamment générique pour répondre aux besoins de l’ensemble de la gamme d’espaces de problèmes, le module de compréhension spatiale a été conçu pour prendre en charge les besoins de deux jeux spécifiques. Sa solution est structurée autour d’un processus spécifique et d’un ensemble d’hypothèses, résumé ci-dessous.

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.

Jeu d’espace de lecture piloté par l’utilisateur « peinture » : pendant la phase d’analyse, l’utilisateur se déplace et regarde autour du rythme de lecture, en peignant efficacement les zones, qui doivent être incluses. Le maillage généré est important pour fournir des commentaires utilisateur pendant cette phase. Installation d’intérieur ou de bureau : les fonctions de requête sont conçues autour de surfaces plates et de murs à angle droit. Il s’agit d’une limitation réversible. Toutefois, au cours de la phase d’analyse, une analyse de l’axe principal est effectuée pour optimiser la pavage du maillage le long de l’axe principal et secondaire. Le fichier SpatialUnderstanding.cs inclus gère le processus de phase d’analyse. Il appelle les fonctions suivantes.

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.

Le flux d’analyse, piloté par le comportement « SpatialUnderstanding » appelle InitScan, puis UpdateScan chaque image. Lorsque la requête de statistiques signale une couverture raisonnable, l’utilisateur est autorisé à s’afficher pour appeler RequestFinish pour indiquer la fin de la phase d’analyse. UpdateScan continue d’être appelé jusqu’à ce que sa valeur de retour indique que la dll a terminé le traitement.

Présentation du maillage

La dll de compréhension stocke en interne l’espace de jeu sous la forme d’une grille de 8 cm de cubes voxel de taille. Au cours de la première partie de l’analyse, une analyse principale des composants est terminée pour déterminer les axes de la salle. En interne, il stocke son espace voxel aligné sur ces axes. Un maillage est généré environ toutes les secondes en extrayant l’isosurface du volume voxel.

Maillage généré à partir du volume voxel
Maillage généré à partir du volume voxel

Dépannage

  • Vérifiez que vous avez défini la fonctionnalité SpatialPerception
  • Lorsque le suivi est perdu, l’événement OnSurfaceChanged suivant supprime tous les maillages.

Mappage spatial dans Mixed Reality Toolkit

Pour plus d’informations sur l’utilisation du mappage spatial avec Mixed Reality Toolkit, consultez la section de sensibilisation spatiale de la documentation MRTK.

Point de contrôle de développement suivant

Si vous suivez le parcours de développement Unity que nous avons mis en place, vous êtes en train d’explorer les blocs de construction principaux MRTK. À partir d’ici, vous pouvez passer au composant suivant :

Ou accéder aux API et fonctionnalités de la plateforme Mixed Reality :

Vous pouvez revenir aux points de contrôle de développement Unity à tout moment.

Voir aussi