Räumliche Abbildung in Unity

Mit der räumlichen Zuordnung können Sie Dreiecksgitter abrufen, die die Oberflächen in der Welt um ein HoloLens Gerät darstellen. Sie können Oberflächendaten für Platzierung, Okklusion und Raumanalyse verwenden, um Ihren Unity-Projekten eine zusätzliche Dosis immersion zu verleihen.

Unity umfasst vollständige Unterstützung für räumliche Zuordnungen, die Entwicklern auf folgende Weise verfügbar gemacht werden:

  1. Raumzuordnungskomponenten, die im MixedRealityToolkit verfügbar sind, bieten einen bequemen und schnellen Weg für die ersten Schritte mit räumlicher Zuordnung
  2. ApIs der räumlichen Zuordnung auf niedriger ebener Ebene, die vollständige Kontrolle bieten und komplexere anwendungsspezifische Anpassungen ermöglichen

Um räumliche Zuordnungen in Ihrer App zu verwenden, muss die SpatialPerception-Funktion in Ihrem AppxManifest festgelegt werden.

Geräteunterstützung

Funktion HoloLens (1. Generation) HoloLens 2 Immersive Headsets
Räumliche Abbildung ✔️ ✔️

Festlegen der SpatialPerception-Funktion

Damit eine App räumliche Zuordnungsdaten nutzen kann, muss die SpatialPerception-Funktion aktiviert sein.

So aktivieren Sie die SpatialPerception-Funktion:

  1. Öffnen Sie im Unity-Editor den Bereich "Player Einstellungen" (Project Einstellungen Player bearbeiten >>)
  2. Wählen Sie auf der Registerkarte "Windows Store" aus.
  3. Erweitern Sie "Veröffentlichung Einstellungen", und überprüfen Sie die Funktion "SpatialPerception" in der Liste "Funktionen".

Hinweis

Wenn Sie Ihr Unity-Projekt bereits in eine Visual Studio Lösung exportiert haben, müssen Sie entweder in einen neuen Ordner exportieren oder diese Funktion manuell im AppxManifest in Visual Studio festlegen.

Räumliche Zuordnung erfordert außerdem mindestens 10.0.10586.0 MaxVersionTested:

  1. Klicken Sie in Visual Studio mit der rechten Maustaste auf "Package.appxmanifest" im Projektmappen-Explorer, und wählen Sie "Code anzeigen" aus.
  2. Suchen Sie die Zeile, die TargetDeviceFamily angibt, und ändern Sie MaxVersionTested="10.0.10240.0" in "MaxVersionTested="10.0.10586.0"
  3. Speichern Sie das Package.appxmanifest.

So fügen Sie Zuordnung in Unity hinzu

System für die räumliche Wahrnehmung

Schauen Sie sich in MRTK den Leitfaden für die ersten Schritte des Räumlichen Bewusstseins an, um Informationen zum Einrichten verschiedener räumlicher Gitterbeobachter zu erhalten.

Informationen zu On-Device-Beobachtern finden Sie in der Konfiguration von Gitterbeobachtern für Geräteleitfaden .

Informationen zu Szenenverständnisbeobachten finden Sie im Guide für Szenenverständnisbeobachter .

Gitteranalyse auf höherer Ebene: Räumliches Verständnis

Achtung

Räumliches Verständnis wurde zugunsten des Szenenverständnisses veraltet.

Das MixedRealityToolkit ist eine Sammlung von Hilfscode für die holografische Entwicklung, die auf den holografischen APIs von Unity basiert.

Räumliches Verständnis

Bei der Platzierung von Hologrammen in der physischen Welt ist es oft wünschenswert, über die Gitter- und Oberflächenebenen der räumlichen Zuordnung hinauszugehen. Wenn die Platzierung verfahrensmäßig erfolgt, ist ein höheres Umweltverständnis wünschenswert. Dies erfordert in der Regel Entscheidungen darüber, was Boden, Decke und Wände sind. Sie haben auch die Möglichkeit, gegen eine Reihe von Platzierungseinschränkungen zu optimieren, um die besten physischen Speicherorte für holografische Objekte zu ermitteln.

