Rumslig mappning i DirectX

Anteckning

Den här artikeln handlar om äldre inbyggda WinRT-API:er. För nya interna appprojekt rekommenderar vi att du använder OpenXR-API:et.

Det här avsnittet beskriver hur du implementerar rumslig mappning i DirectX-appen, inklusive en detaljerad förklaring av exempelprogrammet för rumslig mappning som paketeras med Universal Windows Platform SDK.

Det här avsnittet använder kod från kodexe exemplet HolographicSpatialMapping UWP.

Anteckning

Kodfragmenten i den här artikeln visar för närvarande användning av C++/CX i stället för C++17-kompatibel C++/WinRT som används i den holografiska projektmallen C++. Begreppen är likvärdiga för ett C++/WinRT-projekt, men du måste översätta koden.

Stöd för enheter

Funktion HoloLens (första generationen) HoloLens 2 Integrerande headset
Rumslig mappning ✔️ ✔️

Översikt över DirectX-utveckling

Intern programutveckling för rumslig mappning använder API:erna i Windows. Perception.Spatial-namnrymd. Dessa API:er ger dig fullständig kontroll över funktionerna för rumslig mappning på samma sätt som API:erna för rumslig mappning exponeras av Unity.

Perception-API:er

De primära typerna som tillhandahålls för utveckling av rumslig mappning är följande:

  • SpatialSurfaceObserver ger information om ytor i programangivna områden nära användaren i form av SpatialSurfaceInfo-objekt.
  • SpatialSurfaceInfo beskriver en enda rumslig yta, inklusive ett unikt ID, gränsvolym och tid för den senaste ändringen. Den tillhandahåller en SpatialSurfaceMesh asynkront på begäran.
  • SpatialSurfaceMeshOptions innehåller parametrar som används för att anpassa de SpatialSurfaceMesh-objekt som begärs från SpatialSurfaceInfo.
  • SpatialSurfaceMesh representerar nätdata för en enda rumslig yta. Data för hörnpositioner, brytpunkts normal och triangelindex finns i medlemmen SpatialSurfaceMeshBuffer-objekt.
  • SpatialSurfaceMeshBuffer omsluter en enda typ av nätdata.

När du utvecklar ett program med hjälp av dessa API:er ser det grundläggande programflödet ut så här (som visas i exempelprogrammet som beskrivs nedan):

  • Konfigurera din SpatialSurfaceObserver
    • Anropa RequestAccessAsyncför att se till att användaren har gett behörighet för ditt program att använda enhetens funktioner för rumslig mappning.
    • Instansiera ett SpatialSurfaceObserver-objekt.
    • Anropa SetBoundingVolumes för att ange i vilka områden du vill ha information om rumsliga ytor. Du kan ändra dessa regioner i framtiden genom att anropa den här funktionen igen. Varje region anges med en SpatialBoundingVolume.
    • Registrera dig för händelsen ObservedSurfacesChanged, som kommer att visas när det finns ny information om de rumsliga ytorna i de regioner som du har angett.
  • Bearbeta ObservedSurfacesChanged-händelser
    • I händelsehanteraren anropar du GetObservedSurfaces för att ta emot en karta över SpatialSurfaceInfo-objekt. Med den här kartan kan du uppdatera dina poster för vilka rumsliga ytor som finns i användarens miljö.
    • För varje SpatialSurfaceInfo-objekt kan du fråga TryGetBounds för att fastställa ytans rumsliga omfattningar, uttryckt i ett valande spatialt koordinatsystem.
    • Om du vill begära nät för en rumslig yta anropar du TryComputeLatestMeshAsync. Du kan ange alternativ som anger densiteten för trianglar och formatet för returnerade nätdata.
  • Ta emot och bearbeta nät
    • Varje anrop till TryComputeLatestMeshAsync returnerar asynkront ett SpatialSurfaceMesh-objekt.
    • Från det här objektet kan du komma åt de inneslutna SpatialSurfaceMeshBuffer-objekten, som ger dig åtkomst till triangelindex, hörnpositioner och brytpunkts normal för nät om du begär dem. Dessa data kommer att vara i ett format som är direkt kompatibelt med Direct3D 11-API:er som används för rendering av nät.
    • Härifrån kan ditt program välja att analysera eller bearbeta nätdata och använda dem för rendering och fysik med raycasting och kollision.
    • En viktig detalj att notera är att du måste tillämpa en skala på hörnpositionerna i nät (till exempel i hörnnyansen som används för att återge näten) för att konvertera dem från de optimerade heltalsenheterna som de lagras i bufferten till mätare. Du kan hämta den här skalan genom att anropa VertexPositionScale.

