Mapping spaziale in Unity
Il mapping spaziale consente di recuperare mesh di triangoli che rappresentano le superfici del mondo intorno a un dispositivo HoloLens. È possibile usare i dati di superficie per l'analisi della posizione, dell'occlusione e della stanza per offrire ai progetti Unity una dose aggiuntiva di immersione.
Unity include il supporto completo per il mapping spaziale, esposto agli sviluppatori nei modi seguenti:
- Componenti di mapping spaziale disponibili in MixedRealityToolkit, che forniscono un percorso pratico e rapido per iniziare a usare il mapping spaziale
- API di mapping spaziale di livello inferiore, che forniscono il controllo completo e consentono una personalizzazione più sofisticata specifica dell'applicazione
Per usare il mapping spaziale nella tua app, la funzionalità SpatialPerception deve essere impostata in AppxManifest.
Supporto di dispositivi
Funzionalità | HoloLens (prima generazione) | HoloLens 2 | Visori VR immersive |
---|---|---|---|
Mapping spaziale | ✔️ | ✔️ | ❌ |
Impostazione della funzionalità SpatialPerception
Per consentire a un'app di utilizzare i dati di mapping spaziale, è necessario abilitare la funzionalità SpatialPerception.
Come abilitare la funzionalità SpatialPerception:
- Nell'editor di Unity aprire il riquadro "Impostazioni lettore" (Modifica > lettore impostazioni > progetto)
- Selezionare la scheda "Windows Store"
- Espandere "Impostazioni di pubblicazione" e controllare la funzionalità "SpatialPerception" nell'elenco "Funzionalità"
Nota
Se il progetto Unity è già stato esportato in una soluzione di Visual Studio, sarà necessario esportare in una nuova cartella o impostare manualmente questa funzionalità in AppxManifest in Visual Studio.
Il mapping spaziale richiede anche un valore MaxVersionTested di almeno 10.0.10586.0:
- In Visual Studio fare clic con il pulsante destro del mouse su Package.appxmanifest nel Esplora soluzioni e scegliere Visualizza codice
- Trovare la riga che specifica TargetDeviceFamily e modificare MaxVersionTested="10.0.10240.0" in MaxVersionTested="10.0.10586.0"
- Salvare Package.appxmanifest.
Come aggiungere il mapping in Unity
Sistema di consapevolezza spaziale
In MRTK, vedere la guida introduttiva alla consapevolezza spaziale per informazioni sulla configurazione di vari osservatori di mesh spaziali.
Per informazioni sugli osservatori sul dispositivo, vedere la guida Configurazione degli osservatori mesh per i dispositivi .
Per informazioni sugli osservatori di comprensione della scena, vedere la guida dell'osservatore di comprensione della scena.
Analisi mesh di livello superiore: Spatial Understanding
Attenzione
La comprensione spaziale è stata deprecata a favore di Scene Understanding.
MixedRealityToolkit è una raccolta di codice di utilità per lo sviluppo olografico basato sulle API olografiche di Unity.
Comprensione spaziale
Quando si posizionano gli ologrammi nel mondo fisico, è spesso consigliabile andare oltre le mesh e i piani di superficie del mapping spaziale. Quando il posizionamento viene svolto in modo procedurale, è auspicabile un livello più elevato di comprensione ambientale. Questo di solito richiede decisioni su ciò che è pavimento, soffitto e pareti. È anche possibile ottimizzare in base a un set di vincoli di posizionamento per determinare le posizioni fisiche migliori per gli oggetti olografici.
Durante lo sviluppo di Young Conker e Fragments, Asobo Studios ha affrontato questo problema sviluppando un risolutore di sale. Ognuno di questi giochi aveva esigenze specifiche del gioco, ma condividevano la tecnologia di comprensione spaziale di base. La libreria HoloToolkit.SpatialUnderstanding incapsula questa tecnologia, consentendo di trovare rapidamente spazi vuoti sulle pareti, posizionare gli oggetti sul soffitto, identificare la posizione del carattere da sedersi e una miriade di altre query di comprensione spaziale.
Tutto il codice sorgente è incluso, consentendo di personalizzarlo in base alle proprie esigenze e condividere i miglioramenti con la community. Il codice per il risolutore C++ è stato sottoposto a wrapping in una DLL UWP ed esposto a Unity con un rilascio nel prefab contenuto in MixedRealityToolkit.
Informazioni sui moduli
Sono disponibili tre interfacce principali esposte dal modulo: topologia per query di superficie e spaziali semplici, forma per il rilevamento degli oggetti e risolutore di posizionamento degli oggetti per il posizionamento basato su vincoli di set di oggetti. Ogni modalità è illustrata di seguito. Oltre alle tre interfacce del modulo principale, è possibile usare un'interfaccia di ray casting per recuperare i tipi di superficie contrassegnati e copiare una mesh di spazi di gioco a tenuta d'acqua personalizzata.
Ray Casting
Al termine dell'analisi della stanza, le etichette vengono generate internamente per superfici come il pavimento, il soffitto e le pareti. La PlayspaceRaycast
funzione accetta un raggio e restituisce se il raggio si scontra con una superficie nota e, in tal caso, le informazioni su tale superficie sotto forma di un oggetto 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, il raycast viene calcolato in base alla rappresentazione di voxel a cubo di 8 cm calcolata dello spazio di riproduzione. Ogni voxel contiene un set di elementi di superficie con dati di topologia elaborati (aka surfels). I surfel contenuti all'interno della cella voxel intersecata vengono confrontati e la corrispondenza migliore usata per cercare le informazioni sulla topologia. Questi dati della topologia contengono l'etichettatura restituita sotto forma di enumerazione "SurfaceTypes", nonché l'area di superficie della superficie intersecata.
Nell'esempio unity il cursore esegue il cast di un raggio per ogni fotogramma. Prima di tutto, contro i collisori di Unity. In secondo luogo, rispetto alla rappresentazione del mondo del modulo di comprensione. Infine, ancora elementi dell'interfaccia utente. In questa applicazione, l'interfaccia utente ottiene la priorità, il risultato di comprensione e infine i collisori di Unity. SurfaceType viene segnalato come testo accanto al cursore.
Il tipo di superficie viene etichettato accanto al cursore
Query di topologia
All'interno della DLL, il gestore della topologia gestisce l'etichettatura dell'ambiente. Come accennato in precedenza, gran parte dei dati viene archiviata all'interno di surfel, contenuti all'interno di un volume voxel. Inoltre, la struttura "PlaySpaceInfos" viene usata per archiviare informazioni sullo spazio di gioco, incluso l'allineamento del mondo (maggiori dettagli su questo sotto), pavimento e altezza del soffitto. L'euristica viene usata per determinare pavimento, soffitto e pareti. Ad esempio, la superficie orizzontale più grande e più bassa con una superficie di superficie maggiore di 1 m2 viene considerata il pavimento.
Nota
Anche il percorso della fotocamera durante il processo di scansione viene usato in questo processo.
Un subset delle query esposte dal gestore della topologia viene esposto tramite la dll. Di seguito sono riportate le query di topologia esposte.
QueryTopology_FindPositionsOnWalls
QueryTopology_FindLargePositionsOnWalls
QueryTopology_FindLargestWall
QueryTopology_FindPositionsOnFloor
QueryTopology_FindLargestPositionsOnFloor
QueryTopology_FindPositionsSittable
Ognuna delle query ha un set di parametri, specifico per il tipo di query. Nell'esempio seguente, l'utente specifica l'altezza minima e la larghezza del volume desiderato, l'altezza di posizionamento minima sopra il pavimento e la quantità minima di distanza davanti al volume. Tutte le misurazioni sono in metri.
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)
Ognuna di queste query accetta una matrice preallocata di strutture "TopologyResult". Il parametro "locationCount" specifica la lunghezza della matrice passata. Il valore restituito indica il numero di posizioni restituite. Questo numero non è mai maggiore del parametro "locationCount".
"TopologyResult" contiene la posizione centrale del volume restituito, la direzione rivolta (ad esempio normale) e le dimensioni dello spazio trovato.
struct TopologyResult
{
DirectX::XMFLOAT3 position;
DirectX::XMFLOAT3 normal;
float width;
float length;
};
Nota
Nell'esempio unity, ognuna di queste query viene collegata a un pulsante nel pannello dell'interfaccia utente virtuale. Il codice hardcoded di esempio i parametri per ognuna di queste query in valori ragionevoli. Per altri esempi, vedere SpaceVisualizer.cs nel codice di esempio.
Query shape
Nella dll, l'analizzatore forme ("ShapeAnalyzer_W") usa l'analizzatore della topologia per la corrispondenza con forme personalizzate definite dall'utente. L'esempio unity definisce un set di forme ed espone i risultati tramite il menu di query in-app, all'interno della scheda forma. L'intenzione è che l'utente può definire le proprie query di forma oggetto e usarle in base alle esigenze dell'applicazione.
L'analisi delle forme funziona solo su superfici orizzontali. Un divano, ad esempio, è definito dalla superficie piatta del sedile e dalla parte superiore piatta del divano posteriore. La query shape cerca due superfici di una dimensione, un'altezza e un intervallo di aspetti specifici, con le due superfici allineate e connesse. Usando la terminologia delle API, il sedile divano e la parte posteriore sono componenti della forma e i requisiti di allineamento sono vincoli dei componenti della forma.
Di seguito è riportato un esempio di query definito nell'esempio unity (ShapeDefinition.cs), per gli oggetti "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);
Ogni query di forma è definita da un set di componenti di forma, ognuno con un set di vincoli di componente e un set di vincoli di forma che elencano le dipendenze tra i componenti. In questo esempio sono inclusi tre vincoli in una singola definizione di componente e nessun vincolo di forma tra i componenti ( poiché è presente un solo componente).
Al contrario, la forma divano ha due componenti di forma e quattro vincoli di forma. I componenti sono identificati dal relativo indice nell'elenco dei componenti dell'utente (0 e 1 in questo esempio).
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),
};
Le funzioni wrapper vengono fornite nel modulo Unity per semplificare la creazione di definizioni di forme personalizzate. L'elenco completo dei vincoli di componente e forma è disponibile in "SpatialUnderstandingDll.cs" all'interno delle strutture "ShapeComponentConstraint" e "ShapeConstraint".
La forma rettangolo si trova in questa superficie
Risolutore di posizionamento oggetti
Il risolutore di posizionamento degli oggetti può essere usato per identificare le posizioni ideali nella stanza fisica per posizionare gli oggetti. Il risolutore troverà la posizione più adatta in base alle regole e ai vincoli dell'oggetto. Inoltre, le query sugli oggetti vengono mantenute finché l'oggetto non viene rimosso con chiamate "Solver_RemoveObject" o "Solver_RemoveAllObjects", consentendo la selezione di più oggetti vincolati. Le query di posizionamento degli oggetti sono costituite da tre parti: tipo di posizionamento con parametri, un elenco di regole e un elenco di vincoli. Per eseguire una query, usare l'API seguente.
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)
Questa funzione accetta un nome di oggetto, una definizione di posizionamento e un elenco di regole e vincoli. I wrapper C# forniscono funzioni helper di costruzione per semplificare la costruzione di regole e vincoli. La definizione di posizionamento contiene il tipo di query, ovvero uno dei seguenti.
public enum PlacementType
{
Place_OnFloor,
Place_OnWall,
Place_OnCeiling,
Place_OnShape,
Place_OnEdge,
Place_OnFloorAndCeiling,
Place_RandomInAir,
Place_InMidAir,
Place_UnderFurnitureEdge,
};
Ognuno dei tipi di posizionamento ha un set di parametri univoci per il tipo. La struttura "ObjectPlacementDefinition" contiene un set di funzioni helper statiche per la creazione di queste definizioni. Ad esempio, per trovare un luogo in cui inserire un oggetto sul pavimento, è possibile usare la funzione seguente. public static ObjectPlacementDefinition Create_OnFloor(Vector3 halfDims) Oltre al tipo di posizionamento, è possibile fornire un set di regole e vincoli. Non è possibile violare le regole. Le possibili posizioni di posizionamento che soddisfano il tipo e le regole vengono quindi ottimizzate in base al set di vincoli per selezionare la posizione di posizionamento ottimale. Ognuna delle regole e dei vincoli può essere creata dalle funzioni di creazione statiche fornite. Di seguito è riportata una regola di esempio e una funzione di costruzione dei vincoli.
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 query di posizionamento dell'oggetto seguente cerca un luogo in cui inserire un cubo di mezzo metro sul bordo di una superficie, lontano da altri oggetti posizionati in precedenza e vicino al centro della stanza.
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());
In caso di esito positivo, viene restituita una struttura "ObjectPlacementResult" contenente la posizione, le dimensioni e l'orientamento del posizionamento. Inoltre, il posizionamento viene aggiunto all'elenco interno della dll di oggetti inseriti. Le query di posizionamento successive tengono conto di questo oggetto. Il file "LevelSolver.cs" nell'esempio unity contiene più query di esempio.
Figura 3: Le caselle blu come il risultato da tre posizioni sulle query sul pavimento con lontano dalle regole di posizione della fotocamera
Quando si risolve la posizione di posizionamento di più oggetti necessari per uno scenario di livello o applicazione, risolvere prima di tutto oggetti indispensabili e di grandi dimensioni per massimizzare la probabilità che sia possibile trovare uno spazio. L'ordine di posizionamento è importante. Se non è possibile trovare le posizioni degli oggetti, provare configurazioni meno vincolate. La presenza di un set di configurazioni di fallback è fondamentale per supportare le funzionalità in molte configurazioni di sala.
Processo di scansione sala
Anche se la soluzione di mapping spaziale fornita da HoloLens è progettata per essere abbastanza generica per soddisfare le esigenze dell'intera gamma di spazi problematiche, il modulo di comprensione spaziale è stato creato per supportare le esigenze di due giochi specifici. La soluzione è strutturata intorno a un processo specifico e a un set di presupposti, riepilogati di seguito.
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 guidato dall'utente "pittura" - Durante la fase di scansione, l'utente si muove e guarda intorno al ritmo dei giochi, disegnando efficacemente le aree, che devono essere incluse. La mesh generata è importante per fornire commenti e suggerimenti degli utenti durante questa fase. Interni di casa o installazione ufficio: le funzioni di query sono progettate intorno a superfici e pareti piatte ad angoli giusti. Si tratta di una limitazione flessibile. Tuttavia, durante la fase di scansione, viene completata un'analisi dell'asse principale per ottimizzare la tassellatura mesh lungo l'asse principale e secondario. Il file SpatialUnderstanding.cs incluso gestisce il processo di fase di analisi. Chiama le funzioni seguenti.
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.
Il flusso di analisi, guidato dal comportamento "SpatialUnderstanding" chiama InitScan, quindi UpdateScan ogni fotogramma. Quando la query sulle statistiche segnala una copertura ragionevole, l'utente può chiamare RequestFinish per indicare la fine della fase di analisi. UpdateScan continua a essere chiamato fino a quando il valore restituito indica che la dll ha completato l'elaborazione.
Informazioni sulla mesh
La dll understanding archivia internamente lo spazio di riproduzione come una griglia di 8 cubi voxel di dimensioni pari a 8 cm. Durante la parte iniziale dell'analisi, viene completata un'analisi dei componenti primaria per determinare gli assi della stanza. Internamente, archivia lo spazio voxel allineato a questi assi. Una mesh viene generata approssimativamente ogni secondo estraendo l'isosurface dal volume voxel.
Mesh generata dal volume voxel
Risoluzione dei problemi
- Assicurarsi di aver impostato la funzionalità SpatialPerception
- Quando il rilevamento viene perso, l'evento OnSurfaceChanged successivo rimuoverà tutte le mesh.
Mapping spaziale in Realtà mista Toolkit
Per altre informazioni sull'uso del mapping spaziale con Realtà mista Toolkit, vedere la sezione relativa alla consapevolezza spaziale della documentazione di MRTK.
Successivo checkpoint di sviluppo
Se si sta seguendo il percorso di sviluppo di Unity, si sta esplorando i blocchi predefiniti principali di MRTK. Da qui, è possibile passare al blocco predefinito successivo:
In alternativa, passare alle API e alle funzionalità della piattaforma di realtà mista:
È sempre possibile tornare ai checkpoint per lo sviluppo con Unity in qualsiasi momento.