Freigeben über


Rendern in DirectX

Hinweis

Dieser Artikel bezieht sich auf die älteren winRT nativen APIs. Für neue native App-Projekte empfehlen wir die Verwendung der OpenXR-API.

Windows Mixed Reality basiert auf DirectX, um umfangreiche, 3D-grafische Oberflächen für Benutzer zu erzeugen. Die Renderingstraktion befindet sich direkt oberhalb von DirectX, sodass Apps über die Position und Ausrichtung holografischer Szenenbeobachter nachdenken können, die vom System vorhergesagt werden. Der Entwickler kann dann seine Hologramme basierend auf jeder Kamera suchen, sodass die App diese Hologramme in verschiedenen räumlichen Koordinatensystemen rendern kann, während der Benutzer sich bewegt.

Hinweis: In dieser exemplarischen Vorgehensweise wird das holografische Rendering in Direct3D 11 beschrieben. Eine Direct3D 12 Windows Mixed Reality-App-Vorlage wird auch mit der Mixed Reality-App-Vorlagenerweiterung bereitgestellt.

Aktualisieren für den aktuellen Frame

Um den Anwendungszustand für Hologramme zu aktualisieren, erfolgt einmal pro Frame die App:

  • Rufen Sie ein HolographicFrame aus dem Anzeigeverwaltungssystem ab.
  • Aktualisieren Sie die Szene mit der aktuellen Vorhersage, wo die Kameraansicht angezeigt wird, wenn das Rendern abgeschlossen ist. Beachten Sie, dass für die holografische Szene mehr als eine Kamera vorhanden sein kann.

Um in holografischen Kameraansichten zu rendern, erfolgt einmal pro Frame die App:

  • Rendern Sie für jede Kamera die Szene für den aktuellen Frame mithilfe der Kameraansichts- und Projektionsmatrizen aus dem System.

Erstellen eines neuen holografischen Rahmens und Abrufen der Vorhersage

HolographicFrame enthält Informationen, die die App zum Aktualisieren und Rendern des aktuellen Frames benötigt. Die App beginnt jeden neuen Frame durch Aufrufen der CreateNextFrame-Methode . Wenn diese Methode aufgerufen wird, werden Vorhersagen mithilfe der neuesten sensordaten verfügbar und in CurrentPrediction-Objekt gekapselt.

Ein neues Frameobjekt muss für jeden gerenderten Frame verwendet werden, da es nur für eine sofortige Zeit gültig ist. Die CurrentPrediction-Eigenschaft enthält Informationen wie die Kameraposition. Die Informationen werden auf den genauen Zeitpunkt extrapoliert, zu dem der Frame für den Benutzer sichtbar sein soll.

Der folgende Code stammt aus 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();

Verarbeiten von Kameraupdates

Hintergrundpuffer können von Frame zu Frame geändert werden. Ihre App muss den Hintergrundpuffer für jede Kamera überprüfen und bei Bedarf Ressourcenansichten und Tiefenpuffer freigeben und neu erstellen. Beachten Sie, dass der Satz von Posen in der Vorhersage die autoritative Liste der Kameras ist, die im aktuellen Frame verwendet werden. In der Regel verwenden Sie diese Liste, um den Satz von Kameras zu durchlaufen.

Von AppMain::Update:

m_deviceResources->EnsureCameraResources(holographicFrame, prediction);

Von DeviceResources::EnsureCameraResources:

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

Abrufen des Koordinatensystems zur Verwendung als Grundlage für das Rendern

Mit Windows Mixed Reality können Ihre App verschiedene Koordinatensysteme erstellen, z. B. angefügte und stationäre Referenzframes zum Nachverfolgen von Standorten in der physischen Welt. Ihre App kann diese Koordinatensysteme dann verwenden, um zu ermitteln, wo Hologramme für jeden Frame gerendert werden sollen. Beim Anfordern von Koordinaten aus einer API übergeben Sie immer das SpatialCoordinateSystem , in dem diese Koordinaten ausgedrückt werden sollen.

Von AppMain::Update:

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

Diese Koordinatensysteme können dann zum Generieren von Stereoansichtsmatrizen verwendet werden, wenn der Inhalt in Ihrer Szene gerendert wird.

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

Verarbeiten von Blick- und Gestikeingaben

Blick- und Handeingaben sind nicht zeitbasiert und müssen nicht in der StepTimer-Funktion aktualisiert werden. Diese Eingabe ist jedoch etwas, das die App für jeden Frame betrachten muss.

Verarbeiten zeitbasierter Updates