Felsökning

Genomgång av kodexempel för rumslig mappning

Kodexempel för Holographic Spatial Mapping innehåller kod som du kan använda för att komma igång med att läsa in ytnät i din app, inklusive infrastruktur för att hantera och återge ytnät.

Nu går vi igenom hur du lägger till kapacitet för ytmappning i DirectX-appen. Du kan lägga till den här koden i ditt Windows Holographic-appmallprojekt, eller så kan du följa med genom att bläddra igenom kodexe exemplet som nämns ovan. Det här kodexe exemplet baseras på Windows Holographic-appmallen.

Konfigurera din app för att använda spatialPerception-funktionen

Din app kan använda funktionen för rumslig mappning. Detta är nödvändigt eftersom det rumsliga nätet är en representation av användarens miljö, som kan betraktas som privata data. Deklarera den här funktionen i filen package.appxmanifest för din app. Här är ett exempel:

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

Funktionen kommer från namnområdet uap2. För att få åtkomst till det här namnområdet i ditt manifest inkluderar du det som ett xlmns-attribut < i package->-elementet. Här är ett exempel:

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

Sök efter stöd för funktionen för rumslig mappning

Windows Mixed Reality har stöd för en mängd olika enheter, inklusive enheter som inte stöder rumslig mappning. Om din app kan använda rumslig mappning, eller måste använda rumslig mappning, för att tillhandahålla funktioner, bör den kontrollera att det finns stöd för rumslig mappning innan du försöker använda den. Om till exempel rumslig mappning krävs av din app för mixad verklighet bör den visa ett meddelande om detta om en användare försöker köra den på en enhet utan rumslig mappning. Eller så kan din app återge sin egen virtuella miljö i stället för användarens miljö, vilket ger en upplevelse som liknar vad som skulle hända om rumslig mappning vore tillgänglig. I vilket fall som helst gör detta API att din app kan vara medveten om när den inte får rumsliga mappningsdata och svarar på rätt sätt.

Om du vill kontrollera stöd för rumslig mappning på den aktuella enheten kontrollerar du att UWP-kontraktet är på nivå 4 eller högre och anropar sedan SpatialSurfaceObserver::IsSupported(). Så här gör du i kontexten för kodexe exemplet på Holographic Spatial Mapping. Support kontrolleras precis innan du begär åtkomst.

API:et SpatialSurfaceObserver::IsSupported() är tillgängligt från och med SDK-version 15063. Om det behövs kan du återrikta projektet till plattformsversion 15063 innan du använder det här API:et.

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

När UWP-kontraktet är mindre än nivå 4 bör appen fortsätta som om enheten kan utföra rumslig mappning.

Begära åtkomst till rumsliga mappningsdata

Din app måste begära behörighet att komma åt data för rumslig mappning innan den försöker skapa ytobservatörer. Här är ett exempel som baseras på vårt kodexempel för Surface Mapping. Mer information finns senare på den här sidan:

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

Skapa en ytobservatör

Namnområdet Windows::P ception::Spatial::Surfaces innehåller klassen SpatialSurfaceObserver, som observerar en eller flera volymer som du anger i ett SpatialCoordinateSystem. Använd en SpatialSurfaceObserver-instans för att komma åt surface mesh-data i realtid.

Från 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;

Som vi nämnt i föregående avsnitt måste du begära åtkomst till rumsliga mappningsdata innan din app kan använda dem. Den här åtkomsten beviljas automatiskt på 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();

