Renderowanie w programie DirectX

Uwaga

Ten artykuł dotyczy starszych natywnych interfejsów API winRT. W przypadku nowych projektów aplikacji natywnych zalecamy użycie interfejsu API OpenXR.

Windows Mixed Reality jest oparta na directX, aby tworzyć zaawansowane, graficzne środowiska 3D dla użytkowników. Abstrakcja renderowania znajduje się tuż nad directX, co umożliwia aplikacjom przyczynę położenia i orientacji obserwatorów scen holograficznej przewidywanych przez system. Deweloper może następnie zlokalizować swoje hologramy na podstawie każdej kamery, umożliwiając aplikacji renderowanie tych hologramów w różnych systemach współrzędnych przestrzennych, gdy użytkownik porusza się.

Uwaga: w tym przewodniku opisano renderowanie holograficzne w trybie Direct3D 11. Szablon aplikacji Windows Mixed Reality Direct3D 12 jest również dostarczany z rozszerzeniem szablonów aplikacji Mixed Reality.

Aktualizacja bieżącej ramki

Aby zaktualizować stan aplikacji dla hologramów, raz na ramkę aplikacja:

  • Pobierz element HolographicFrame z systemu zarządzania wyświetlaniem.
  • Zaktualizuj scenę przy użyciu bieżącego przewidywania miejsca, w którym widok aparatu będzie wyświetlany po zakończeniu renderowania. Uwaga: może istnieć więcej niż jeden aparat dla sceny holograficznej.

Aby renderować widoki kamery holograficznej, raz na ramkę aplikacja wykona następujące elementy:

  • Dla każdej kamery renderuj scenę dla bieżącej ramki przy użyciu macierzy widoku kamery i projekcji z systemu.

Tworzenie nowej ramki holograficznej i uzyskiwanie jej przewidywania

Element HolographicFrame zawiera informacje potrzebne do zaktualizowania i renderowania bieżącej ramki. Aplikacja rozpoczyna każdą nową ramkę, wywołując metodę CreateNextFrame . Po wywołaniu tej metody przewidywania są tworzone przy użyciu najnowszych dostępnych danych czujnika i hermetyzowane w obiekcie CurrentPrediction .

Nowy obiekt ramki musi być używany dla każdej renderowanej ramki, ponieważ jest on ważny tylko przez chwilę w czasie. Właściwość CurrentPrediction zawiera informacje, takie jak położenie aparatu. Informacje są ekstrapolowane do dokładnego momentu w czasie, gdy ramka ma być widoczna dla użytkownika.

Poniższy kod jest fragmentowany z aplikacji 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();

Przetwarzanie aktualizacji aparatu

Bufory wsteczne mogą zmieniać się z ramki na ramkę. Aplikacja musi zweryfikować bufor wsteczny dla każdej kamery oraz zwolnić i ponownie utworzyć widoki zasobów i bufory głębokości zgodnie z potrzebami. Zwróć uwagę, że zestaw pozy w przewidywaniu jest autorytatywną listą kamer używanych w bieżącej ramce. Zwykle używasz tej listy do iterowania na zestawie kamer.

Z aplikacji AppMain::Update:

m_deviceResources->EnsureCameraResources(holographicFrame, prediction);

W obszarze DeviceResources::EnsureCameraResources:

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

Pobieranie systemu współrzędnych do użycia jako podstawy renderowania

Windows Mixed Reality umożliwia aplikacji tworzenie różnych systemów współrzędnych, takich jak dołączone i stacjonarne ramy referencyjne do śledzenia lokalizacji w świecie fizycznym. Aplikacja może następnie użyć tych systemów współrzędnych, aby uzasadnić, gdzie renderować hologramy każdej ramki. Podczas żądania współrzędnych z interfejsu API zawsze przekazujesz element SpatialCoordinateSystem , w którym mają być wyrażone te współrzędne.

Z aplikacji AppMain::Update:

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

Te układy współrzędnych mogą następnie służyć do generowania macierzy widoku stereo podczas renderowania zawartości w scenie.

Z pliku 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);

Przetwarzanie wprowadzania spojrzenia i gestu

Dane wejściowe spojrzenia i ręki nie są oparte na czasie i nie muszą być aktualizowane w funkcji StepTimer . Jednak te dane wejściowe są czymś, co aplikacja musi przyjrzeć się każdej ramce.

Przetwarzanie aktualizacji opartych na czasie

