Mapping spaziale in DirectXSpatial mapping in DirectX

Nota

Questo articolo si riferisce alle API native di WinRT legacy.This article relates to the legacy WinRT native APIs. Per i nuovi progetti di app native, è consigliabile usare l' API OpenXR.For new native app projects, we recommend using the OpenXR API.

Questo argomento descrive come implementare il mapping spaziale nell'app DirectX, inclusa una spiegazione dettagliata dell'applicazione di esempio per il mapping spaziale in pacchetto con l'SDK piattaforma UWP (Universal Windows Platform).This topic describes how to implement spatial mapping in your DirectX app, including a detailed explanation of the spatial mapping sample application packaged with the Universal Windows Platform SDK.

Questo argomento usa il codice dell'esempio di codice UWP di HolographicSpatialMapping .This topic uses code from the HolographicSpatialMapping UWP code sample.

Nota

I frammenti di codice in questo articolo illustrano attualmente l'uso di C++/CX anziché C + 17 conforme a C++/WinRT come usato nel modello di progetto olografico c++.The code snippets in this article currently demonstrate use of C++/CX rather than C++17-compliant C++/WinRT as used in the C++ holographic project template. I concetti sono equivalenti per un progetto C++/WinRT, anche se sarà necessario tradurre il codice.The concepts are equivalent for a C++/WinRT project, though you will need to translate the code.

Supporto di dispositiviDevice support

FunzionalitàFeature HoloLens (prima generazione)HoloLens (1st gen) HoloLens 2HoloLens 2 Visori VR immersiveImmersive headsets
Mapping spazialeSpatial mapping ✔️✔️ ✔️✔️

Panoramica dello sviluppo DirectXDirectX development overview

Lo sviluppo di applicazioni native per il mapping spaziale USA le API nello spazio dei nomi Windows. Perception. Spatial .Native application development for spatial mapping uses the APIs in the Windows.Perception.Spatial namespace. Queste API offrono il controllo completo della funzionalità di mapping spaziale, nello stesso modo in cui le API di mapping spaziale sono esposte da Unity.These APIs give you full control of spatial mapping functionality, in the same way that spatial mapping APIs are exposed by Unity.

API di percezionePerception APIs

I tipi primari forniti per lo sviluppo di mapping spaziale sono i seguenti:The primary types provided for spatial mapping development are as follows:

  • SpatialSurfaceObserver fornisce informazioni sulle superfici in aree di spazio specificate dall'applicazione in prossimità dell'utente, sotto forma di oggetti SpatialSurfaceInfo.SpatialSurfaceObserver provides information about surfaces in application-specified regions of space near the user, in the form of SpatialSurfaceInfo objects.
  • SpatialSurfaceInfo descrive una singola superficie spaziale esistente, incluso un ID univoco, il volume di delimitazione e l'ora dell'Ultima modifica.SpatialSurfaceInfo describes a single extant spatial surface, including a unique ID, bounding volume and time of last change. Fornirà un SpatialSurfaceMesh in modo asincrono su richiesta.It will provide a SpatialSurfaceMesh asynchronously upon request.
  • SpatialSurfaceMeshOptions contiene i parametri usati per personalizzare gli oggetti SpatialSurfaceMesh richiesti da SpatialSurfaceInfo.SpatialSurfaceMeshOptions contains parameters used to customize the SpatialSurfaceMesh objects requested from SpatialSurfaceInfo.
  • SpatialSurfaceMesh rappresenta i dati mesh per una singola superficie spaziale.SpatialSurfaceMesh represents the mesh data for a single spatial surface. I dati relativi a posizioni vertice, normali vertici e indici triangolare sono contenuti negli oggetti SpatialSurfaceMeshBuffer del membro.The data for vertex positions, vertex normals, and triangle indices is contained in member SpatialSurfaceMeshBuffer objects.
  • SpatialSurfaceMeshBuffer esegue il wrapping di un singolo tipo di dati mesh.SpatialSurfaceMeshBuffer wraps a single type of mesh data.

