Prostorové mapování v DirectX

Funkce HoloLens (1. generace) HoloLens 2 Imerzivní náhlavní soupravy
Prostorové mapování ✔️ ✔️

Přehled vývoje pro DirectX

Vývoj nativních aplikací pro prostorové mapování používá rozhraní API v Windows. Perception.Spatial – obor názvů. Tato rozhraní API poskytují úplnou kontrolu nad funkcemi prostorového mapování stejným způsobem, jakým unity zveřejňuje rozhraní API prostorového mapování.

Rozhraní API pro vnímání

Pro vývoj prostorového mapování jsou k dispozici tyto primární typy:

  • Spatial AzureObserver poskytuje informace o površích v oblastech v blízkosti uživatele v oblastech určených aplikací ve formě objektů SpatialServerInfo.
  • SpatialInfo popisuje jeden vnější prostorový povrch, včetně jedinečného ID, ohraničovacího svazku a času poslední změny. Na vyžádání bude asynchronně poskytovat Spatial RequestMesh.
  • Spatial Jejich parametry obsahují parametry, které se používají k přizpůsobení objektů SpatialInfoMesh požadovaných z SpatialInfo.
  • Spatial Znázorňuje data sítě pro jeden prostorový povrch. Data pro pozice vrcholů, normály vrcholů a trojúhelníkové indexy jsou obsažena v členech objektů SpatialMusaceMeshBuffer.
  • SpatialEventMeshBuffer zabalí jeden typ dat ze sítě.

Při vývoji aplikace pomocí těchto rozhraní API bude základní tok programu vypadat takto (jak je znázorněno v ukázkové aplikaci popsané níže):

  • Nastavení spatial Zamykacíobserver
    • Volejte metodu RequestAccessAsync,abyste zajistili, že uživatel aplikaci udělil oprávnění k používání prostorového mapování zařízení.
    • Vytvořte instanci objektu Spatial Zamykacíobserver.
    • Voláním metody SetBoundingVolumes určete oblasti prostoru, ve kterých chcete získat informace o prostorových površích. Tyto oblasti můžete v budoucnu upravit tak, že tuto funkci znovu zavoláte. Každá oblast se zadá pomocí spatialBoundingVolume.
    • Zaregistrujte se k události ObservedEventsChanged, která se zobrazí vždy, když jsou k dispozici nové informace o prostorových površích v zadaných oblastech prostoru.
  • Zjištěné události procesu
    • V obslužné rutině události zavolejte GetObservedKuaces, abyste obdrželi mapu objektů Spatial UžasInfo. Pomocí této mapy můžete aktualizovat záznamy o tom, které prostorové povrchy existují v uživatelském prostředí.
    • Pro každý objekt SpatialInfo můžete zadat dotaz TryGetBounds a určit prostorové rozsahy povrchu vyjádřené v prostorovém souřadnicovém systému podle vašeho výběru.
    • Pokud se rozhodnete požádat o vytvoření sítě pro prostorový povrch, zavolejte TryComputeLatestMeshAsync. Můžete zadat možnosti, které určují hustotu trojúhelníků a formát vrácených dat sítě.
  • Příjem a zpracování sítě
    • Každé volání metody TryComputeLatestMeshAsync asynchronně vrátí jeden objekt SpatialAsyncMesh.
    • Z tohoto objektu můžete přistupovat k obsaženým objektům SpatialElaceMeshBuffer, které vám umožní přístup k trojúhelníkovým indexům, pozicím vrcholů a normálům vrcholů sítě, pokud o ně budete žádat. Tato data budou ve formátu, který je přímo kompatibilní s rozhraními API Direct3D 11 používanými pro vykreslování sítí.
    • Tady může vaše aplikace volitelně analyzovat nebo zpracovávat data ze sítě a použít je k vykreslování a fyzikálním paprskům a kolizím.
    • Jedním z důležitých podrobností, které je třeba poznamenat, je, že je nutné použít měřítko na pozice vrcholu sítě (například v shaderu vrcholu použitém k vykreslení sítí), aby se převést z optimalizovaných celočíselných jednotek, ve kterých jsou uloženy ve vyrovnávací paměti, na měřiče. Toto škálování můžete načíst voláním vertexPositionScale.

Řešení potíží

Názorný postup ukázky kódu prostorového mapování

Ukázka kódu Holographic Spatial Mapping obsahuje kód, který můžete použít k tomu, abyste mohli začít načítat sítě ploch do vaší aplikace, včetně infrastruktury pro správu a vykreslování povrchových sítí.

