Rendering in DirectX

Nota

Questo articolo è correlato alle API native WinRT legacy. Per i nuovi progetti di app native, è consigliabile usare l'API OpenXR.

Windows Mixed Reality è basato su DirectX per produrre esperienze grafiche 3D e significative per gli utenti. L'astrazione del rendering si trova appena sopra DirectX, che consente alle app di pensare alla posizione e all'orientamento degli osservatori della scena olografica stimati dal sistema. Lo sviluppatore può quindi individuare gli ologrammi in base a ogni fotocamera, consentendo all'app di eseguire il rendering di questi ologrammi in vari sistemi di coordinate spaziali mentre l'utente si sposta.

Nota: questa procedura dettagliata descrive il rendering olografico in Direct3D 11. Un modello di app Windows Mixed Reality Direct3D 12 viene fornito anche con l'Realtà mista dei modelli di app.

Aggiornamento per il frame corrente

Per aggiornare lo stato dell'applicazione per gli ologrammi, una volta per fotogramma l'app:

  • Ottenere un holographicFrame dal sistema di gestione dello schermo.
  • Aggiornare la scena con la stima corrente della posizione in cui si trova la fotocamera al termine del rendering. Si noti che possono essere presenti più fotocamere per la scena olografica.

Per eseguire il rendering nelle visualizzazioni della fotocamera olografica, una volta per fotogramma l'app:

  • Per ogni fotocamera, esegui il rendering della scena per il fotogramma corrente, usando le matrici di visualizzazione e proiezione della fotocamera del sistema.

Creare un nuovo fotogramma olografico e ottenere la stima

HolographicFrame contiene informazioni che l'app deve aggiornare ed eseguire il rendering del frame corrente. L'app inizia ogni nuovo frame chiamando il metodo CreateNextFrame . Quando viene chiamato questo metodo, le stime vengono effettuate usando i dati del sensore più recenti disponibili e incapsulate nell'oggetto CurrentPrediction .

È necessario usare un nuovo oggetto frame per ogni frame sottoposto a rendering perché è valido solo per un istante nel tempo. La proprietà CurrentPrediction contiene informazioni quali la posizione della fotocamera. Le informazioni vengono estrapolate nel momento esatto in cui si prevede che il frame sia visibile all'utente.

Il codice seguente è estratto da 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();

Elaborare gli aggiornamenti della fotocamera

I buffer back possono cambiare da frame a frame. L'app deve convalidare il buffer nascosto per ogni fotocamera e rilasciare e ricreare le visualizzazioni delle risorse e i buffer di profondità in base alle esigenze. Si noti che il set di posizioni nella stima è l'elenco autorevole di fotocamere usate nel fotogramma corrente. In genere, questo elenco viene utilizzato per eseguire l'iterazione sul set di fotocamere.

Da AppMain::Update:

m_deviceResources->EnsureCameraResources(holographicFrame, prediction);

Da DeviceResources::EnsureCameraResources:

for (HolographicCameraPose const& cameraPose : prediction.CameraPoses())
{
    HolographicCameraRenderingParameters renderingParameters = frame.GetRenderingParameters(cameraPose);
    CameraResources* pCameraResources = cameraResourceMap[cameraPose.HolographicCamera().Id()].get();
    pCameraResources->CreateResourcesForBackBuffer(this, renderingParameters);
}

Ottenere il sistema di coordinate da usare come base per il rendering

Windows Mixed Reality consente all'app di creare vari sistemi di coordinate, ad esempio fotogrammi di riferimento collegati e stazionari per tenere traccia delle posizioni nel mondo fisico. L'app può quindi usare questi sistemi di coordinate per sapere dove eseguire il rendering degli ologrammi per ogni fotogramma. Quando si richiedono coordinate da un'API, si passerà sempre l'oggetto SpatialCoordinateSystem all'interno del quale si vuole esprimere tali coordinate.

Da AppMain::Update:

pose = SpatialPointerPose::TryGetAtTimestamp(
    m_stationaryReferenceFrame.CoordinateSystem(), prediction.Timestamp());

Questi sistemi di coordinate possono quindi essere usati per generare matrici di visualizzazione stereo durante il rendering del contenuto nella scena.

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