Każda aplikacja renderowania w czasie rzeczywistym będzie potrzebować pewnego sposobu przetwarzania aktualizacji opartych na czasie — szablon aplikacji Windows Holographic korzysta z implementacji StepTimer, podobnie jak krokTimer podany w szablonie aplikacji platformy UNIWERSALNEj systemu Windows DirectX 11. Ta przykładowa klasa pomocnika StepTimer może zapewnić stałe aktualizacje kroków czasowych, zmienne aktualizacje kroku czasu, a tryb domyślny to zmienne kroki czasu.

W przypadku renderowania holograficznego wybraliśmy, aby nie umieścić zbyt wiele w funkcji czasomierza, ponieważ można ją skonfigurować tak, aby była to stały krok czasu. Może być wywoływany więcej niż raz na ramkę — lub w ogóle nie, w przypadku niektórych ramek — a nasze aktualizacje danych holograficzne powinny nastąpić raz na ramkę.

Z aplikacji AppMain::Update:

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

Położenie i obracanie hologramów w układzie współrzędnych

Jeśli korzystasz z jednego systemu współrzędnych, ponieważ szablon jest używany w elemencie SpatialStationaryReferenceFrame, ten proces nie różni się od tego, co jest używane w grafice 3D. W tym miejscu obracamy moduł i ustawiamy macierz modelu na podstawie położenia w układzie współrzędnych stacjonarnych.

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

Uwaga dotycząca zaawansowanych scenariuszy: Moduł wirujący to prosty przykład położenia hologramu w jednej ramce odniesienia. Istnieje również możliwość użycia wielu systemów SpatialCoordinateSystems w tej samej renderowanej ramce w tym samym czasie.

Aktualizowanie danych buforu stałego

Przekształcenia modelu dla zawartości są aktualizowane jak zwykle. Do tej pory obliczysz prawidłowe przekształcenia dla systemu współrzędnych, w którym będziesz renderować.

From SpinningCubeRenderer::Update:

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

Co z przekształceniami widoku i projekcji? Aby uzyskać najlepsze wyniki, chcemy poczekać, aż będziemy prawie gotowi na nasze wywołania rysowania, zanim je uzyskamy.

Renderowanie bieżącej ramki

Renderowanie na Windows Mixed Reality nie różni się znacznie od renderowania na ekranie mono 2D, ale istnieje kilka różnic:

  • Przewidywania ram holograficzne są ważne. Im bliżej przewidywania jest prezentowana ramka, tym lepiej będą wyglądać hologramy.
  • Windows Mixed Reality steruje widokami aparatu. Renderuj do każdego z nich, ponieważ ramka holograficzna będzie prezentować je później.
  • Zalecamy wykonywanie renderowania stereo przy użyciu rysunku wystąpionego do macierzy docelowej renderowania. Szablon aplikacji holograficznej używa zalecanego podejścia do rysunku wystąpienia do tablicy docelowej renderowania, która używa widoku docelowego renderowania na teksturę2DArray.
  • Jeśli chcesz renderować bez używania trybu stancingu stereo, musisz utworzyć dwa obiekty RenderTargetView bez tablicy, po jednym dla każdego oka. Każdy element RenderTargetView odwołuje się do jednego z dwóch wycinków w elemencie Texture2DArray dostarczonym do aplikacji z systemu. Nie jest to zalecane, ponieważ zazwyczaj jest wolniejsze niż używanie instancingu.

Pobieranie zaktualizowanego przewidywania holographicFrame

Aktualizowanie przewidywania ramek zwiększa skuteczność stabilizacji obrazu. Uzyskasz dokładniejsze pozycjonowanie hologramów z powodu krótszego czasu między przewidywaniem a tym, kiedy ramka jest widoczna dla użytkownika. Najlepiej zaktualizować przewidywanie ramek tuż przed renderowaniem.

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

Renderowanie do każdej kamery

Pętla na zestawie aparatów pozuje w przewidywaniu i renderuj do każdej kamery w tym zestawie.

Konfigurowanie przebiegu renderowania

Windows Mixed Reality używa renderowania steroskopowego, aby zwiększyć iluzję głębokości i renderować steroskopowo, więc zarówno lewy, jak i prawy wyświetlacz są aktywne. W przypadku renderowania steroskopowego istnieje przesunięcie między dwoma wyświetlaczami, które mózg może pogodzić jako rzeczywistą głębokość. W tej sekcji opisano renderowanie steroskopowe przy użyciu stancingu przy użyciu kodu z szablonu aplikacji Windows Holographic.