Während der Entwicklung von Young Conker und Fragmenten stellte sich Asobo Studios diesem Problem gegenüber, indem er einen Raumlöser entwickelt. Jede dieser Spiele hatte spielspezifische Anforderungen, aber sie haben die Kerntechnik für räumliches Verständnis geteilt. Die HoloToolkit.SpatialUnderstanding-Bibliothek kapselt diese Technologie, kapselt diese Technologie, sodass Sie schnell leere Leerräume auf den Wänden finden, Objekte an der Decke platzieren, das Zeichen sitzen lassen und eine Reihe anderer räumlicher Verständnisabfragen erkennen können.

Der gesamte Quellcode ist enthalten, sodass Sie ihn an Ihre Anforderungen anpassen und Ihre Verbesserungen mit der Community teilen können. Der Code für die C++-Solver wurde in eine UWP-DLL umschlossen und Unity mit einem Drop in Prefab im MixedRealityToolkit verfügbar gemacht.

Grundlegendes zu Modulen

Es gibt drei primäre Schnittstellen, die vom Modul verfügbar gemacht werden: Topologie für einfache Oberflächen- und räumliche Abfragen, Shape für die Objekterkennung und die Objektplatzierungslöser für die einschränkungsbasierte Platzierung von Objektsätzen. Beide Methoden werden im Folgenden beschrieben. Zusätzlich zu den drei primären Modulschnittstellen kann eine Ray Casting-Schnittstelle verwendet werden, um markierte Oberflächentypen abzurufen, und ein benutzerdefiniertes wasserdichtes Playspace-Gitter kann kopiert werden.

Ray Casting

Nach Abschluss des Raumscans werden Bezeichnungen intern für Oberflächen wie Boden, Decke und Wände generiert. Die PlayspaceRaycast Funktion übernimmt einen Ray und gibt zurück, wenn der Ray mit einer bekannten Oberfläche kollidiert und wenn ja, Informationen über diese Oberfläche in Form einer 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;
};

Intern wird der Raycast mit der berechneten 8-cm-würfeligen Voxeldarstellung des Playspace berechnet. Jedes Voxel enthält eine Reihe von Oberflächenelementen mit verarbeiteten Topologiedaten (aka surfels). Die Surfel, die in der intersecierten Voxelzelle enthalten sind, werden verglichen und die besten Übereinstimmungen verwendet, um die Topologieinformationen nachzuschlagen. Diese Topologiedaten enthalten die Bezeichnung, die in Form der Aufzählung "SurfaceTypes" zurückgegeben wird, sowie den Oberflächenbereich der überschneidenden Oberfläche.

Im Unity-Beispiel wandelt der Cursor jeden Frame um. Zuerst gegen die Collider von Unity. Zweitens gegen die Weltdarstellung des Verständnismoduls. Und schließlich wieder UI-Elemente. In dieser Anwendung erhält die Benutzeroberfläche Priorität, als Nächstes das Verständnisergebnis und schließlich Unitys Kollder. Der SurfaceType wird als Text neben dem Cursor gemeldet.

Surface type is labeled next to the cursor
Der Surface-Typ wird neben dem Cursor beschriftet.

Topologieabfragen

Innerhalb der DLL behandelt der Topologie-Manager die Bezeichnung der Umgebung. Wie oben erwähnt, werden viele der Daten in Surfeln gespeichert, die in einem Voxelvolumen enthalten sind. Darüber hinaus wird die "PlaySpaceInfos"-Struktur verwendet, um Informationen über den Playspace zu speichern, einschließlich der Weltausrichtung (weitere Details dazu unten), Boden- und Deckenhöhe. Heuristiken werden zum Bestimmen von Boden, Decke und Wänden verwendet. Beispielsweise wird die größte und niedrigste horizontale Oberfläche mit größer als 1 m2 Fläche als Boden betrachtet.