Jede Echtzeitrendering-App benötigt eine Möglichkeit, zeitbasierte Updates zu verarbeiten. Die Windows Holographic-App-Vorlage verwendet eine StepTimer-Implementierung, ähnlich wie der in der DirectX 11-UWP-App-Vorlage bereitgestellte StepTimer . Diese StepTimer-Beispielhilfsklasse kann feste Zeitschrittaktualisierungen, variable Zeitschrittaktualisierungen und der Standardmodus ist variable Zeitschritte.

Für holografisches Rendering haben wir uns entschieden, nicht zu viel in die Timerfunktion einzufügen, da Sie es so konfigurieren können, dass es sich um einen festen Zeitschritt bezieht. Es kann mehr als einmal pro Frame aufgerufen werden – oder gar nicht für einige Frames – und unsere holografischen Datenaktualisierungen sollten einmal pro Frame erfolgen.

Von AppMain::Update:

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

Positionieren und Drehen von Hologrammen im Koordinatensystem

Wenn Sie in einem einzigen Koordinatensystem arbeiten, wie die Vorlage mit dem SpatialStationaryReferenceFrame funktioniert, unterscheidet sich dieser Prozess nicht von dem, was Sie andernfalls in 3D-Grafiken verwenden. Hier drehen wir den Würfel und legen die Modellmatrix basierend auf der Position im stationären Koordinatensystem fest.

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

Hinweis zu erweiterten Szenarien: Der sich drehende Würfel ist ein einfaches Beispiel für die Positionierung eines Hologramms innerhalb eines einzelnen Referenzrahmens. Es ist auch möglich, mehrere SpatialCoordinateSystems im gleichen gerenderten Frame gleichzeitig zu verwenden.

Aktualisieren von Konstantenpufferdaten

Modelltransformationen für Inhalte werden wie gewohnt aktualisiert. Derzeit haben Sie gültige Transformationen für das Koordinatensystem berechnet, in dem Sie rendern.

Von SpinningCubeRenderer::Update:

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

Was ist mit Ansichts- und Projektionstransformationen? Um optimale Ergebnisse zu erzielen, möchten wir warten, bis wir fast bereit für unsere Draw-Aufrufe sind, bevor wir diese erhalten.

Rendern des aktuellen Frames

Das Rendern unter Windows Mixed Reality unterscheidet sich nicht wesentlich vom Rendern auf einer 2D-Mono-Anzeige, aber es gibt einige Unterschiede:

  • Holografische Rahmenvorhersagen sind wichtig. Je näher die Vorhersage ist, wenn Ihr Frame präsentiert wird, desto besser sieht Ihr Hologramm aus.
  • Windows Mixed Reality steuert die Kameraansichten. Rendern Sie für jeden, da der holografische Frame sie später für Sie präsentiert.
  • Es wird empfohlen, das Stereorendering mithilfe einer instanzierten Zeichnung in einem Renderzielarray durchzuführen. Die Hologramm-App-Vorlage verwendet den empfohlenen Ansatz der instanzierten Zeichnung auf ein Renderzielarray, das eine Renderzielansicht auf einem Texture2DArray verwendet.
  • Wenn Sie ohne Stereo-Instancing rendern möchten, müssen Sie zwei Nicht-Array-RenderTargetViews erstellen, eines für jedes Auge. Jede RenderTargetViews verweist auf eines der beiden Segmente in der Texture2DArray, die der App vom System bereitgestellt wird. Dies wird nicht empfohlen, da dies in der Regel langsamer ist als die Verwendung der Instancing.

Abrufen einer aktualisierten HolographicFrame-Vorhersage

Durch das Aktualisieren der Framevorhersage wird die Effektivität der Bildstabilisierung verbessert. Sie erhalten eine genauere Positionierung von Hologrammen aufgrund der kürzeren Zeit zwischen der Vorhersage und dem Zeitpunkt, an dem der Frame für den Benutzer sichtbar ist. Aktualisieren Sie ihre Framevorhersage idealerweise direkt vor dem Rendern.

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

Rendern für jede Kamera

Schleife auf den Satz von Kamera-Posen in der Vorhersage und Rendern für jede Kamera in diesem Satz.

Einrichten des Renderingdurchlaufs

Windows Mixed Reality verwendet stereoskopisches Rendering, um die Illusion der Tiefe zu verbessern und stereoskopisch zu rendern, sodass sowohl die linke als auch die rechte Anzeige aktiv sind. Bei stereoskopischer Darstellung gibt es einen Offset zwischen den beiden Displays, die das Gehirn als tatsächliche Tiefe in Einklang bringen kann. In diesem Abschnitt wird das stereoskopische Rendering mithilfe der Instancing mithilfe von Code aus der Windows Holographic-App-Vorlage behandelt.