Elaborare lo sguardo fisso e l'input dei movimenti

Losguardo fisso e l'input della mano non sono basati sul tempo e non devono essere aggiornati nella funzione StepTimer . Tuttavia, questo input è un elemento che l'app deve esaminare in ogni frame.

Elaborare gli aggiornamenti basati sul tempo

Qualsiasi app per il rendering in tempo reale avrà bisogno di un modo per elaborare gli aggiornamenti basati sul tempo: il modello di app Windows Holographic usa un'implementazione stepTimer, simile a Quella fornita nel modello di app UWP DirectX 11. Questa classe helper di esempio StepTimer può fornire aggiornamenti temporici fissi, aggiornamenti di passaggi temporici variabili e la modalità predefinita è passaggi temporici variabili.

Per il rendering olografico, abbiamo scelto di non inserire troppo nella funzione timer perché è possibile configurarla come passaggio fisso. Potrebbe essere chiamato più di una volta per fotogramma , o non essere stato chiamato per alcuni fotogrammi, e gli aggiornamenti dei dati olografici dovrebbero essere emessi una volta per fotogramma.

Da AppMain::Update:

m_timer.Tick([this]()
{
    m_spinningCubeRenderer->Update(m_timer);
});

Posizionare e ruotare gli ologrammi nel sistema di coordinate

Se si opera in un singolo sistema di coordinate, come nel modello con SpatialStationaryReferenceFrame, questo processo non è diverso da quello usato in altro modo nella grafica 3D. Qui si ruota il cubo e si imposta la matrice del modello in base alla posizione nel sistema di coordinate stazionarie.

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

Nota sugli scenari avanzati: Il cubo rotante è un semplice esempio di come posizionare un ologramma all'interno di un singolo frame di riferimento. È anche possibile usare contemporaneamente più SpatialCoordinateSystems nello stesso frame sottoposto a rendering.

Aggiornare i dati del buffer costante

Le trasformazioni del modello per il contenuto vengono aggiornate come di consueto. A questo punto, saranno state calcolate trasformazioni valide per il sistema di coordinate in cui si esegue il rendering.

Da SpinningCubeRenderer::Update:

// Update the model transform buffer for the hologram.
context->UpdateSubresource(
    m_modelConstantBuffer.Get(),
    0,
    nullptr,
    &m_modelConstantBufferData,
    0,
    0
);

E le trasformazioni di visualizzazione e proiezione? Per ottenere risultati ottimali, è necessario attendere fino a quando non si è quasi pronti per le chiamate di disegno prima di ottenere queste chiamate.

Eseguire il rendering del frame corrente

Il rendering Windows Mixed Reality non è molto diverso dal rendering su uno schermo mono 2D, ma esistono alcune differenze:

  • Le stime dei fotogrammi olografici sono importanti. Più la stima è vicina alla presentazione del fotogramma, migliore sarà l'aspetto degli ologrammi.
  • Windows Mixed Reality controlla le visualizzazioni della fotocamera. Eseguire il rendering di ognuno di essi perché il frame olografico li presenterà in un secondo momento.
  • È consigliabile eseguire il rendering stereo usando il disegno con istanze in una matrice di destinazione di rendering. Il modello di app olografica usa l'approccio consigliato di disegno con istanze a una matrice di destinazione di rendering, che usa una visualizzazione di destinazione di rendering in un oggetto Texture2DArray.
  • Se vuoi eseguire il rendering senza usare istanze stereo, dovrai creare due oggetti RenderTargetView non di matrice, uno per ogni occhio. Ogni elemento RenderTargetViews fa riferimento a una delle due sezioni dell'oggetto Texture2DArray fornito all'app dal sistema. Questa operazione non è consigliata, perché in genere è più lenta rispetto all'uso delle istanze.

Ottenere una stima aggiornata di HolographicFrame

L'aggiornamento della stima dei fotogrammi migliora l'efficacia della stabilizzazione delle immagini. Si ottiene un posizionamento più accurato degli ologrammi a causa del tempo più breve tra la stima e il momento in cui il fotogramma è visibile all'utente. Aggiornare idealmente la stima dei fotogrammi subito prima del rendering.

