Rendu dans DirectX
Remarque
Cet article concerne les API natives WinRT héritées. Pour les nouveaux projets d’application natifs, nous vous recommandons d’utiliser l’API OpenXR.
Windows Mixed Reality repose sur DirectX pour produire des expériences graphiques 3D enrichies pour les utilisateurs. L’abstraction de rendu se trouve juste au-dessus de DirectX, ce qui permet aux applications de raisonner sur la position et l’orientation des observateurs de scène holographique prédits par le système. Le développeur peut ensuite localiser ses hologrammes en fonction de chaque caméra, ce qui permet à l’application d’afficher ces hologrammes dans différents systèmes de coordonnées spatiales à mesure que l’utilisateur se déplace.
Remarque : cette procédure pas à pas décrit le rendu holographique dans Direct3D 11. Un modèle d’application Windows Mixed Reality Direct3D 12 est également fourni avec l’extension de modèles d’application Mixed Reality.
Mise à jour de l’image actuelle
Pour mettre à jour l’état de l’application pour les hologrammes, une fois par image, l’application :
- Obtenez un HolographicFrame à partir du système de gestion d’affichage.
- Mettez à jour la scène avec la prédiction actuelle de l’emplacement où la vue de l’appareil photo sera terminée. Notez qu’il peut y avoir plusieurs caméras pour la scène holographique.
Pour afficher les vues de caméra holographique, une fois par image, l’application effectue les événements suivants :
- Pour chaque caméra, affichez la scène pour le cadre actuel, à l’aide des matrices de vue et de projection de la caméra à partir du système.
Créer une trame holographique et obtenir sa prédiction
HolographicFrame contient des informations que l’application doit mettre à jour et afficher l’image actuelle. L’application commence chaque nouvelle image en appelant la méthode CreateNextFrame . Lorsque cette méthode est appelée, les prédictions sont effectuées à l’aide des données de capteur les plus récentes disponibles et encapsulées dans l’objet CurrentPrediction .
Un nouvel objet frame doit être utilisé pour chaque image rendue, car il n’est valide que pour un instant dans le temps. La propriété CurrentPrediction contient des informations telles que la position de la caméra. Les informations sont extrapées au moment exact dans le temps où l’image est censée être visible par l’utilisateur.
Le code suivant est extrait de AppMain ::Update :
// The HolographicFrame has information that the app needs in order
// to update and render the current frame. The app begins each new
// frame by calling CreateNextFrame.
HolographicFrame holographicFrame = m_holographicSpace.CreateNextFrame();
// Get a prediction of where holographic cameras will be when this frame
// is presented.
HolographicFramePrediction prediction = holographicFrame.CurrentPrediction();
Traiter les mises à jour de la caméra
Les mémoires tampons de retour peuvent passer du frame au frame. Votre application doit valider la mémoire tampon d’arrière-plan pour chaque caméra et libérer et recréer les vues de ressources et les mémoires tampons de profondeur en fonction des besoins. Notez que l’ensemble de poses dans la prédiction est la liste faisant autorité des caméras utilisées dans le cadre actuel. En règle générale, vous utilisez cette liste pour itérer sur l’ensemble des caméras.
À partir d’AppMain ::Update :
m_deviceResources->EnsureCameraResources(holographicFrame, prediction);
À partir de DeviceResources ::EnsureCameraResources :
for (HolographicCameraPose const& cameraPose : prediction.CameraPoses())
{
HolographicCameraRenderingParameters renderingParameters = frame.GetRenderingParameters(cameraPose);
CameraResources* pCameraResources = cameraResourceMap[cameraPose.HolographicCamera().Id()].get();
pCameraResources->CreateResourcesForBackBuffer(this, renderingParameters);
}
Obtenir le système de coordonnées à utiliser comme base pour le rendu
Windows Mixed Reality permet à votre application de créer différents systèmes de coordonnées, tels que des cadres de référence fixes et attachés pour le suivi des emplacements dans le monde physique. Votre application peut ensuite utiliser ces systèmes de coordonnées pour raisonner l’emplacement de rendu des hologrammes chaque image. Lorsque vous demandez des coordonnées à partir d’une API, vous passez toujours dans spatialCoordinateSystem dans lequel vous souhaitez que ces coordonnées soient exprimées.
À partir d’AppMain ::Update :
pose = SpatialPointerPose::TryGetAtTimestamp(
m_stationaryReferenceFrame.CoordinateSystem(), prediction.Timestamp());
Ces systèmes de coordonnées peuvent ensuite être utilisés pour générer des matrices d’affichage stéréo lors du rendu du contenu dans votre scène.
À partir de CameraResources ::UpdateViewProjectionBuffer :
// Get a container object with the view and projection matrices for the given
// pose in the given coordinate system.
auto viewTransformContainer = cameraPose.TryGetViewTransform(coordinateSystem);
Traiter le regard et l’entrée de mouvement
Les entrées de regard et de main ne sont pas basées sur le temps et n’ont pas à être mises à jour dans la fonction StepTimer. Toutefois, cette entrée est quelque chose que l’application doit examiner chaque image.
Traiter les mises à jour temporelles
Toute application de rendu en temps réel a besoin d’un moyen de traiter les mises à jour basées sur le temps : le modèle d’application Holographique Windows utilise une implémentation StepTimer , similaire à stepTimer fournie dans le modèle d’application UWP DirectX 11. Cet exemple d’assistance StepTimer peut fournir des mises à jour chronologiques fixes, des mises à jour d’étape variable et le mode par défaut est des étapes de temps variable.
Pour le rendu holographique, nous avons choisi de ne pas placer trop dans la fonction du minuteur, car vous pouvez la configurer pour qu’elle soit une étape de temps fixe. Il peut être appelé plus d’une fois par image ( ou pas du tout, pour certaines images), et nos mises à jour de données holographiques doivent se produire une fois par image.
À partir d’AppMain ::Update :
m_timer.Tick([this]()
{
m_spinningCubeRenderer->Update(m_timer);
});
Positionner et faire pivoter les hologrammes dans votre système de coordonnées
Si vous utilisez un système de coordonnées unique, comme le fait le modèle avec SpatialStationaryReferenceFrame, ce processus n’est pas différent de ce que vous utilisez dans les graphiques 3D. Ici, nous pivotons le cube et définissons la matrice de modèle en fonction de la position dans le système de coordonnées stationnaire.
À partir de SpinningCubeRenderer ::Update :
// Rotate the cube.
// Convert degrees to radians, then convert seconds to rotation angle.
const float radiansPerSecond = XMConvertToRadians(m_degreesPerSecond);
const double totalRotation = timer.GetTotalSeconds() * radiansPerSecond;
const float radians = static_cast<float>(fmod(totalRotation, XM_2PI));
const XMMATRIX modelRotation = XMMatrixRotationY(-radians);
// Position the cube.
const XMMATRIX modelTranslation = XMMatrixTranslationFromVector(XMLoadFloat3(&m_position));
// Multiply to get the transform matrix.
// Note that this transform does not enforce a particular coordinate system. The calling
// class is responsible for rendering this content in a consistent manner.
const XMMATRIX modelTransform = XMMatrixMultiply(modelRotation, modelTranslation);
// The view and projection matrices are provided by the system; they are associated
// with holographic cameras, and updated on a per-camera basis.
// Here, we provide the model transform for the sample hologram. The model transform
// matrix is transposed to prepare it for the shader.
XMStoreFloat4x4(&m_modelConstantBufferData.model, XMMatrixTranspose(modelTransform));
Remarque sur les scénarios avancés : le cube épinglé est un exemple simple de la façon de positionner un hologramme dans un cadre de référence unique. Il est également possible d’utiliser plusieurs SpatialCoordinateSystems dans le même frame rendu, en même temps.
Mettre à jour les données de mémoire tampon constante
Les transformations de modèle pour le contenu sont mises à jour comme d’habitude. À présent, vous disposez de transformations valides calculées pour le système de coordonnées dans lequel vous allez effectuer le rendu.
À partir de SpinningCubeRenderer ::Update :
// Update the model transform buffer for the hologram.
context->UpdateSubresource(
m_modelConstantBuffer.Get(),
0,
nullptr,
&m_modelConstantBufferData,
0,
0
);
Qu’en est-il des transformations d’affichage et de projection ? Pour de meilleurs résultats, nous voulons attendre jusqu’à ce que nous sommes presque prêts pour nos appels de tirage avant de les obtenir.
Afficher le frame actuel
Le rendu sur Windows Mixed Reality n’est pas beaucoup différent du rendu sur un affichage mono 2D, mais il existe quelques différences :
- Les prédictions d’images holographiques sont importantes. Plus la prédiction est proche du moment où votre image est présentée, mieux vos hologrammes s’afficheront.
- Windows Mixed Reality contrôle les vues de la caméra. Affichez chacun d’eux, car le cadre holographique les présentera ultérieurement.
- Nous vous recommandons d’effectuer un rendu stéréo à l’aide d’un dessin instancené vers un tableau cible de rendu. Le modèle d’application holographique utilise l’approche recommandée du dessin instancené pour un tableau cible de rendu, qui utilise une vue cible de rendu sur une texture2DArray.
- Si vous souhaitez effectuer un rendu sans utiliser d’instanciation stéréo, vous devez créer deux RenderTargetViews non matricielles, une pour chaque œil. Chaque RenderTargetViews fait référence à l’une des deux tranches de texture2DArray fournies à l’application à partir du système. Cela n’est pas recommandé, car il est généralement plus lent que d’utiliser l’instanciation.
Obtenir une prédiction HolographicFrame mise à jour
La mise à jour de la prédiction de trame améliore l’efficacité de la stabilisation d’image. Vous obtenez un positionnement plus précis des hologrammes en raison du temps plus court entre la prédiction et le moment où l’image est visible par l’utilisateur. Dans l’idéal, mettez à jour votre prédiction d’images juste avant le rendu.
holographicFrame.UpdateCurrentPrediction();
HolographicFramePrediction prediction = holographicFrame.CurrentPrediction();
Rendu sur chaque appareil photo
Effectuez une boucle sur l’ensemble de poses de caméra dans la prédiction et affichez chaque caméra dans cet ensemble.
Configurer votre passe de rendu
Windows Mixed Reality utilise le rendu stéréoscopique pour améliorer l’illusion de profondeur et pour rendre stéréoscopiquement, de sorte que l’affichage gauche et droit sont actifs. Avec le rendu stéréoscopique, il y a un décalage entre les deux écrans, que le cerveau peut rapprocher comme profondeur réelle. Cette section traite du rendu stéréoscopique à l’aide de l’instanciation, à l’aide du code à partir du modèle d’application Windows Holographic.
Chaque caméra a sa propre cible de rendu (mémoire tampon arrière), et les matrices de vue et de projection, dans l’espace holographique. Votre application doit créer d’autres ressources basées sur une caméra, telles que la mémoire tampon de profondeur, par caméra. Dans le modèle d’application Windows Holographic, nous fournissons une classe d’assistance pour regrouper ces ressources dans DX ::CameraResources. Commencez par configurer les vues cibles de rendu :
À partir d’AppMain ::Render :
// This represents the device-based resources for a HolographicCamera.
DX::CameraResources* pCameraResources = cameraResourceMap[cameraPose.HolographicCamera().Id()].get();
// Get the device context.
const auto context = m_deviceResources->GetD3DDeviceContext();
const auto depthStencilView = pCameraResources->GetDepthStencilView();
// Set render targets to the current holographic camera.
ID3D11RenderTargetView *const targets[1] =
{ pCameraResources->GetBackBufferRenderTargetView() };
context->OMSetRenderTargets(1, targets, depthStencilView);
// Clear the back buffer and depth stencil view.
if (m_canGetHolographicDisplayForCamera &&
cameraPose.HolographicCamera().Display().IsOpaque())
{
context->ClearRenderTargetView(targets[0], DirectX::Colors::CornflowerBlue);
}
else
{
context->ClearRenderTargetView(targets[0], DirectX::Colors::Transparent);
}
context->ClearDepthStencilView(
depthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
Utilisez la prédiction pour obtenir les matrices d’affichage et de projection pour l’appareil photo
Les matrices d’affichage et de projection pour chaque caméra holographique changent avec chaque image. Actualisez les données dans la mémoire tampon constante pour chaque caméra holographique. Effectuez cette opération après avoir mis à jour la prédiction et avant d’effectuer des appels de dessin pour cette caméra.
À partir d’AppMain ::Render :
// The view and projection matrices for each holographic camera will change
// every frame. This function refreshes the data in the constant buffer for
// the holographic camera indicated by cameraPose.
if (m_stationaryReferenceFrame)
{
pCameraResources->UpdateViewProjectionBuffer(
m_deviceResources, cameraPose, m_stationaryReferenceFrame.CoordinateSystem());
}
// Attach the view/projection constant buffer for this camera to the graphics pipeline.
bool cameraActive = pCameraResources->AttachViewProjectionBuffer(m_deviceResources);
Ici, nous montrons comment les matrices sont acquises à partir de la pose de la caméra. Pendant ce processus, nous obtenons également la fenêtre d’affichage actuelle pour l’appareil photo. Notez comment nous fournissons un système de coordonnées : il s’agit du même système de coordonnées que celui que nous avons utilisé pour comprendre le regard, et c’est celui que nous avons utilisé pour positionner le cube de rotation.
À partir de CameraResources ::UpdateViewProjectionBuffer :
// The system changes the viewport on a per-frame basis for system optimizations.
auto viewport = cameraPose.Viewport();
m_d3dViewport = CD3D11_VIEWPORT(
viewport.X,
viewport.Y,
viewport.Width,
viewport.Height
);
// The projection transform for each frame is provided by the HolographicCameraPose.
HolographicStereoTransform cameraProjectionTransform = cameraPose.ProjectionTransform();
// Get a container object with the view and projection matrices for the given
// pose in the given coordinate system.
auto viewTransformContainer = cameraPose.TryGetViewTransform(coordinateSystem);
// If TryGetViewTransform returns a null pointer, that means the pose and coordinate
// system cannot be understood relative to one another; content cannot be rendered
// in this coordinate system for the duration of the current frame.
// This usually means that positional tracking is not active for the current frame, in
// which case it is possible to use a SpatialLocatorAttachedFrameOfReference to render
// content that is not world-locked instead.
DX::ViewProjectionConstantBuffer viewProjectionConstantBufferData;
bool viewTransformAcquired = viewTransformContainer != nullptr;
if (viewTransformAcquired)
{
// Otherwise, the set of view transforms can be retrieved.
HolographicStereoTransform viewCoordinateSystemTransform = viewTransformContainer.Value();
// Update the view matrices. Holographic cameras (such as Microsoft HoloLens) are
// constantly moving relative to the world. The view matrices need to be updated
// every frame.
XMStoreFloat4x4(
&viewProjectionConstantBufferData.viewProjection[0],
XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Left) *
XMLoadFloat4x4(&cameraProjectionTransform.Left))
);
XMStoreFloat4x4(
&viewProjectionConstantBufferData.viewProjection[1],
XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Right) *
XMLoadFloat4x4(&cameraProjectionTransform.Right))
);
}
La fenêtre d’affichage doit être définie sur chaque image. Votre nuanceur de vertex (au moins) a généralement besoin d’accéder aux données de vue/projection.
À partir de CameraResources ::AttachViewProjectionBuffer :
// Set the viewport for this camera.
context->RSSetViewports(1, &m_d3dViewport);
// Send the constant buffer to the vertex shader.
context->VSSetConstantBuffers(
1,
1,
m_viewProjectionConstantBuffer.GetAddressOf()
);
Affichez la mémoire tampon arrière de la caméra et validez la mémoire tampon de profondeur :
Il est judicieux de vérifier que TryGetViewTransform a réussi avant d’essayer d’utiliser les données de vue/projection, car si le système de coordonnées n’est pas locatable (par exemple, le suivi a été interrompu) votre application ne peut pas s’afficher avec elle pour cette image. Le modèle appelle uniquement Render sur le cube épinglage si la classe CameraResources indique une mise à jour réussie.
Windows Mixed Reality inclut des fonctionnalités de stabilisation d’image pour conserver les hologrammes positionnés où un développeur ou un utilisateur les place dans le monde. La stabilisation d’image permet de masquer la latence inhérente à un pipeline de rendu pour garantir les meilleures expériences holographiques pour les utilisateurs. Un point de focus peut être spécifié pour améliorer la stabilisation de l’image encore plus loin, ou une mémoire tampon de profondeur peut être fournie pour la stabilisation d’image optimisée en temps réel.
Pour obtenir de meilleurs résultats, votre application doit fournir une mémoire tampon de profondeur à l’aide de l’API CommitDirect3D11DepthBuffer . Windows Mixed Reality peut ensuite utiliser des informations géométriques à partir de la mémoire tampon de profondeur pour optimiser la stabilisation d’image en temps réel. Le modèle d’application Windows Holographic valide la mémoire tampon de profondeur de l’application par défaut, ce qui permet d’optimiser la stabilité de l’hologramme.
À partir d’AppMain ::Render :
// Only render world-locked content when positional tracking is active.
if (cameraActive)
{
// Draw the sample hologram.
m_spinningCubeRenderer->Render();
if (m_canCommitDirect3D11DepthBuffer)
{
// On versions of the platform that support the CommitDirect3D11DepthBuffer API, we can
// provide the depth buffer to the system, and it will use depth information to stabilize
// the image at a per-pixel level.
HolographicCameraRenderingParameters renderingParameters =
holographicFrame.GetRenderingParameters(cameraPose);
IDirect3DSurface interopSurface =
DX::CreateDepthTextureInteropObject(pCameraResources->GetDepthStencilTexture2D());
// Calling CommitDirect3D11DepthBuffer causes the system to queue Direct3D commands to
// read the depth buffer. It will then use that information to stabilize the image as
// the HolographicFrame is presented.
renderingParameters.CommitDirect3D11DepthBuffer(interopSurface);
}
}
Remarque
Windows traite votre texture de profondeur sur le GPU. Il doit donc être possible d’utiliser votre mémoire tampon de profondeur comme ressource de nuanceur. L’ID3D11Texture2D que vous créez doit être dans un format sans type et doit être lié en tant qu’affichage des ressources de nuanceur. Voici un exemple de création d’une texture de profondeur qui peut être validée pour la stabilisation d’image.
Code pour la création de ressources de mémoire tampon de profondeur pour CommitDirect3D11DepthBuffer :
// Create a depth stencil view for use with 3D rendering if needed.
CD3D11_TEXTURE2D_DESC depthStencilDesc(
DXGI_FORMAT_R16_TYPELESS,
static_cast<UINT>(m_d3dRenderTargetSize.Width),
static_cast<UINT>(m_d3dRenderTargetSize.Height),
m_isStereo ? 2 : 1, // Create two textures when rendering in stereo.
1, // Use a single mipmap level.
D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE
);
winrt::check_hresult(
device->CreateTexture2D(
&depthStencilDesc,
nullptr,
&m_d3dDepthStencil
));
CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(
m_isStereo ? D3D11_DSV_DIMENSION_TEXTURE2DARRAY : D3D11_DSV_DIMENSION_TEXTURE2D,
DXGI_FORMAT_D16_UNORM
);
winrt::check_hresult(
device->CreateDepthStencilView(
m_d3dDepthStencil.Get(),
&depthStencilViewDesc,
&m_d3dDepthStencilView
));
Dessiner du contenu holographique
Le modèle d’application Windows Holographic restitue le contenu en stéréo à l’aide de la technique recommandée pour dessiner la géométrie instanceée sur une Texture2DArray de taille 2. Examinons la partie d’instanciation de ce problème et la façon dont elle fonctionne sur Windows Mixed Reality.
À partir de SpinningCubeRenderer ::Render :
// Draw the objects.
context->DrawIndexedInstanced(
m_indexCount, // Index count per instance.
2, // Instance count.
0, // Start index location.
0, // Base vertex location.
0 // Start instance location.
);
Chaque instance accède à une matrice d’affichage/de projection différente de la mémoire tampon constante. Voici la structure de mémoire tampon constante, qui n’est qu’un tableau de deux matrices.
À partir de VertexShaderShared.hlsl, inclus par VPRTVertexShader.hlsl :
// A constant buffer that stores each set of view and projection matrices in column-major format.
cbuffer ViewProjectionConstantBuffer : register(b1)
{
float4x4 viewProjection[2];
};
L’index du tableau cible de rendu doit être défini pour chaque pixel. Dans l’extrait de code suivant, output.viewId est mappé à la sémantique SV_RenderTargetArrayIndex . Cela nécessite la prise en charge d’une fonctionnalité Direct3D 11.3 facultative, ce qui permet à la sémantique de l’index du tableau cible de rendu d’être définie à partir de n’importe quelle étape du nuanceur.
À partir de VPRTVertexShader.hlsl :
// Per-vertex data passed to the geometry shader.
struct VertexShaderOutput
{
min16float4 pos : SV_POSITION;
min16float3 color : COLOR0;
// The render target array index is set here in the vertex shader.
uint viewId : SV_RenderTargetArrayIndex;
};
À partir de VertexShaderShared.hlsl, inclus par VPRTVertexShader.hlsl :
// Per-vertex data used as input to the vertex shader.
struct VertexShaderInput
{
min16float3 pos : POSITION;
min16float3 color : COLOR0;
uint instId : SV_InstanceID;
};
// Simple shader to do vertex processing on the GPU.
VertexShaderOutput main(VertexShaderInput input)
{
VertexShaderOutput output;
float4 pos = float4(input.pos, 1.0f);
// Note which view this vertex has been sent to. Used for matrix lookup.
// Taking the modulo of the instance ID allows geometry instancing to be used
// along with stereo instanced drawing; in that case, two copies of each
// instance would be drawn, one for left and one for right.
int idx = input.instId % 2;
// Transform the vertex position into world space.
pos = mul(pos, model);
// Correct for perspective and project the vertex position onto the screen.
pos = mul(pos, viewProjection[idx]);
output.pos = (min16float4)pos;
// Pass the color through without modification.
output.color = input.color;
// Set the render target array index.
output.viewId = idx;
return output;
}
Si vous souhaitez utiliser vos techniques de dessin instances existantes avec cette méthode de dessin sur un tableau cible de rendu stéréo, dessinez deux fois le nombre d’instances que vous avez normalement. Dans le nuanceur, divisez input.instId par 2 pour obtenir l’ID d’instance d’origine, qui peut être indexé dans (par exemple) une mémoire tampon de données par objet : int actualIdx = input.instId / 2;
Remarque importante sur le rendu du contenu stéréo sur HoloLens
Windows Mixed Reality prend en charge la possibilité de définir l’index du tableau cible de rendu à partir de n’importe quelle étape du nuanceur. Normalement, il s’agit d’une tâche qui ne peut être effectuée qu’à l’étape du nuanceur geometry en raison de la façon dont la sémantique est définie pour Direct3D 11. Ici, nous montrons un exemple complet de configuration d’un pipeline de rendu avec uniquement les étapes de vertex et de nuanceur de pixels définies. Le code du nuanceur est décrit ci-dessus.
À partir de SpinningCubeRenderer ::Render :
const auto context = m_deviceResources->GetD3DDeviceContext();
// Each vertex is one instance of the VertexPositionColor struct.
const UINT stride = sizeof(VertexPositionColor);
const UINT offset = 0;
context->IASetVertexBuffers(
0,
1,
m_vertexBuffer.GetAddressOf(),
&stride,
&offset
);
context->IASetIndexBuffer(
m_indexBuffer.Get(),
DXGI_FORMAT_R16_UINT, // Each index is one 16-bit unsigned integer (short).
0
);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetInputLayout(m_inputLayout.Get());
// Attach the vertex shader.
context->VSSetShader(
m_vertexShader.Get(),
nullptr,
0
);
// Apply the model constant buffer to the vertex shader.
context->VSSetConstantBuffers(
0,
1,
m_modelConstantBuffer.GetAddressOf()
);
// Attach the pixel shader.
context->PSSetShader(
m_pixelShader.Get(),
nullptr,
0
);
// Draw the objects.
context->DrawIndexedInstanced(
m_indexCount, // Index count per instance.
2, // Instance count.
0, // Start index location.
0, // Base vertex location.
0 // Start instance location.
);
Remarque importante sur le rendu sur les appareils non HoloLens
La définition de l’index du tableau cible de rendu dans le nuanceur de vertex nécessite que le pilote graphique prenne en charge une fonctionnalité Direct3D 11.3 facultative, prise en charge par HoloLens. Votre application peut implémenter en toute sécurité cette technique pour le rendu, et toutes les exigences seront remplies pour s’exécuter sur Microsoft HoloLens.
Il peut s’agir du cas où vous souhaitez également utiliser l’émulateur HoloLens, qui peut également être un outil de développement puissant pour votre application holographique , et prendre en charge les appareils de casque immersif Windows Mixed Reality attachés aux PC Windows 10. La prise en charge du chemin d’accès de rendu non-HoloLens ( pour l’ensemble de Windows Mixed Reality) est également intégrée au modèle d’application Windows Holographic. Dans le code du modèle, vous trouverez du code pour permettre à votre application holographique de s’exécuter sur le GPU dans votre PC de développement. Voici comment la classe DeviceResources recherche cette prise en charge facultative des fonctionnalités.
À partir de DeviceResources ::CreateDeviceResources :
// Check for device support for the optional feature that allows setting the render target array index from the vertex shader stage.
D3D11_FEATURE_DATA_D3D11_OPTIONS3 options;
m_d3dDevice->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS3, &options, sizeof(options));
if (options.VPAndRTArrayIndexFromAnyShaderFeedingRasterizer)
{
m_supportsVprt = true;
}
Pour prendre en charge le rendu sans cette fonctionnalité facultative, votre application doit utiliser un nuanceur geometry pour définir l’index du tableau cible de rendu. Cet extrait de code est ajouté après VSSetConstantBuffers et avant PSSetShader dans l’exemple de code présenté dans la section précédente qui explique comment afficher la stéréo sur HoloLens.
À partir de SpinningCubeRenderer ::Render :
if (!m_usingVprtShaders)
{
// On devices that do not support the D3D11_FEATURE_D3D11_OPTIONS3::
// VPAndRTArrayIndexFromAnyShaderFeedingRasterizer optional feature,
// a pass-through geometry shader is used to set the render target
// array index.
context->GSSetShader(
m_geometryShader.Get(),
nullptr,
0
);
}
REMARQUE HLSL : Dans ce cas, vous devez également charger un nuanceur de vertex légèrement modifié qui transmet l’index du tableau cible de rendu au nuanceur geometry à l’aide d’une sémantique de nuanceur toujours autorisée, telle que TEXCOORD0. Le nuanceur geometry n’a pas à effectuer de travail ; le nuanceur de géométrie du modèle passe toutes les données, à l’exception de l’index du tableau cible de rendu, qui est utilisé pour définir la sémantique SV_RenderTargetArrayIndex.
Code du modèle d’application pour GeometryShader.hlsl :
// Per-vertex data from the vertex shader.
struct GeometryShaderInput
{
min16float4 pos : SV_POSITION;
min16float3 color : COLOR0;
uint instId : TEXCOORD0;
};
// Per-vertex data passed to the rasterizer.
struct GeometryShaderOutput
{
min16float4 pos : SV_POSITION;
min16float3 color : COLOR0;
uint rtvId : SV_RenderTargetArrayIndex;
};
// This geometry shader is a pass-through that leaves the geometry unmodified
// and sets the render target array index.
[maxvertexcount(3)]
void main(triangle GeometryShaderInput input[3], inout TriangleStream<GeometryShaderOutput> outStream)
{
GeometryShaderOutput output;
[unroll(3)]
for (int i = 0; i < 3; ++i)
{
output.pos = input[i].pos;
output.color = input[i].color;
output.rtvId = input[i].instId;
outStream.Append(output);
}
}
Présenter
Activer l’image holographique pour présenter la chaîne d’échange
Avec Windows Mixed Reality, le système contrôle la chaîne d’échange. Le système gère ensuite la présentation d’images sur chaque caméra holographique pour garantir une expérience utilisateur de haute qualité. Il fournit également une mise à jour de la fenêtre d’affichage chaque image, pour chaque caméra, afin d’optimiser les aspects du système, tels que la stabilisation d’image ou la capture de réalité mixte. Par conséquent, une application holographique utilisant DirectX n’appelle pas Present sur une chaîne d’échange DXGI. Au lieu de cela, vous utilisez la classe HolographicFrame pour présenter toutes les chaînes d’échange pour un cadre une fois que vous avez terminé de le dessiner.
À partir de DeviceResources ::P resent :
HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();
Par défaut, cette API attend que l’image se termine avant de retourner. Les applications holographiques doivent attendre la fin de l’image précédente avant de commencer le travail sur une nouvelle image, car cela réduit la latence et permet de meilleurs résultats à partir des prédictions de trames holographiques. Ce n’est pas une règle difficile et si vous avez des images qui prennent plus de temps qu’une actualisation de l’écran pour afficher, vous pouvez désactiver cette attente en passant le paramètre HolographicFramePresentWaitBehavior à PresentUsingCurrentPrediction. Dans ce cas, vous utiliseriez probablement un thread de rendu asynchrone pour maintenir une charge continue sur le GPU. Le taux d’actualisation de l’appareil HoloLens est de 60 hz, où une image a une durée d’environ 16 ms. Les appareils de casque immersif peuvent passer de 60 hz à 90 hz ; lors de l’actualisation de l’affichage à 90 hz, chaque image aura une durée d’environ 11 ms.
Gérer les scénarios DeviceLost en collaboration avec HolographicFrame
Les applications DirectX 11 souhaitent généralement vérifier le HRESULT retourné par la fonction Present de la chaîne d’échange DXGI pour déterminer s’il y a eu une erreur DeviceLost. La classe HolographicFrame gère cela pour vous. Inspectez le holographicFramePresentResult retourné pour savoir si vous devez libérer et recréer les ressources direct3D et basées sur l’appareil.
// The PresentUsingCurrentPrediction API will detect when the graphics device
// changes or becomes invalid. When this happens, it is considered a Direct3D
// device lost scenario.
if (presentResult == HolographicFramePresentResult::DeviceRemoved)
{
// The Direct3D device, context, and resources should be recreated.
HandleDeviceLost();
}
Si l’appareil Direct3D a été perdu et que vous l’avez recréé, vous devez indiquer à HolographicSpace de commencer à utiliser le nouvel appareil. La chaîne d’échange sera recréée pour cet appareil.
À partir de DeviceResources ::InitializeUsingHolographicSpace :
m_holographicSpace.SetDirect3D11Device(m_d3dInteropDevice);
Une fois que votre image est présentée, vous pouvez revenir à la boucle de programme principale et l’autoriser à passer à l’image suivante.
Pc graphiques hybrides et applications de réalité mixte
Mise à jour Windows 10 Créateurs PC peuvent être configurés avec des GPU discrets et intégrés. Avec ces types d’ordinateurs, Windows choisit l’adaptateur auxquels le casque est connecté. Les applications doivent s’assurer que l’appareil DirectX qu’il crée utilise la même carte.
L’exemple de code Direct3D le plus général illustre la création d’un appareil DirectX à l’aide de la carte matérielle par défaut, qui sur un système hybride peut ne pas être identique à celle utilisée pour le casque.
Pour contourner tous les problèmes, utilisez holographicAdapterID à partir de HolographicSpace. PrimaryAdapterId() ou HolographicDisplay. AdapterId(). Cet id d’adaptateur peut ensuite être utilisé pour sélectionner le DXGIAdapter approprié à l’aide de IDXGIFactory4.EnumAdapterByLuid.
À partir de DeviceResources ::InitializeUsingHolographicSpace :
// The holographic space might need to determine which adapter supports
// holograms, in which case it will specify a non-zero PrimaryAdapterId.
LUID id =
{
m_holographicSpace.PrimaryAdapterId().LowPart,
m_holographicSpace.PrimaryAdapterId().HighPart
};
// When a primary adapter ID is given to the app, the app should find
// the corresponding DXGI adapter and use it to create Direct3D devices
// and device contexts. Otherwise, there is no restriction on the DXGI
// adapter the app can use.
if ((id.HighPart != 0) || (id.LowPart != 0))
{
UINT createFlags = 0;
// Create the DXGI factory.
ComPtr<IDXGIFactory1> dxgiFactory;
winrt::check_hresult(
CreateDXGIFactory2(
createFlags,
IID_PPV_ARGS(&dxgiFactory)
));
ComPtr<IDXGIFactory4> dxgiFactory4;
winrt::check_hresult(dxgiFactory.As(&dxgiFactory4));
// Retrieve the adapter specified by the holographic space.
winrt::check_hresult(
dxgiFactory4->EnumAdapterByLuid(
id,
IID_PPV_ARGS(&m_dxgiAdapter)
));
}
else
{
m_dxgiAdapter.Reset();
}
Code pour mettre à jour DeviceResources ::CreateDeviceResources pour utiliser IDXGIAdapter
// Create the Direct3D 11 API device object and a corresponding context.
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> context;
const D3D_DRIVER_TYPE driverType = m_dxgiAdapter == nullptr ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_UNKNOWN;
const HRESULT hr = D3D11CreateDevice(
m_dxgiAdapter.Get(), // Either nullptr, or the primary adapter determined by Windows Holographic.
driverType, // Create a device using the hardware graphics driver.
0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE.
creationFlags, // Set debug and Direct2D compatibility flags.
featureLevels, // List of feature levels this app can support.
ARRAYSIZE(featureLevels), // Size of the list above.
D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Windows Runtime apps.
&device, // Returns the Direct3D device created.
&m_d3dFeatureLevel, // Returns feature level of device created.
&context // Returns the device immediate context.
);
Graphiques hybrides et Media Foundation
L’utilisation de Media Foundation sur des systèmes hybrides peut entraîner des problèmes où la vidéo ne sera pas rendue ou la texture vidéo est endommagée, car Media Foundation est défini par défaut sur un comportement système. Dans certains scénarios, la création d’un ID3D11Device distinct est nécessaire pour prendre en charge le multithreading et les indicateurs de création corrects sont définis.
Lors de l’initialisation de l’ID3D11Device, D3D11_CREATE_DEVICE_VIDEO_SUPPORT indicateur doit être défini dans le cadre de la D3D11_CREATE_DEVICE_FLAG. Une fois l’appareil et le contexte créés, appelez SetMultithreadProtected pour activer le multithreading. Pour associer l’appareil au IMFDXGIDeviceManager, utilisez la fonction IMFDXGIDeviceManager ::ResetDevice .
Code permettant d’associer un ID3D11Device à IMFDXGIDeviceManager :
// create dx device for media pipeline
winrt::com_ptr<ID3D11Device> spMediaDevice;
// See above. Also make sure to enable the following flags on the D3D11 device:
// * D3D11_CREATE_DEVICE_VIDEO_SUPPORT
// * D3D11_CREATE_DEVICE_BGRA_SUPPORT
if (FAILED(CreateMediaDevice(spAdapter.get(), &spMediaDevice)))
return;
// Turn multithreading on
winrt::com_ptr<ID3D10Multithread> spMultithread;
if (spContext.try_as(spMultithread))
{
spMultithread->SetMultithreadProtected(TRUE);
}
// lock the shared dxgi device manager
// call MFUnlockDXGIDeviceManager when no longer needed
UINT uiResetToken;
winrt::com_ptr<IMFDXGIDeviceManager> spDeviceManager;
hr = MFLockDXGIDeviceManager(&uiResetToken, spDeviceManager.put());
if (FAILED(hr))
return hr;
// associate the device with the manager
hr = spDeviceManager->ResetDevice(spMediaDevice.get(), uiResetToken);
if (FAILED(hr))
return hr;