Teď vás provedeme přidáním možnosti mapování ploch do aplikace DirectX. Tento kód můžete přidat do projektu šablony Windows holografické aplikace, nebo můžete postupovat podle pokynů v ukázce kódu uvedené výše. Tato ukázka kódu je založená na šabloně Windows holografické aplikace.

Nastavení aplikace pro použití funkce spatialPerception

Vaše aplikace může používat schopnost prostorového mapování. To je nezbytné, protože prostorová síť je reprezentací prostředí uživatele, které lze považovat za soukromá data. Tuto funkci deklarujte v souboru package.appxmanifest pro vaši aplikaci. Tady je příklad:

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

Funkce pochází z oboru názvů uap2. Pokud chcete získat přístup k tomuto oboru názvů v manifestu, zahrnovat ho jako atribut xlmns v elementu > Package. Tady je příklad:

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

Kontrola podpory funkce prostorového mapování

Windows Mixed Reality podporuje širokou škálu zařízení, včetně zařízení, která nepodporují prostorové mapování. Pokud vaše aplikace může použít prostorové mapování nebo musí k zajištění funkčnosti použít prostorové mapování, měla by před pokusem o použití zkontrolovat, jestli je prostorové mapování podporované. Pokud například aplikace hybridní reality vyžaduje prostorové mapování, měla by se zobrazit zpráva, pokud se ji uživatel pokusí spustit na zařízení bez prostorového mapování. Nebo může vaše aplikace místo uživatelského prostředí vykreslit vlastní virtuální prostředí, což poskytuje prostředí podobné tomu, co by se stalo, kdyby bylo k dispozici prostorové mapování. V každém případě toto rozhraní API umožňuje vaší aplikaci vědět, kdy nezíska data prostorového mapování a odpovídajícím způsobem reagovat.

Pokud chcete v aktuálním zařízení zkontrolovat podporu prostorového mapování, nejprve se ujistěte, že je kontrakt UPW na úrovni 4 nebo vyšší, a potom zavolejte SpatialServerServer::IsSupported(). Tady je postup, jak to provést v kontextu ukázky kódu Holographic Spatial Mapping. Podpora se kontroluje těsně před vyžádáním přístupu.

Rozhraní API SpatialServerServer::IsSupported() je dostupné od verze sady SDK 15063. V případě potřeby před použitím tohoto rozhraní API projekt znovu zacílete na platformu verze 15063.

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

Pokud je kontrakt UPW nižší než úroveň 4, měla by aplikace pokračovat, jako by zařízení bylo schopné prostorové mapování.

Vyžádání přístupu k datům prostorového mapování

Než se vaše aplikace pokouší vytvořit případné pozorovatele povrchu, musí požádat o oprávnění pro přístup k datům prostorového mapování. Tady je příklad založený na naší ukázce kódu pro surface mapping s podrobnějšími údaji, které najdete dále na této stránce:

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

Vytvoření pozorovatele povrchu

Obor názvů Windows::P erception::Spatial::Surfaces obsahuje třídu SpatialServer, která pozoruje jeden nebo více svazků, které zadáte v spatialCoordinateSystem. Pro přístup k datům ze sítě povrchu v reálném čase použijte instanci SpatialServerServer.

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

Jak je vidět v předchozí části, před použitím aplikace musíte požádat o přístup k datům prostorového mapování. Tento přístup se uděluje automaticky na 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ále musíte nakonfigurovat pozorovatele povrchu tak, aby pozoroval konkrétní ohraničující svazek. Tady pozorujeme na krabici, která je 20 × 20 x 5 metrů a je zasaována na střed od počátku souřadnicového systému.

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

Místo toho můžete nastavit více ohraničující svazků.

Toto je pseudokód:

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

Je také možné použít jiné ohraničující tvary, například frustum zobrazení nebo ohraničující rámeček, který není zarovnaný na ose.

Toto je pseudokód:

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

Pokud vaše aplikace potřebuje dělat něco jinak, když nejsou k dispozici data mapování ploch, můžete napsat kód pro reakci na případ, kdy spatialPerceptionAccessStatus není Povoleno – například nebude povolený na počítačích s připojenými imerzivními zařízeními, protože tato zařízení nemají hardware pro prostorové mapování. U těchto zařízení byste místo toho měli spoléhat na prostorovou fázi, která obsahuje informace o prostředí uživatele a konfiguraci zařízení.

Inicializace a aktualizace kolekce surface mesh

Pokud se pozorovatel povrchu úspěšně vytvořil, můžeme pokračovat v inicializaci naší kolekce povrchových sítí. Tady použijeme rozhraní API modelu vyžádané instalace k získání aktuální sady pozorovaných povrchů ihned:

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

