Ruimtelijke toewijzing in DirectX

Notitie

Dit artikel heeft betrekking op de verouderde, native WinRT-API's. Voor nieuwe native app-projecten raden we u aan de OpenXR API te gebruiken.

In dit onderwerp wordt beschreven hoe u ruimtelijke toewijzing implementeert in uw DirectX-app, inclusief een gedetailleerde uitleg van de voorbeeldtoepassing voor ruimtelijke toewijzing die is verpakt met de Universal Windows Platform SDK.

In dit onderwerp wordt code uit het HolographicSpatialMapping UWP-codevoorbeeld gebruikt.

Notitie

De codefragmenten in dit artikel demonstreren momenteel het gebruik van C++/CX in plaats van C++17-compatibele C++/WinRT, zoals gebruikt in de C++-holographic-projectsjabloon. De concepten zijn gelijk aan een C++/WinRT-project, maar u moet de code vertalen.

Ondersteuning voor apparaten

Functie HoloLens (eerste generatie) HoloLens 2 Immersive headsets
Ruimtelijke toewijzing ✔️ ✔️

Overzicht van DirectX-ontwikkeling

Systeemeigen toepassingsontwikkeling voor ruimtelijke toewijzing maakt gebruik van de API's in de Windows. Perception.Spatial namespace. Deze API's bieden u volledige controle over de functionaliteit voor ruimtelijke toewijzing, op dezelfde manier als de API's voor ruimtelijke toewijzing beschikbaar worden gesteld door Unity.

Perception-API's

De primaire typen voor het ontwikkelen van ruimtelijke toewijzingen zijn als volgt:

  • SpatialSurfaceObserver biedt informatie over de oppervlakten in toepassings opgegeven ruimtegebieden in de buurt van de gebruiker, in de vorm van SpatialSurf surfaceInfo-objecten.
  • Spatial Surf surfaceInfo beschrijft één ruimtelijk gebied dat wordt gebruikt, met inbegrip van een unieke id, begrenzingsvolume en tijdstip van de laatste wijziging. Het biedt op aanvraag asynchroon een SpatialSurfaceMesh.
  • SpatialSurfaceMeshOptions bevat parameters die worden gebruikt om de SpatialSurfaceMesh-objecten aan te passen die zijn aangevraagd bij SpatialSurfaceInfo.
  • SpatialSurf surfaceMesh vertegenwoordigt de mesh-gegevens voor één ruimtelijk oppervlak. De gegevens voor hoekpuntposities, hoekpuntnormen en driehoeksindexen bevinden zich in lid SpatialSurfaceMeshBuffer-objecten.
  • SpatialSurfaceMeshBuffer verpakt één type mesh-gegevens.