Quando si sviluppa un'applicazione con queste API, il flusso del programma di base sarà simile al seguente (come illustrato nell'applicazione di esempio descritta di seguito):When developing an application using these APIs, your basic program flow will look like this (as demonstrated in the sample application described below):

  • Configurare il SpatialSurfaceObserverSet up your SpatialSurfaceObserver
    • Chiamare RequestAccessAsyncper assicurarsi che l'utente disponga dell'autorizzazione per l'uso delle funzionalità di mapping spaziale del dispositivo da parte dell'applicazione.Call RequestAccessAsync, to ensure that the user has given permission for your application to use the device's spatial mapping capabilities.
    • Creare un'istanza di un oggetto SpatialSurfaceObserver.Instantiate a SpatialSurfaceObserver object.
    • Chiamare SetBoundingVolumes per specificare le aree di spazio in cui si desiderano informazioni sulle superfici spaziali.Call SetBoundingVolumes to specify the regions of space in which you want information about spatial surfaces. È possibile modificare queste aree in futuro chiamando di nuovo questa funzione.You may modify these regions in the future by calling this function again. Ogni area viene specificata usando un SpatialBoundingVolume.Each region is specified using a SpatialBoundingVolume.
    • Eseguire la registrazione per l'evento ObservedSurfacesChanged , che verrà attivato ogni volta che sono disponibili nuove informazioni sulle superfici spaziali nelle aree di spazio specificate.Register for the ObservedSurfacesChanged event, which will fire whenever new information is available about the spatial surfaces in the regions of space you've specified.
  • Elabora eventi ObservedSurfacesChangedProcess ObservedSurfacesChanged events
    • Nel gestore eventi chiamare GetObservedSurfaces per ricevere una mappa di oggetti SpatialSurfaceInfo.In your event handler, call GetObservedSurfaces to receive a map of SpatialSurfaceInfo objects. Utilizzando questa mappa, è possibile aggiornare i record delle superfici spaziali presenti nell'ambiente dell'utente.Using this map, you can update your records of which spatial surfaces exist in the user's environment.
    • Per ogni oggetto SpatialSurfaceInfo, è possibile eseguire una query su TryGetBounds per determinare gli extent spaziali della superficie, espressa in un sistema di coordinate spaziali a scelta.For each SpatialSurfaceInfo object, you may query TryGetBounds to determine the spatial extents of the surface, expressed in a spatial coordinate system of your choosing.
    • Se si decide di richiedere, mesh per una superficie spaziale, chiamare TryComputeLatestMeshAsync.If you decide to request, mesh for a spatial surface, call TryComputeLatestMeshAsync. È possibile specificare opzioni che specificano la densità dei triangoli e il formato dei dati di mesh restituiti.You may provide options specifying the density of triangles, and the format of the returned mesh data.
  • Rete di ricezione ed elaborazioneReceive and process mesh
    • Ogni chiamata a TryComputeLatestMeshAsync restituirà in modo asincrono un oggetto SpatialSurfaceMesh.Each call to TryComputeLatestMeshAsync will asynchronously return one SpatialSurfaceMesh object.
    • Da questo oggetto è possibile accedere agli oggetti SpatialSurfaceMeshBuffer contenuti, che consente di accedere agli indici del triangolo, alle posizioni dei vertici e alle normali del vertice della mesh se vengono richiesti.From this object, you can access the contained SpatialSurfaceMeshBuffer objects, which gives you access to the triangle indices, vertex positions, and vertex normals of the mesh if you request them. Questi dati saranno in un formato compatibile direttamente con le API Direct3D 11 usate per il rendering dei mesh.This data will be in a format directly compatible with the Direct3D 11 APIs used for rendering meshes.
    • Da qui l'applicazione può facoltativamente analizzare o elaborare i dati mesh e usarli per il rendering e la Raycasting fisica e il conflitto.From here your application can optionally analyze or process the mesh data, and use it for rendering and physics raycasting and collision.
    • Un dettaglio importante da tenere presente è che è necessario applicare una scala alle posizioni dei vertici della mesh, ad esempio nel vertex shader usato per il rendering delle mesh, per convertirle dalle unità Integer ottimizzate in cui sono archiviate nel buffer, ai contatori.One important detail to note is that you must apply a scale to the mesh vertex positions (for example in the vertex shader used for rendering the meshes), to convert them from the optimized integer units in which they're stored in the buffer, to meters. È possibile recuperare questa scala chiamando VertexPositionScale.You can retrieve this scale by calling VertexPositionScale.

Risoluzione dei problemiTroubleshooting

Procedura dettagliata di esempio di codice di mapping spazialeSpatial Mapping code sample walkthrough

L'esempio di codice di mapping spaziale olografico include codice che è possibile usare per iniziare a caricare mesh di superficie nell'app, inclusa l'infrastruttura per la gestione e il rendering di mesh di superficie.The Holographic Spatial Mapping code sample includes code that you can use to get started loading surface meshes into your app, including infrastructure for managing and rendering surface meshes.

A questo punto, viene illustrato come aggiungere funzionalità di mapping di superficie all'app DirectX.Now, we walk through how to add surface-mapping capability to your DirectX app. È possibile aggiungere questo codice al progetto di modello di app Windows olografico oppure è possibile seguire l'esempio di codice riportato in precedenza.You can add this code to your Windows Holographic app template project, or you can follow along by browsing through the code sample mentioned above. Questo esempio di codice è basato sul modello di app olografico di Windows.This code sample is based on the Windows Holographic app template.

Configurare l'app per l'uso della funzionalità spatialPerceptionSet up your app to use the spatialPerception capability

L'app può usare la funzionalità di mapping spaziale.Your app can use the spatial mapping capability. Questa operazione è necessaria perché la mesh spaziale è una rappresentazione dell'ambiente dell'utente, che può essere considerato dati privati.This is necessary because the spatial mesh is a representation of the user's environment, which may be considered private data. Dichiarare questa funzionalità nel file Package. appxmanifest per l'app.Declare this capability in the package.appxmanifest file for your app. Ecco un esempio:Here's an example:

<Capabilities>
  <uap2:Capability Name="spatialPerception" />
</Capabilities>

La funzionalità deriva dallo spazio dei nomi uap2 .The capability comes from the uap2 namespace. Per ottenere l'accesso a questo spazio dei nomi nel manifesto, includerlo come attributo xlmns nell' < elemento> del pacchetto.To get access to this namespace in your manifest, include it as an xlmns attribute in the <Package> element. Ecco un esempio:Here's an example:

<Package
    xmlns="https://schemas.microsoft.com/appx/manifest/foundation/windows10"
    xmlns:mp="https://schemas.microsoft.com/appx/2014/phone/manifest"
    xmlns:uap="https://schemas.microsoft.com/appx/manifest/uap/windows10"
    xmlns:uap2="https://schemas.microsoft.com/appx/manifest/uap/windows10/2"
    IgnorableNamespaces="uap uap2 mp"
    >

Verificare il supporto della funzionalità di mapping spazialeCheck for spatial mapping feature support