K dispozici je také model nabízených oznámení pro získání dat ze sítě ploch. Aplikaci můžete volně navrhnout tak, aby v případě, že se rozhodnete, používat pouze model vyžádané instalace. V takovém případě se budete dotazovat na data tak často – například jednou za snímek – nebo během určitého časového období, například během nastavování hry. Pokud ano, výše uvedený kód je to, co potřebujete.

V našem vzorové kódu jsme se rozhodli předvést použití obou modelů pro základní účely. Tady se přihlásíme k odběru události, která bude přijímat aktuální data o smyšlicích plochách vždy, když systém rozpozná změnu.

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

Náš vzorový kód je také nakonfigurovaný tak, aby na tyto události reagoval. Pojďme si projít, jak to uděláme.

POZNÁMKA: Pro vaši aplikaci to nemusí být nejúčinnější způsob zpracování dat ze sítě. Tento kód je napsán pro přehlednost a není optimalizovaný.

Data ze sítě ploch jsou k dispozici v mapě jen pro čtení, která ukládá objekty SpatialInfo pomocíplatformy Platform::Guids jako klíčových hodnot.

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

Při zpracování těchto dat nejprve podíváme na klíčové hodnoty, které nejsou v naší kolekci. Podrobnosti o způsobu uložení dat v naší ukázkové aplikaci najdete dále v tomto tématu.

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

Musíme také odebrat sítě ploch, které jsou v naší kolekci povrchových sítí, ale které už nejsou v kolekci systému. Abychom to mohli udělat, musíme udělat něco podobného jako opak toho, co jsme právě ukázali pro přidávání a aktualizaci sítí; Ve smyčce používáme kolekci naší aplikace a zkontrolujeme, jestli guid, který máme, je v systémové kolekci. Pokud není v kolekci systému, odebereme ho z naší kolekce.

Z naší obslužné rutiny události v appMain.cpp:

m_meshCollection->PruneMeshCollection(surfaceCollection);

Implementace vyřazování sítě v Realtime PředběžnéMeshRenderer.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);
    }
}

Získání a použití vyrovnávacích pamětí dat ze sítě ploch

Získání informací o plošné síti bylo stejně snadné jako stažení kolekce dat a zpracování aktualizací této kolekce. Teď se podrobně budeme podrobněji zanachytovat, jak můžete data používat.

V našem příkladu kódu jsme se rozhodli použít pro vykreslování sítě ploch. Toto je běžný scénář pro zachytění hologramů za povrchy z reálného světa. Můžete také vykreslit sítě nebo vykreslit jejich zpracované verze, abyste uživateli ukázali, jaké oblasti místnosti se před tím, než začnete poskytovat funkce aplikace nebo hry, naskenují.

Ukázka kódu spustí proces, když obdrží aktualizace sítě surface mesh z obslužné rutiny události, kterou jsme popsali v předchozí části. Důležitým řádek kódu v této funkci je volání pro aktualizaci sítě ploch:do této doby už jsme zpracují informace o síti a podle toho, jak to uvidíme, načtou se data vrcholů a indexů.

Ze souboru RealtimeRenderaceMeshRenderer.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());
}

Náš vzorový kód je navržený tak, aby datová třída SurfaceMeshzpracuje zpracování a vykreslování dat ze sítě. Tyto sítě ve skutečnosti uchová mapu objektu RealtimeEventMeshRenderer. Každý z nich obsahuje odkaz na prostorovou mřížku, ze které pochází, takže ji můžete použít, kdykoli potřebujete přístup k vyrovnávacím pamětím vrcholů nebo indexů sítě nebo získat transformaci pro síť. Teď tuto síť označíme příznakem, že je potřeba aktualizovat.

Ze souboru SurfaceMesh.cpp:

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

Až se příště zobrazí dotaz, jestli se má síť vykreslit, zkontroluje nejprve příznak . Pokud je potřeba aktualizace, aktualizují se vyrovnávací paměti vrcholů a indexů na GPU.

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

Nejprve získáme nezpracované datové vyrovnávací paměti:

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;

Pak vytvoříme vyrovnávací paměti zařízení Direct3D s daty ze sítě poskytovanými 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;
}

POZNÁMKA: Pomocná funkce CreateDirectXBuffer použitá v předchozím fragmentu kódu najdete v ukázce kódu Surface Mapping: SurfaceMesh.cpp, GetDataFromIBuffer.h. Vytvoření prostředku zařízení je teď dokončené a síť se považuje za načtenou a připravenou k aktualizaci a vykreslení.

