Mappage spatial dans DirectX
Notes
Cet article concerne les API natives WinRT héritées. Pour les nouveaux projets d’application native, nous vous recommandons d’utiliser l’API OpenXR.
Cette rubrique explique comment implémenter le mappage spatial dans votre application DirectX, y compris une explication détaillée de l’exemple d’application de mappage spatial empaqueté avec le KIT de développement logiciel (SDK) plateforme Windows universelle.
Cette rubrique utilise le code de l’exemple de code UWP HolographicSpatialMapping .
Notes
Les extraits de code de cet article illustrent actuellement l’utilisation de C++/CX plutôt que C++/WinRT compatible C++/WinRT, comme utilisé dans le modèle de projet holographique C++. Les concepts sont équivalents pour un projet C++/WinRT, mais vous devez traduire le code.
Prise en charge des appareils
Fonctionnalité | HoloLens (1ère génération) | HoloLens 2 | Casques immersifs |
Mappage spatial | ✔️ | ✔️ | ❌ |
Vue d’ensemble du développement DirectX
Le développement d’applications natives pour le mappage spatial utilise les API de l’espace de noms Windows.Perception.Spatial . Ces API vous donnent un contrôle total des fonctionnalités de mappage spatial, de la même manière que les API de mappage spatial sont exposées par Unity.
API de perception
Les principaux types fournis pour le développement de mappage spatial sont les suivants :
- SpatialSurfaceObserver fournit des informations sur les surfaces dans les régions d’espace spécifiées par l’application près de l’utilisateur, sous la forme d’objets SpatialSurfaceInfo.
- SpatialSurfaceInfo décrit une seule surface spatiale existante, y compris un ID unique, un volume englobant et l’heure de la dernière modification. Il fournit un SpatialSurfaceMesh de manière asynchrone sur demande.
- SpatialSurfaceMeshOptions contient les paramètres utilisés pour personnaliser les objets SpatialSurfaceMesh demandés à SpatialSurfaceInfo.
- SpatialSurfaceMesh représente les données de maillage d’une surface spatiale unique. Les données relatives aux positions de vertex, aux normales de vertex et aux index triangles sont contenues dans les objets SpatialSurfaceMeshBuffer membres.
- SpatialSurfaceMeshBuffer encapsule un type unique de données de maillage.
Lorsque vous développez une application à l’aide de ces API, votre flux de programme de base se présente comme suit (comme illustré dans l’exemple d’application décrit ci-dessous) :
- Configurer votre SpatialSurfaceObserver
- Appelez RequestAccessAsync pour vous assurer que l’utilisateur a donné l’autorisation à votre application d’utiliser les fonctionnalités de mappage spatial de l’appareil.
- Instanciez un objet SpatialSurfaceObserver.
- Appelez SetBoundingVolumes pour spécifier les régions d’espace dans lesquelles vous souhaitez obtenir des informations sur les surfaces spatiales. Vous pouvez modifier ces régions à l’avenir en appelant à nouveau cette fonction. Chaque région est spécifiée à l’aide d’un spatialBoundingVolume.
- Inscrivez-vous à l’événement ObservedSurfacesChanged , qui se déclenche chaque fois que de nouvelles informations sont disponibles sur les surfaces spatiales dans les régions d’espace que vous avez spécifiées.
- Événements Process ObservedSurfacesChanged
- Dans votre gestionnaire d’événements, appelez GetObservedSurfaces pour recevoir une carte d’objets SpatialSurfaceInfo. À l’aide de cette carte, vous pouvez mettre à jour vos enregistrements des surfaces spatiales qui existent dans l’environnement de l’utilisateur.
- Pour chaque objet SpatialSurfaceInfo, vous pouvez interroger TryGetBounds pour déterminer les étendues spatiales de la surface, exprimées dans un système de coordonnées spatiales de votre choix.
- Si vous décidez de demander un maillage pour une surface spatiale, appelez TryComputeLatestMeshAsync. Vous pouvez fournir des options spécifiant la densité des triangles et le format des données de maillage retournées.
- Maillage de réception et de traitement
- Chaque appel à TryComputeLatestMeshAsync retourne de manière asynchrone un objet SpatialSurfaceMesh.
- À partir de cet objet, vous pouvez accéder aux objets SpatialSurfaceMeshBuffer contenus, ce qui vous permet d’accéder aux index triangles, aux positions de vertex et aux normales de vertex du maillage si vous les demandez. Ces données seront dans un format directement compatible avec les API Direct3D 11 utilisées pour le rendu des maillages.
- À partir de là, votre application peut éventuellement analyser ou traiter les données de maillage et les utiliser pour le rendu et la physique raycasting et la collision.
- Un détail important à noter est que vous devez appliquer une échelle aux positions de vertex de maillage (par exemple, dans le nuanceur de vertex utilisé pour le rendu des maillages), pour les convertir des unités entières optimisées dans lesquelles elles sont stockées dans la mémoire tampon, en compteurs. Vous pouvez récupérer cette échelle en appelant VertexPositionScale.
Dépannage
- N’oubliez pas de mettre à l’échelle les positions de vertex maillage dans votre nuanceur de vertex, à l’aide de l’échelle retournée par SpatialSurfaceMesh.VertexPositionScale
Exemple de procédure pas à pas de code de mappage spatial
L’exemple de code Holographic Spatial Mapping inclut du code que vous pouvez utiliser pour commencer à charger des maillages de surface dans votre application, y compris l’infrastructure de gestion et de rendu des maillages de surface.
À présent, nous allons découvrir comment ajouter une fonctionnalité de mappage de surface à votre application DirectX. Vous pouvez ajouter ce code à votre projet de modèle d’application Holographique Windows , ou vous pouvez suivre en parcourant l’exemple de code mentionné ci-dessus. Cet exemple de code est basé sur le modèle d’application Holographique Windows.
Configurer votre application pour utiliser la fonctionnalité spatialPerception
Votre application peut utiliser la fonctionnalité de mappage spatial. Cela est nécessaire, car le maillage spatial est une représentation de l’environnement de l’utilisateur, qui peut être considéré comme des données privées. Déclarez cette fonctionnalité dans le fichier package.appxmanifest pour votre application. Voici un exemple :
<Capabilities>
<uap2:Capability Name="spatialPerception" />
</Capabilities>
La fonctionnalité provient de l’espace de noms uap2 . Pour accéder à cet espace de noms dans votre manifeste, incluez-le en tant qu’attribut xlmns dans l’élément <Package> . Voici un exemple :
<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"
>
Vérifier la prise en charge de la fonctionnalité de mappage spatial
Windows Mixed Reality prend en charge un large éventail d’appareils, y compris les appareils, qui ne prennent pas en charge le mappage spatial. Si votre application peut utiliser le mappage spatial ou doit utiliser le mappage spatial pour fournir des fonctionnalités, elle doit case activée s’assurer que le mappage spatial est pris en charge avant d’essayer de l’utiliser. Par exemple, si le mappage spatial est requis par votre application de réalité mixte, il doit afficher un message à cet effet si un utilisateur tente de l’exécuter sur un appareil sans mappage spatial. Votre application peut également restituer son propre environnement virtuel à la place de l’environnement de l’utilisateur, offrant une expérience similaire à ce qui se passerait si le mappage spatial était disponible. En tout état de cause, cette API permet à votre application de savoir quand elle n’obtient pas de données de mappage spatial et de répondre de la manière appropriée.
Pour case activée l’appareil actuel pour la prise en charge du mappage spatial, vérifiez d’abord que le contrat UWP est au niveau 4 ou supérieur, puis appelez SpatialSurfaceObserver::IsSupported(). Voici comment procéder dans le contexte de l’exemple de code De mappage spatial holographique . La prise en charge est vérifiée juste avant la demande d’accès.
L’API SpatialSurfaceObserver::IsSupported() est disponible à partir de la version 15063 du SDK. Si nécessaire, reciblez votre projet sur la plateforme version 15063 avant d’utiliser cette API.
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 ...
Lorsque le contrat UWP est inférieur au niveau 4, l’application doit continuer comme si l’appareil était capable d’effectuer un mappage spatial.
Demander l’accès aux données de mappage spatial
Votre application doit demander l’autorisation d’accéder aux données de mappage spatial avant d’essayer de créer des observateurs de surface. Voici un exemple basé sur notre exemple de code de mappage de surface, avec plus de détails fournis plus loin sur cette page :
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.
}
}
Créer un observateur surface
L’espace de noms Windows::P erception::Spatial::Surfaces inclut la classe SpatialSurfaceObserver, qui observe un ou plusieurs volumes que vous spécifiez dans un SpatialCoordinateSystem. Utilisez un instance SpatialSurfaceObserver pour accéder aux données surface mesh en temps réel.
À partir d’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;
Comme indiqué dans la section précédente, vous devez demander l’accès aux données de mappage spatial avant que votre application puisse les utiliser. Cet accès est accordé automatiquement sur 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();
Ensuite, vous devez configurer l’observateur de surface pour observer un volume englobant spécifique. Ici, nous observons une boîte de 20x20x5 mètres, centrée à l’origine du système de coordonnées.
// 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);
Vous pouvez définir plusieurs volumes englobants à la place.
Il s’agit d’un pseudocode :
m_surfaceObserver->SetBoundingVolumes(/* iterable collection of bounding volumes*/);
Il est également possible d’utiliser d’autres formes englobantes, telles qu’un frustum d’affichage ou un cadre englobant qui n’est pas aligné sur l’axe.
Il s’agit d’un pseudocode :
m_surfaceObserver->SetBoundingVolume(
SpatialBoundingVolume::FromFrustum(/*SpatialCoordinateSystem*/, /*SpatialBoundingFrustum*/)
);
Si votre application doit faire quoi que ce soit différemment lorsque les données de mappage de surface ne sont pas disponibles, vous pouvez écrire du code pour répondre au cas où spatialPerceptionAccessStatus n’est pas autorisé . Par exemple, il ne sera pas autorisé sur les PC avec des appareils immersifs attachés, car ces appareils ne disposent pas de matériel pour le mappage spatial. Pour ces appareils, vous devez plutôt vous fier à la phase spatiale pour obtenir des informations sur l’environnement et la configuration de l’appareil de l’utilisateur.
Initialiser et mettre à jour la collection surface mesh
Si l’observateur de surface a été créé avec succès, nous pouvons continuer à initialiser notre collection de maillage de surface. Ici, nous utilisons l’API de modèle pull pour obtenir immédiatement l’ensemble actuel de surfaces observées :
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);
}
Il existe également un modèle Push disponible pour obtenir des données de maillage de surface. Vous êtes libre de concevoir votre application pour utiliser uniquement le modèle d’extraction si vous le souhaitez, auquel cas vous interrogerez les données de temps en temps ( par exemple, une fois par image) ou pendant une période spécifique, par exemple pendant la configuration du jeu. Si c’est le cas, le code ci-dessus est ce dont vous avez besoin.
Dans notre exemple de code, nous avons choisi de démontrer l’utilisation des deux modèles à des fins pédagogiques. Ici, nous nous abonneons à un événement pour recevoir des données de maillage de surface à jour chaque fois que le système reconnaît une modification.
m_surfaceObserver->ObservedSurfacesChanged += ref new TypedEventHandler<SpatialSurfaceObserver^, Platform::Object^>(
bind(&HolographicDesktopAppMain::OnSurfacesChanged, this, _1, _2)
);
Notre exemple de code est également configuré pour répondre à ces événements. Passons en revue la façon dont nous faisons cela.
NOTE: Ce n’est peut-être pas le moyen le plus efficace pour votre application de gérer les données de maillage. Ce code est écrit pour plus de clarté et n’est pas optimisé.
Les données de maillage de surface sont fournies dans une carte en lecture seule qui stocke les objets SpatialSurfaceInfo à l’aide de Platform::Guids comme valeurs de clé.
IMapView<Guid, SpatialSurfaceInfo^>^ const& surfaceCollection = sender->GetObservedSurfaces();
Pour traiter ces données, nous recherchons d’abord les valeurs de clé qui ne figurent pas dans notre collection. Des détails sur la façon dont les données sont stockées dans notre exemple d’application seront présentés plus loin dans cette rubrique.
// 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);
}
}
Nous devons également supprimer les maillages de surface qui se trouvent dans notre collection de maillages de surface, mais qui ne sont plus dans la collection système. Pour ce faire, nous devons faire quelque chose de semblable à l’opposé de ce que nous venons de montrer pour ajouter et mettre à jour des maillages; nous effectuons une boucle sur la collection de notre application et case activée pour voir si le GUID que nous avons se trouve dans la collection système. Si elle n’est pas dans la collection système, nous la supprimons de la nôtre.
À partir de notre gestionnaire d’événements dans AppMain.cpp :
m_meshCollection->PruneMeshCollection(surfaceCollection);
Implémentation de l’élagage de maillage dans 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);
}
}
Acquérir et utiliser des mémoires tampons de données de maillage de surface
L’obtention des informations de maillage de surface était aussi simple que l’extraction d’une collection de données et le traitement des mises à jour de cette collection. À présent, nous allons entrer en détail sur la façon dont vous pouvez utiliser les données.
Dans notre exemple de code, nous avons choisi d’utiliser les maillages de surface pour le rendu. Il s’agit d’un scénario courant pour l’ocluding d’hologrammes derrière des surfaces réelles. Vous pouvez également afficher les maillages, ou afficher les versions traitées de ceux-ci, pour montrer à l’utilisateur quelles zones de la salle sont analysées avant de commencer à fournir des fonctionnalités d’application ou de jeu.
L’exemple de code démarre le processus lorsqu’il reçoit des mises à jour de maillage de surface à partir du gestionnaire d’événements que nous avons décrit dans la section précédente. La ligne de code importante dans cette fonction est l’appel à mettre à jour le maillage de surface : à ce stade, nous avons déjà traité les informations de maillage, et nous allons obtenir les données de vertex et d’index pour une utilisation comme bon nous semble.
À partir de 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());
}
Notre exemple de code est conçu pour qu’une classe de données, SurfaceMesh, gère le traitement et le rendu des données de maillage. Ces maillages sont ce que RealtimeSurfaceMeshRenderer conserve en fait une carte. Chacun d’eux a une référence au SpatialSurfaceMesh dont il provient. Vous pouvez donc l’utiliser chaque fois que vous avez besoin d’accéder au vertex de maillage ou aux mémoires tampons d’index, ou d’obtenir une transformation pour le maillage. Pour l’instant, nous signalons le maillage comme nécessitant une mise à jour.
À partir de SurfaceMesh.cpp :
void SurfaceMesh::UpdateSurface(SpatialSurfaceMesh^ surfaceMesh)
{
m_surfaceMesh = surfaceMesh;
m_updateNeeded = true;
}
La prochaine fois que le maillage est invité à dessiner lui-même, il case activée d’abord l’indicateur. Si une mise à jour est nécessaire, les mémoires tampons de vertex et d’index sont mises à jour sur le GPU.
void SurfaceMesh::CreateDeviceDependentResources(ID3D11Device* device)
{
m_indexCount = m_surfaceMesh->TriangleIndices->ElementCount;
if (m_indexCount < 3)
{
// Not enough indices to draw a triangle.
return;
}
Tout d’abord, nous acquérons les mémoires tampons de données brutes :
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;
Ensuite, nous créons des mémoires tampons d’appareil Direct3D avec les données de maillage fournies par 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;
}
NOTE: Pour la fonction d’assistance CreateDirectXBuffer utilisée dans l’extrait de code précédent, consultez l’exemple de code Mappage de Surface : SurfaceMesh.cpp, GetDataFromIBuffer.h. La création de la ressource d’appareil est terminée et le maillage est considéré comme chargé et prêt pour la mise à jour et le rendu.
Mettre à jour et afficher les maillages de surface
Notre classe SurfaceMesh a une fonction de mise à jour spécialisée. Chaque SpatialSurfaceMesh a sa propre transformation, et notre exemple utilise le système de coordonnées actuel pour notre SpatialStationaryReferenceFrame afin d’acquérir la transformation. Ensuite, il met à jour la mémoire tampon constante du modèle sur le 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
);
}
Lorsqu’il est temps de restituer les maillages de surface, nous effectuons un travail de préparation avant de restituer la collection. Nous avons configuré le pipeline du nuanceur pour la configuration de rendu actuelle et nous avons configuré l’étape de l’assembleur d’entrée. La classe d’assistance de caméra holographique CameraResources.cpp a déjà configuré la mémoire tampon de constante de vue/projection.
À partir de 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
);
}
Une fois cette opération effectuée, nous effectuons une boucle sur nos maillages et disons à chacun de se dessiner. NOTE: Cet exemple de code n’est pas optimisé pour utiliser une sorte d’élimination des frustums, mais vous devez inclure cette fonctionnalité dans votre application.
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);
}
Les maillages individuels sont responsables de la configuration de la mémoire tampon de vertex et d’index, de la stride et de la mémoire tampon constante de transformation de modèle. Comme avec le cube en rotation dans le modèle d’application Holographique Windows, nous effectuons le rendu dans des mémoires tampons stéréoscopiques à l’aide de l’instanciation.
À partir de 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.
);
Choix de rendu avec mappage de surface
L’exemple de code Mappage de surface offre du code pour le rendu occlusion uniquement des données de maillage de surface et pour le rendu à l’écran des données de maillage de surface. Le chemin que vous choisissez (ou les deux) dépend de votre application. Nous allons parcourir les deux configurations dans ce document.
Rendu des mémoires tampons d’occlusion pour l’effet holographique
Commencez par effacer la vue cible de rendu pour la caméra virtuelle actuelle.
À partir d’AppMain.cpp :
context->ClearRenderTargetView(pCameraResources->GetBackBufferRenderTargetView(), DirectX::Colors::Transparent);
Il s’agit d’une passe de « pré-rendu ». Ici, nous créons une mémoire tampon d’occlusion en demandant au convertisseur de maillage de restituer uniquement la profondeur. Dans cette configuration, nous n’attachons pas d’affichage cible de rendu, et le convertisseur de maillage définit l’étape du nuanceur de pixels sur nullptr afin que le GPU ne s’embête pas à dessiner des pixels. La géométrie sera rastérisée dans la mémoire tampon de profondeur, et le pipeline graphique s’arrêtera 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);
Nous pouvons dessiner des hologrammes avec un test de profondeur supplémentaire sur la mémoire tampon d’occlusion de mappage de surface. Dans cet exemple de code, nous restituons les pixels sur le cube d’une couleur différente s’ils se trouvent derrière une surface.
À partir d’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()
);
Basé sur le code de 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);
}
Note: Pour notre routine GatherDepthLess , consultez l’exemple de code mappage de surface : SpecialEffectPixelShader.hlsl.
Rendu des données de maillage de surface sur l’affichage
Nous pouvons également simplement dessiner les maillages de surface dans les mémoires tampons d’affichage stéréo. Nous avons choisi de dessiner des visages complets avec l’éclairage, mais vous êtes libre de dessiner un wireframe, de traiter les maillages avant le rendu, d’appliquer une carte de texture, etc.
Ici, notre exemple de code indique au convertisseur de maillage de dessiner la collection. Cette fois, nous ne spécifions pas de passe de profondeur uniquement, il attache un nuanceur de pixels et termine le pipeline de rendu à l’aide des cibles que nous avons spécifiées pour la caméra virtuelle actuelle.
// 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);