Térbeli leképezés a DirectX-ban

Szolgáltatás HoloLens (1. generációs) HoloLens 2. Modern headsetek
Térbeli leképezés ✔️ ✔️

A DirectX-fejlesztés áttekintése

A térbeli leképezéshez használt natív alkalmazásfejlesztés az api-kat használja a Windows. Perception.Spatial névtér. Ezek az API-k teljes körű vezérlést adnak a térbeli leképezési funkciók felett, ugyanúgy, ahogyan a Unity a térleképezési API-kat is elérhetővé teszi.

Észlelési API-k

A térbeli leképezés fejlesztéséhez biztosított elsődleges típusok a következők:

  • A SpatialSurfaceObserver spatialSurfaceInfo objektumok formájában információkat nyújt a felhasználó közelében található, alkalmazás által megadott térbeli felületekről.
  • A SpatialSurfaceInfo egyetlen nagy térbeli felületet ír le, beleértve az egyedi azonosítót, a határoló mennyiséget és az utolsó módosítás idejét. Kérésre aszinkron módon fogja biztosítani a SpatialSurfaceMesh adatokat.
  • A SpatialSurfaceMeshOptions olyan paramétereket tartalmaz, amelyek a SpatialSurfaceInfo fájlból kért SpatialSurfaceMesh objektumok testreszabásához használhatók.
  • A SpatialSurfaceMesh egyetlen térbeli felület hálóadatát jelöli. A csúcspontok pozíciói, a csúcspontok normál adatai és a háromszögindexek a spatialSurfaceMeshBuffer tagobjektumban vannak.
  • A SpatialSurfaceMeshBuffer egyetlen típusú hálóadatot burköl.

Ha ezekkel az API-okkal fejleszt alkalmazásokat, az alapszintű programfolyam a következő lesz (ahogy azt az alább leírt mintaalkalmazás mutatja):

  • A SpatialSurfaceObserver beállítása
    • Hívja meg a RequestAccessAsyncmetódust annak biztosításához, hogy a felhasználó engedélyt adott az alkalmazásnak az eszköz térbeli leképezési képességeinek használatára.
    • Egy SpatialSurfaceObserver objektum példányosodása.
    • A SetBoundingVolumes hívása megadásával adhatja meg azokat a térrégió-régiókat, amelyekben a térbeli felületekkel kapcsolatos információkat szeretne. Ezeket a régiókat a jövőben módosíthatja a függvény újrahívása után. Minden régió a SpatialBoundingVolume használatával van megadva.
    • Regisztráljon a ObservedSurfacesChanged eseményre, amely akkor jelentkezik, ha új információ érhető el a térbeli felületekkel kapcsolatban a megadott térbeli régiókban.
  • Process ObservedSurfacesChanged események
    • Az eseménykezelőben hívja meg a GetObservedSurfaces adatokat a SpatialSurfaceInfo objektumok térképének fogadásához. Ezzel a térképpel frissítheti a felhasználó környezetében található térbeli felületek rekordjait.
    • Minden SpatialSurfaceInfo objektumhoz lekérdezheti aTryGetBounds lekérdezést a felület térbeli kiterjedésének meghatározásához, egy Ön által választott térbeli koordinátarendszerben kifejezve.
    • Ha úgy dönt, hogy egy térbeli felülethez kéri a mesht, hívja meg a TryComputeLatestMeshAsync metódust. Megadhatja a háromszögek sűrűségét és a visszaadott hálóadatok formátumát.
  • Háló fogadása és feldolgozása
    • A TryComputeLatestMeshAsync minden hívása aszinkron módon egy SpatialSurfaceMesh objektumot ad vissza.
    • Ebből az objektumból elérheti a tartalmazott SpatialSurfaceMeshBuffer objektumokat, amelyek a háló háromszögindexéhez, csúcspontpozícióihoz és csúcspont-normálihoz biztosít hozzáférést, ha ön kéri őket. Ezek az adatok közvetlenül kompatibilisek lesznek a hálók renderelése során használt Direct3D 11 API-okkal.
    • Innen az alkalmazás igény szerint elemezheti vagy feldolgozhatja a hálóadatokat, és felhasználhatja renderelésre és fizikai sugárképezésre és ütközésre.
    • Fontos megjegyezni, hogy skálát kell alkalmaznia a háló csúcspontpozícióira (például a hálók megjelenítéséhez használt csúcs árnyékolóban), hogy átalakítsa őket a pufferben tárolt optimalizált egész számegységekből a mérőkre. Ezt a skálát a VertexPositionScale hívásával lehet lekérni.

