Prostorové mapování v rozhraní DirectX

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í API OpenXR.

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 Univerzální platforma Windows SDK.

V tomto tématu se používá kód z ukázky kódu UWP HolographicSpatialMapping .

Poznámka

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

Podpora zařízení

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

Přehled vývoje rozhraní DirectX

Vývoj nativních aplikací pro prostorové mapování používá rozhraní API v oboru názvů Windows.Perception.Spatial . Tato rozhraní API poskytují plnou kontrolu nad funkcemi prostorového mapování, stejně jako rozhraní API prostorového mapování zpřístupnuje Unity.

Rozhraní API pro vnímání

Primární typy poskytované pro vývoj prostorového mapování jsou následující:

  • SpatialSurfaceObserver poskytuje informace o površích v aplikací určených oblastech prostoru v blízkosti uživatele ve formě objektů SpatialSurfaceInfo.
  • SpatialSurfaceInfo popisuje jeden existující prostorový povrch, včetně jedinečného ID, ohraničujícího objemu a času poslední změny. Na vyžádání poskytne SpatialSurfaceMesh asynchronně.
  • SpatialSurfaceMeshOptions obsahuje parametry používané k přizpůsobení SpatialSurfaceMesh objekty požadované z SpatialSurfaceInfo.
  • SpatialSurfaceMesh představuje data sítě pro jeden prostorový povrch. Data pro pozice vrcholů, vrcholové normály a trojúhelníkové indexy jsou obsaženy v člen SpatialSurfaceMeshBuffer objekty.
  • SpatialSurfaceMeshBuffer zabalí jeden typ dat sítě.

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

  • Nastavení spatialSurfaceObserveru
    • Volejte metodu RequestAccessAsync a ujistěte se, že uživatel udělil vaší aplikaci oprávnění k používání možností prostorového mapování zařízení.
    • Vytvořte instanci objektu SpatialSurfaceObserver.
    • Volejte SetBoundingVolumes a určete oblasti prostoru, ve kterých chcete získat informace o prostorových površích. Tyto oblasti můžete v budoucnu upravit opětovným voláním této funkce. Každá oblast je určena pomocí SpatialBoundingVolume.
    • Zaregistrujte se na událost ObservedSurfacesChanged , která se aktivuje vždy, když budou k dispozici nové informace o prostorových površích v oblastech prostoru, které jste zadali.
  • Process ObservedSurfacesChanged events
  • Příjem a zpracování sítě
    • Každé volání TryComputeLatestMeshAsync async vrátí jeden SpatialSurfaceMesh objekt.
    • Z tohoto objektu můžete získat přístup k obsaženým objektům SpatialSurfaceMeshBuffer, které vám umožní přístup k indexům trojúhelníků, pozicím vrcholů a normám vrcholů sítě, pokud je požadujete. 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í.
    • Odsud může vaše aplikace volitelně analyzovat nebo zpracovávat data sítě a používat je pro vykreslování a fyzikální raycasting a kolizi.
    • Jedním z důležitých detailů, které je třeba si uvědomit, je, že musíte použít měřítko pro pozice vrcholů sítě (například v shaderu vrcholů používaném k vykreslení sítí), abyste je mohli 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 měřítko můžete načíst voláním VertexPositionScale.

Poradce při potížích

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 zahájení načítání povrchových sítí do aplikace, včetně infrastruktury pro správu a vykreslování sítí povrchů.

Teď vás provedeme, jak do aplikace DirectX přidat funkci mapování povrchu. Tento kód můžete přidat do projektu šablony aplikace Pro Windows Holographic nebo si můžete projít vzorový kód uvedený výše. Tato ukázka kódu je založená na šabloně aplikace Windows Holographic.

Nastavení aplikace tak, aby používala funkci spatialPerception

Vaše aplikace může používat funkci prostorového mapování. To je nezbytné, protože prostorová síť představuje prostředí uživatele, které lze považovat za soukromá data. Deklarujte tuto schopnost 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, zahrňte 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 funkcí 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 k poskytování funkcí používat prostorové mapování nebo musí používat prostorové mapování, měla by před pokusem o jeho použití zkontrolovat, zda je prostorové mapování podporováno. Pokud například aplikace pro hybridní realitu vyžaduje prostorové mapování, měla by se zobrazit zpráva v tomto smyslu, pokud se ho uživatel pokusí spustit na zařízení bez prostorového mapování. Nebo může vaše aplikace místo prostředí uživatele vykreslit vlastní virtuální prostředí, které poskytuje prostředí podobné tomu, co by se stalo, kdyby bylo k dispozici prostorové mapování. Toto rozhraní API v každém případě umožňuje, aby vaše aplikace věděla, kdy nedostane data prostorového mapování a vhodným způsobem zareaguje.

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