Wanneer u een toepassing ontwikkelt met behulp van deze API's, ziet uw basisprogrammastroom er als deze uit (zoals wordt gedemonstreerd in de voorbeeldtoepassing die hieronder wordt beschreven):

  • Uw SpatialSurfaceObserver instellen
    • Roep RequestAccessAsync aanom ervoor te zorgen dat de gebruiker uw toepassing toestemming heeft gegeven voor het gebruik van de ruimtelijke toewijzingsmogelijkheden van het apparaat.
    • Instantieer een SpatialSurfaceObserver-object.
    • Roep SetBoundingVolumes aan om de ruimtegebieden op te geven waarin u informatie wilt over ruimtelijke oppervlakten. U kunt deze regio's in de toekomst wijzigen door deze functie opnieuw aan te roepen. Elke regio wordt opgegeven met behulp van een SpatialBoundingVolume.
    • Registreer u voor de gebeurtenis ObservedSurfacesChanged, die wordt gegeven wanneer er nieuwe informatie beschikbaar is over de ruimtelijke oppervlakten in de gebieden van de ruimte die u hebt opgegeven.
  • ObservedSurfacesChanged-gebeurtenissen verwerken
    • Roep in uw gebeurtenis-handler GetObservedSurfaces aan om een kaart van SpatialSurfaceInfo-objecten te ontvangen. Met behulp van deze kaart kunt u uw records bijwerken van de ruimtelijke oppervlakten in de omgeving van de gebruiker.
    • Voor elk SpatialSurf surfaceInfo-object kunt u een query uitvoeren op TryGetBounds om de ruimtelijke omvang van het oppervlak te bepalen, uitgedrukt in een ruimtelijk coördinaatsysteem van uw keuze.
    • Als u besluit om een aanvraag in te sturen voor een ruimtelijk gebied, roept u TryComputeLatestMeshAsync aan. U kunt opties opgeven om de dichtheid van driehoeken en de indeling van de geretourneerde mesh-gegevens op te geven.
  • Ontvangst- en proces-mesh
    • Elke aanroep van TryComputeLatestMeshAsync retourneert asynchroon één SpatialSurfaceMesh-object.
    • Vanuit dit object hebt u toegang tot de ingesloten SpatialSurfaceMeshBuffer-objecten, waarmee u toegang krijgt tot de driehoeksindexen, hoekpuntposities en hoekpuntnormalen van de mesh als u ze aanvraagt. Deze gegevens hebben een indeling die rechtstreeks compatibel is met de Direct3D 11-API's die worden gebruikt voor het weergeven van meshes.
    • Hier kan uw toepassing eventueel de mesh-gegevens analyseren of verwerken en deze gebruiken voor rendering en fysica-raycasting en -aanrijding.
    • Een belangrijk detail is dat u een schaal moet toepassen op de mesh-hoekpuntposities (bijvoorbeeld in de hoekpunt-shader die wordt gebruikt voor het weergeven van de meshes), om deze te converteren van de geoptimaliseerde gehele getallen-eenheden waarin ze in de buffer worden opgeslagen, naar meters. U kunt deze schaal ophalen door VertexPositionScale aan te roepen.

Problemen oplossen

Voorbeeld van code voor ruimtelijke toewijzing

Het holographic Spatial Mapping-codevoorbeeld bevat code die u kunt gebruiken om surface meshes in uw app te laden, inclusief infrastructuur voor het beheren en weergeven van surface meshes.

We gaan nu kijken hoe u de mogelijkheid voor surface-mapping toevoegt aan uw DirectX-app. U kunt deze code toevoegen aan uw Windows Holographic-app-sjabloonproject, of u kunt de code volgen door het bovenstaande codevoorbeeld te bekijken. Dit codevoorbeeld is gebaseerd op de sjabloon Windows Holographic-app.

Uw app instellen voor het gebruik van de mogelijkheid spatialPerception

Uw app kan gebruikmaken van de mogelijkheid voor ruimtelijke toewijzing. Dit is nodig omdat de ruimtelijke mesh een weergave is van de omgeving van de gebruiker, die kan worden beschouwd als persoonlijke gegevens. Declareer deze mogelijkheid in het bestand package.appxmanifest voor uw app. Hier volgt een voorbeeld:

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

De mogelijkheid is afkomstig van de uap2-naamruimte. Als u toegang wilt krijgen tot deze naamruimte in uw manifest, moet u deze opnemen als een xlmns-kenmerk in het <> pakketelement. Hier volgt een voorbeeld:

<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"
    >

Controleren op ondersteuning van ruimtelijke toewijzingsfunctie

Windows Mixed Reality biedt ondersteuning voor een breed scala aan apparaten, waaronder apparaten, die geen ondersteuning bieden voor ruimtelijke toewijzing. Als uw app ruimtelijke toewijzing kan gebruiken of ruimtelijke toewijzing moet gebruiken om functionaliteit te bieden, moet deze controleren of ruimtelijke toewijzing wordt ondersteund voordat u deze gaat gebruiken. Als ruimtelijke toewijzing bijvoorbeeld vereist is voor uw mixed reality-app, moet er een bericht voor dat effect worden weergegeven als een gebruiker het op een apparaat probeert uit te werken zonder ruimtelijke toewijzing. Of uw app kan een eigen virtuele omgeving renderen in plaats van de omgeving van de gebruiker, waardoor een ervaring wordt aangeboden die vergelijkbaar is met wat er zou gebeuren als er ruimtelijke toewijzing beschikbaar zou zijn. In elk geval kan uw app met deze API rekening houden met wanneer er geen ruimtelijke toewijzingsgegevens worden verzameld en op de juiste manier worden gereageerd.