Każda kamera ma własny obiekt docelowy renderowania (bufor wsteczny) oraz macierze widoku i projekcji w przestrzeni holograficznej. Aplikacja będzie musiała utworzyć inne zasoby oparte na kamerze — takie jak bufor głębokości — na podstawie aparatu. W szablonie aplikacji Systemu Windows Holographic udostępniamy klasę pomocnika w celu łączenia tych zasobów w usługach DX::CameraResources. Rozpocznij od skonfigurowania widoków docelowych renderowania:

Z elementu 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);

Użyj przewidywania, aby uzyskać macierze widoku i projekcji dla aparatu

Macierze widoku i projekcji dla każdej kamery holograficznej zmienią się z każdą ramką. Odśwież dane w buforze stałym dla każdej kamery holograficznej. Zrób to po zaktualizowaniu przewidywania i przed wykonaniem jakichkolwiek wywołań rysowania dla tego aparatu.

Z elementu 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);

Tutaj pokazujemy, jak macierze są pozyskiwane z pozy aparatu. W trakcie tego procesu uzyskujemy również bieżący widok dla aparatu. Zwróć uwagę na to, jak udostępniamy układ współrzędnych: jest to ten sam układ współrzędnych, który użyliśmy do zrozumienia spojrzenia, i jest to ten sam, który użyliśmy do pozycjonowania modułu wirującego.

Z pliku 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))
    );
}

Port widoku powinien być ustawiony dla każdej ramki. Cieniator wierzchołka (przynajmniej) będzie zazwyczaj potrzebować dostępu do danych widoku/projekcji.

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

Renderuj do buforu tylnego aparatu i zatwierdź bufor głębokości:

Warto sprawdzić, czy funkcja TryGetViewTransform powiodła się przed próbą użycia danych widoku/projekcji, ponieważ jeśli system współrzędnych nie jest lokalizowalny (na przykład śledzenie zostało przerwane), aplikacja nie może renderować jej dla tej ramki. Szablon wywołuje funkcję Render w module obracającym się tylko wtedy, gdy klasa CameraResources wskazuje pomyślną aktualizację.

Windows Mixed Reality zawiera funkcje stabilizacji obrazu, aby zachować położenie hologramów, w których deweloper lub użytkownik umieszcza je na świecie. Stabilizacja obrazu pomaga ukryć opóźnienie związane z potokiem renderowania, aby zapewnić użytkownikom najlepsze środowiska holograficzne. Punkt koncentracji uwagi można określić, aby jeszcze bardziej zwiększyć stabilizację obrazu, lub można udostępnić bufor głębokości w celu obliczenia zoptymalizowanej stabilizacji obrazu w czasie rzeczywistym.

Aby uzyskać najlepsze wyniki, aplikacja powinna zapewnić bufor głębokości przy użyciu interfejsu API CommitDirect3D1DepthBuffer . Windows Mixed Reality może następnie użyć informacji geometrycznych z buforu głębokości, aby zoptymalizować stabilizację obrazu w czasie rzeczywistym. Szablon aplikacji Holographic systemu Windows domyślnie zatwierdza bufor głębokości aplikacji, pomagając zoptymalizować stabilność hologramu.

Z elementu 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);
    }
}

Uwaga

System Windows przetworzy teksturę głębokości na procesorze GPU, więc musi być możliwe użycie buforu głębokości jako zasobu cieniowania. Utworzony tekst ID3D11Texture2D powinien być w formacie bez wpisywania i powinien być powiązany jako widok zasobu cieniowania. Oto przykład tworzenia tekstury głębokości, która może zostać zatwierdzona na potrzeby stabilizacji obrazu.

Kod tworzenia zasobu buforu głębokości dla 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
    ));

Rysowanie zawartości holograficznej

Szablon aplikacji Systemu Windows Holographic renderuje zawartość w stereo przy użyciu zalecanej techniki rysowania geometrii wystąpień do tekstury 2DArray o rozmiarze 2. Przyjrzyjmy się części instancingu tego i jak działa na Windows Mixed Reality.

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

Każde wystąpienie uzyskuje dostęp do innej macierzy widoku/projekcji z buforu stałego. Oto stała struktura buforu, która jest tylko tablicą dwóch macierzy.

Z VertexShaderShared.hlsl, zawarte przez 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];
};

Indeks tablicy docelowej renderowania musi być ustawiony dla każdego piksela. W poniższym fragmencie kodu parametr output.viewId jest mapowany na semantyczną SV_RenderTargetArrayIndex . Wymaga to obsługi opcjonalnej funkcji Direct3D 11.3, która umożliwia ustawianie semantyki indeksu tablicy docelowej renderowania z dowolnego etapu cieniowania.

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