Hibaelhárítás

Térbeli leképezési kódminta bemutatója

A holografikus térbeli leképezés kódmintája olyan kódot tartalmaz, amely segítségével elkezdheti betölteni a felületi hálókat az alkalmazásba, beleértve a felületi hálók kezelésére és renderelésére szolgáló infrastruktúrát.

Most végigadjuk, hogyan adhat hozzá felületleképezési képességet a DirectX-alkalmazáshoz. Ezt a kódot hozzáadhatja a Windows Holographic app sablonprojektjéhez, vagy végigböngészheti a fenti kódmintát. Ez a kódminta a holografikus Windows alapul.

Az alkalmazás beállítása a spatialPerception képesség használatára

Az alkalmazás használhatja a térbeli leképezési képességet. Erre azért van szükség, mert a térbeli háló a felhasználói környezet reprezentációja, amely privát adatnak tekinthető. Deklarálja ezt a képességet az alkalmazás package.appxmanifest fájljában. Bemutatunk egy példát:

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

A képesség az uap2 névtérből származik. Ahhoz, hogy hozzáférjen ehhez a névtérhez a jegyzékfájlban, foglalja bele xlmns attribútumként a Package > elembe. Bemutatunk egy példát:

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

Térbeli leképezés funkciótámogatásának ellenőrzése

Windows Mixed Reality eszközök széles körét támogatja, beleértve a térbeli leképezést nem támogató eszközöket is. Ha az alkalmazás használhatja a térbeli leképezést, vagy térbeli leképezést kell használnia a funkciók kiosztása érdekében, a használatának kipróbálása előtt ellenőrizze, hogy a térbeli leképezés támogatott-e. Ha például a térbeli leképezést a vegyes valóságon keresztüli alkalmazáshoz kell megadnia, akkor egy üzenetet kell megjelenítenie erre a hatásra, ha a felhasználó térbeli leképezés nélkül próbálja azt egy eszközön futtatni. Vagy az alkalmazás renderelheti a saját virtuális környezetét a felhasználó környezete helyére, így a térbeli leképezés elérhetővé tétele esetén hasonló élményt nyújt. Ez az API minden esetben lehetővé teszi, hogy az alkalmazás értesülni fog arról, ha nem kap térbeli leképezési adatokat, és a megfelelő módon válaszol.

A térbeli leképezés támogatásának ellenőrzéséhez először ellenőrizze, hogy az UWP-szerződés a 4. vagy magasabb szinten van-e, majd hívja meg a SpatialSurfaceObserver::IsSupported() parancsot. Ezt a holografikus térleképezési kódminta kontextusában így lehet megtenni. A támogatás ellenőrzése csak a hozzáférés kérése előtt van ellenőrizve.

A SpatialSurfaceObserver::IsSupported() API az SDK 15063-as verziójától kezdve érhető el. Ha szükséges, az API használata előtt célként használja újra a projektet az 15063-as platformverzióra.

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

Ha az UWP-szerződés kevesebb, mint 4. szint, az alkalmazásnak úgy kell haladni, mintha az eszköz képes lenne térbeli leképezést folytatni.

Térbeli leképezési adatokhoz való hozzáférés kérése

