Ruimtelijke toewijzing in DirectX

Notitie

Dit artikel heeft betrekking op de verouderde systeemeigen WinRT-API's. Voor nieuwe systeemeigen 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 Universeel Windows-platform SDK.

In dit onderwerp wordt code uit het UWP-codevoorbeeld HolographicSpatialMapping 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++-holografische projectsjabloon. De concepten zijn gelijkwaardig voor een C++/WinRT-project, maar u moet de code vertalen.

Ondersteuning voor apparaten

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

Overzicht van DirectX-ontwikkeling

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

Perceptie-API's

De primaire typen die beschikbaar zijn voor de ontwikkeling van ruimtelijke toewijzing zijn als volgt:

  • SpatialSurfaceObserver biedt informatie over oppervlakken in door de toepassing opgegeven ruimtegebieden in de buurt van de gebruiker, in de vorm van SpatialSurfaceInfo-objecten.
  • SpatialSurfaceInfo beschrijft één bestaande ruimteoppervlak, inclusief een unieke id, begrenzingsvolume en tijdstip van laatste wijziging. Het biedt asynchroon een SpatialSurfaceMesh op verzoek.
  • SpatialSurfaceMeshOptions bevat parameters die worden gebruikt om de SpatialSurfaceMesh-objecten aan te passen die zijn aangevraagd bij SpatialSurfaceInfo.
  • SpatialSurfaceMesh vertegenwoordigt de mesh-gegevens voor één ruimteoppervlak. De gegevens voor hoekpuntposities, hoekpuntnormten en driehoekindexen zijn opgenomen in de spatialSurfaceMeshBuffer-objecten van de leden.
  • SpatialSurfaceMeshBuffer verpakt één type mesh-gegevens.

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

  • Uw SpatialSurfaceObserver instellen
    • Roep RequestAccessAsync aan om ervoor te zorgen dat de gebruiker uw toepassing toestemming heeft gegeven om de ruimtelijke toewijzingsmogelijkheden van het apparaat te gebruiken.
    • Maak een SpatialSurfaceObserver-object.
    • Roep SetBoundingVolumes aan om de ruimtegebieden op te geven waarin u informatie wilt over ruimtelijke oppervlakken. 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 geactiveerd wanneer er nieuwe informatie beschikbaar is over de ruimtelijke oppervlakken in de ruimtegebieden die u hebt opgegeven.
  • Process ObservedSurfacesChanged-gebeurtenissen
  • 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 driehoekindexen, hoekpuntposities en hoekpuntnormaten van het net als u deze aanvraagt. Deze gegevens hebben een indeling die rechtstreeks compatibel is met de Direct3D 11-API's die worden gebruikt voor het weergeven van meshes.
    • Vanaf hier kan uw toepassing optioneel de mesh-gegevens analyseren of verwerken en deze gebruiken voor rendering en physics raycasting en botsing.
    • Een belangrijk detail is dat u een schaal moet toepassen op de mesh-hoekpuntposities (bijvoorbeeld in de hoekpuntschaduw die wordt gebruikt voor het weergeven van de meshes), om ze te converteren van de geoptimaliseerde gehele getallen waarin ze zijn opgeslagen in de buffer, naar meters. U kunt deze schaal ophalen door VertexPositionScale aan te roepen.

Problemen oplossen

Voorbeeld van codevoorbeeld voor ruimtelijke toewijzing

Het codevoorbeeld holographic spatial mapping bevat code die u kunt gebruiken om oppervlaktegaas in uw app te laden, inclusief infrastructuur voor het beheren en weergeven van oppervlaktegaas.

Nu wordt uitgelegd hoe u surface-toewijzingsfuncties toevoegt aan uw DirectX-app. U kunt deze code toevoegen aan uw sjabloonproject voor de Windows Holographic-app of u kunt de code volgen door het bovenstaande codevoorbeeld te bekijken. Dit codevoorbeeld is gebaseerd op de app-sjabloon Windows Holographic.

Uw app instellen voor 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 privé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, neemt u deze op 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 voor functies voor ruimtelijke toewijzing