La realtà mista di Windows supporta un'ampia gamma di dispositivi, inclusi i dispositivi, che non supportano il mapping spaziale.Windows Mixed Reality supports a wide range of devices, including devices, which don't support spatial mapping. Se l'app può usare il mapping spaziale o deve usare il mapping spaziale per fornire funzionalità, deve verificare che il mapping spaziale sia supportato prima di provare a usarlo.If your app can use spatial mapping, or must use spatial mapping, to provide functionality, it should check to make sure spatial mapping is supported before trying to use it. Se, ad esempio, il mapping spaziale è richiesto dall'app per la realtà mista, viene visualizzato un messaggio in tal senso se un utente prova a eseguirlo su un dispositivo senza mapping spaziale.For example, if spatial mapping is required by your mixed reality app, it should display a message to that effect if a user tries running it on a device without spatial mapping. In alternativa, l'app può eseguire il rendering del proprio ambiente virtuale al posto dell'ambiente dell'utente, offrendo un'esperienza simile a quella che verrebbe eseguita se il mapping spaziale fosse disponibile.Or, your app can render its own virtual environment in place of the user's environment, providing an experience that is similar to what would happen if spatial mapping were available. In ogni caso, questa API consente all'app di essere a conoscenza di quando non ottiene i dati di mapping spaziale e risponde nel modo appropriato.In any event, this API allows your app to be aware of when it won't get spatial mapping data and respond in the appropriate way.

Per controllare il dispositivo corrente per il supporto del mapping spaziale, verificare prima di tutto che il contratto UWP sia al livello 4 o superiore, quindi chiamare SpatialSurfaceObserver:: di supporto ().To check the current device for spatial mapping support, first make sure the UWP contract is at level 4 or greater and then call SpatialSurfaceObserver::IsSupported(). Di seguito viene illustrato come eseguire questa operazione nel contesto dell'esempio di codice di mapping spaziale olografico .Here's how to do so in the context of the Holographic Spatial Mapping code sample. Il supporto viene verificato immediatamente prima di richiedere l'accesso.Support is checked just before requesting access.

L'API SpatialSurfaceObserver:: di supporto () è disponibile a partire dalla versione 15063 dell'SDK.The SpatialSurfaceObserver::IsSupported() API is available starting in SDK version 15063. Se necessario, ridestinare il progetto alla versione della piattaforma 15063 prima di usare questa API.If necessary, retarget your project to platform version 15063 before using this API.