Hinweis

Der Kamerapfad während des Scanvorgangs wird auch in diesem Prozess verwendet.

Eine Teilmenge der abfragen, die vom Topologie-Manager verfügbar gemacht werden, werden über die DLL verfügbar gemacht. Die verfügbar gemachten Topologieabfragen sind wie folgt.

QueryTopology_FindPositionsOnWalls
QueryTopology_FindLargePositionsOnWalls
QueryTopology_FindLargestWall
QueryTopology_FindPositionsOnFloor
QueryTopology_FindLargestPositionsOnFloor
QueryTopology_FindPositionsSittable

Jede Abfrage verfügt über eine Reihe von Parametern, die für den Abfragetyp spezifisch sind. Im folgenden Beispiel gibt der Benutzer die mindesthöhenbreite & des gewünschten Volumens, die mindestplatzierungshöhe über dem Boden und den mindesten Abstand vor dem Volumen an. Alle Messungen befinden sich in Metern.

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)

Jede dieser Abfragen verwendet ein vordefiniertes Array von "TopologyResult"-Strukturen. Der Parameter "locationCount" gibt die Länge des übergebenen Arrays an. Der Rückgabewert meldet die Anzahl der zurückgegebenen Speicherorte. Diese Zahl ist nie größer als der übergebene Parameter "locationCount".

Das "TopologyResult" enthält die mittlere Position des zurückgegebenen Volumes, die gerichtete Richtung (z. B. normal) und die Abmessungen des gefundenen Raums.

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

Hinweis

Im Unity-Beispiel wird jede dieser Abfragen mit einer Schaltfläche im bereich der virtuellen Benutzeroberfläche verknüpft. Im Beispiel werden die Parameter für jede dieser Abfragen auf angemessene Werte festgelegt. Weitere Beispiele finden Sie unter SpaceVisualizer.cs im Beispielcode.

Shape-Abfragen

In der DLL verwendet die Shape-Analyse ("ShapeAnalyzer_W") die Topologieanalyse, um mit benutzerdefinierten Shapes abzugleichen, die vom Benutzer definiert werden. Im Unity-Beispiel wird eine Reihe von Shapes definiert und die Ergebnisse über das In-App-Abfragemenü innerhalb der Shape-Registerkarte verfügbar gemacht. Die Absicht besteht darin, dass der Benutzer eigene Objekt-Shape-Abfragen definieren und diese nach Bedarf in ihrer Anwendung verwenden kann.

Die Shape-Analyse funktioniert nur auf horizontalen Oberflächen. Eine Couch, z. B. wird durch die flache Sitzfläche und die flache Oberseite der Couch zurück definiert. Die Shape-Abfrage sucht nach zwei Oberflächen einer bestimmten Größe, Höhe und Seitenbereich, wobei die beiden Oberflächen ausgerichtet und verbunden sind. Bei Verwendung der APIs-Terminologie sind die Sitz- und Rückseitenteile Formenkomponenten und die Ausrichtungsanforderungen sind Shape-Komponenteneinschränkungen.

Eine beispielabfrage, die im Unity-Beispiel (ShapeDefinition.cs) für "sittable"-Objekte definiert ist, lautet wie folgt.

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

Jede Shape-Abfrage wird durch eine Reihe von Shape-Komponenten definiert, wobei jeweils eine Reihe von Komponenteneinschränkungen und eine Reihe von Formeneinschränkungen aufgeführt werden, die Abhängigkeiten zwischen den Komponenten auflisten. Dieses Beispiel enthält drei Einschränkungen in einer einzelnen Komponentendefinition und keine Formeinschränkungen zwischen Komponenten (da nur eine Komponente vorhanden ist).