Als u het huidige apparaat wilt controleren op ondersteuning voor ruimtelijke toewijzing, moet u eerst controleren of het UWP-contract op niveau 4 of hoger is en vervolgens SpatialSurfaceObserver::IsSupported() aanroepen. Dit doet u als voorbeeld in de context van het codevoorbeeld Voor ruimtelijke toewijzing van Holographic. Ondersteuning wordt net vóór het aanvragen van toegang gecontroleerd.

De SpatialSurfaceObserver::IsSupported()-API is beschikbaar vanaf SDK-versie 15063. Wijs uw project zo nodig opnieuw toe op platformversie 15063 voordat u deze API gebruikt.

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 ...

Wanneer het UWP-contract kleiner is dan niveau 4, moet de app doorgaan alsof het apparaat ruimtelijke toewijzing kan doen.

Toegang aanvragen tot ruimtelijke toewijzingsgegevens

Uw app moet toestemming vragen voor toegang tot ruimtelijke toewijzingsgegevens voordat u surface-waarnemers gaat maken. Hier is een voorbeeld op basis van ons surface mapping-codevoorbeeld, met meer informatie verder op deze pagina:

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.
    }
}

Een surface observer maken

De naamruimte Windows::P erception::Spatial::Surfaces bevat de klasse SpatialSurfaceObserver, die een of meer volumes observeert die u opgeeft in spatialCoordinateSystem. Gebruik een SpatialSurfaceObserver-exemplaar om in realtime toegang te krijgen tot surface mesh-gegevens.

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

Zoals vermeld in de vorige sectie, moet u toegang aanvragen tot ruimtelijke toewijzingsgegevens voordat uw app deze kan gebruiken. Deze toegang wordt automatisch verleend op de 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();

Vervolgens moet u de waarnemer van het oppervlak configureren om een specifiek begrensingsvolume te observeren. Hier zien we een vak van 20x20x5 meter, gecentreerd op de oorsprong van het coördinaatsysteem.

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

U kunt in plaats daarvan meerdere begrendelen instellen.

Dit is pseudocode:

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

Het is ook mogelijk om andere begrensingsvormen te gebruiken, zoals een view frustum of een begrensingsvak dat niet op de as is uitgelijnd.

Dit is pseudocode:

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

Als uw app iets anders moet doen wanneer er geen gegevens over de toewijzing van de surface beschikbaar zijn, kunt u code schrijven om te reageren op het geval waarin spatialPerceptionAccessStatus niet is toegestaan. Dit is bijvoorbeeld niet toegestaan op pc's met insluitingsapparaten die zijn gekoppeld, omdat deze apparaten geen hardware hebben voor ruimtelijke toewijzing. Voor deze apparaten moet u in plaats daarvan vertrouwen op de ruimtelijke fase voor informatie over de omgeving en apparaatconfiguratie van de gebruiker.

De Surface Mesh-verzameling initialiseren en bijwerken

Als de surface observer is gemaakt, kunnen we doorgaan met het initialiseren van onze Surface Mesh-verzameling. Hier gebruiken we de PULL-model-API om direct de huidige set waargenomen oppervlakten op te halen:

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

Er is ook een pushmodel beschikbaar om surface mesh-gegevens op te halen. U kunt uw app zo ontwerpen dat deze alleen het pull-model gebruikt als u wilt. In dat geval pollt u om de zoveel tijd naar gegevens , bijvoorbeeld één keer per frame, of tijdens een bepaalde periode, zoals tijdens het instellen van de game. Zo ja, dan hebt u de bovenstaande code nodig.