Windows Mixed Reality ondersteunt 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 probeert te gebruiken. Als ruimtelijke toewijzing bijvoorbeeld vereist is voor uw mixed reality-app, moet er een bericht van die strekking worden weergegeven als een gebruiker deze probeert uit te voeren op een apparaat zonder ruimtelijke toewijzing. Of uw app kan een eigen virtuele omgeving weergeven in plaats van de omgeving van de gebruiker, waardoor een ervaring wordt geboden die vergelijkbaar is met wat er zou gebeuren als ruimtelijke toewijzing beschikbaar zou zijn. Met deze API kan uw app in elk geval op de hoogte zijn van wanneer er geen ruimtelijke toewijzingsgegevens worden opgehaald en op de juiste manier worden gereageerd.

Als u het huidige apparaat wilt controleren op ondersteuning voor ruimtelijke toewijzing, controleert u eerst of het UWP-contract zich op niveau 4 of hoger bevindt en roept u SpatialSurfaceObserver::IsSupported() aan. U doet dit als volgt in de context van het codevoorbeeld Holographic Spatial Mapping . Ondersteuning wordt gecontroleerd net voordat toegang wordt aangevraagd.

De API SpatialSurfaceObserver::IsSupported() is beschikbaar vanaf SDK-versie 15063. Indien nodig moet u uw project opnieuw instellen 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 ruimtelijk kan toewijzen.

Toegang tot ruimtelijke toewijzingsgegevens aanvragen

Uw app moet toestemming vragen voor toegang tot ruimtelijke toewijzingsgegevens voordat u surface-waarnemers probeert te maken. Hier volgt een voorbeeld op basis van ons codevoorbeeld voor Surface-toewijzing, met meer details verderop 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-waarnemer maken

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

Van 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 tot ruimtelijke toewijzingsgegevens aanvragen 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 surface-waarnemer configureren om een specifiek begrenzingsvolume te observeren. Hier zien we een doos van 20x20x5 meter, gecentreerd aan de oorsprong van het coördinatensysteem.

// 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 begrenzingsvolumes instellen.

Dit is pseudocode:

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

Het is ook mogelijk om andere begrenzingshapes te gebruiken, zoals een weergave-frustum of een begrenzingsvak 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 surface-toewijzingsgegevens niet 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 waarop insluitende apparaten zijn gekoppeld, omdat die apparaten geen hardware voor ruimtelijke toewijzing hebben. Voor deze apparaten moet u in plaats daarvan vertrouwen op de ruimtelijke fase voor informatie over de omgeving en apparaatconfiguratie van de gebruiker.

Initialiseren en bijwerken van de surface mesh-verzameling

Als de surface-waarnemer is gemaakt, kunnen we doorgaan met het initialiseren van onze oppervlaktegaasverzameling. Hier gebruiken we de pull-model-API om de huidige set waargenomen oppervlakken direct 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 push-model beschikbaar voor het ophalen van surface mesh-gegevens. U kunt uw app zo ontwerpen dat alleen het pull-model wordt gebruikt als u dat wilt. In dat geval vraagt u om de zoveel tijd gegevens op , bijvoorbeeld eenmaal per frame, of tijdens een specifieke periode, zoals tijdens het instellen van games. Als dat het zo is, hebt u de bovenstaande code nodig.

In ons codevoorbeeld hebben we ervoor gekozen om het gebruik van beide modellen voor pedagogische doeleinden te demonstreren. Hier abonneren we ons op een gebeurtenis om up-to-date 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 op deze gebeurtenissen te reageren. Laten we eens bekijken hoe we dit doen.

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

De surface mesh-gegevens worden geleverd in een alleen-lezen kaart 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 niet in onze verzameling voorkomen. Details over hoe de gegevens worden opgeslagen in onze voorbeeld-app, worden verderop 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 oppervlaktegaas verwijderen die in onze surface mesh collectie zitten, maar die niet meer in de systeemverzameling zitten. 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 zich in de systeemverzameling bevindt. Als het niet in de systeemverzameling staat, verwijderen we het uit de onze.

Vanuit onze gebeurtenis-handler in AppMain.cpp:

m_meshCollection->PruneMeshCollection(surfaceCollection);

De implementatie van mesh-snoeien 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 detail in op hoe u de gegevens kunt gebruiken.

In ons codevoorbeeld hebben we ervoor gekozen om de oppervlaktenetten te gebruiken voor rendering. Dit is een veelvoorkomend scenario voor het overnemen van hologrammen achter echte oppervlakken. U kunt ook de meshes of verwerkte versies ervan weergeven om de gebruiker te laten zien welke gebieden van de ruimte worden gescand voordat u app- of gamefunctionaliteit gaat bieden.

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 het oppervlaknet bij te werken: tegen deze tijd hebben we de mesh-informatie al verwerkt en we staan op het punt om de hoekpunt- en indexgegevens op te halen voor gebruik zoals we dat nodig achten.