Jede Kamera verfügt über ein eigenes Renderziel (Hintergrundpuffer) und Ansichts- und Projektionsmatrizen im holografischen Raum. Ihre App muss alle anderen kamerabasierten Ressourcen – z. B. den Tiefenpuffer – auf Kamerabasis erstellen. In der Windows Holographic-App-Vorlage stellen wir eine Hilfsklasse bereit, um diese Ressourcen in DX::CameraResources zusammenzupacken. Richten Sie zunächst die Renderzielansichten ein:

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

Verwenden der Vorhersage zum Abrufen der Ansichts- und Projektionsmatrizen für die Kamera

Die Ansichts- und Projektionsmatrizen für jede holografische Kamera ändern sich mit jedem Frame. Aktualisieren Sie die Daten im Konstantenpuffer für jede holografische Kamera. Führen Sie dies aus, nachdem Sie die Vorhersage aktualisiert haben, und bevor Sie Zeichnungsaufrufe für diese Kamera tätigen.

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

Hier zeigen wir, wie die Matrizen aus der Kamera-Pose abgerufen werden. Während dieses Vorgangs erhalten wir auch den aktuellen Viewport für die Kamera. Beachten Sie, wie wir ein Koordinatensystem bereitstellen: Dies ist dasselbe Koordinatensystem, das wir zum Verständnis des Blicks verwendet haben, und es ist die gleiche, die wir zum Positionieren des sich drehenden Würfels verwendet haben.

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

Der Viewport sollte für jeden Frame festgelegt werden. Ihr Vertex-Shader (zumindest) benötigt im Allgemeinen Zugriff auf die Ansichts-/Projektionsdaten.

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

Rendern sie im Kamerarückpuffer, und übernehmen Sie den Tiefenpuffer:

Es empfiehlt sich, zu überprüfen, ob TryGetViewTransform erfolgreich war, bevor Sie versuchen, die Ansichts-/Projektionsdaten zu verwenden, da die App für diesen Frame nicht gerendert werden kann, wenn das Koordinatensystem nicht ablösbar ist (z. B. die Nachverfolgung wurde unterbrochen). Die Vorlage ruft Render nur auf dem sich drehenden Cube auf, wenn die CameraResources-Klasse ein erfolgreiches Update angibt.

Windows Mixed Reality enthält Features für die Bildstabilisierung , um Hologramme in der Lage zu halten, wo ein Entwickler oder Benutzer sie in die Welt versetzt. Die Bildstabilisierung hilft dabei, die Latenz in einer Renderingpipeline auszublenden, um die besten holografischen Erfahrungen für Benutzer sicherzustellen. Ein Fokuspunkt kann angegeben werden, um die Bildstabilisierung noch weiter zu verbessern, oder ein Tiefenpuffer kann bereitgestellt werden, um eine optimierte Bildstabilisierung in Echtzeit zu berechnen.

Um optimale Ergebnisse zu erzielen, sollte Ihre App einen Tiefenpuffer mit der CommitDirect3D11DepthBuffer-API bereitstellen. Windows Mixed Reality kann dann Geometrieinformationen aus dem Tiefenpuffer verwenden, um die Bildstabilisierung in Echtzeit zu optimieren. Die Windows Holographic-App-Vorlage setzt standardmäßig den Tiefenpuffer der App fest, wodurch die Stabilität des Hologramms optimiert wird.

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

Hinweis

Windows verarbeitet Ihre Tiefentextur auf der GPU. Daher muss es möglich sein, den Tiefenpuffer als Shaderressource zu verwenden. Die id3D11Texture2D, die Sie erstellen, sollte in einem typlosen Format vorliegen und als Shaderressourcenansicht gebunden werden. Nachfolgend finden Sie ein Beispiel zum Erstellen einer Tiefentextur, die für die Bildstabilisierung übernommen werden kann.

Code für die Erstellung von Tiefenpufferressourcen für 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
    ));

Zeichnen holografischer Inhalte

Die Vorlage der Windows Holographic-App rendert Inhalte in Stereo unter Verwendung der empfohlenen Technik der Zeichnungsinstanzengeometrie auf ein Texture2DArray von Größe 2. Sehen wir uns den Teil der Refinanzierung an und wie es unter Windows Mixed Reality funktioniert.

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

Jede Instanz greift vom Konstantenpuffer auf eine andere Ansichts-/Projektionsmatrix zu. Dies ist die Konstantenpufferstruktur, die nur ein Array von zwei Matrizen ist.

Von VertexShaderShared.hlsl, enthalten von 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];
};