In ons codevoorbeeld hebben we ervoor gekozen om het gebruik van beide modellen voor numerieke doeleinden te demonstreren. Hier abonneren we ons op een gebeurtenis om bijgewerkte Surface Mesh-gegevens te ontvangen wanneer het systeem een wijziging herkent.

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

Ons codevoorbeeld is ook geconfigureerd om te reageren op deze gebeurtenissen. Laten we eens kijken hoe we dit doen.

OPMERKING: Dit is mogelijk niet de meest efficiënte manier voor uw app om mesh-gegevens te verwerken. Deze code is voor de duidelijkheid geschreven en is niet geoptimaliseerd.

De surface mesh-gegevens worden geleverd in een alleen-lezenkaart waarin SpatialSurfaceInfo-objecten worden opgeslagen met Platform::Guids als sleutelwaarden.

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

Voor het verwerken van deze gegevens zoeken we eerst naar sleutelwaarden die zich niet in onze verzameling hebben. Meer informatie over hoe de gegevens worden opgeslagen in onze voorbeeld-app wordt verder in dit onderwerp weergegeven.

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

We moeten ook surface meshes verwijderen die zich in onze surface mesh-verzameling, maar die zich niet meer in de systeemverzameling. Om dit te doen, moeten we iets doen dat lijkt op het tegenovergestelde van wat we zojuist hebben laten zien voor het toevoegen en bijwerken van meshes; We herhalen de verzameling van onze app en controleren of de Guid die we hebben, zich in de systeemverzameling bevat. Als deze niet in de systeemverzameling staat, verwijderen we deze van de onze.

Vanuit onze gebeurtenis-handler in AppMain.cpp:

m_meshCollection->PruneMeshCollection(surfaceCollection);

De implementatie van mesh-ruiming 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);
    }
}

Surface Mesh-gegevensbuffers verkrijgen en gebruiken

Het ophalen van de surface mesh-informatie was net zo eenvoudig als het ophalen van een gegevensverzameling en het verwerken van updates voor die verzameling. Nu gaan we in op de manier waarop u de gegevens kunt gebruiken.

In ons codevoorbeeld hebben we ervoor gekozen om de surface meshes te gebruiken voor rendering. Dit is een veelvoorkomende situatie voor het occluseren van hologrammen achter echte oppervlakken. U kunt de meshes ook renderen of verwerkte versies ervan renderen om de gebruiker te laten zien welke gebieden van de ruimte worden gescand voordat u begint met het bieden van app- of gamefunctionaliteit.

Het codevoorbeeld start het proces wanneer het Surface Mesh-updates ontvangt van de gebeurtenis-handler die we in de vorige sectie hebben beschreven. De belangrijke coderegel in deze functie is de aanroep om de surface mesh bij te werken: op dit moment hebben we de mesh-gegevens al verwerkt en staan we op het punt om de hoekpunt- en indexgegevens op te halen voor gebruik zoals we dat willen.

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

Onze voorbeeldcode is zo ontworpen dat een gegevensklasse, SurfaceMesh, mesh-gegevensverwerking en -rendering verwerkt. Deze meshes zijn waar realtimeSurfaceMeshRenderer een kaart van bewaart. Elke toepassing heeft een verwijzing naar de SpatialSurfaceMesh waar het vandaan komt, zodat u deze telkens kunt gebruiken wanneer u toegang nodig hebt tot de mesh-hoekpunt- of indexbuffers, of een transformatie voor de mesh kunt krijgen. Op dit moment markeren we de mesh als er een update nodig is.

Vanuit SurfaceMesh.cpp:

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

De volgende keer dat de mesh wordt gevraagd om zichzelf te tekenen, wordt eerst de vlag controleren. Als er een update nodig is, worden de hoekpunt- en indexbuffers bijgewerkt op de GPU.

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

Eerst halen we de onbewerkte gegevensbuffers op:

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;