Rozhraní API SpatialSurfaceObserver::IsSupported() je dostupné od verze sady SDK 15063. V případě potřeby před použitím tohoto rozhraní API zacílte projekt na verzi platformy 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 postupovat, jako by zařízení bylo schopné provádět prostorové mapování.

Žádost o přístup k datům prostorového mapování

Než se pokusíte vytvořit pozorovatele povrchu, vaše aplikace musí požádat o oprávnění pro přístup k datům prostorového mapování. Tady je příklad založený na ukázce kódu mapování zařízení Surface s dalšími podrobnostmi, 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 na povrchu

Windows ::P erception::Spatial::Surfaces zahrnuje spatialSurfaceObserver třída, která sleduje jeden nebo více svazků, které zadáte v SpatialCoordinateSystem. Použijte instanci SpatialSurfaceObserver pro přístup k datům surface mesh v reálném čase.

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 uvedeno v předchozí části, musíte požádat o přístup k datům prostorového mapování, než je vaše aplikace bude moct používat. Tento přístup se uděluje automaticky na HoloLensu.

// 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. Zde vidíme pole o rozsadě 20x20x5 metrů, které je na středu 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í svazky.

Toto je pseudokód:

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

Je také možné použít jiné ohraničující obrazce – například ohraničující rámeček nebo ohraničující rámeček, který není zarovnaný osou.

Toto je pseudokód:

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

Pokud vaše aplikace potřebuje udělat něco jinak, když data mapování povrchu nejsou k dispozici, můžete napsat kód, který bude reagovat na případ, kdy spatialPerceptionAccessStatus není povolený – 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ěli místo toho spoléhat na prostorovou fázi, kde najdete informace o uživatelském prostředí a konfiguraci zařízení.

Inicializace a aktualizace kolekce sítí surface

Pokud byl pozorovatel surface úspěšně vytvořen, můžeme pokračovat v inicializaci kolekce povrchových sítí. Tady použijeme rozhraní API modelu vyžádání obsahu k tomu, abychom okamžitě získali aktuální sadu pozorovaných povrchů:

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 push pro získání dat sítě surface. Pokud chcete, můžete aplikaci navrhnout tak, aby používala jenom model vyžádání. V takovém případě se budete dotazovat na data tak často – řekněme jednou za snímek – nebo během určitého časového období, například při nastavování hry. Pokud ano, potřebujete výše uvedený kód.

V ukázce kódu jsme se rozhodli předvést použití obou modelů pro pedagogické účely. Tady se přihlásíme k odběru události, abychom dostávali aktuální data povrchové sítě vždy, když systém rozpozná změnu.

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

Naše ukázka kódu je také nakonfigurovaná tak, aby reagovala na tyto události. Pojďme si projít, jak to děláme.

POZNÁMKA: To nemusí být pro vaši aplikaci nejúčinnější způsob, jak zpracovávat data ze sítě. Tento kód je napsaný pro přehlednost a není optimalizovaný.

Data sítě surface jsou k dispozici v mapě jen pro čtení, která ukládá objekty SpatialSurfaceInfo pomocí Platform::Guids jako klíčové hodnoty.

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

Abychom tato data mohli zpracovat, nejprve vyhledáme hodnoty klíčů, které nejsou v naší kolekci. Podrobnosti o tom, jak jsou data uložená v naší ukázkové aplikaci, najdete dál 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é odstranit povrchové sítě, které jsou v naší kolekci sítí surface, ale které už nejsou v systémové kolekci. Abychom to mohli udělat, musíme udělat něco podobného opaku toho, co jsme právě ukázali při přidávání a aktualizaci sítí; provedeme smyčku v kolekci naší aplikace a zkontrolujeme, jestli je identifikátor GUID , který máme, v systémové kolekci. Pokud není v systémové kolekci, odebereme ji z naší kolekce.

Z obslužné rutiny události v AppMain.cpp:

m_meshCollection->PruneMeshCollection(surfaceCollection);

Implementace vyřezávání sítí v 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);
    }
}

Získání a používání vyrovnávacích pamětí dat sítě surface

Získání informací o síti surface bylo stejně snadné jako načtení kolekce dat a zpracování aktualizací této kolekce. Teď se podrobně podíváme na to, jak můžete data používat.

V našem příkladu kódu jsme se rozhodli pro vykreslování použít sítě povrchů. Jedná se o běžný scénář za okryvování hologramů za reálnými povrchy. Můžete také vykreslit sítě nebo jejich zpracované verze, abyste uživateli před zahájením poskytování funkcí aplikace nebo hry ukázali, jaké oblasti místnosti se naskenují.