holographicFrame.UpdateCurrentPrediction();
HolographicFramePrediction prediction = holographicFrame.CurrentPrediction();

Eseguire il rendering in ogni fotocamera

Loop sul set di posizioni della fotocamera nella stima ed eseguirne il rendering in ogni fotocamera in questo set.

Configurare il passaggio di rendering

Windows Mixed Reality usa il rendering stereoscopico per migliorare la profondità e per eseguire il rendering stereoscopico, in modo che sia lo schermo sinistro che quello destro siano attivi. Con il rendering stereoscopico, c'è un offset tra i due schermi, che il cervello può riconciliare come profondità effettiva. Questa sezione illustra il rendering stereoscopico tramite istanze, usando il codice Windows modello di app Holographic.

Ogni fotocamera ha una propria destinazione di rendering (buffer nascosto) e matrici di visualizzazione e proiezione nello spazio olografico. L'app dovrà creare qualsiasi altra risorsa basata su fotocamera, ad esempio il buffer di profondità, per ogni fotocamera. Nel modello Windows app Holographic viene specificata una classe helper per aggregare queste risorse in DX::CameraResources. Per iniziare, configurare le visualizzazioni di destinazione di rendering:

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

Usare la stima per ottenere le matrici di visualizzazione e proiezione per la fotocamera

Le matrici di visualizzazione e proiezione per ogni fotocamera olografica cambieranno a ogni fotogramma. Aggiornare i dati nel buffer costante per ogni fotocamera olografica. Eseguire questa operazione dopo aver aggiornato la stima e prima di effettuare chiamate di disegno per la fotocamera.

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

In questo esempio viene illustrato come le matrici vengono acquisite dalla posizione della fotocamera. Durante questo processo, si ottiene anche il viewport corrente per la fotocamera. Si noti come viene fornito un sistema di coordinate: si tratta dello stesso sistema di coordinate usato per comprendere lo sguardo fisso ed è lo stesso usato per posizionare il cubo rotante.

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

Il viewport deve essere impostato per ogni frame. Il vertex shader (almeno) dovrà in genere accedere ai dati di visualizzazione/proiezione.

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

Eseguire il rendering nel buffer nascosto della fotocamera ed eseguire il commit del buffer di profondità:

È buona idea verificare che TryGetViewTransform sia riuscito prima di provare a usare i dati di visualizzazione/proiezione, perché se il sistema di coordinate non è localizzato (ad esempio, il rilevamento è stato interrotto) l'app non può eseguire il rendering con tale frame. Il modello chiama Render sul cubo rotante solo se la classe CameraResources indica un aggiornamento riuscito.

Windows Mixed Reality include funzionalità per la stabilizzazione delle immagini per mantenere gli ologrammi posizionati dove uno sviluppatore o un utente li inserisce nel mondo. La stabilizzazione delle immagini consente di nascondere la latenza intrinseca in una pipeline di rendering per garantire le migliori esperienze olografiche per gli utenti. È possibile che venga specificato un punto di messa a fuoco per migliorare ulteriormente la stabilizzazione delle immagini oppure che venga fornito un buffer di profondità per la stabilizzazione delle immagini ottimizzata per il calcolo in tempo reale.

Per ottenere risultati ottimali, l'app deve fornire un buffer di profondità usando l'API CommitDirect3D11DepthBuffer . Windows Mixed Reality usare le informazioni sulla geometria dal buffer di profondità per ottimizzare la stabilizzazione delle immagini in tempo reale. Il Windows di app Holographic esegue il commit del buffer di profondità dell'app per impostazione predefinita, consentendo di ottimizzare la stabilità dell'ologramma.

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

Nota

Windows la trama di profondità nella GPU, quindi deve essere possibile usare il buffer di profondità come risorsa shader. L'ID3D11Texture2D creato deve essere in un formato senza tipo e deve essere associato come visualizzazione delle risorse shader. Di seguito è riportato un esempio di come creare una trama di profondità di cui è possibile eseguire il commit per la stabilizzazione delle immagini.

Codice per la creazione della risorsa buffer depth per 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
    ));

