Mappage spatial dans Unity

Le mappage spatial vous permet de récupérer des maillages de triangles qui représentent les surfaces dans le monde autour d’un appareil HoloLens. Vous pouvez utiliser les données de surface pour le placement, l’occlusion et l’analyse de la 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 des manières suivantes :

  1. Composants de mappage spatial disponibles dans MixedRealityToolkit, qui fournissent un chemin pratique et rapide pour bien démarrer avec le mappage spatial
  2. API de mappage spatial de niveau inférieur, qui fournissent un contrôle total et permettent une personnalisation plus sophistiquée spécifique à 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 case activée la fonctionnalité « SpatialPerception » dans la liste « Capabilities »

Notes

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 reconnaissance spatiale pour plus d’informations sur la configuration de différents observateurs de maillage spatial.

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

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

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

Attention

Spatial Understanding a été déprécié 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 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 qu’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.

Pendant le développement de Young Conker et Fragments, Asobo Studios a fait face à ce problème de tête en développant un résolveur de salle. Chacun de ces jeux avait des besoins spécifiques au jeu, mais ils partageaient la technologie de compréhension spatiale de base. 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 au plafond, d’identifier les éléments placés pour que le caractère soit assis 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 suppression du préfabriqué 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 résolveur de placement d’objets pour le placement basé sur les contraintes des jeux d’objets. Chacun de ces éléments est décrit ci-dessous. En plus des trois interfaces de module principales, une interface de coulage de rayons peut être utilisée pour récupérer des types de surface marqués et un maillage d’espace de jeu étanche personnalisé peut être copié.

Diffusion de rayons

Une fois la numérisation de la pièce terminée, des é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, le raycast est calculé sur la représentation voxel cube de 8 cm 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 (ou 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 projette 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, à nouveau des éléments d’interface utilisateur. Dans cette application, l’interface utilisateur obtient la priorité, puis le résultat de la compréhension et, enfin, les collisionneurs d’Unity. Le 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, contenus 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 d’informations à ce sujet 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 supérieure à 1 m2 est considérée comme le sol.

Notes

Le chemin de la caméra pendant le processus d’analyse est également utilisé dans ce processus.

Un sous-ensemble des requêtes exposées par le gestionnaire de topologie sont exposées 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 & largeur du volume souhaité, la hauteur minimale de placement au-dessus du plancher et la quantité minimale de dégagement devant le volume. Toutes les mesures sont exprimées 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 utilise un tableau pré-alloué de structures « TopologyResult ». Le paramètre « locationCount » spécifie la longueur du passé dans le tableau. La valeur de retour indique le nombre d’emplacements retournés. Ce nombre n’est jamais supérieur au passé dans le paramètre « locationCount ».

Le « TopologyResult » contient la position centrale du volume retourné, la direction de face (normale) et les dimensions de l’espace trouvé.

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

Notes

Dans l’exemple Unity, chacune de ces requêtes est liée à un bouton dans le panneau de l’interface utilisateur virtuelle. L’exemple code en dur les paramètres de chacune de ces requêtes sur 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 forme (« 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’objectif est que l’utilisateur puisse 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 les surfaces horizontales. Un canapé, par exemple, est défini par la surface du siège plat et le haut plat du dos du canapé. 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. Selon la terminologie des API, le siège du canapé et le haut arrière sont des composants de forme et les exigences d’alignement sont des contraintes de composant de forme.

Voici un exemple de requête défini dans l’exemple Unity (ShapeDefinition.cs) pour les objets « sittable ».

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’y a qu’un seul composant).

En revanche, la forme du 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 se trouve sur cette surface
La forme rectangle se trouve sur cette surface

Solveur de placement d’objet

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 l’emplacement le mieux adapté en fonction des règles et contraintes de l’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 placement multi-objets 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 emplacement 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’objet ci-dessous recherche un emplacement pour placer un cube d’un demi-mètre sur le bord d’une surface, loin des autres objets précédemment placés et près du centre de la pièce.

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

En cas de réussite, 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 prendront cet objet en compte. Le fichier « LevelSolver.cs » dans l’exemple Unity contient d’autres exemples de requêtes.

Résultats du placement de l’objet
Figure 3 : Zones bleues de la façon dont le résultat de trois requêtes de place sur le sol avec les règles de position loin de la caméra

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

Processus d’analyse de salle

Bien que la solution de mappage spatial fournie par HoloLens soit suffisamment générique pour répondre aux besoins de l’ensemble des 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ées 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.

« Peinture » de l’espace de jeu piloté par l’utilisateur : pendant la phase de numérisation, l’utilisateur se déplace et regarde autour du rythme des jeux, peignant efficacement les zones, qui doivent être incluses. Le maillage généré est important pour fournir des commentaires aux utilisateurs pendant cette phase. Installation à l’intérieur de la maison ou du 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, pendant la phase d’analyse, une analyse de l’axe principal est effectuée pour optimiser le 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 trame. Lorsque la requête de statistiques signale une couverture raisonnable, l’utilisateur est autorisé à airtap 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 cubes voxel de 8 cm. Pendant la partie initiale de l’analyse, une analyse des composants principaux est effectué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 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 préparé, vous êtes en train d’explorer les blocs de construction de base MRTK. À partir de là, vous pouvez passer au module 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