Der Renderzielarrayindex muss für jedes Pixel festgelegt werden. Im folgenden Codeausschnitt wird output.viewId der SV_RenderTargetArrayIndex Semantik zugeordnet. Dies erfordert Unterstützung für ein optionales Direct3D 11.3-Feature, mit dem die Renderzielindexsemantik aus jeder Shaderstufe festgelegt werden kann.

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

Von VertexShaderShared.hlsl, enthalten von 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;
}

Wenn Sie ihre vorhandenen instanzierten Zeichnungstechniken mit dieser Methode zum Zeichnen auf ein Stereo-Renderzielarray verwenden möchten, zeichnen Sie zweimal die Anzahl der Instanzen, die Sie normalerweise haben. Dividieren Sie im Shader "input.instId " um 2, um die ursprüngliche Instanz-ID abzurufen, die in einen Puffer pro Objektdaten indiziert werden kann (z. B.): int actualIdx = input.instId / 2;

Wichtiger Hinweis zum Rendern von Stereoinhalten auf HoloLens

Windows Mixed Reality unterstützt die Möglichkeit, den Renderzielarrayindex aus jeder Shaderstufe festzulegen. Normalerweise ist dies eine Aufgabe, die nur in der Geometrie-Shaderphase ausgeführt werden konnte, da die Semantik für Direct3D 11 definiert ist. Hier zeigen wir ein vollständiges Beispiel für das Einrichten einer Renderingpipeline mit nur den festgelegten Vertex- und Pixelshaderstufen. Der Shadercode ist wie oben beschrieben.

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

Wichtiger Hinweis zum Rendern auf Nicht-HoloLens-Geräten

Das Festlegen des Renderzielarrayindex im Vertex-Shader erfordert, dass der Grafiktreiber ein optionales Direct3D 11.3-Feature unterstützt, das HoloLens unterstützt. Ihre App kann möglicherweise genau diese Technik zum Rendern implementieren, und alle Anforderungen werden für die Ausführung auf microsoft HoloLens erfüllt.

Es kann der Fall sein, dass Sie auch den HoloLens-Emulator verwenden möchten, was ein leistungsstarkes Entwicklungstool für Ihre holografische App sein kann – und Windows Mixed Reality immersive Headset-Geräte unterstützen, die an Windows 10-PCs angeschlossen sind. Die Unterstützung für den Nicht-HoloLens-Renderingpfad – für alle Windows Mixed Reality – ist auch in die Windows Holographic-App-Vorlage integriert. Im Vorlagencode finden Sie Code, mit dem Ihre holografische App auf der GPU auf Ihrem Entwicklungs-PC ausgeführt werden kann. Hier erfahren Sie, wie die DeviceResources-Klasse nach dieser optionalen Featureunterstützung sucht.

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

Um das Rendern ohne dieses optionale Feature zu unterstützen, muss Ihre App einen Geometrie-Shader verwenden, um den Renderzielarrayindex festzulegen. Dieser Codeausschnitt würde nach VSSetConstantBuffers und vor PSSetShader im codebeispiel hinzugefügt, das im vorherigen Abschnitt gezeigt wird, in dem erläutert wird, wie Stereo auf HoloLens gerendert wird.

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

HLSL-HINWEIS: In diesem Fall müssen Sie auch einen leicht geänderten Vertex-Shader laden, der den Renderzielarrayindex mithilfe einer immer zulässigen Shadersemantik an den Geometrie-Shader übergibt, z. B. TEXCOORD0. Der Geometrie-Shader muss keine Arbeit ausführen. Der Vorlagengeometrie-Shader durchläuft alle Daten, mit Ausnahme des Renderzielarrayindex, der zum Festlegen der SV_RenderTargetArrayIndex Semantik verwendet wird.

App-Vorlagencode für 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);
    }
}

Vorhanden

Aktivieren des holografischen Frames zum Darstellen der Swapchain

Mit Windows Mixed Reality steuert das System die Swapchain. Das System verwaltet dann die Darstellung von Frames für jede holografische Kamera, um eine qualitativ hochwertige Benutzererfahrung sicherzustellen. Außerdem wird ein Viewport-Update für jeden Frame für jede Kamera bereitgestellt, um Aspekte des Systems wie Bildstabilisierung oder Mixed Reality Capture zu optimieren. Daher ruft eine holografische App mit DirectX "Present" nicht in einer DXGI-Swapchain auf. Stattdessen verwenden Sie die HolographicFrame-Klasse , um alle Swapchains für einen Frame darzustellen, sobald Sie mit dem Zeichnen fertig sind.