Disegnare contenuto olografico

Il Windows modello di app Holographic esegue il rendering del contenuto in stereo usando la tecnica consigliata per disegnare la geometria con istanze in un oggetto Texture2DArray di dimensioni 2. Di seguito viene ora illustrata la parte di creazione di istanze di questo oggetto e il suo funzionamento Windows Mixed Reality.

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

Ogni istanza accede a una matrice di visualizzazione/proiezione diversa dal buffer costante. Ecco la struttura del buffer costante, che è solo una matrice di due matrici.

Da VertexShaderShared.hlsl, incluso da 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'indice della matrice di destinazione di rendering deve essere impostato per ogni pixel. Nel frammento di codice seguente viene eseguito il mapping di output.viewId SV_RenderTargetArrayIndex semantica . Ciò richiede il supporto per una funzionalità Direct3D 11.3 facoltativa, che consente di impostare la semantica dell'indice della matrice di destinazione di rendering da qualsiasi fase dello shader.

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

Da VertexShaderShared.hlsl, incluso da 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;
}

Se si vogliono usare le tecniche di disegno con istanze esistenti con questo metodo di disegno in una matrice di destinazione di rendering stereo, disegnare il doppio del numero di istanze normalmente in uso. Nello shader dividere input.instId per 2 per ottenere l'ID istanza originale, che può essere indicizzato in un buffer di dati per oggetto: int actualIdx = input.instId / 2;

Nota importante sul rendering di contenuto stereo HoloLens

Windows Mixed Reality supporta la possibilità di impostare l'indice della matrice di destinazione di rendering da qualsiasi fase dello shader. In genere, si tratta di un'attività che può essere eseguita solo nella fase geometry shader a causa del modo in cui la semantica è definita per Direct3D 11. In questo esempio viene illustrato un esempio completo di come configurare una pipeline di rendering con solo il vertice e pixel shader fasi impostate. Il codice dello shader è come descritto in precedenza.

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

Nota importante sul rendering in dispositivi non HoloLens

L'impostazione dell'indice della matrice di destinazione di rendering nel vertex shader richiede che il driver di grafica supporti una funzionalità Direct3D 11.3 facoltativa, che HoloLens supporta. L'app può implementare in modo sicuro solo questa tecnica per il rendering e tutti i requisiti verranno soddisfatti per l'esecuzione nel Microsoft HoloLens.

Potrebbe essere necessario usare anche l'emulatore HoloLens, che può essere un potente strumento di sviluppo per l'app olografica e supportare dispositivi visori immersivi collegati Windows Mixed Reality PC Windows 10. Il supporto per il percorso di rendering non HoloLens, per tutti Windows Mixed Reality, è integrato anche nel modello di app Windows Holographic. Nel codice del modello è presente il codice per consentire l'esecuzione dell'app olografica nella GPU nel PC di sviluppo. Ecco come la classe DeviceResources verifica il supporto di questa funzionalità facoltativa.

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

Per supportare il rendering senza questa funzionalità facoltativa, l'app deve usare uno shader geometry per impostare l'indice della matrice di destinazione del rendering. Questo frammento di codice viene aggiunto dopoVSSetConstantBuffers e primadiPSSetShader nell'esempio di codice illustrato nella sezione precedente che illustra come eseguire il rendering stereo su HoloLens.

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

NOTA HLSL: in questo caso, è anche necessario caricare un vertex shader leggermente modificato che passa l'indice della matrice di destinazione di rendering al geometry shader usando una semantica dello shader sempre consentita, ad esempio TEXCOORD0. Lo shader geometry non deve eseguire alcuna operazione; lo shader geometry modello passa tutti i dati, ad eccezione dell'indice della matrice di destinazione di rendering, usato per impostare la SV_RenderTargetArrayIndex semantica.

Codice del modello di app per 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);
    }
}

Presente

Abilitare il frame olografico per presentare la catena di scambio

