Partager via


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

Windows Mixed Reality s’appuie sur DirectX pour produire des expériences graphiques 3D riches 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édites par le système. Le développeur peut ensuite localiser ses hologrammes en fonction de chaque caméra, ce qui permet à l’application de restituer ces hologrammes dans différents systèmes de coordonnées spatiales au fur et à 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 Direct3D 12 Windows Mixed Reality est également fourni avec l’extension Mixed Reality modèles d’application.

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 de l’affichage.
  • Mettez à jour la scène avec la prédiction actuelle de l’emplacement de la vue de la caméra lorsque le rendu est terminé. Notez qu’il peut y avoir plusieurs caméras pour la scène holographique.

Pour effectuer un rendu sur des vues de caméra holographiques, une fois par image, l’application :

  • Pour chaque caméra, restituez la scène pour l’image actuelle, à l’aide des matrices de vue et de projection de la caméra du système.

Créer une image holographique et obtenir sa prédiction

HolographicFrame contient les informations dont l’application a besoin pour mettre à jour et restituer 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 dernières données de capteur disponibles et encapsulées dans l’objet CurrentPrediction .

Un nouvel objet frame doit être utilisé pour chaque image rendue, car il n’est valide qu’un instant dans le temps. La propriété CurrentPrediction contient des informations telles que la position de la caméra. Les informations sont extrapolée au moment exact dans le temps où la trame est censée être visible par l’utilisateur.

Le code suivant est extrait d’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 arrière peuvent changer d’image en image. Votre application doit valider la mémoire tampon d’arrière-plan pour chaque caméra et libérer et recréer des vues de ressources et des tampons de profondeur si nécessaire. Notez que l’ensemble des 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 déterminer où afficher les hologrammes de chaque image. Lorsque vous demandez des coordonnées à partir d’une API, vous transmettez toujours le 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);

Traitement du regard et de l’entrée de mouvement

Les entrées de regard et de main ne sont pas basées sur le temps et ne doivent 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 basées sur le temps

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 de classe d’assistance StepTimer peut fournir des mises à jour d’étape de temps fixes, des mises à jour d’étape de temps variable, et le mode par défaut est des étapes à temps variable.

Pour le rendu holographique, nous avons choisi de ne pas trop mettre dans la fonction de minuteur, car vous pouvez le configurer pour qu’il s’agit d’une étape de temps fixe. Il peut être appelé plusieurs fois par image (ou pas du tout, pour certaines images), et nos mises à jour holographiques doivent se produire une seule 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 modèle le fait avec SpatialStationaryReferenceFrame, ce processus n’est pas différent de ce à quoi vous êtes habitué dans les graphiques 3D. Ici, nous faisons pivoter 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 tournant 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 la même image rendue, 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. À l’heure actuelle, vous aurez calculé des transformations valides 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 de vue et de projection ? Pour de meilleurs résultats, nous voulons attendre d’être presque prêts pour nos appels de tirage avant de les obtenir.

Rendu de l’image actuelle

Le rendu sur Windows Mixed Reality n’est pas très 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 lorsque votre image est présentée, plus vos hologrammes seront de qualité.
  • Windows Mixed Reality contrôle les vues de la caméra. Effectuez un rendu sur 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 instance dans un tableau cible de rendu. Le modèle d’application holographique utilise l’approche recommandée du dessin instance pour un tableau cible de rendu, qui utilise une vue cible de rendu sur une Texture2DArray.
  • Si vous souhaitez effectuer un rendu sans utiliser l’instanciation stéréo, vous devez créer deux RenderTargetViews non tableaux, un pour chaque œil. Chaque RenderTargetViews fait référence à l’une des deux tranches de texture2DArray fournie à l’application à partir du système. Cela n’est pas recommandé, car il est généralement plus lent que l’utilisation de l’instanciation.

Obtenir une prédiction HolographicFrame mise à jour

La mise à jour de la prédiction d’images améliore l’efficacité de la stabilisation d’image. Vous obtenez un positionnement plus précis des hologrammes en raison du délai 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 des poses de caméra dans la prédiction et effectuez un rendu sur chaque caméra de 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 affichages, que le cerveau peut rapprocher comme profondeur réelle. Cette section décrit le rendu stéréoscopique à l’aide de l’instanciation, à l’aide du code du modèle d’application Windows Holographic.

Chaque caméra a sa propre cible de rendu (mémoire tampon arrière) et des matrices de vue et de projection dans l’espace holographique. Votre application doit créer d’autres ressources basées sur l’appareil photo, telles que la mémoire tampon de profondeur, par caméra. Dans le modèle d’application Holographique Windows, 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 de l’appareil photo

Les matrices d’affichage et de projection de chaque caméra holographique changent à 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. Au cours de ce processus, nous obtenons également la fenêtre d’affichage actuelle de 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 le même que celui que nous avons utilisé pour positionner le cube tournant.

À 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 chaque image. Votre nuanceur de vertex (au moins) aura généralement besoin d’accéder aux données d’affichage/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()
);

Effectuez un rendu sur la mémoire tampon arrière de la caméra et validez la mémoire tampon de profondeur :

Il est judicieux de case activée que TryGetViewTransform a réussi avant d’essayer d’utiliser les données d’affichage/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 afficher avec elle pour ce cadre. Le modèle appelle uniquement Render sur le cube tournant si la classe CameraResources indique une mise à jour réussie.