Därefter måste du konfigurera ytobservatör för att observera en specifik avgränsande volym. Här ser vi en ruta som är 20 x 20 x 5 meter centrerad vid koordinatsystemets ursprung.

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

Du kan ange flera gränsvolymer i stället.

Det här är pseudokod:

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

Du kan också använda andra avgränsande former, till exempel en vy frustum eller en avgränsare som inte är axeljusterad.

Det här är pseudokod:

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

Om din app behöver göra något annorlunda när data för ytmappning inte är tillgängliga kan du skriva kod för att svara på fall där SpatialPerceptionAccessStatus inte tillåts till exempel tillåts det inte på datorer med integrerande enheter som är anslutna eftersom dessa enheter inte har någon maskinvara för rumslig mappning. För dessa enheter bör du i stället förlita dig på det rumsliga stadiet för information om användarens miljö och enhetskonfiguration.

Initiera och uppdatera surface mesh-samlingen

Om ytobservatör har skapats kan vi fortsätta att initiera vår samling med ytnät. Här använder vi PULL-modell-API:et för att hämta den aktuella uppsättningen observerade ytor direkt:

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

Det finns också en push-modell som kan hämta data från surface mesh. Du kan utforma din app så att den endast använder pull-modellen om du väljer, i vilket fall du avsöker data så ofta, till exempel en gång per bildruta, eller under en viss tidsperiod, till exempel under spelkonfigurationen. I så fall är ovanstående kod det du behöver.

I vårt kodexempel har vi valt att demonstrera användningen av båda modellerna i ologiskt syfte. Här prenumererar vi på en händelse för att ta emot aktuella surface mesh-data när systemet identifierar en ändring.

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

Vårt kodexempel är också konfigurerat för att svara på dessa händelser. Nu ska vi gå igenom hur vi gör detta.

Obs! Det kanske inte är det effektivaste sättet för din app att hantera nätdata. Den här koden är skriven för tydlighetens skull och är inte optimerad.

Data för ytnät tillhandahålls i en skrivskyddad karta som lagrar SpatialSurfaceInfo-objekt med Hjälp av Platform::Guids som nyckelvärden.

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

För att bearbeta dessa data letar vi först efter nyckelvärden som inte finns i vår samling. Information om hur data lagras i vår exempelapp visas senare i det här avsnittet.

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

Vi måste också ta bort ytnät som finns i vår samling med ytnät, men som inte finns i systemsamlingen längre. För att göra det måste vi göra något som liknar motsatsen till det vi precis visade för att lägga till och uppdatera nät. vi loopar i appens samling och kontrollerar om det Guid vi har finns i systemsamlingen. Om den inte finns i systemsamlingen tar vi bort den från vår.

Från vår händelsehanterare i AppMain.cpp:

m_meshCollection->PruneMeshCollection(surfaceCollection);

Implementeringen av mesh-rensning i 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);
    }
}

Hämta och använda databuffertar för surface mesh

Att hämta information om surface mesh var lika enkelt som att hämta en datainsamling och bearbeta uppdateringar till samlingen. Nu ska vi gå in i detalj på hur du kan använda data.

I vårt kodexempel valde vi att använda ytnäten för rendering. Det här är ett vanligt scenario för ockluderande hologram bakom verkliga ytor. Du kan också rendera näten eller återge bearbetade versioner av dem för att visa användaren vilka delar av rummet som genomsöks innan du börjar tillhandahålla app- eller spelfunktioner.

Kodexe exemplet startar processen när det tar emot uppdateringar av surface mesh från händelsehanteraren som vi beskrev i föregående avsnitt. Den viktiga kodraden i den här funktionen är anropet för att uppdatera ytnätet: nu har vi redan bearbetat nätinformationen och vi är på väg att hämta hörn- och indexdata för användning som vi vill.

Från 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());
}

Vår exempelkod är utformad så att dataklassen SurfaceMesh hanterar bearbetning och rendering av nätdata. Dessa nät är det som RealtimeSurfaceMeshRenderer faktiskt sparar en karta över. Var och en har en referens till SpatialSurfaceMesh som den kom från, så du kan använda den varje gång du behöver komma åt näthörnet eller indexbuffertarna, eller hämta en transformering för näten. För tillfället flaggar vi att näten behöver en uppdatering.