Con Windows Mixed Reality, il sistema controlla la catena di scambio. Il sistema gestisce quindi la presentazione di fotogrammi a ogni fotocamera olografica per garantire un'esperienza utente di alta qualità. Fornisce anche un aggiornamento del viewport per ogni fotogramma, per ogni fotocamera, per ottimizzare aspetti del sistema, ad esempio la stabilizzazione delle immagini o Realtà mista Capture. Pertanto, un'app olografica che usa DirectX non chiama Present in una catena di scambio DXGI. Al contrario, si usa la classe HolographicFrame per presentare tutti gli swapchain per un frame al termine del disegno.

Da DeviceResources::P resent:

HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();

Per impostazione predefinita, questa API attende il completamento del frame prima che venga restituito. Le app olografiche devono attendere il completamento del frame precedente prima di iniziare a lavorare su un nuovo frame, perché ciò riduce la latenza e consente risultati migliori dalle stime dei fotogrammi olografici. Non si tratta di una regola rigida e se sono presenti fotogrammi che durano più di un aggiornamento dello schermo per il rendering, è possibile disabilitare questa attesa passando il parametro HolographicFramePresentWaitBehavior a PresentUsingCurrentPrediction. In questo caso, è probabile che si usi un thread di rendering asincrono per mantenere un carico continuo sulla GPU. La frequenza di aggiornamento HoloLens dispositivo è di 60 Hz, in cui un frame ha una durata di circa 16 ms. I dispositivi vr immersivi possono variare da 60 Hz a 90 Hz; quando si aggiorna la visualizzazione a 90 hz, ogni fotogramma avrà una durata di circa 11 ms.

Gestire scenari DeviceLost in collaborazione con HolographicFrame

Le app DirectX 11 in genere vogliono controllare il valore HRESULT restituito dalla funzione Present della catena di scambio DXGI per determinare se si è verificato un errore di DeviceLost . La classe HolographicFrame gestisce automaticamente questa operazione. Esaminare l'oggetto HolographicFramePresentResult restituito per scoprire se è necessario rilasciare e ricreare il dispositivo Direct3D e le risorse basate sul dispositivo.

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

Se il dispositivo Direct3D è stato perso ed è stato ricreato, è necessario indicare a HolographicSpace di iniziare a usare il nuovo dispositivo. La catena di scambio verrà ricreata per questo dispositivo.

Da DeviceResources::InitializeUsingHolographicSpace:

m_holographicSpace.SetDirect3D11Device(m_d3dInteropDevice);

Dopo aver presentato il frame, è possibile tornare al ciclo del programma principale e consentirlo di continuare con il frame successivo.

PC con grafica ibrida e applicazioni di realtà mista

Windows 10 Creators Update pc possono essere configurati con GPU discrete e integrate. Con questi tipi di computer, Windows l'adattatore a cui è connesso il visore. Le applicazioni devono garantire che il dispositivo DirectX creato usi lo stesso adattatore.

Il codice di esempio Direct3D più generale illustra la creazione di un dispositivo DirectX usando la scheda hardware predefinita, che in un sistema ibrido potrebbe non essere uguale a quella usata per il visore.

Per risolvere eventuali problemi, usare HolographicAdapterID da HolographicSpace. PrimaryAdapterId() o HolographicDisplay. AdapterId(). Questo adapterId può quindi essere usato per selezionare il DXGIAdapter giusto usando IDXGIFactory4.EnumAdapterByLuid.

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

Codice per aggiornare DeviceResources::CreateDeviceResources per l'uso di 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.
);

Grafica ibrida e Media Foundation

L Media Foundation nei sistemi ibridi può causare problemi in cui il rendering del video non viene eseguito o la trama video è danneggiata perché Media Foundation comportamento predefinito del sistema. In alcuni scenari è necessario creare un dispositivo ID3D11 separato per supportare il multithreading e impostare i flag di creazione corretti.

Quando si inizializza ID3D11Device, D3D11_CREATE_DEVICE_VIDEO_SUPPORT deve essere definito come parte del D3D11_CREATE_DEVICE_FLAG. Dopo aver creato il dispositivo e il contesto, chiamare SetMultithreadProtected per abilitare il multithreading. Per associare il dispositivo a IMFDXGIDeviceManager, usare la funzione IMFDXGIDeviceManager::ResetDevice .

Codice per associare un dispositivo ID3D11 a 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;

Vedi anche