Windows Mixed Reality inclut des fonctionnalités de stabilisation d’image pour maintenir les hologrammes positionnés là 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 encore davantage la stabilisation de l’image, ou une mémoire tampon de profondeur peut être fournie pour calculer 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 pouvez ensuite utiliser les informations geometry de la mémoire tampon de profondeur pour optimiser la stabilisation d’image en temps réel. Le modèle d’application Holographique Windows 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);
    }
}

Notes

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 de 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 Holographique Windows affiche le contenu en stéréo à l’aide de la technique recommandée de dessin de géométrie d’instance sur une Texture2DArray de taille 2. Examinons la partie instanciation de cette opération et son fonctionnement 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/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, qui permet de définir la sémantique de l’index du tableau cible de rendu à 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 d’instance existantes avec cette méthode de dessin dans un tableau cible de rendu stéréo, dessinez deux fois le nombre d’instances dont vous disposez normalement. Dans le nuanceur, divisez input.instId par 2 pour obtenir l’ID de 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 de géométrie en raison de la façon dont la sémantique est définie pour Direct3D 11. Ici, nous montrons un exemple complet de la configuration d’un pipeline de rendu avec uniquement les étapes de vertex et de nuanceur de pixels définies. Le code du nuanceur est comme 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

Pour définir l’index du tableau cible de rendu dans le nuanceur de vertex, le pilote graphique prend en charge une fonctionnalité Direct3D 11.3 facultative, que HoloLens prend en charge. Votre application peut implémenter en toute sécurité cette technique pour le rendu, et toutes les conditions requises pour l’exécution sur le Microsoft HoloLens.

Il se peut que vous souhaitiez également utiliser l’émulateur HoloLens, qui peut être un outil de développement puissant pour votre application holographique et prendre en charge Windows Mixed Reality appareils casque immersifs attachés à Windows 10 PC. La prise en charge du chemin de rendu non HoloLens ( pour tous les Windows Mixed Reality ) est également intégrée au modèle d’application Holographique Windows. Dans le code du modèle, vous trouverez le code permettant à votre application holographique de s’exécuter sur le GPU de votre PC de développement. Voici comment la classe DeviceResources vérifie la prise en charge de cette fonctionnalité facultative.

À 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èsVSSetConstantBuffers et avantPSSetShader dans l’exemple de code présenté dans la section précédente qui explique comment restituer 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 de géométrie n’a pas besoin d’effectuer un travail ; le nuanceur de géométrie de modèle traverse toutes les données, à l’exception de l’index de tableau cible de rendu, qui est utilisé pour définir la sémantique SV_RenderTargetArrayIndex.

Code de 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ésent

Activer le cadre 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 des images à 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 pour chaque image, pour chaque caméra, afin d’optimiser les aspects du système tels que la stabilisation d’image ou Mixed Reality Capture. Ainsi, une application holographique utilisant DirectX n’appelle pas Present sur une chaîne de permutation DXGI. Au lieu de cela, vous utilisez la classe HolographicFrame pour présenter toutes les chaînes d’échange d’une image 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 le cadre se termine avant qu’il ne retourne. Les applications holographiques doivent attendre que l’image précédente se termine avant de commencer à travailler sur une nouvelle image, car cela réduit la latence et permet de meilleurs résultats à partir des prédictions d’images holographiques. Il ne s’agit pas d’une règle stricte. Si le rendu des images prend plus de temps qu’une actualisation de l’écran, vous pouvez désactiver cette attente en transmettant le paramètre HolographicFramePresentWaitBehavior à PresentUsingCurrentPrediction. Dans ce cas, vous utiliserez probablement un thread de rendu asynchrone pour maintenir une charge continue sur le GPU. La fréquence 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 varier 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 coopération avec HolographicFrame

Les applications DirectX 11 voudraient généralement case activée 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. Examinez le holographicFramePresentResult retourné pour déterminer si vous devez libérer et recréer les ressources basées sur l’appareil Et l’appareil Direct3D.

// 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 votre image présentée, vous pouvez revenir à la boucle du programme main et lui permettre de passer à l’image suivante.

PC graphiques hybrides et applications de réalité mixte

Windows 10 Creators Update PC peuvent être configurés avec des GPU discrets et intégrés. Avec ces types d’ordinateurs, Windows choisit l’adaptateur à laquelle le casque est connecté. Les applications doivent s’assurer que l’appareil DirectX qu’elles créent utilise le même adaptateur.

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 les problèmes, utilisez holographicAdapterID à partir de HolographicSpace. PrimaryAdapterId() ou HolographicDisplay. AdapterId(). Cet adapterId peut ensuite être utilisé pour sélectionner le bon DXGIAdapter à l’aide d’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 s’affiche pas ou la texture vidéo est endommagée, car Media Foundation utilise par défaut 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 du D3D11_CREATE_DEVICE_FLAG. Une fois l’appareil et le contexte créés, appelez SetMultithreadProtected pour activer le multithreading. Pour associer l’appareil à la fonction IMFDXGIDeviceManager, utilisez la fonction IMFDXGIDeviceManager::ResetDevice .

Code pour 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;

Voir aussi