Z VertexShaderShared.hlsl, zawarte przez 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;
}

Jeśli chcesz użyć istniejących technik rysunkowych wystąpień z tą metodą rysowania do tablicy docelowej renderowania stereo, narysuj dwa razy więcej wystąpień, które zwykle masz. W cieniatorze podziel wartość input.instId na 2, aby uzyskać oryginalny identyfikator wystąpienia, który można indeksować (na przykład) bufor danych dla poszczególnych obiektów: int actualIdx = input.instId / 2;

Ważna uwaga dotycząca renderowania zawartości stereo na urządzeniu HoloLens

Windows Mixed Reality obsługuje możliwość ustawiania indeksu tablicy docelowej renderowania z dowolnego etapu cieniowania. Zwykle jest to zadanie, które można wykonać tylko na etapie cieniowania geometrii ze względu na sposób, w jaki semantyka jest definiowana dla direct3D 11. W tym miejscu przedstawiono kompletny przykład konfigurowania potoku renderowania z ustawionymi etapami cieniowania wierzchołków i cieniowania pikseli. Kod cieniowania jest opisany powyżej.

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

Ważna uwaga dotycząca renderowania na urządzeniach innych niż HoloLens

Ustawienie indeksu tablicy docelowej renderowania w cieniatorze wierzchołka wymaga, aby sterownik graficzny obsługiwał opcjonalną funkcję Direct3D 11.3, która obsługuje urządzenie HoloLens. Aplikacja może bezpiecznie zaimplementować tę technikę renderowania, a wszystkie wymagania zostaną spełnione do uruchamiania na Microsoft HoloLens.

Może się zdarzyć, że chcesz również użyć emulatora Urządzenia HoloLens, co może być zaawansowanym narzędziem deweloperów dla aplikacji holograficznej — i obsługuje Windows Mixed Reality immersywnych urządzeń nagłownych dołączonych do komputerów Windows 10. Obsługa ścieżki renderowania innej niż HoloLens — dla wszystkich Windows Mixed Reality — jest również wbudowana w szablon aplikacji Systemu Windows Holographic. W kodzie szablonu znajdziesz kod umożliwiający uruchamianie aplikacji holograficznej na procesorze GPU na komputerze deweloperskich. Oto jak klasa DeviceResources sprawdza obsługę tej opcjonalnej funkcji.

W obszarze 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;
}

Aby obsługiwać renderowanie bez tej opcjonalnej funkcji, aplikacja musi użyć cieniowania geometrii, aby ustawić indeks tablicy docelowej renderowania. Ten fragment kodu zostanie dodany poelemencie VSSetConstantBuffers i przedprogramem PSSetShader w przykładzie kodu pokazanym w poprzedniej sekcji, w ramach którego wyjaśniono, jak renderować stereo na urządzeniu HoloLens.

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

UWAGA HLSL: W tym przypadku należy również załadować nieco zmodyfikowany cień wierzchołka, który przekazuje indeks tablicy docelowej renderowania do cieniowania geometrii przy użyciu zawsze dozwolonej semantyki cieniowania, takiej jak TEXCOORD0. Cieniator geometrii nie musi wykonywać żadnej pracy; moduł cieniowania geometrii szablonu przechodzi przez wszystkie dane z wyjątkiem indeksu tablicy docelowej renderowania, który służy do ustawiania semantyki SV_RenderTargetArrayIndex.

Kod szablonu aplikacji dla 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);
    }
}

Obecny

Umożliwianie ramce holograficznej prezentowania łańcucha wymiany

W Windows Mixed Reality system kontroluje łańcuch wymiany. Następnie system zarządza prezentowaniem ramek do każdej kamery holograficznej w celu zapewnienia wysokiej jakości środowiska użytkownika. Zapewnia również aktualizację widoku dla każdej ramki dla każdej kamery w celu zoptymalizowania aspektów systemu, takich jak stabilizacja obrazu lub Mixed Reality Przechwytywanie. W związku z tym aplikacja holgraficzna korzystająca z directX nie wywołuje funkcji Present w łańcuchu wymiany DXGI. Zamiast tego należy użyć klasy HolographicFrame , aby przedstawić wszystkie zamiany dla ramki po zakończeniu rysowania.

W obszarze DeviceResources::P resent:

HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();