Vanuit 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, de verwerking en rendering van mesh-gegevens verwerkt. Deze meshes zijn waar de RealtimeSurfaceMeshRenderer eigenlijk een kaart van bijhoudt. Elk item heeft een verwijzing naar de SpatialSurfaceMesh waaruit het afkomstig is, zodat u deze op elk gewenst moment kunt gebruiken om toegang te krijgen tot het mesh-hoekpunt of indexbuffers, of om een transformatie voor het mesh te krijgen. Voorlopig markeren we de mesh als er een update nodig is.

Van SurfaceMesh.cpp:

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

De volgende keer dat het gaas wordt gevraagd om zichzelf te tekenen, wordt eerst de vlag gecontroleerd. 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 verkrijgen we de buffers voor onbewerkte gegevens:

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 worden geleverd:

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: Voor de helperfunctie CreateDirectXBuffer die in het vorige codefragment is gebruikt, raadpleegt u het codevoorbeeld voor Surface-toewijzing: SurfaceMesh.cpp, GetDataFromIBuffer.h. Nu is het maken van de apparaatresource voltooid en wordt de mesh beschouwd als geladen en gereed voor update en render.

Oppervlaktegaas bijwerken en weergeven

Onze SurfaceMesh-klasse heeft een gespecialiseerde updatefunctie. Elke SpatialSurfaceMesh heeft een eigen transformatie en ons voorbeeld gebruikt het huidige coördinatensysteem voor ons SpatialStationaryReferenceFrame om de transformatie te verkrijgen. Vervolgens wordt de modelconstante buffer 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 oppervlaktegaas weer te geven, doen we wat voorbereidend werk voordat we de collectie weer geven. We hebben de shader-pijplijn ingesteld voor de huidige renderingconfiguratie en we stellen de invoerassemblyerfase in. De holografische camerahelperklasse CameraResources.cpp heeft de weergave-/projectieconstantebuffer al ingesteld.

Vanuit 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, lusen we op onze meshes en vertellen we iedereen om zichzelf te tekenen. OPMERKING: Deze voorbeeldcode is niet geoptimaliseerd voor het gebruik van frustum-ruiming, maar u moet deze functie 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 hoekpunt- en indexbuffer, de stride en de modeltransformatiebuffer. Net als bij de draaiende kubus in de sjabloon van de Windows Holographic-app, worden we weergegeven in stereoscopische buffers met behulp van instancing.

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

Weergaveopties met Surface-toewijzing

Het codevoorbeeld voor Surface-toewijzing biedt code voor het weergeven van surface mesh-gegevens met alleen occlusie en voor het weergeven op het scherm van surface mesh-gegevens. Welk pad u kiest, of beide, is afhankelijk van uw toepassing. We doorlopen beide configuraties in dit document.

Occlusiebuffers weergeven voor holografisch 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'-pas. Hier maken we een occlusiebuffer door de mesh-renderer te vragen om alleen diepte weer te geven. In deze configuratie koppelen we geen doelweergave en stelt de mesh-renderer de pixel-shader-fase in op nullptr , zodat de GPU niet de moeite neemt om pixels te tekenen. De geometrie wordt gerasterd 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 tegen de occlusiebuffer van Surface-toewijzing. In dit codevoorbeeld geven we pixels op de kubus een andere kleur weer als ze zich achter een oppervlak bevinden.

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: Voor onze GatherDepthLess-routine raadpleegt u het codevoorbeeld voor Surface-toewijzing: SpecialEffectPixelShader.hlsl.

Surface Mesh-gegevens weergeven naar het beeldscherm

We kunnen ook gewoon de oppervlaktegaas naar de stereoschermbuffers tekenen. We hebben ervoor gekozen om volledige gezichten te tekenen met belichting, maar u kunt draadmodel tekenen, meshes verwerken voordat ze worden weergegeven, een patroonkaart toepassen, enzovoort.

Hier vertelt ons codevoorbeeld de mesh-renderer om de verzameling te tekenen. Deze keer geven we geen diepte-only-pass op, er wordt een pixel-shader gekoppeld en de rendering-pijplijn voltooid 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