Från SurfaceMesh.cpp:

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

Nästa gång näten uppmanas att rita sig själva kontrollerar det flaggan först. Om en uppdatering behövs uppdateras brytpunkts- och indexbuffertarna på GPU:n.

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

Först hämtar vi rådatabuffertarna:

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;

Sedan skapar vi Direct3D-enhetsbuffertar med nätdata som tillhandahålls av 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;
}

Obs! Information om hjälpfunktionen CreateDirectXBuffer som användes i föregående kodavsnitt finns i Kodexempel för Surface Mapping: SurfaceMesh.cpp, GetDataFromIBuffer.h. Nu är enhetsresursen klar och nätet anses vara inläst och redo för uppdatering och rendering.

Uppdatera och rendera ytnät

Vår SurfaceMesh-klass har en specialiserad uppdateringsfunktion. Varje SpatialSurfaceMesh har sin egen transformering och vårt exempel använder det aktuella koordinatsystemet för vår SpatialStationaryReferenceFrame för att hämta transformeringen. Sedan uppdateras modellens konstanta buffert på GPU:n.

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

När det är dags att rendera ytnät gör vi en del förberedelser innan vi återger samlingen. Vi konfigurerade shader-pipelinen för den aktuella renderingskonfigurationen och konfigurerade indatamonteraren. Den holografiska kamerahjälpklassen CameraResources.cpp har redan ställt in konstantbufferten för vy/projektion vid det här laget.

Från 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
        );
}

När det är klart loopar vi på näten och säger till var och en att rita sig själv. Obs! Den här exempelkoden är inte optimerad för att använda någon form av frustum-användandet, men du bör inkludera den här funktionen i din 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 enskilda näten ansvarar för att konfigurera brytpunkts- och indexbufferten, steget och modelltransformeringens konstantbuffert. Precis som med den snurrande kuben Windows Holographic-appmallen renderas vi till stereobuffertar med hjälp av instancing.

Från 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.
    );

Renderingsalternativ med Surface Mapping

Kodexempel för Surface Mapping erbjuder kod för ocklusion– endast återgivning av surface mesh-data och för återgivning av surface mesh-data på skärmen. Vilken sökväg du väljer – eller båda – beror på ditt program. Vi går igenom båda konfigurationerna i det här dokumentet.

Återgivning av ocklusionsbuffertar för holografiska effekter

Börja med att rensa renderingsmålvyn för den aktuella virtuella kameran.

Från AppMain.cpp:

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

Det här är ett "förrenderings"-pass. Här skapar vi en ocklusionsbuffert genom att be nätåtergivningen att endast återge djup. I den här konfigurationen kopplar vi inte en renderingsmålvy, och nätr renderaren ställer in pixelnytningssteget på nullptr så att GPU:n inte bryr sig om att rita bildpunkter. Geometrin kommer att vara mitt i djupbufferten och grafikpipelinen stoppas där.

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

Vi kan rita hologram med ett extra djuptest mot ocklusionsbufferten för Ytmappning. I det här kodexe exemplet återger vi bildpunkter på kuben en annan färg om de finns bakom en yta.

Från 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()
    );

Baserat på kod från 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);
}

Obs! Vår GatherDepthLess-rutin finns i kodexempel för Surface Mapping: SpecialEffectPixelShader.hlsl.

Rendering av surface mesh-data till skärmen

Vi kan också bara rita ytnäten till stereovisningsbuffertarna. Vi valde att rita fullständiga ansikten med belysning, men du kan rita trådramar, bearbeta nät innan de återges, tillämpa en strukturkarta och så vidare.

Här säger vårt kodexempel till nätåtergivningen att rita samlingen. Den här gången anger vi inget djup, utan kopplar en pixelnyning och slutför pipelinen för rendering med hjälp av de mål som vi har angett för den aktuella virtuella kameran.

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

Se även