Az alkalmazásnak engedélyt kell kérnie a térleképezési adatokhoz való hozzáféréshez, mielőtt felületi megfigyelőket próbál létrehozni. Az alábbi példa a Surface Mapping-kódmintán alapul, és további részleteket talál az oldal későbbi oldalán:

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

Surface Observer létrehozása

A Windows::P erception::Spatial::Felület névtér tartalmazza a SpatialSurfaceObserver osztályt, amely egy vagy több, a SpatialCoordinateSystemosztályban megadott kötetet figyel meg. A SpatialSurfaceObserver példány használatával valós időben férhet hozzá a felületi háló adataihoz.

Az AppMain.h-ból:

// Obtains surface mapping data from the device in real time.
Windows::Perception::Spatial::Surfaces::SpatialSurfaceObserver^     m_surfaceObserver;
Windows::Perception::Spatial::Surfaces::SpatialSurfaceMeshOptions^  m_surfaceMeshOptions;

Amint azt az előző szakaszban említettük, hozzáférést kell kérnie a térbeli leképezési adatokhoz, mielőtt az alkalmazás használni tudja őket. Ez a hozzáférés automatikusan megtörténik a 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();

Ezután konfigurálnia kell a Surface Observert egy adott határoló kötet megfigyelésére. Itt egy 20x20x5 m-es dobozt figyelünk meg, amely a koordinátarendszer eredetére van központú.

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

Ehelyett több határoló kötetet is be lehet állítani.

Ez egy álkód:

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

Más határoló alakzatok is használhatók, például egy nézet frustumja vagy egy tengelyhez nem igazodó határolókeret.

Ez egy álkód:

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

Ha az alkalmazásnak bármi mást kell tennie, ha a felületleképezési adatok nem érhetők el, írhat olyan kódot, amely reagál arra az esetre, amikor a SpatialPerceptionAccessStatus nem engedélyezett például nem lesz engedélyezve a modern eszközöket csatlakoztatott számítógépeken, mert ezek az eszközök nem tartalmaznak térbeli leképezési hardvert. Ezen eszközök esetében ehelyett a térbeli szakaszra kell támaszkodnia a felhasználó környezetével és eszközkonfigurációval kapcsolatos információkért.

A felületi hálógyűjtemény inicializálása és frissítése

Ha a Surface Observer létrehozása sikeres volt, folytathatjuk a felületi hálógyűjtemény inicializálását. Itt a lekért modell API-jának használatával azonnal lekértük a megfigyelt felületek aktuális készletét:

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

Elérhető egy leküldéses modell is a felületi háló adatainak leküldéséhez. Az alkalmazást szabadon úgy tervezheti meg, hogy csak a lekérdező modellt használja, ha úgy dönt, hogy ebben az esetben minden alkalommal lekérdezi az adatokat – például keretenként egyszer – vagy egy adott időszakban, például a játék beállítása során. Ha igen, akkor a fenti kódra lesz szüksége.

A kódmintában úgy döntöttünk, hogy mindkét modell két modell használatát mutatjuk be mindkét célra. Itt feliratkozunk egy eseményre, hogy naprakész felületi hálóadatokat fogadunk, amikor a rendszer felismer egy változást.

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

A kódminta úgy van konfigurálva, hogy reagáljon ezekre az eseményekre. Nézzük végig, hogyan tudjuk ezt megtenni.

MEGJEGYZÉS: Nem biztos, hogy ez a leghatékonyabb módszer az alkalmazás számára a hálóadatok kezeléséhez. Ez a kód az egyértelműség kedvéért lett megírva, és nincs optimalizálva.

A felületháló adatai egy csak olvasható térképen találhatók, amely a SpatialSurfaceInfo objektumokat tárolja a Platform::Guids kulcsértékekként használva.

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

Az adatok feldolgozásához először olyan kulcsértékeket keresünk, amelyek nem a gyűjteményben vannak. A témakör későbbi, az adatok mintaalkalmazásban való tárolásával kapcsolatos részleteket a témakör későbbi részletezi.

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

