Mapping spaziale in Unity
Il mapping spaziale consente di recuperare le mesh triangoli che rappresentano le superfici nel mondo intorno a un dispositivo HoloLens. È possibile usare i dati di superficie per il posizionamento, l'occlusione e l'analisi della stanza per fornire 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 offrono 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 nell'app, la funzionalità SpatialPerception deve essere impostata in AppxManifest.
Supporto dei dispositivi
Funzionalità | HoloLens (prima generazione) | HoloLens 2 | Visori vr immersive |
---|---|---|---|
Mapping spaziale | ✔️ | ✔️ | ❌ |
Impostazione della funzionalità SpatialPerception
Affinché un'app possa usare i dati di mapping spaziale, è necessario abilitare la funzionalità SpatialPerception.
Come abilitare la funzionalità SpatialPerception:
- Nella Editor 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 esportarlo 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 sensibilizzazione spaziale
In MRTK esaminare 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 di rete per il dispositivo .
Per informazioni sugli osservatori di comprensione della scena, vedere la guida dell'osservatore di comprensione della scena .
Analisi delle mesh di livello superiore: Comprensione spaziale
Attenzione
Spatial Understanding è stato deprecato a favore di Scene Understanding.
MixedRealityToolkit è una raccolta di codice di utilità per lo sviluppo olografico basato sulle API olografiche di Unity.
Informazioni spaziali
Quando si posizionano ologrammi nel mondo fisico, è spesso consigliabile andare oltre la mesh e i piani di superficie del mapping spaziale. Quando il posizionamento viene eseguito in modo procedurale, è auspicabile un livello più elevato di comprensione ambientale. Questo di solito richiede di prendere decisioni su ciò che è pavimento, soffitto, e pareti. È anche possibile ottimizzare rispetto 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 affrontò 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 il posto in cui posizionare il carattere 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 incluso in una DLL UWP ed esposto a Unity con un rilascio di prefab contenuto in MixedRealityToolkit.
Informazioni sui moduli
Il modulo espone tre interfacce principali: la topologia per query di superficie e spaziali semplici, la forma per il rilevamento di oggetti e il risolutore di posizionamento degli oggetti per il posizionamento basato su vincoli dei set di oggetti. Ognuno di questi è descritto di seguito. Oltre alle tre interfacce dei moduli principali, è possibile usare un'interfaccia di ray casting per recuperare i tipi di superficie con tag e copiare una mesh di playspace impermeabile personalizzata.
Ray Casting
Dopo aver completato la scansione 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 caso affermativo, informazioni su tale superficie sotto forma di .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 con cubo di 8 cm calcolata dello spazio di riproduzione. Ogni voxel contiene un set di elementi di superficie con dati di topologia elaborati (detti surfel). Vengono confrontati i surfel contenuti all'interno della cella voxel intersecata e viene usata la corrispondenza migliore per cercare le informazioni sulla topologia. Questi dati della topologia contengono l'etichettatura restituita sotto forma di enumerazione "SurfaceTypes", nonché la superficie della superficie intersecata.
Nell'esempio Unity il cursore esegue il cast di un raggio ogni fotogramma. Prima di tutto, contro i collisori di Unity. In secondo luogo, contro la rappresentazione globale del modulo di comprensione. Infine, di nuovo elementi dell'interfaccia utente. In questa applicazione l'interfaccia utente ottiene la priorità, quindi il risultato di comprensione e infine i collisori di Unity. SurfaceType viene segnalato come testo accanto al cursore.
Il tipo di superficie è etichettato accanto al cursore
Query sulla topologia
All'interno della DLL, gestione topologia gestisce l'etichettatura dell'ambiente. Come accennato in precedenza, gran parte dei dati viene archiviata all'interno dei surfel, contenuti in un volume voxel. Inoltre, la struttura "PlaySpaceInfos" viene usata per archiviare informazioni sullo spazio di gioco, tra cui l'allineamento del mondo (altri dettagli su questo sotto), il pavimento e l'altezza del soffitto. L'euristica viene usata per determinare il pavimento, il soffitto e le pareti. Ad esempio, la superficie orizzontale più grande e più bassa con una superficie maggiore di 1 m2 è considerata il pavimento.
Nota
In questo processo viene usato anche il percorso della fotocamera durante il processo di scansione.
Un subset delle query esposte da Gestione topologia viene esposto tramite la DLL. Le query di topologia esposte sono le seguenti.
QueryTopology_FindPositionsOnWalls
QueryTopology_FindLargePositionsOnWalls
QueryTopology_FindLargestWall
QueryTopology_FindPositionsOnFloor
QueryTopology_FindLargestPositionsOnFloor
QueryTopology_FindPositionsSittable
Ogni query ha un set di parametri, specifico del tipo di query. Nell'esempio seguente l'utente specifica l'altezza minima & larghezza del volume desiderato, l'altezza minima di posizionamento sopra il pavimento e la quantità minima di spazio libero 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" passato.
"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 è collegata a un pulsante nel pannello dell'interfaccia utente virtuale. Il codice rigido di esempio codifica i parametri per ognuna di queste query in valori ragionevoli. Per altri esempi, vedere SpaceVisualizer.cs nel codice di esempio.
Query sulla forma
Nella DLL l'analizzatore di forme ("ShapeAnalyzer_W") usa l'analizzatore di topologia per la corrispondenza con le forme personalizzate definite dall'utente. L'esempio Unity definisce un set di forme ed espone i risultati tramite il menu query in-app, all'interno della scheda forma. L'intenzione è che l'utente possa definire le proprie query sulla forma dell'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 del sedile piatta e dalla parte superiore piatta del divano posteriore. La query sulla forma cerca due superfici di dimensioni specifiche, altezza e intervallo di aspetti, con le due superfici allineate e connesse. Usando la terminologia delle API, il sedile del divano e la parte superiore posteriore sono componenti di forma e i requisiti di allineamento sono vincoli dei componenti di forma.
Di seguito è illustrata una query di esempio definita 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 della forma, ognuno con un set di vincoli di componente e un set di vincoli di forma che elencano le dipendenze tra i componenti. Questo esempio include 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 del divano ha due componenti di forma e quattro vincoli di forma. I componenti vengono 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 sono disponibili nel modulo Unity per semplificare la creazione di definizioni di forme personalizzate. L'elenco completo dei vincoli di componenti e forme è disponibile in "SpatialUnderstandingDll.cs" all'interno delle strutture "ShapeComponentConstraint" e "ShapeConstraint".
Forma rettangolo si trova su questa superficie
Risolutore posizionamento oggetti
Il risolutore di posizionamento degli oggetti può essere usato per identificare le posizioni ideali nella stanza fisica in cui posizionare gli oggetti. Il risolutore troverà la posizione più adatta in base alle regole e ai vincoli dell'oggetto. Inoltre, le query su oggetti vengono mantenute fino a quando l'oggetto non viene rimosso con chiamate "Solver_RemoveObject" o "Solver_RemoveAllObjects", consentendo il posizionamento vincolato di più oggetti. 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 una posizione 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. Le regole non possono essere violate. 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 di 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 degli oggetti seguente sta cercando una posizione 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 di posizionamento, le dimensioni e l'orientamento. 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 altre query di esempio.
Figura 3: Le caselle blu illustrano il risultato di tre query sul pavimento senza regole di posizione della fotocamera
Quando si risolve la posizione di posizionamento di più oggetti necessari per uno scenario di livello o applicazione, risolvere innanzitutto gli oggetti indispensabili e di grandi dimensioni per massimizzare la probabilità che uno spazio possa essere trovato. L'ordine di posizionamento è importante. Se non è possibile trovare i posizionamenti degli oggetti, provare a usare 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 della sala
Anche se la soluzione di mapping spaziale fornita da HoloLens è progettata per essere sufficientemente generica da soddisfare le esigenze dell'intera gamma di spazi problematici, il modulo di comprensione spaziale è stato creato per supportare le esigenze di due giochi specifici. La soluzione è strutturata in base 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.
"pittura" dello spazio di gioco guidato dall'utente: durante la fase di scansione, l'utente si sposta e si guarda intorno al ritmo di riproduzione, dipingendo di fatto le aree, che devono essere incluse. La mesh generata è importante per fornire commenti e suggerimenti agli utenti durante questa fase. Configurazione della casa o dell'ufficio all'interno: le funzioni di query sono progettate intorno a superfici e pareti piane ad angolo retto. Si tratta di una limitazione temporanea. Tuttavia, durante la fase di scansione, viene completata un'analisi dell'asse primario per ottimizzare la tassellatura della 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ò eseguire l'airtap per chiamare RequestFinish per indicare la fine della fase di analisi. UpdateScan continua a essere chiamato fino a quando il valore restituito non indica che l'elaborazione della DLL è stata completata.
Informazioni sulla mesh
La DLL di understanding archivia internamente lo spazio di riproduzione come griglia di cubi voxel di dimensioni 8 cm. Durante la parte iniziale dell'analisi, viene completata un'analisi primaria dei componenti per determinare gli assi della sala. Internamente, archivia lo spazio voxel allineato a questi assi. Una mesh viene generata circa ogni secondo estraendo l'isosurface dal volume voxel.
Mesh generata prodotta 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.
Checkpoint di sviluppo successivo
Se si segue il percorso di sviluppo di Unity definito, si sta esplorando i blocchi predefiniti principali di MRTK. Da qui è possibile continuare con il blocco predefinito successivo:
In alternativa, passare a Realtà mista funzionalità e API della piattaforma:
È sempre possibile tornare ai checkpoint di sviluppo di Unity in qualsiasi momento.