Térbeli leképezés a DirectX-ben
Megjegyzés
Ez a cikk az örökölt WinRT natív API-kkal kapcsolatos. Új natív alkalmazásprojektek esetén az OpenXR API használatát javasoljuk.
Ez a témakör azt ismerteti, hogyan implementálhat térbeli leképezést a DirectX-alkalmazásban, beleértve a Univerzális 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 jelen cikkben szereplő kódrészletek jelenleg a C++/CX használatát mutatják be a C++17-kompatibilis C++/WinRT helyett, a C++ holografikus projektsablonban használt módon. A fogalmak egyenértékűek egy C++/WinRT-projekt esetében, bár a kódot le kell fordítania.
Eszköztámogatás
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és natív alkalmazásfejlesztése a Windows.Perception.Spatial névtér API-jait használja. Ezek az API-k teljes körű ellenőrzést biztosítanak a térbeli leképezési funkciók felett, ugyanúgy, ahogyan a Unity a térbeli leképezési API-kat is elérhetővé teszi.
Percepciós API-k
A térbeli leképezés fejlesztéséhez biztosított elsődleges típusok a következők:
- A SpatialSurfaceObserver információt nyújt a felhasználó közelében lévő alkalmazás által megadott térterületek felületeiről SpatialSurfaceInfo objektumok formájában.
- A SpatialSurfaceInfo egyetlen külső térbeli felületet ír le, beleértve az egyedi azonosítót, a határoló kötetet és az utolsó módosítás időpontját. Kérésre aszinkron módon biztosítja a SpatialSurfaceMesh-t.
- A SpatialSurfaceMeshOptions a SpatialSurfaceInfo által kért SpatialSurfaceMesh objektumok testreszabásához használt paramétereket tartalmaz.
- A SpatialSurfaceMesh egyetlen térbeli felület hálóadatait jelöli. A csúcspontpozíciók, a csúcspontok normaljai és a háromszögindexek adatait a tag SpatialSurfaceMeshBuffer objektumok tartalmazzák.
- A SpatialSurfaceMeshBuffer egyetlen típusú hálóadatokat burkol.
Amikor alkalmazásokat fejleszt ezen API-k használatával, az alapszintű programfolyamat így fog kinézni (ahogy az alább ismertetett mintaalkalmazásban is látható):
- A SpatialSurfaceObserver beállítása
- Hívja meg a RequestAccessAsync parancsot, és győződjön meg arról, 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ányosítása.
- Hívja meg a SetBoundingVolumes parancsot a térbeli felületekkel kapcsolatos információk megadásához. A jövőben módosíthatja ezeket a régiókat a függvény ismételt meghívásával. Minden régió egy SpatialBoundingVolume használatával van megadva.
- Regisztráljon a ObservedSurfacesChanged eseményre, amely akkor aktiválódik, ha új információ áll rendelkezésre a megadott térterületek térbeli felületeiről.
- Process ObservedSurfacesChanged events
- Az eseménykezelőben hívja meg a GetObservedSurfaces függvényt , hogy megkapja a SpatialSurfaceInfo objektumok térképét. Ezzel a térképpel frissítheti azokat a rekordokat, amelyek térbeli felületei a felhasználó környezetében léteznek.
- Minden SpatialSurfaceInfo objektum esetében lekérdezheti a TryGetBounds lekérdezést a felület térbeli kiterjedésének meghatározásához, a választott térbeli koordinátarendszerben kifejezve.
- Ha úgy dönt, hogy térbeli felületet kér, hívja meg a TryComputeLatestMeshAsync parancsot. 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 hozzáférést biztosítanak a háló háromszögindexeihez, csúcspontpozícióihoz és csúcspontjaihoz, ha kéri őket. Ezek az adatok közvetlenül kompatibilisek lesznek a hálók renderelésére használt Direct3D 11 API-kkal .
- Innen az alkalmazás opcionálisan elemezheti vagy feldolgozhatja a hálóadatokat, és felhasználhatja a rendereléshez és a fizikai sugárszóráshoz és az ütközéshez.
- Fontos megjegyezni, hogy skálát kell alkalmaznia a háló csúcspontpozícióira (például a hálók rendereléséhez használt csúcspontárnyékolóban), hogy azokat a pufferben tárolt optimalizált egész számegységekből mérőkké alakítsa. Ezt a skálát a VertexPositionScale meghívásával lehet lekérni.
Hibaelhárítás
- Ne felejtse el skálázni a háló csúcspontpozícióit a csúcspontárnyékolóban a SpatialSurfaceMesh.VertexPositionScale által visszaadott skálával
Térbeli leképezési kódminta – útmutató
A holografikus térbeli leképezési kódminta olyan kódot tartalmaz, amellyel megkezdheti a felületi hálók alkalmazásba való betöltését, beleértve a felületi hálók kezelésére és renderelésére szolgáló infrastruktúrát is.
Most bemutatjuk, hogyan adhat felületleképezési képességet a DirectX-alkalmazáshoz. Ezt a kódot hozzáadhatja a Windows Holographic-alkalmazássablon projektjéhez, vagy a fent említett kódmintát böngészve követheti. Ez a kódminta a Windows Holographic alkalmazássablonon 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ó környezetének 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. Ha hozzá szeretne férni ehhez a névtérhez a jegyzékben, vegye fel xlmns attribútumként a <Csomag> 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ési funkció támogatásának ellenőrzése
Windows Mixed Reality számos olyan eszközt támogat, beleértve az eszközöket is, amelyek nem támogatják a térbeli leképezést. Ha az alkalmazás használhat térbeli leképezést, vagy térbeli leképezést kell használnia a funkciók biztosításához, a használat előtt ellenőriznie kell, hogy támogatott-e a térbeli leképezés. Ha például a térbeli leképezésre van szükség a vegyes valóság alkalmazásához, akkor egy erre vonatkozó üzenetet kell megjelenítenie, ha egy felhasználó térbeli leképezés nélkül próbálja futtatni egy eszközön. Vagy az alkalmazás saját virtuális környezetét is megjelenítheti a felhasználó környezete helyett, és olyan élményt nyújt, amely hasonló ahhoz, ami akkor történne, ha a térbeli leképezés elérhető lenne. Ez az API mindenesetre lehetővé teszi, hogy az alkalmazás tisztában legyen azzal, hogy mikor nem kap térbeli leképezési adatokat, és mikor válaszol a megfelelő módon.
Az aktuális eszköz térbeli leképezési támogatásának ellenőrzéséhez először győződjön meg arról, hogy az UWP-szerződés 4. vagy annál magasabb szinten van, majd hívja meg a SpatialSurfaceObserver::IsSupported() parancsot. Ezt a holografikus térbeli leképezési kódminta kontextusában teheti meg. A támogatás ellenőrzése közvetlenül a hozzáférés kérése előtt történik.
A SpatialSurfaceObserver::IsSupported() API az SDK 15063-es verziójától kezdve érhető el. Ha szükséges, az API használata előtt indítsa újra a projektet az 15063-es platformra.
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 kisebb, mint a 4. szint, az alkalmazásnak úgy kell haladnia, mintha az eszköz képes volna térbeli leképezést végezni.
Hozzáférés kérése térbeli leképezési adatokhoz
Az alkalmazásnak engedélyt kell kérnie a térbeli leképezési adatokhoz való hozzáféréshez, mielőtt felszíni megfigyelőket próbálna létrehozni. Íme egy példa a Surface Mapping-kódminta alapján, amelyről később további részleteket talál ezen az oldalon:
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.
}
}
Felszíni megfigyelő létrehozása
A Windows::P erception::Spatial::Surface névtér tartalmazza a SpatialSurfaceObserver osztályt, amely a SpatialCoordinateSystemben megadott egy vagy több kötetet figyeli meg. Egy SpatialSurfaceObserver-példány használatával valós időben érheti el a surface mesh-adatokat.
Az AppMain.h fájlbó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;
Ahogy az előző szakaszban is említettük, a térbeli leképezési adatokhoz való hozzáférést kell kérnie ahhoz, hogy az alkalmazás használni tudja azokat. Ez a hozzáférés automatikusan meg van adva a HoloLensben.
// 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 felszíni megfigyelőt egy adott határoló kötet megfigyeléséhez. Itt egy 20x20x5 méteres dobozt figyelünk meg, amely a koordinátarendszer forrásánál van.
// 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állíthat.
Ez a pseudocode:
m_surfaceObserver->SetBoundingVolumes(/* iterable collection of bounding volumes*/);
Más határoló alakzatokat is használhat – például nézet frustumot vagy nem tengelyre igazított határolókeretet.
Ez a pseudocode:
m_surfaceObserver->SetBoundingVolume(
SpatialBoundingVolume::FromFrustum(/*SpatialCoordinateSystem*/, /*SpatialBoundingFrustum*/)
);
Ha az alkalmazásnak másképpen kell tennie, ha a felületleképezési adatok nem érhetők el, írhat kódot arra az esetre, ha a SpatialPerceptionAccessStatus nem engedélyezett – például nem lesz engedélyezve olyan számítógépeken, amelyekhez modern eszközök vannak csatolva, mert ezek az eszközök nem rendelkeznek térbeli leképezéshez szükséges hardverrel. Ezeknél az eszközöknél inkább a térbeli szakaszra kell támaszkodnia a felhasználó környezetével és eszközkonfigurációjával kapcsolatos információkhoz.
A Surface Mesh-gyűjtemény inicializálása és frissítése
Ha a felszíni megfigyelő sikeresen létrejött, folytathatjuk a surface mesh-gyűjtemény inicializálását. Itt a lekéréses modell API-val azonnal lekérjü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);
}
A surface mesh-adatok lekéréséhez egy leküldéses modell is rendelkezésre áll. Az alkalmazást szabadon megtervezheti úgy, hogy csak a lekéréses modellt használja, ha úgy dönt, ebben az esetben minden alkalommal – például keretenként egyszer – vagy egy adott időszakban, például a játék beállítása során kérdezi le az adatokat. Ha igen, a fenti kódra van szüksége.
A kódmintánkban mindkét modell pedagógiai célokra való használatát mutattuk be. Itt előfizetünk egy eseményre, hogy naprakész surface mesh-adatokat fogadjunk, 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ódmintánk úgy is konfigurálva van, hogy reagáljon ezekre az eseményekre. Nézzük meg, hogyan csináljuk ezt.
MEGJEGYZÉS: Előfordulhat, hogy nem ez a leghatékonyabb módja annak, hogy az alkalmazás kezelje a hálóadatokat. Ez a kód az egyértelműség kedvéért van megírva, és nincs optimalizálva.
A felületi háló adatai egy csak olvasható térképen találhatók, amely a Platform::Guids kulcsértékek használatával tárolja a SpatialSurfaceInfo objektumokat.
IMapView<Guid, SpatialSurfaceInfo^>^ const& surfaceCollection = sender->GetObservedSurfaces();
Az adatok feldolgozásához először olyan kulcsértékeket keresünk, amelyek nem szerepelnek a gyűjteményünkben. Az adatok mintaalkalmazásban való tárolásának részleteit a jelen témakör későbbi részében ismertetjük.
// 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);
}
}
El kell távolítanunk a felületi hálógyűjteményben lévő, de a rendszergyűjteményben már nem szereplő felületi hálószemeket is. Ehhez a hálók hozzáadásához és frissítéséhez az imént bemutatott ellenkezőjét kell tennünk; hurkot hozunk létre az alkalmazás gyűjteményén, és ellenőrizzük, hogy a guid azonosító szerepel-e a rendszergyűjteményben. Ha nem szerepel a rendszergyűjteményben, eltávolítjuk a miénkből.
Az AppMain.cpp-ben található eseménykezelőből:
m_meshCollection->PruneMeshCollection(surfaceCollection);
A hálómetszet implementálása a RealtimeSurfaceMeshRenderer.cpp fájlban:
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 Surface Mesh információinak lekérése olyan egyszerű volt, mint egy adatgyűjtés lekérése és a gyűjtemény frissítéseinek feldolgozása. Most részletesen bemutatjuk, hogyan használhatja fel az adatokat.
A kód példájában úgy döntöttünk, hogy a felületi hálókat használjuk a rendereléshez. Ez gyakori forgatókönyv a hologramok valós felületek mögötti elzárásához. Renderelheti a hálókat, vagy renderelheti azok feldolgozott verzióit is, hogy megmutassa a felhasználónak, hogy a szoba mely területeit vizsgálja meg, mielőtt elkezdené az alkalmazás- vagy játékfunkciók biztosítását.
A kódminta akkor indítja el a folyamatot, amikor surface mesh-frissítéseket kap az előző szakaszban ismertetett eseménykezelőtől. Ebben a függvényben a kód fontos sora a felületi háló frissítésének hívása: ekkorra már feldolgoztuk a háló adatait, és a csúcspont és az index adatainak lekérésére készülünk, hogy a megfelelőnek látsszuk.
Forrás: 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());
}
A mintakód úgy lett kialakítva, hogy a SurfaceMesh adatosztály kezelje a hálós adatfeldolgozást és -renderelést. Ezek a hálók azok, amelyekről a RealtimeSurfaceMeshRenderer valójában térképet vezet. Mindegyik rendelkezik hivatkozással a kapott SpatialSurfaceMesh fájlra, így bármikor használhatja, amikor hozzá kell férnie a háló csúcspontjaihoz vagy indexpuffereihez, vagy átalakíthatja a hálót. Egyelőre frissítésre szorulóként jelöljük meg a hálót.
A SurfaceMesh.cpp fájlból:
void SurfaceMesh::UpdateSurface(SpatialSurfaceMesh^ surfaceMesh)
{
m_surfaceMesh = surfaceMesh;
m_updateNeeded = true;
}
Amikor legközelebb a hálót arra kérik, hogy rajzolja meg magát, először a jelzőt fogja ellenőrizni. Ha frissítésre van szükség, a csúcspont és az indexpufferek frissülnek a 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;
}
Először a nyers adatpuffereket szerezzük be:
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 HoloLens által biztosított hálóadatokkal:
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édfüggvényhez tekintse meg a Surface-leképezési kódmintát: SurfaceMesh.cpp, GetDataFromIBuffer.h. Az eszközerőforrás létrehozása befejeződött, és a háló be van töltve, és készen áll a frissítésre és a renderelésre.
Felületi hálók frissítése és renderelése
A SurfaceMesh osztály egy speciális frissítési függvénnyel rendelkezik. Minden SpatialSurfaceMesh saját átalakítást használ, és a minta a SpatialStationaryReferenceFrame aktuális koordinátarendszerét használja az átalakítás beszerzéséhez. Ezután frissíti a modellállandó puffert a 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
);
}
Amikor itt az ideje a felületi hálók renderelésének, a gyűjtemény renderelése előtt el kell végeznünk néhány előkészítő munkát. Beállítjuk az árnyékoló folyamatot az aktuális renderelési konfigurációhoz, és beállítjuk a bemeneti összeszerelő fázist. A CameraResources.cpp holografikus kamera segédosztálya már beállította a nézet-/vetítési állandó puffert.
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 vonunk a hálóinkra, és elmondjuk mindegyiknek, hogy rajzolja meg magát. MEGJEGYZÉS: Ez a mintakód nem úgy van optimalizálva, hogy bármilyen frustum-selejtezést használjon, de ezt a funkciót fel kell vennie az alkalmazásba.
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 felelősek a csúcs- és indexpuffer, a stride és a modellátalakítás konstanspufferének beállításáért. A Windows Holographic alkalmazássablon forgó kockáihoz hasonlóan a sztereoszkopikus pufferekbe is instancing használatával renderelünk.
A SurfaceMesh::D raw-ból:
// 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.
);
Megjelenítési lehetőségek Surface-leképezéssel
A Surface Mapping-kódminta kódot kínál a surface mesh-adatok csak occlusion-rendereléséhez és a surface mesh-adatok képernyőn történő rendereléséhez. A választott elérési út – vagy mindkettő – az alkalmazástól függ. Ebben a dokumentumban mindkét konfigurációt áttekintjük.
Elzáródási pufferek renderelése holografikus effektushoz
Először törölje az aktuális virtuális kamera renderelési célnézetét.
Az AppMain.cpp fájlból:
context->ClearRenderTargetView(pCameraResources->GetBackBufferRenderTargetView(), DirectX::Colors::Transparent);
Ez egy "előmegjelenítési" passz. Itt létrehozunk egy elzáródási puffert úgy, hogy 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 nullptrre állítja a képpontárnyékoló fázisát, hogy a GPU ne fáradjon a képpontok rajzolására. A geometria a mélységi pufferbe lesz raszterizálva, és a grafikus folyamat ott fog leállni.
// 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 elzáródási pufferének további mélységi vizsgálatával hologramokat rajzolhatunk. Ebben a kódmintában a kockán lévő képpontokat más színnel jelenítjük meg, ha azok egy felület mögött találhatók.
Az AppMain.cpp fájlból:
// 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ód 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áját: SpecialEffectPixelShader.hlsl.
Surface Mesh-adatok megjelenítése a kijelzőn
A felületi hálókat a sztereó kijelzőpufferekhez is megrajzolhatjuk. Úgy döntöttünk, hogy teljes arcokat rajzolunk világítással, de szabadon rajzolhat drótvázvázokat, feldolgozhatja a hálókat a renderelés előtt, textúratérképet alkalmazhat stb.
Itt a kódminta arra utasítja a mesh renderelőt, hogy rajzolja meg a gyűjteményt. Ezúttal nem ad meg csak mélységi átengedést, 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);