A felületi hálógyűjteményben található, de a rendszergyűjteményben már nem található felületi hálókat is el kell távolítani. Ahhoz, hogy ezt megtedjük, a hálók hozzáadásához és frissítéséhez éppen az ellenkezőjeként kell eltennünk; Hurkot hozunk létre az alkalmazás gyűjteményében, és ellenőrizzük, hogy a rendszergyűjteményben van-e a guid, amink van. Ha nem a rendszergyűjteményben van, eltávolítjuk a sajátunkból.

Az AppMain.cpp eseménykezelőből:

m_meshCollection->PruneMeshCollection(surfaceCollection);

A hálócsonkolás megvalósítása a RealtimeSurfaceMeshRenderer.cpp-ben:

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-adatpufferek beszerzése és használata

A felületi háló adatainak lekért információ egyszerűen lekért egy adatgyűjtést, és feldolgozta a gyűjtemény frissítéseit. Most részletesen is bemutatja, hogyan használhatja az adatokat.

A példakódban úgy döntöttünk, hogy a felületi hálókat használjuk a rendereléshez. Ez egy gyakori forgatókönyv a valós felületek mögötti hologramok eltolására. A hálókat vagy azok feldolgozott verzióit is renderelheti, így az alkalmazás vagy a játék funkcióinak biztosítása előtt megmutatja a felhasználónak, hogy a helyiség melyik területeit ellenőrzi a rendszer.

A kódminta akkor kezdi el a folyamatot, amikor felületi hálófrissítéseket fogad az előző szakaszban leírt eseménykezelőtől. Ebben a függvényben a fontos kódsor a felületi háló frissítésére való hívás:ezúttal már feldolgoztunk a hálóadatokat, és a csúcspont és az index adatait már a megfelelő időpontban használjuk.

A RealtimeSurfaceMeshRenderer.cpp-ben:

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

Mintakódunk úgy lett kialakítva, hogy a SurfaceMeshadatosztály kezelje a hálók adatfeldolgozását és -renderelését. A RealtimeSurfaceMeshRenderer valójában ezekre a hálókra tartja a térképet. Mindegyik rendelkezik hivatkozással arra a SpatialSurfaceMesh-re, amelyből érkezett, így bármikor használhatja, ha hozzá kell férni a háló csúcsponthoz vagy indexpufferhez, vagy le kell kapnia a háló átalakítását. A hálót jelenleg frissítésre vonatkozóként jelöljük meg.

A SurfaceMesh.cpp-ben:

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

Amikor a hálót a rendszer a rajzoláskor kéri, először a jelölőt ellenőrzi. Ha frissítésre van szükség, a csúcspont és az indexpufferek frissülnek a GPU-ban.

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

Először a nyers adatpuffereket kérjük le:

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;

Ezután Direct3D-eszközpuffereket hozunk létre a tároló által biztosított hálóadatokkal 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;
}

MEGJEGYZÉS: Az előző kódrészletben használt CreateDirectXBuffer segítő függvényhez tekintse meg a Surface Mapping kódmintát: SurfaceMesh.cpp, GetDataFromIBuffer.h. Az eszközerőforrás létrehozása befejeződött, és a háló betöltődött, és készen áll a frissítésre és renderelésre.

A felületi hálók frissítése és renderelése

A SurfaceMesh osztályunk speciális frissítési funkcióval rendelkezik. Minden SpatialSurfaceMesh saját átalakítással rendelkezik, és a minta az aktuális koordinátarendszert használja a SpatialStationaryReferenceFrame-hez az átalakítás megszerzéséhez. Ezután frissíti a modell állandó pufferét a GPU-on.

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