Domyślnie ten interfejs API czeka na zakończenie ramki przed jej zwróceniem. Aplikacje holograficzne powinny czekać na zakończenie poprzedniej ramki przed rozpoczęciem pracy nad nową ramką, ponieważ zmniejsza to opóźnienie i pozwala uzyskać lepsze wyniki z przewidywań ram holograficznej. Nie jest to twarda reguła, a jeśli masz ramki, które trwa dłużej niż jedno odświeżanie ekranu, aby renderować, możesz wyłączyć to oczekiwanie, przekazując parametr HolographicFramePresentWaitBehavior do presentUsingCurrentPrediction. W takim przypadku prawdopodobnie użyjesz asynchronicznego wątku renderowania, aby zachować ciągłe obciążenie procesora GPU. Częstotliwość odświeżania urządzenia HoloLens wynosi 60 hz, gdzie jedna ramka ma czas trwania około 16 ms. Immersyjne urządzenia nagłowne mogą wahać się od 60 hz do 90 hz; podczas odświeżania wyświetlacza o częstotliwości 90 hz każda ramka będzie miała czas trwania około 11 ms.

Obsługa scenariuszy DeviceLost we współpracy z elementem HolographicFrame

Aplikacje DirectX 11 zwykle chcą sprawdzić HRESULT zwrócony przez funkcję Present łańcucha wymiany DXGI, aby dowiedzieć się, czy wystąpił błąd DeviceLost . Klasa HolographicFrame obsługuje to za Ciebie. Sprawdź zwrócony element HolographicFramePresentResult , aby dowiedzieć się, czy musisz zwolnić i ponownie utworzyć urządzenie Direct3D i zasoby oparte na urządzeniach.

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

Jeśli urządzenie Direct3D zostało utracone i zostało utworzone ponownie, musisz poinformować HolographicSpace , aby rozpocząć korzystanie z nowego urządzenia. Łańcuch wymiany zostanie ponownie utworzony dla tego urządzenia.

Z deviceResources::InitializeUsingHolographicSpace:

m_holographicSpace.SetDirect3D11Device(m_d3dInteropDevice);

Po przedstawieniu ramki możesz wrócić do pętli głównego programu i zezwolić na kontynuowanie następnej ramki.

Hybrydowe komputery graficzne i aplikacje rzeczywistości mieszanej

Windows 10 komputerów aktualizacji dla twórców można skonfigurować zarówno przy użyciu dyskretnych, jak i zintegrowanych procesorów GPU. W przypadku tych typów komputerów system Windows wybierze adapter, z którą jest podłączony zestaw słuchawkowy. Aplikacje muszą upewnić się, że tworzone urządzenie DirectX używa tej samej karty.

Większość ogólnego przykładowego kodu Direct3D pokazuje tworzenie urządzenia DirectX przy użyciu domyślnej karty sprzętowej, która w systemie hybrydowym może nie być taka sama jak używana dla zestawu słuchawkowego.

Aby obejść wszelkie problemy, użyj identyfikatora HolographicAdapterID z witryny HolographicSpace. PrimaryAdapterId() lub HolographicDisplay. AdapterId(). Ten identyfikator adapterId może następnie służyć do wybierania odpowiedniego elementu DXGIAdapter przy użyciu identyfikatora IDXGIFactory4.EnumAdapterByLuid.

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

Kod aktualizowania elementu DeviceResources::CreateDeviceResources w celu używania identyfikatora 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.
);

Grafika hybrydowa i Media Foundation

Korzystanie z programu Media Foundation w systemach hybrydowych może powodować problemy, w których wideo nie będzie renderowane lub tekstura wideo jest uszkodzona, ponieważ program Media Foundation domyślnie używa zachowania systemu. W niektórych scenariuszach tworzenie oddzielnego identyfikatora ID3D11Device jest wymagane do obsługi wielowątkowego i ustawiono poprawne flagi tworzenia.

Podczas inicjowania identyfikatora ID3D11Urządzenia należy zdefiniować flagę D3D11_CREATE_DEVICE_VIDEO_SUPPORT jako część D3D11_CREATE_DEVICE_FLAG. Po utworzeniu urządzenia i kontekstu wywołaj metodę SetMultithreadProtected , aby włączyć wielowątkowanie. Aby skojarzyć urządzenie z funkcją IMFDXGIDeviceManager, użyj funkcji IMFDXGIDeviceManager::ResetDeviceManager .

Kod do skojarzenia identyfikatora ID3D11Device z elementem 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;

Zobacz też