Im Gegensatz dazu verfügt die Couchform über zwei Formenkomponenten und vier Formeneinschränkungen. Komponenten werden anhand ihres Indexes in der Komponentenliste des Benutzers (0 und 1 in diesem Beispiel) identifiziert.

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

Wrapperfunktionen werden im Unity-Modul zur einfachen Erstellung von benutzerdefinierten Shape-Definitionen bereitgestellt. Die vollständige Liste der Komponenten- und Formeinschränkungen finden Sie in "SpatialUnderstandingDll.cs" in den Strukturen "ShapeComponentConstraint" und "ShapeConstraint".

Rectangle shape is found on this surface
Rechteckform befindet sich auf dieser Oberfläche

Objektplatzierungslöser

Der Objektplatzierungslöser kann verwendet werden, um ideale Orte im physischen Raum zu identifizieren, um Ihre Objekte zu platzieren. Der Solver findet die beste Passende Position angesichts der Objektregeln und Einschränkungen. Darüber hinaus werden Objektabfragen beibehalten, bis das Objekt mit "Solver_RemoveObject"- oder "Solver_RemoveAllObjects"-Aufrufen entfernt wird, sodass eingeschränkte Multiobjektplatzierung ermöglicht wird. Objektplatzierungsabfragen bestehen aus drei Teilen: Platzierungstyp mit Parametern, einer Liste von Regeln und einer Liste von Einschränkungen. Verwenden Sie die folgende API, um eine Abfrage auszuführen.

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)

Diese Funktion verwendet einen Objektnamen, eine Platzierungsdefinition und eine Liste der Regeln und Einschränkungen. Die C#-Wrapper bieten Bauhilfefunktionen, um Regel- und Einschränkungskonstruktion einfach zu gestalten. Die Platzierungsdefinition enthält den Abfragetyp – also eine der folgenden.

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

Jede der Platzierungstypen weist einen Satz von Parametern auf, die für den Typ eindeutig sind. Die Struktur "ObjectPlacementDefinition" enthält eine Reihe statischer Hilfsfunktionen zum Erstellen dieser Definitionen. Um beispielsweise einen Ort zu finden, an dem ein Objekt auf dem Boden platziert werden soll, können Sie die folgende Funktion verwenden. Public Static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims) Zusätzlich zum Platzierungstyp können Sie eine Reihe von Regeln und Einschränkungen bereitstellen. Regeln können nicht verletzt werden. Mögliche Platzierungsorte, die den Typ und die Regeln erfüllen, werden dann für die Reihe von Einschränkungen optimiert, um die optimale Platzierungsposition auszuwählen. Jede der Regeln und Einschränkungen kann durch die bereitgestellten statischen Erstellungsfunktionen erstellt werden. Nachfolgend finden Sie eine Beispielregel- und Einschränkungskonstruktionsfunktion.

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

Die untere Objektplatzierungsabfrage sucht nach einem Ort, um einen halb meterigen Würfel an den Rand einer Oberfläche zu platzieren, von anderen zuvor platzierten Objekten und in der Nähe der Mitte des Raums.

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

Bei erfolgreicher Ausführung wird eine "ObjectPlacementResult"-Struktur, die die Platzierungsposition, Dimensionen und Ausrichtung enthält, zurückgegeben. Darüber hinaus wird die Platzierung der internen Liste der platzierten Objekte der DLL hinzugefügt. Nachfolgende Platzierungsabfragen berücksichtigen dieses Objekt. Die Datei "LevelSolver.cs" im Unity-Beispiel enthält weitere Beispielabfragen.

Results of object placement
Abbildung 3: Die blauen Felder, wie das Ergebnis aus drei Platz auf Bodenabfragen ohne Kamerapositionsregeln entsteht