Von DeviceResources::P Resent:

HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();

Standardmäßig wartet diese API, bis der Frame abgeschlossen ist, bevor er zurückgegeben wird. Holografische Apps sollten warten, bis der vorherige Frame abgeschlossen ist, bevor Sie mit der Arbeit an einem neuen Frame beginnen, da dies die Latenz verringert und bessere Ergebnisse aus holografischen Framevorhersagen ermöglicht. Dies ist keine harte Regel, und wenn Sie Frames haben, die länger als eine Bildschirmaktualisierung zum Rendern dauern, können Sie diese Wartezeit deaktivieren, indem Sie den HolographicFramePresentWaitBehavior-Parameter an PresentUsingCurrentPrediction übergeben. In diesem Fall würden Sie wahrscheinlich einen asynchronen Renderingthread verwenden, um eine kontinuierliche Auslastung der GPU aufrechtzuerhalten. Die Aktualisierungsrate des HoloLens-Geräts beträgt 60 Hz, wobei ein Frame eine Dauer von ca. 16 ms hat. Immersive Headset-Geräte können von 60 Hz bis 90 Hz reichen; Beim Aktualisieren der Anzeige mit 90 Hz hat jeder Frame eine Dauer von ca. 11 ms.

Behandeln von DeviceLost-Szenarien in Zusammenarbeit mit HolographicFrame

DirectX 11-Apps würden in der Regel das von der Present-Funktion der DXGI-Swapchain zurückgegebene HRESULT überprüfen, um herauszufinden, ob ein DeviceLost-Fehler aufgetreten ist. Die HolographicFrame-Klasse behandelt dies für Sie. Überprüfen Sie das zurückgegebene HolographicFramePresentResult , um herauszufinden, ob Sie das Direct3D-Gerät und die gerätebasierten Ressourcen freigeben und neu erstellen müssen.

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

Wenn das Direct3D-Gerät verloren gegangen ist und Sie es neu erstellt haben, müssen Sie holographicSpace anweisen, mit der Verwendung des neuen Geräts zu beginnen. Die Swapchain wird für dieses Gerät neu erstellt.

Von DeviceResources::InitializeUsingHolographicSpace:

m_holographicSpace.SetDirect3D11Device(m_d3dInteropDevice);

Nachdem Ihr Frame dargestellt wurde, können Sie wieder zur Hauptprogrammschleife zurückkehren und es erlauben, zum nächsten Frame zurückzukehren.

Hybridgrafik-PCs und Mixed Reality-Anwendungen

Windows 10 Creators Update-PCs können sowohl mit separaten als auch integrierten GPUs konfiguriert werden. Bei diesen Computertypen wählt Windows den Adapter aus, mit dem das Headset verbunden ist. Anwendungen müssen sicherstellen, dass das von ihr erstellte DirectX-Gerät denselben Adapter verwendet.

Der allgemeine Direct3D-Beispielcode veranschaulicht das Erstellen eines DirectX-Geräts mit dem Standardhardwareadapter, der auf einem Hybridsystem möglicherweise nicht mit dem für das Headset verwendeten Gerät identisch ist.

Um Probleme zu umgehen, verwenden Sie die HolographicAdapterID von holographicSpace. PrimaryAdapterId() oder HolographicDisplay. AdapterId(). Diese adapterId kann dann zum Auswählen des richtigen DXGIAdapters mit IDXGIFactory4.EnumAdapterByLuid verwendet werden.

Von 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 zum Aktualisieren von DeviceResources::CreateDeviceResources zur Verwendung von 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.
);

Hybridgrafiken und Media Foundation

Die Verwendung von Media Foundation auf Hybridsystemen kann zu Problemen führen, bei denen Video nicht gerendert oder videotextur beschädigt ist, da Media Foundation standardmäßig auf ein Systemverhalten festgelegt ist. In einigen Szenarien ist das Erstellen einer separaten ID3D11Device erforderlich, um Multithreading zu unterstützen, und die richtigen Erstellungskennzeichnungen werden festgelegt.

Beim Initialisieren der ID3D11Device muss D3D11_CREATE_DEVICE_VIDEO_SUPPORT Flag als Teil des D3D11_CREATE_DEVICE_FLAG definiert werden. Nachdem das Gerät und der Kontext erstellt wurden, rufen Sie SetMultithreadProtected auf, um Multithreading zu aktivieren. Um das Gerät dem IMFDXGIDeviceManager zuzuordnen, verwenden Sie die FUNKTION IMFDXGIDeviceManager::ResetDevice.

Code zum Zuordnen einer ID3D11Device zu 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;

Weitere Informationen