Amikor a felületi hálók renderelése eltelik, a gyűjtemény renderelése előtt el kell készülnünk néhány előkészítő munkára. Beállítottuk a shader folyamatot az aktuális renderelési konfigurációhoz, és beállítottuk a bemeneti szerelvény-szakaszt. A CameraResources.cpp holografikus kamera-segítőosztály már beállította a megtekintési/leképezés állandó pufferét.

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

Ha ez megtörtént, hurkot rajzolunk a hálókra, és arra mondjuk meg mindegyiket, hogy rajzolja meg magát. MEGJEGYZÉS: Ez a mintakód nincs optimalizálva semmilyen frustum-kiesés használatára, de érdemes ezt a funkciót az alkalmazásba is beveszi.

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

Az egyes hálók feladata a csúcs- és indexpuffer, a stride és a modell átalakító állandó pufferének beállítása. A forgó kockához Windows Holographic alkalmazássablonhoz is sztereotikus pufferekbe renderelünk instancing használatával.

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

Lehetőségek renderelése a Surface Mappingtel

A Felületleképezés kódmintája kódokat kínál a felületi háló adatainak csak eltakarítására, valamint a felületi háló adatainak a képernyőn való megjelenítésére. A választott elérési út – vagy mindkettő – az alkalmazástól függ. Ebben a dokumentumban mindkét konfigurációt végig fogjuk járni.

Eltakarási pufferek renderelése a holografikus hatás érdekében

Először törölnie kell az aktuális virtuális kamera renderelési célnézetét.

Az AppMain.cpp-ben:

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

Ez egy "előzetes renderelési" pass. Itt egy eltolópuffer létrehozásához megkérjük a háló renderelőt, hogy csak a mélységet renderelje. Ebben a konfigurációban nem csatolunk renderelési célnézetet, és a háló renderelője nullptr-ra állítja a képpont árnyékoló fázisát, hogy a GPU ne rajzoljon képpontokat. A geometria a mélységi pufferre lesz raszterizálva, és ott leáll a grafikus folyamat.

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

A Surface Mapping occlusion pufferen egy további mélységi teszttel rajzolhat hologramokat. Ebben a kódmintában képpontokat renderelünk a kockán, ha azok egy felület mögött vannak.

Az AppMain.cpp-ben:

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

A SpecialEffectPixelShader.hlsl kódja alapján:

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

Megjegyzés: A GatherDepthLess rutinhoz tekintse meg a Surface Mapping kódmintát: SpecialEffectPixelShader.hlsl.

A felületi háló adatainak megjelenítése a kijelzőre

A felületi hálókat egyszerűen a sztereotenes megjelenítési pufferre is kirajzoljuk. Úgy döntöttünk, hogy teljes arcokat rajzolunk megvilágítással, de rajzolhat vonalkeretet, folyamathálókat a renderelés előtt, alkalmazhat egy textúratérképet stb.

Itt a kódminta arra utasítja a háló renderelőt, hogy rajzolja meg a gyűjteményt. Ezúttal nem ad meg csak mélységi bérletet, hanem egy képpont árnyékolót csatol, és befejezi a renderelési folyamatot az aktuális virtuális kamerához megadott célokkal.

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

Lásd még

Megjegyzés

Ez a cikk az örökölt WinRT natív API-okkal kapcsolatos. Új natív alkalmazásprojektek esetén az OpenXR API használatát javasoljuk.

Ez a témakör bemutatja, hogyan valósíthat meg térbeli leképezést a DirectX-alkalmazásban, beleértve a Universal Windows Platform SDK-val csomagolt térbeli leképezési mintaalkalmazás részletes magyarázatát.

Ez a témakör a HolographicSpatialMapping UWP-kódmintából származó kódot használja.

Megjegyzés

A cikkben lévő kódrészletek jelenleg a C++ holografikus projektsablonban használt C++/CX használatát mutatják be a C++17-kompatibilis C++/WinRT helyett. Az alapfogalmak egyenértékűek a C++/WinRT-projektekkel, de a kódot le kell fordítania.

Eszköztámogatás