Vervolgens maken we Direct3D-apparaatbuffers met de mesh-gegevens die door de 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;
}

OPMERKING: Zie het codevoorbeeld Surface Mapping: SurfaceMesh.cpp, GetDataFromIBuffer.h voor de helperfunctie CreateDirectXBuffer die in het vorige codefragment wordt gebruikt. Het maken van de apparaatresource is nu voltooid en de mesh wordt beschouwd als geladen en gereed voor update en render.

Surface Meshes bijwerken en renderen

Onze SurfaceMesh-klasse heeft een gespecialiseerde updatefunctie. Elke SpatialSurfaceMesh heeft een eigen transformatie en ons voorbeeld gebruikt het huidige coördinaatsysteem voor het SpatialStationaryReferenceFrame om de transformatie te verkrijgen. Vervolgens wordt de constante buffer van het model op de GPU bijgewerkt.

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

Wanneer het tijd is om surface meshes weer te geven, doen we wat voorbereidingswerkzaamheden voordat we de verzameling gaan weergeven. We stellen de shader-pijplijn in voor de huidige renderingconfiguratie en we stellen de fase van de invoerassemblage in. De helperklasse CameraResources.cpp voor de holografische camera heeft de constante buffer voor weergave/projectie al ingesteld.

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

Zodra dit is gedaan, herhalen we onze meshes en vertellen we iedereen om zichzelf te tekenen. OPMERKING: Deze voorbeeldcode is niet geoptimaliseerd voor het gebruik van een frustum-sorteerfunctie, maar u moet deze functie wel opnemen in uw 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);
}

De afzonderlijke meshes zijn verantwoordelijk voor het instellen van de constante buffer voor hoekpunt- en indexbuffer, stap en modeltransformatie. Net als bij de draaiende kubus in Windows Holographic-app-sjabloon, maken we stereoscopische buffers weer met behulp van instancing.

Van SurfaceMesh::D raw:

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

Keuzes voor rendering met Surface Mapping

Het codevoorbeeld Surface Mapping biedt code voor occlusie-only rendering van surface mesh-gegevens en voor weergave op het scherm van surface mesh-gegevens. Welk pad u kiest, of beide, is afhankelijk van uw toepassing. We bekijken beide configuraties in dit document.

Occlusiebuffers weergeven voor holografische effect

Begin met het wissen van de weergavedoelweergave voor de huidige virtuele camera.

Vanuit AppMain.cpp:

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

Dit is een 'pre-rendering'-pass. Hier maken we een occlusiebuffer door de mesh-renderer te vragen alleen diepte weer te geven. In deze configuratie koppelen we geen weergave van een renderdoel en stelt de mesh-renderer de pixel-shaderfase in op nullptr, zodat de GPU geen moeite heeft om pixels te tekenen. De geometrie wordt ge rasteriseerd naar de dieptebuffer en de grafische pijplijn stopt daar.

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

We kunnen hologrammen tekenen met een extra dieptetest op de occlusiebuffer Surface Mapping. In dit codevoorbeeld geven we pixels op de kubus een andere kleur als ze zich achter een oppervlak vormen.

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

Op basis van code van 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);
}

Opmerking: Zie het Codevoorbeeld voor Surface Mapping: SpecialEffectPixelShader.hlsl voor onze GatherDepthLess-routine.

Mesh-gegevens van het oppervlak weergeven naar de weergave

We kunnen ook gewoon de surface meshes naar de stereo-displaybuffers tekenen. We hebben ervoor gekozen om volledige gezichten met belichting te tekenen, maar u kunt wireframe tekenen, meshes verwerken vóór rendering, een patroonkaart toepassen, en meer.

Hier vertelt ons codevoorbeeld de mesh-renderer om de verzameling te tekenen. Deze keer geven we geen diepte-only-pass op, maar koppelen we een pixel-shader en voltooien we de renderingpijplijn met behulp van de doelen die we hebben opgegeven voor de huidige virtuele 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);

Zie ook