Ukázka kódu zahájí proces, když obdrží aktualizace surface mesh z obslužné rutiny události, kterou jsme popsali v předchozí části. Důležitým řádkem kódu v této funkci je volání pro aktualizaci povrchové sítě: do této doby jsme už zpracovali informace o síti a chystáme se získat data vrcholu a indexu pro použití, jak uznáme za vhodné.

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

Náš ukázkový kód je navržený tak, aby datová třída SurfaceMesh zpracovávala a vykreslovat síťová data. Tyto sítě jsou to, co RealtimeSurfaceMeshRenderer ve skutečnosti udržuje mapu. Každý z nich má odkaz na prostorový povrch, ze které pochází, takže ho můžete použít, kdykoli potřebujete získat přístup k vyrovnávacím pamětím vrcholů nebo indexů sítě nebo získat transformaci pro síť. Prozatím označíme síť příznakem, že potřebujeme aktualizaci.

Ze zařízení SurfaceMesh.cpp:

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

Až se příště zobrazí výzva, aby se síť sama nakreslila, nejprve zkontroluje příznak. Pokud je potřeba provést aktualizaci, vrchol a vyrovnávací paměť indexu se aktualizují 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é vyrovnávací paměti dat:

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 sítě, které poskytuje 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: Pomocnou funkci CreateDirectXBuffer použitou v předchozím fragmentu kódu najdete v ukázce kódu mapování zařízení Surface: SurfaceMesh.cpp, GetDataFromIBuffer.h. Teď je vytvoření prostředku zařízení dokončeno a síť se považuje za načtenou a připravenou k aktualizaci a vykreslení.

Aktualizace a vykreslení sítí povrchů

Naše třída SurfaceMesh má specializovanou aktualizační funkci. Každá funkce SpatialSurfaceMesh má vlastní transformaci a naše ukázka k získání transformace používá aktuální souřadnicový systém pro objekt 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 povrchové sítě, před vykreslením kolekce provedeme určitou přípravu. Nastavíme kanál shaderu pro aktuální konfiguraci vykreslování a nastavíme vstupní fázi assembleru. Třída pomocníka holografické kamery CameraResources.cpp už má nastavenou vyrovnávací paměť zobrazení/projekce.

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

Jakmile to uděláme, zamyčkáme se na našich sítích a řekneme každému, aby nakreslil sám sebe. POZNÁMKA: Tento ukázkový kód není optimalizovaný tak, aby používal jakýkoli druh vyřazování frustum, 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 vrcholu a indexu, kroku a vyrovnávací paměti transformace modelu. Stejně jako u rotující datové krychle v šabloně aplikace Windows Holographic se vykresluje do stereoskopických vyrovnávacích pamětí pomocí instanceng.

Ze zařízení 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.
    );

Volby vykreslování pomocí mapování zařízení Surface

Ukázka kódu mapování zařízení Surface nabízí kód pro vykreslování dat sítě surface pouze pro okluzi a pro vykreslování dat sítě surface na obrazovce. Zvolená cesta (nebo obojí) závisí na vaší aplikaci. Obě konfigurace si projdeme v tomto dokumentu.

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

Začněte vymazáním zobrazení cíle vykreslení aktuální virtuální kamery.

Z AppMain.cpp:

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

Toto je "předkreslovací" průchod. Tady vytvoříme vyrovnávací paměť okluze tak, že požádáme vykreslovač sítě, aby vykresloval pouze hloubku. V této konfiguraci nepřipojujeme zobrazení cíle vykreslení a vykreslovač sítě nastaví fázi shaderu pixelů na nullptr , aby se GPU neobtěžuje nakreslit pixely. Geometrie bude rastrována do hloubkové vyrovnávací paměti a grafický kanál se tam zastaví.

// 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 s dodatečným hloubkovým testem proti vyrovnávací paměti okluze mapování povrchu. V této ukázce kódu vykreslíme pixely na datové krychli jinou barvu, pokud jsou za povrchem.

Z 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 z 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: Informace o naší rutině GatherDepthLess najdete v ukázce kódu mapování zařízení Surface: SpecialEffectPixelShader.hlsl.

Vykreslení dat sítě surface na displej

Můžeme také jednoduše nakreslit sítě povrchu do vyrovnávací paměti stereo displeje. Rozhodli jsme se kreslit celé plochy pomocí osvětlení, ale máte možnost kreslit drátěný model, zpracovat sítě před vykreslením, použít texturovou mapu atd.

Tady ukázka kódu říká vykreslovači sítě, aby nakreslil kolekci. Tentokrát nezadáváme průchod pouze hloubkou, připojíme pixel shader a dokončíme kanál vykreslování pomocí cílů, které jsme zadali pro aktuální virtuální kameru.

// 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é