Aktualizace a vykreslení povrchových sítí

Naše třída SurfaceMesh má specializovanou aktualizační funkci. Každá spatialFrameaceMesh má svou vlastní transformaci a náš vzorek k získání transformace používá aktuální souřadnicový systém pro náš SpatialStationaryReferenceFrame. Pak aktualizuje konstantní vyrovnávací paměť modelu na 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
        );
}

Když je čas vykreslit sítě ploch, před vykreslením kolekce se trochu připravíme. Nastavili jsme kanál shaderu pro aktuální konfiguraci vykreslování a nastavili jsme fázi vstupního assembleru. Pomocná třída holografické kamery CameraResources.cpp už nastavila konstantní vyrovnávací paměť zobrazení/projekce.

Z Realtime Ovládací prvekMeshRenderer::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
        );
}

Až to bude hotové, budeme se ve smyčce opakovat a každému z nich řekneme, aby se vykreslili. POZNÁMKA: Tento ukázkový kód není optimalizovaný pro použití jakéhokoli druhu frustumu, ale tuto funkci byste měli zahrnout do své aplikace.

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

Jednotlivé sítě zodpovídají za nastavení vyrovnávací paměti vrcholů a indexů, krokování a konstantní vyrovnávací paměť transformace modelu. Stejně jako u rotující datové krychle v šabloně Windows Holographic se pomocí vytváření instancí vykreslíme do stereografických vyrovnávacích pamětí.

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

Vykreslování možností s mapováním ploch

Ukázka kódu Surface Mapping nabízí kód pro vykreslování dat ze sítě ploch pouze pro okluzi a pro vykreslování dat ze sítě na obrazovce. Cesta, kterou zvolíte , nebo obojí, závisí na vaší aplikaci. V tomto dokumentu si projdeme obě konfigurace.

Vykreslení vyrovnávacích pamětí okluze pro holografický efekt

Začněte vymazáním zobrazení cíle vykreslování pro aktuální virtuální fotoaparát.

Ze souboru AppMain.cpp:

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

Jedná se o průchod "před vykreslením". Tady vytvoříme vyrovnávací paměť okluze tak, že se vykreslovací nástroj sítě zeptáme na vykreslení pouze hloubky. V této konfiguraci nepřikláníme cílové zobrazení vykreslování a renderer sítě nastaví fázi shaderu pixelů na nullptr, aby GPU nezatěžoval kreslení pixelů. Geometrie bude rastrovaná do vyrovnávací paměti hloubky a tam se zastaví grafický kanál.

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

Hologramy můžeme nakreslit pomocí dodatečného hloubkového testu proti vyrovnávací paměti okluze pro surface mapping. V této ukázce kódu vykreslíme pixely na datové krychli jinou barvu, pokud jsou za povrchem.

Ze souboru 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()
    );

Na základě kódu ze souboru 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);
}

Poznámka: Naši rutinu GatherDepthLess najdete v ukázce kódu Surface Mapping: SpecialEffectPixelShader.hlsl.

Vykreslení dat ze sítě ploch na zobrazení

Můžeme také pouze nakreslit sítě ploch do vyrovnávacích pamětí pro stereo zobrazení. Rozhodli jsme se nakreslit plné tváře s osvětlením, ale můžete si zdarma nakreslit wireframe, zpracovat sítě před vykreslením, použít mapu textur a tak dále.

Náš vzorový kód v tomto případě sděluje rendereru sítě, aby nakreslil kolekci. Tentokrát nezadáme průchod pouze do hloubky, připojí pixel shader a dokončí kanál vykreslování pomocí cílů, které jsme zadali pro aktuální virtuální fotoaparát.

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

Viz také

Poznámka

Tento článek se týká starších nativních rozhraní API WinRT. Pro nové projekty nativních aplikací doporučujeme použít rozhraní OPENXR API.

Toto téma popisuje, jak implementovat prostorové mapování v aplikaci DirectX, včetně podrobného vysvětlení ukázkové aplikace prostorového mapování zabalené se sadou SDK pro Univerzální Windows Platform.

Toto téma používá kód z ukázky kódu HolographicSpatialMapping UPW.

Poznámka

Fragmenty kódu v tomto článku aktuálně ukazují použití C++/CX místo C++17 kompatibilního C++/WinRT, jak se používá v šabloně holografického projektu C++. Koncepty jsou pro projekt C++/WinRT ekvivalentní, i když budete muset kód přeložit.

Podpora zařízení