if (m_surfaceObserver == nullptr)
   {
       using namespace Windows::Foundation::Metadata;
       if (ApiInformation::IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 4))
       {
           if (!SpatialSurfaceObserver::IsSupported())
           {
               // The current system does not have spatial mapping capability.
               // Turn off spatial mapping.
               m_spatialPerceptionAccessRequested = true;
               m_surfaceAccessAllowed = false;
           }
       }

       if (!m_spatialPerceptionAccessRequested)
       {
           /// etc ...

Quando il contratto UWP è inferiore al livello 4, l'app deve continuare come se il dispositivo fosse in grado di eseguire il mapping spaziale.When the UWP contract is less than level 4, the app should proceed as though the device is capable of doing spatial mapping.

Richiedere l'accesso ai dati di mapping spazialeRequest access to spatial mapping data

L'app deve richiedere l'autorizzazione per accedere ai dati di mapping spaziale prima di provare a creare gli osservatori di superficie.Your app needs to request permission to access spatial mapping data before trying to create any surface observers. Di seguito è riportato un esempio basato sull'esempio di codice Surface mapping, con ulteriori dettagli disponibili più avanti in questa pagina:Here's an example based upon our Surface Mapping code sample, with more details provided later on this page:

auto initSurfaceObserverTask = create_task(SpatialSurfaceObserver::RequestAccessAsync());
initSurfaceObserverTask.then([this, coordinateSystem](Windows::Perception::Spatial::SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // Create a surface observer.
    }
    else
    {
        // Handle spatial mapping unavailable.
    }
}

Creare un osservatore di superficieCreate a surface observer

Lo spazio dei nomi Windows::P erception:: Spatial:: Surfaces include la classe SpatialSurfaceObserver , che osserva uno o più volumi specificati in un SpatialCoordinateSystem.The Windows::Perception::Spatial::Surfaces namespace includes the SpatialSurfaceObserver class, which observes one or more volumes that you specify in a SpatialCoordinateSystem. Usare un'istanza di SpatialSurfaceObserver per accedere ai dati di Surface Mesh in tempo reale.Use a SpatialSurfaceObserver instance to access surface mesh data in real time.

Da AppMain. h:From AppMain.h:

// Obtains surface mapping data from the device in real time.
Windows::Perception::Spatial::Surfaces::SpatialSurfaceObserver^     m_surfaceObserver;
Windows::Perception::Spatial::Surfaces::SpatialSurfaceMeshOptions^  m_surfaceMeshOptions;

Come indicato nella sezione precedente, è necessario richiedere l'accesso ai dati di mapping spaziale prima che l'app possa usarla.As noted in the previous section, you must request access to spatial mapping data before your app can use it. Questo accesso viene concesso automaticamente in HoloLens.This access is granted automatically on the HoloLens.

// The surface mapping API reads information about the user's environment. The user must
// grant permission to the app to use this capability of the Windows Mixed Reality device.
auto initSurfaceObserverTask = create_task(SpatialSurfaceObserver::RequestAccessAsync());
initSurfaceObserverTask.then([this, coordinateSystem](Windows::Perception::Spatial::SpatialPerceptionAccessStatus status)
{
    if (status == SpatialPerceptionAccessStatus::Allowed)
    {
        // If status is allowed, we can create the surface observer.
        m_surfaceObserver = ref new SpatialSurfaceObserver();

Successivamente, è necessario configurare l'osservatore della superficie per osservare un volume di delimitazione specifico.Next, you need to configure the surface observer to observe a specific bounding volume. Qui viene osservato un riquadro 20x20x5 metri, centrato sull'origine del sistema di coordinate.Here, we observe a box that is 20x20x5 meters, centered at the origin of the coordinate system.

// The surface observer can now be configured as needed.

        // In this example, we specify one area to be observed using an axis-aligned
        // bounding box 20 meters in width and 5 meters in height and centered at the
        // origin.
        SpatialBoundingBox aabb =
        {
            { 0.f,  0.f, 0.f },
            {20.f, 20.f, 5.f },
        };

        SpatialBoundingVolume^ bounds = SpatialBoundingVolume::FromBox(coordinateSystem, aabb);
        m_surfaceObserver->SetBoundingVolume(bounds);

È invece possibile impostare più volumi di delimitazione.You can set multiple bounding volumes instead.

Si tratta di pseudocodice:This is pseudocode:

m_surfaceObserver->SetBoundingVolumes(/* iterable collection of bounding volumes*/);

È anche possibile usare altre forme di delimitazione, ad esempio una vista tronco, o un rettangolo di delimitazione che non è allineato all'asse.It's also possible to use other bounding shapes - such as a view frustum, or a bounding box that isn't axis aligned.

Si tratta di pseudocodice:This is pseudocode:

m_surfaceObserver->SetBoundingVolume(
            SpatialBoundingVolume::FromFrustum(/*SpatialCoordinateSystem*/, /*SpatialBoundingFrustum*/)
            );

Se l'app deve eseguire operazioni in modo diverso quando i dati di mapping di superficie non sono disponibili, è possibile scrivere codice per rispondere al caso in cui il SpatialPerceptionAccessStatus non è consentito , ad esempio, non sarà consentito nei PC con dispositivi immersivi collegati perché i dispositivi non hanno hardware per il mapping spaziale.If your app needs to do anything differently when surface mapping data isn't available, you can write code to respond to the case where the SpatialPerceptionAccessStatus isn't Allowed - for example, it won't be allowed on PCs with immersive devices attached because those devices don't have hardware for spatial mapping. Per questi dispositivi, è invece necessario basarsi sulla fase spaziale per ottenere informazioni sull'ambiente e la configurazione del dispositivo dell'utente.For these devices, you should instead rely on the spatial stage for information about the user's environment and device configuration.

Inizializzare e aggiornare la raccolta Surface MeshInitialize and update the surface mesh collection

Se l'osservatore della superficie è stato creato correttamente, è possibile continuare a inizializzare la raccolta di mesh della superficie.If the surface observer was successfully created, we can continue to initialize our surface mesh collection. Qui viene usata l'API del modello pull per ottenere immediatamente il set corrente di superfici osservate:Here, we use the pull model API to get the current set of observed surfaces right away:

auto mapContainingSurfaceCollection = m_surfaceObserver->GetObservedSurfaces();
        for (auto& pair : mapContainingSurfaceCollection)
        {
            // Store the ID and metadata for each surface.
            auto const& id = pair->Key;
            auto const& surfaceInfo = pair->Value;
            m_meshCollection->AddOrUpdateSurface(id, surfaceInfo);
        }

Per ottenere i dati di Surface Mesh è disponibile anche un modello push.There's also a push model available to get surface mesh data. È possibile progettare l'applicazione in modo che usi solo il modello pull, se si sceglie, nel qual caso verrà eseguito il polling dei dati ogni tanto spesso, ad esempio per ogni fotogramma o durante un periodo di tempo specifico, ad esempio durante l'installazione del gioco.You're free to design your app to use only the pull model if you choose, in which case you'll poll for data every so often - say, once per frame - or during a specific time period, such as during game setup. In tal caso, il codice precedente è quello che ti serve.If so, the above code is what you need.

Nell'esempio di codice si è scelto di illustrare l'uso di entrambi i modelli a scopo pedagogico.In our code sample, we chose to demonstrate the use of both models for pedagogical purposes. Qui viene sottoscritto un evento per ricevere dati di Surface Mesh aggiornati ogni volta che il sistema riconosce una modifica.Here, we subscribe to an event to receive up-to-date surface mesh data whenever the system recognizes a change.

m_surfaceObserver->ObservedSurfacesChanged += ref new TypedEventHandler<SpatialSurfaceObserver^, Platform::Object^>(
            bind(&HolographicDesktopAppMain::OnSurfacesChanged, this, _1, _2)
            );

Il codice di esempio è inoltre configurato per rispondere a questi eventi.Our code sample is also configured to respond to these events. Esaminiamo ora come eseguire questa operazione.Let's walk through how we do this.

Nota: Questo potrebbe non essere il modo più efficiente per gestire i dati della mesh nell'app.NOTE: This might not be the most efficient way for your app to handle mesh data. Questo codice viene scritto per maggiore chiarezza e non è ottimizzato.This code is written for clarity and isn't optimized.

I dati della superficie mesh sono disponibili in una mappa di sola lettura che archivia gli oggetti SpatialSurfaceInfo usando Platform:: GUID come valori chiave.The surface mesh data is provided in a read-only map that stores SpatialSurfaceInfo objects using Platform::Guids as key values.

IMapView<Guid, SpatialSurfaceInfo^>^ const& surfaceCollection = sender->GetObservedSurfaces();

Per elaborare questi dati, si cerca prima i valori di chiave che non sono presenti nella raccolta.To process this data, we look first for key values that aren't in our collection. Informazioni dettagliate sulla modalità di archiviazione dei dati nell'app di esempio verranno illustrate più avanti in questo argomento.Details on how the data is stored in our sample app will be presented later in this topic.

// Process surface adds and updates.
for (const auto& pair : surfaceCollection)
{
    auto id = pair->Key;
    auto surfaceInfo = pair->Value;

    if (m_meshCollection->HasSurface(id))
    {
        // Update existing surface.
        m_meshCollection->AddOrUpdateSurface(id, surfaceInfo);
    }
    else
    {
        // New surface.
        m_meshCollection->AddOrUpdateSurface(id, surfaceInfo);
    }
}

È anche necessario rimuovere le mesh di superficie presenti nella raccolta di mesh della superficie, ma che non sono più presenti nella raccolta di sistema.We also have to remove surface meshes that are in our surface mesh collection, but that aren't in the system collection anymore. A tale scopo, è necessario eseguire un'operazione analoga al contrario di quanto appena illustrato per l'aggiunta e l'aggiornamento delle mesh; viene visualizzato un ciclo nella raccolta dell'app e viene verificato se il GUID è presente nella raccolta di sistema.To do so, we need to do something akin to the opposite of what we just showed for adding and updating meshes; we loop on our app's collection, and check to see if the Guid we have is in the system collection. Se non è presente nella raccolta di sistema, viene rimossa dalla nostra.If it's not in the system collection, we remove it from ours.

Dal gestore eventi in AppMain. cpp:From our event handler in AppMain.cpp:

m_meshCollection->PruneMeshCollection(surfaceCollection);

Implementazione della potatura mesh in RealtimeSurfaceMeshRenderer. cpp:The implementation of mesh pruning in RealtimeSurfaceMeshRenderer.cpp:

void RealtimeSurfaceMeshRenderer::PruneMeshCollection(IMapView<Guid, SpatialSurfaceInfo^>^ const& surfaceCollection)
{
    std::lock_guard<std::mutex> guard(m_meshCollectionLock);
    std::vector<Guid> idsToRemove;

    // Remove surfaces that moved out of the culling frustum or no longer exist.
    for (const auto& pair : m_meshCollection)
    {
        const auto& id = pair.first;
        if (!surfaceCollection->HasKey(id))
        {
            idsToRemove.push_back(id);
        }
    }

    for (const auto& id : idsToRemove)
    {
        m_meshCollection.erase(id);
    }
}

Acquisire e usare i buffer di dati di Surface MeshAcquire and use surface mesh data buffers

Il recupero delle informazioni sulla mesh della superficie è semplice quanto il pull di una raccolta di dati e l'elaborazione degli aggiornamenti in tale raccolta.Getting the surface mesh information was as easy as pulling a data collection and processing updates to that collection. A questo punto, verranno fornite informazioni dettagliate su come è possibile usare i dati.Now, we'll go into detail on how you can use the data.

Nell'esempio di codice si è scelto di usare le mesh di superficie per il rendering.In our code example, we chose to use the surface meshes for rendering. Si tratta di uno scenario comune per gli ologrammi occlusione dietro le superfici reali.This is a common scenario for occluding holograms behind real-world surfaces. È anche possibile eseguire il rendering delle mesh o eseguire il rendering delle versioni elaborate di tali mesh per mostrare all'utente quali aree della chat vengono analizzate prima di iniziare a fornire funzionalità per app o giochi.You can also render the meshes, or render processed versions of them, to show the user what areas of the room are scanned before you start providing app or game functionality.

L'esempio di codice avvia il processo quando riceve gli aggiornamenti di Surface Mesh dal gestore eventi descritto nella sezione precedente.The code sample starts the process when it receives surface mesh updates from the event handler that we described in the previous section. La riga di codice importante in questa funzione è la chiamata a Update the Surface mesh: da questo momento abbiamo già elaborato le informazioni di mesh e stiamo per ottenere i dati relativi ai vertici e agli indici da usare nel modo appropriato.The important line of code in this function is the call to update the surface mesh: by this time we have already processed the mesh info, and we're about to get the vertex and index data for use as we see fit.

Da RealtimeSurfaceMeshRenderer. cpp:From RealtimeSurfaceMeshRenderer.cpp:

void RealtimeSurfaceMeshRenderer::AddOrUpdateSurface(Guid id, SpatialSurfaceInfo^ newSurface)
{
    auto options = ref new SpatialSurfaceMeshOptions();
    options->IncludeVertexNormals = true;

    auto createMeshTask = create_task(newSurface->TryComputeLatestMeshAsync(1000, options));
    createMeshTask.then([this, id](SpatialSurfaceMesh^ mesh)
    {
        if (mesh != nullptr)
        {
            std::lock_guard<std::mutex> guard(m_meshCollectionLock);
            '''m_meshCollection[id].UpdateSurface(mesh);'''
        }
    }, task_continuation_context::use_current());
}

Il codice di esempio è progettato in modo che una classe di dati, SurfaceMesh, gestisca l'elaborazione e il rendering dei dati di rete.Our sample code is designed so that a data class, SurfaceMesh, handles mesh data processing and rendering. Questi mesh sono gli elementi di cui RealtimeSurfaceMeshRenderer mantiene effettivamente una mappa.These meshes are what the RealtimeSurfaceMeshRenderer actually keeps a map of. Ognuno di essi contiene un riferimento al SpatialSurfaceMesh da cui proviene, quindi è possibile usarlo ogni volta che è necessario accedere al vertex mesh o ai buffer di indice oppure ottenere una trasformazione per la mesh.Each one has a reference to the SpatialSurfaceMesh it came from, so you can use it anytime you need to access the mesh vertex or index buffers, or get a transform for the mesh. Per il momento, la mesh viene contrassegnata in modo da richiedere un aggiornamento.For now, we flag the mesh as needing an update.

Da SurfaceMesh. cpp:From SurfaceMesh.cpp:

void SurfaceMesh::UpdateSurface(SpatialSurfaceMesh^ surfaceMesh)
{
    m_surfaceMesh = surfaceMesh;
    m_updateNeeded = true;
}

Alla successiva richiesta di tracciatura da parte della mesh, il flag verrà prima controllato.Next time the mesh is asked to draw itself, it will check the flag first. Se è necessario un aggiornamento, i buffer di Vertex e index verranno aggiornati nella GPU.If an update is needed, the vertex and index buffers will be updated on the GPU.

void SurfaceMesh::CreateDeviceDependentResources(ID3D11Device* device)
{
    m_indexCount = m_surfaceMesh->TriangleIndices->ElementCount;
    if (m_indexCount < 3)
    {
        // Not enough indices to draw a triangle.
        return;
    }

In primo luogo, vengono acquisiti i buffer di dati non elaborati:First, we acquire the raw data buffers:

Windows::Storage::Streams::IBuffer^ positions = m_surfaceMesh->VertexPositions->Data;
    Windows::Storage::Streams::IBuffer^ normals   = m_surfaceMesh->VertexNormals->Data;
    Windows::Storage::Streams::IBuffer^ indices   = m_surfaceMesh->TriangleIndices->Data;

Quindi, creiamo buffer del dispositivo Direct3D con i dati mesh forniti da HoloLens:Then, we create Direct3D device buffers with the mesh data provided by the HoloLens:

CreateDirectXBuffer(device, D3D11_BIND_VERTEX_BUFFER, positions, m_vertexPositions.GetAddressOf());
    CreateDirectXBuffer(device, D3D11_BIND_VERTEX_BUFFER, normals,   m_vertexNormals.GetAddressOf());
    CreateDirectXBuffer(device, D3D11_BIND_INDEX_BUFFER,  indices,   m_triangleIndices.GetAddressOf());

    // Create a constant buffer to control mesh position.
    CD3D11_BUFFER_DESC constantBufferDesc(sizeof(SurfaceTransforms), D3D11_BIND_CONSTANT_BUFFER);
    DX::ThrowIfFailed(
        device->CreateBuffer(
            &constantBufferDesc,
            nullptr,
            &m_modelTransformBuffer
            )
        );

    m_loadingComplete = true;
}

Nota: Per la funzione helper CreateDirectXBuffer usata nel frammento di codice precedente, vedere l'esempio di codice di mapping di Surface: SurfaceMesh. cpp, GetDataFromIBuffer. h.NOTE: For the CreateDirectXBuffer helper function used in the previous snippet, see the Surface Mapping code sample: SurfaceMesh.cpp, GetDataFromIBuffer.h. A questo punto, la creazione delle risorse del dispositivo è stata completata e il mesh viene considerato caricato e pronto per l'aggiornamento e il rendering.Now the device resource creation is complete, and the mesh is considered to be loaded and ready for update and render.

Aggiornare ed eseguire il rendering delle mesh di superficieUpdate and render surface meshes

La classe SurfaceMesh dispone di una funzione di aggiornamento specializzata.Our SurfaceMesh class has a specialized update function. Ogni SpatialSurfaceMesh ha una propria trasformazione e l'esempio usa il sistema di coordinate corrente per il SpatialStationaryReferenceFrame per acquisire la trasformazione.Each SpatialSurfaceMesh has its own transform, and our sample uses the current coordinate system for our SpatialStationaryReferenceFrame to acquire the transform. Aggiorna quindi il buffer costante del modello nella GPU.Then it updates the model constant buffer on the GPU.

void SurfaceMesh::UpdateTransform(
    ID3D11DeviceContext* context,
    SpatialCoordinateSystem^ baseCoordinateSystem
    )
{
    if (m_indexCount < 3)
    {
        // Not enough indices to draw a triangle.
        return;
    }

    XMMATRIX transform = XMMatrixIdentity();

    auto tryTransform = m_surfaceMesh->CoordinateSystem->TryGetTransformTo(baseCoordinateSystem);
    if (tryTransform != nullptr)
    {
        transform = XMLoadFloat4x4(&tryTransform->Value);
    }

    XMMATRIX scaleTransform = XMMatrixScalingFromVector(XMLoadFloat3(&m_surfaceMesh->VertexPositionScale));

    XMStoreFloat4x4(
        &m_constantBufferData.vertexWorldTransform,
        XMMatrixTranspose(
            scaleTransform * transform
            )
        );

    // Normals don't need to be translated.
    XMMATRIX normalTransform = transform;
    normalTransform.r[3] = XMVectorSet(0.f, 0.f, 0.f, XMVectorGetW(normalTransform.r[3]));
    XMStoreFloat4x4(
        &m_constantBufferData.normalWorldTransform,
        XMMatrixTranspose(
            normalTransform
        )
        );

    if (!m_loadingComplete)
    {
        return;
    }

    context->UpdateSubresource(
        m_modelTransformBuffer.Get(),
        0,
        NULL,
        &m_constantBufferData,
        0,
        0
        );
}

Quando è il momento di eseguire il rendering delle mesh della superficie, le operazioni di preparazione vengono eseguite prima del rendering della raccolta.When it's time to render surface meshes, we do some prep work before rendering the collection. Viene configurata la pipeline dello shader per la configurazione di rendering corrente e viene configurata la fase dell'assembler di input.We set up the shader pipeline for the current rendering configuration, and we set up the input assembler stage. La classe helper della fotocamera olografica CameraResources. cpp ha già configurato il buffer costante di visualizzazione/proiezione da ora.The holographic camera helper class CameraResources.cpp already has set up the view/projection constant buffer by now.

Da RealtimeSurfaceMeshRenderer:: Render:From RealtimeSurfaceMeshRenderer::Render:

auto context = m_deviceResources->GetD3DDeviceContext();

context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetInputLayout(m_inputLayout.Get());

// Attach our vertex shader.
context->VSSetShader(
    m_vertexShader.Get(),
    nullptr,
    0
    );

// The constant buffer is per-mesh, and will be set as such.

if (depthOnly)
{
    // Explicitly detach the later shader stages.
    context->GSSetShader(nullptr, nullptr, 0);
    context->PSSetShader(nullptr, nullptr, 0);
}
else
{
    if (!m_usingVprtShaders)
    {
        // Attach the passthrough geometry shader.
        context->GSSetShader(
            m_geometryShader.Get(),
            nullptr,
            0
            );
    }

    // Attach our pixel shader.
    context->PSSetShader(
        m_pixelShader.Get(),
        nullptr,
        0
        );
}

Al termine di questa operazione, viene eseguito il loop sulle mesh e si indica a ognuno di disegnarlo.Once this is done, we loop on our meshes and tell each one to draw itself. Nota: Questo codice di esempio non è ottimizzato per l'uso di qualsiasi tipo di eliminazione di tronco, ma è necessario includere questa funzionalità nell'app.NOTE: This sample code isn't optimized to use any sort of frustum culling, but you should include this feature in your app.

std::lock_guard<std::mutex> guard(m_meshCollectionLock);

auto device = m_deviceResources->GetD3DDevice();

// Draw the meshes.
for (auto& pair : m_meshCollection)
{
    auto& id = pair.first;
    auto& surfaceMesh = pair.second;

    surfaceMesh.Draw(device, context, m_usingVprtShaders, isStereo);
}

Le singole mesh sono responsabili della configurazione del buffer di Vertex e index buffer, stride e della trasformazione del modello.The individual meshes are responsible for setting up the vertex and index buffer, stride, and model transform constant buffer. Come per il cubo rotante nel modello di app olografico di Windows, viene eseguito il rendering nei buffer stereoscopici usando le istanze.As with the spinning cube in the Windows Holographic app template, we render to stereoscopic buffers using instancing.

Da SurfaceMesh::D RAW:From SurfaceMesh::Draw:

// The vertices are provided in {vertex, normal} format

const auto& vertexStride = m_surfaceMesh->VertexPositions->Stride;
const auto& normalStride = m_surfaceMesh->VertexNormals->Stride;

UINT strides [] = { vertexStride, normalStride };
UINT offsets [] = { 0, 0 };
ID3D11Buffer* buffers [] = { m_vertexPositions.Get(), m_vertexNormals.Get() };

context->IASetVertexBuffers(
    0,
    ARRAYSIZE(buffers),
    buffers,
    strides,
    offsets
    );

const auto& indexFormat = static_cast<DXGI_FORMAT>(m_surfaceMesh->TriangleIndices->Format);

context->IASetIndexBuffer(
    m_triangleIndices.Get(),
    indexFormat,
    0
    );

context->VSSetConstantBuffers(
    0,
    1,
    m_modelTransformBuffer.GetAddressOf()
    );

if (!usingVprtShaders)
{
    context->GSSetConstantBuffers(
        0,
        1,
        m_modelTransformBuffer.GetAddressOf()
        );
}

context->PSSetConstantBuffers(
    0,
    1,
    m_modelTransformBuffer.GetAddressOf()
    );

context->DrawIndexedInstanced(
    m_indexCount,       // Index count per instance.
    isStereo ? 2 : 1,   // Instance count.
    0,                  // Start index location.
    0,                  // Base vertex location.
    0                   // Start instance location.
    );

Opzioni di rendering con mapping di superficieRendering choices with Surface Mapping

Il codice di esempio Surface mapping fornisce il codice per il rendering di solo occlusione dei dati di Surface Mesh e per il rendering su schermo dei dati della superficie mesh.The Surface Mapping code sample offers code for occlusion-only rendering of surface mesh data, and for on-screen rendering of surface mesh data. Il percorso scelto, o entrambi, dipende dall'applicazione.Which path you choose - or both - depends on your application. In questo documento verranno illustrate in dettaglio entrambe le configurazioni.We'll walk through both configurations in this document.

Rendering dei buffer di occlusione per effetto olograficoRendering occlusion buffers for holographic effect

Per iniziare, deselezionare la visualizzazione della destinazione di rendering per la fotocamera virtuale corrente.Start by clearing the render target view for the current virtual camera.

Da AppMain. cpp:From AppMain.cpp:

context->ClearRenderTargetView(pCameraResources->GetBackBufferRenderTargetView(), DirectX::Colors::Transparent);

Si tratta di un passaggio di pre-rendering.This is a "pre-rendering" pass. Qui viene creato un buffer di occlusione chiedendo al renderer mesh di eseguire il rendering solo della profondità.Here, we create an occlusion buffer by asking the mesh renderer to render only depth. In questa configurazione non viene collegata una visualizzazione della destinazione di rendering e il renderer di mesh imposta la fase pixel shader su nullptr in modo che la GPU non preferisca disegnare pixel.In this configuration, we don't attach a render target view, and the mesh renderer sets the pixel shader stage to nullptr so that the GPU doesn't bother to draw pixels. La geometria verrà rasterizzata nel buffer di profondità e la pipeline grafica verrà arrestata.The geometry will be rasterized to the depth buffer, and the graphics pipeline will stop there.

// Pre-pass rendering: Create occlusion buffer from Surface Mapping data.
context->ClearDepthStencilView(pCameraResources->GetSurfaceDepthStencilView(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

// Set the render target to null, and set the depth target occlusion buffer.
// We will use this same buffer as a shader resource when drawing holograms.
context->OMSetRenderTargets(0, nullptr, pCameraResources->GetSurfaceOcclusionDepthStencilView());

// The first pass is a depth-only pass that generates an occlusion buffer we can use to know which
// hologram pixels are hidden behind surfaces in the environment.
m_meshCollection->Render(pCameraResources->IsRenderingStereoscopic(), true);

È possibile creare ologrammi con un test di profondità aggiuntivo sul buffer di occlusione del mapping della superficie.We can draw holograms with an extra depth test against the Surface Mapping occlusion buffer. In questo esempio di codice viene eseguito il rendering dei pixel sul cubo con un colore diverso se si trovano dietro una superficie.In this code sample, we render pixels on the cube a different color if they are behind a surface.

Da AppMain. cpp:From AppMain.cpp:

// Hologram rendering pass: Draw holographic content.
context->ClearDepthStencilView(pCameraResources->GetHologramDepthStencilView(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

// Set the render target, and set the depth target drawing buffer.
ID3D11RenderTargetView *const targets[1] = { pCameraResources->GetBackBufferRenderTargetView() };
context->OMSetRenderTargets(1, targets, pCameraResources->GetHologramDepthStencilView());

// Render the scene objects.
// In this example, we draw a special effect that uses the occlusion buffer we generated in the
// Pre-Pass step to render holograms using X-Ray Vision when they are behind physical objects.
m_xrayCubeRenderer->Render(
    pCameraResources->IsRenderingStereoscopic(),
    pCameraResources->GetSurfaceOcclusionShaderResourceView(),
    pCameraResources->GetHologramOcclusionShaderResourceView(),
    pCameraResources->GetDepthTextureSamplerState()
    );

In base al codice di SpecialEffectPixelShader. HLSL:Based on code from SpecialEffectPixelShader.hlsl:

// Draw boundaries
min16int surfaceSum = GatherDepthLess(envDepthTex, uniSamp, input.pos.xy, pixelDepth, input.idx.x);

if (surfaceSum <= -maxSum)
{
    // The pixel and its neighbors are behind the surface.
    // Return the occluded 'X-ray' color.
    return min16float4(0.67f, 0.f, 0.f, 1.0f);
}
else if (surfaceSum < maxSum)
{
    // The pixel and its neighbors are a mix of in front of and behind the surface.
    // Return the silhouette edge color.
    return min16float4(1.f, 1.f, 1.f, 1.0f);
}
else
{
    // The pixel and its neighbors are all in front of the surface.
    // Return the color of the hologram.
    return min16float4(input.color, 1.0f);
}

Nota: Per la routine GatherDepthLess , vedere l'esempio di codice di mapping di Surface: SpecialEffectPixelShader. HLSL.Note: For our GatherDepthLess routine, see the Surface Mapping code sample: SpecialEffectPixelShader.hlsl.

Rendering dei dati di Surface Mesh sullo schermoRendering surface mesh data to the display

È anche possibile creare solo le mesh della superficie nei buffer di visualizzazione stereo.We can also just draw the surface meshes to the stereo display buffers. Abbiamo scelto di creare i visi completi con l'illuminazione, ma è possibile creare wireframe, elaborare mesh prima del rendering, applicare una mappa di trama e così via.We chose to draw full faces with lighting, but you're free to draw wireframe, process meshes before rendering, apply a texture map, and so on.

Qui, l'esempio di codice indica al renderer mesh di creare la raccolta.Here, our code sample tells the mesh renderer to draw the collection. Questa volta non viene specificato un passaggio di solo profondità, verrà collegato un pixel shader e verrà completata la pipeline di rendering usando le destinazioni specificate per la fotocamera virtuale corrente.This time we don't specify a depth-only pass, it'll attach a pixel shader and complete the rendering pipeline using the targets that we specified for the current virtual camera.

// Spatial Mapping mesh rendering pass: Draw Spatial Mapping mesh over the world.
context->ClearDepthStencilView(pCameraResources->GetSurfaceOcclusionDepthStencilView(), D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

// Set the render target to the current holographic camera's back buffer, and set the depth buffer.
ID3D11RenderTargetView *const targets[1] = { pCameraResources->GetBackBufferRenderTargetView() };
context->OMSetRenderTargets(1, targets, pCameraResources->GetSurfaceDepthStencilView());

// This drawing pass renders the surface meshes to the stereoscopic display. The user will be
// able to see them while wearing the device.
m_meshCollection->Render(pCameraResources->IsRenderingStereoscopic(), false);

Vedere ancheSee also