Bei der Lösung für die Platzierungsposition mehrerer Objekte, die für ein Level- oder Anwendungsszenario erforderlich sind, lösen Sie zunächst unverzichtbare und große Objekte, um die Wahrscheinlichkeit zu maximieren, dass ein Raum gefunden werden kann. Die Platzierungsreihenfolge ist wichtig. Wenn Objektplatzierungen nicht gefunden werden können, versuchen Sie weniger eingeschränkte Konfigurationen. Das Vorhandensein einer Reihe von Fallbackkonfigurationen ist entscheidend für die Unterstützung von Funktionen in vielen Raumkonfigurationen.

Raumscanprozess

Während die von der HoloLens bereitgestellte Raumzuordnungslösung so konzipiert ist, dass sie generisch genug ist, um den Anforderungen der gesamten Bandbreite von Problemräumen gerecht zu werden, wurde das Raumverständnismodul erstellt, um die Anforderungen von zwei bestimmten Spielen zu unterstützen. Seine Lösung ist um einen bestimmten Prozess und eine Reihe von Annahmen strukturiert, die unten zusammengefasst sind.

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.

Benutzergesteuertes Spielraum "Malen" – Während der Scanphase bewegt sich der Benutzer und sieht sich das Spieltempo an, wobei die Bereiche effektiv bemalt werden sollten, die eingeschlossen werden sollen. Das generierte Gitter ist wichtig, während dieser Phase Benutzerfeedback bereitzustellen. Innen- oder Büroeinrichtung – Die Abfragefunktionen sind um flache Oberflächen und Wände in rechten Winkeln ausgelegt. Dies ist eine weiche Einschränkung. Während der Überprüfungsphase wird jedoch eine Primärachsenanalyse abgeschlossen, um die Gittertesselation entlang der Haupt- und Nebenachse zu optimieren. Die enthaltene Datei "SpatialUnderstanding.cs" verwaltet den Überprüfungsphasesprozess. Sie ruft die folgenden Funktionen auf.

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.

Der Scanfluss, der durch das Verhalten "SpatialUnderstanding" gesteuert wird, ruft InitScan auf und aktualisiert dann jeden Frame. Wenn die Statistikabfrage eine angemessene Abdeckung meldet, kann der Benutzer "RequestFinish" aufrufen, um das Ende der Überprüfungsphase anzugeben. UpdateScan wird weiterhin aufgerufen, bis der Rückgabewert angibt, dass die DLL die Verarbeitung abgeschlossen hat.

Grundlegendes Mesh

Die grundlegende DLL speichert den Playspace intern als Raster von 8 cm größe voxel cubes. Während des ersten Teils des Scannens wird eine Primäre Komponentenanalyse abgeschlossen, um die Achsen des Raums zu bestimmen. Intern speichert er seinen Voxelraum, der an diese Achsen ausgerichtet ist. Ein Gitter wird ungefähr jede Sekunde generiert, indem das Isosurface aus dem Voxelvolumen extrahiert wird.

Generated mesh produced from the voxel volume
Generiertes Gitter aus dem Voxelvolumen

Problembehandlung

  • Stellen Sie sicher, dass Sie die SpatialPerception-Funktion festgelegt haben
  • Wenn die Nachverfolgung verloren geht, entfernt das nächste OnSurfaceChanged-Ereignis alle Gitter.

Räumliche Zuordnung im Mixed Reality Toolkit

Weitere Informationen zur Verwendung der räumlichen Zuordnung mit Mixed Reality Toolkit finden Sie im Abschnitt "Räumliches Bewusstsein" der MRTK-Dokumente.

Nächster Entwicklungsprüfpunkt

Wenn Sie die Unity-Entwicklungsreise verfolgt, die wir eingerichtet haben, befinden Sie sich mitten in der Erkundung der MRTK-Kernbausteine. Von hier aus können Sie mit dem nächsten Baustein fortfahren:

Oder fahren Sie mit den Funktionen und APIs der Mixed Reality-Plattform fort:

Sie können jederzeit zu den Prüfpunkten für die Unity-Entwicklung zurückkehren.

Siehe auch