Отрисовка в DirectX

Примечание

Эта статья относится к устаревшим собственным API WinRT. Для новых проектов собственных приложений рекомендуется использовать API OpenXR.

Windows Mixed Reality создана на основе DirectX для создания полнофункционированных трехмерных графических возможностей для пользователей. Абстракция отрисовки находится чуть выше DirectX, что позволяет приложениям обдумать положение и ориентацию голографических наблюдателей сцены, прогнозируемых системой. Затем разработчик может найти свои голограммы на основе каждой камеры, позволяя приложению визуализировать эти голограммы в различных системах пространственных координат по мере перемещения пользователя.

Примечание. В этом пошаговом руководстве описывается голографическая отрисовка в Direct3D 11. Шаблон приложения Direct3D 12 Windows Mixed Reality также предоставляется вместе с расширением Смешанная реальность шаблонов приложений.

Обновление текущего кадра

Чтобы обновить состояние приложения для голограмм, для каждого кадра приложение будет выполнять следующие действия:

  • Получите HolographicFrame из системы управления дисплеем.
  • Обновите сцену текущим прогнозом, где будет отображаться представление камеры после завершения отрисовки. Обратите внимание, что для голографической сцены может быть несколько камер.

Для отрисовки в голографических представлениях камеры приложение будет один раз за кадр:

  • Для каждой камеры отрисовка сцены для текущего кадра с помощью матрицы просмотра камеры и проекции из системы.

Создание голографического кадра и получение его прогноза

HolographicFrame содержит сведения, необходимые приложению для обновления и отрисовки текущего кадра. Приложение начинает каждый новый кадр с вызова метода CreateNextFrame . При вызове этого метода прогнозы выполняются с использованием последних доступных данных датчика и инкапсулируются в объект CurrentPrediction .

Для каждого отображаемого кадра необходимо использовать новый объект кадра, так как он действителен только в течение определенного момента времени. Свойство CurrentPrediction содержит такие сведения, как положение камеры. Информация экстраполируется на тот момент времени, когда кадр должен быть виден пользователю.

Следующий код является выдержкой из 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();

Обработка обновлений камеры

Задние буферы могут меняться от кадра к кадру. Приложению необходимо проверить задний буфер для каждой камеры, а также освободить и повторно создать представления ресурсов и буферы глубины по мере необходимости. Обратите внимание, что набор поз в прогнозе является заслуживающим доверия списком камер, используемых в текущем кадре. Обычно этот список используется для итерации по набору камер.

Из AppMain::Update:

m_deviceResources->EnsureCameraResources(holographicFrame, prediction);

Из DeviceResources::EnsureCameraResources:

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

Получение системы координат для использования в качестве основы для отрисовки

Windows Mixed Reality позволяет приложению создавать различные системы координат, например прикрепленные и стационарные опорные кадры для отслеживания расположений в физическом мире. Затем приложение может использовать эти системы координат, чтобы подумать о том, где отрисовывать голограммы для каждого кадра. При запросе координат из API вы всегда передаете объект SpatialCoordinateSystem , в котором должны быть выражены эти координаты.

Из AppMain::Update:

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

Эти системы координат затем можно использовать для создания матриц стерео представления при отрисовке содержимого в сцене.

Из 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);

Ввод взгляда и жеста процесса

Входные данные взгляда и руки не основаны на времени и не должны обновляться в функции StepTimer . Однако это то, что приложение должно смотреть на каждый кадр.

Обработка обновлений на основе времени

Любому приложению отрисовки в режиме реального времени потребуется какой-то способ обработки обновлений на основе времени. Шаблон приложения Windows Holographic использует реализацию StepTimer , аналогичную stepTimer, предоставленной в шаблоне приложения UWP Для DirectX 11. Этот пример вспомогательного класса StepTimer может предоставлять фиксированные обновления по шагам времени, обновления с переменными временными шагами, а режимом по умолчанию являются переменные шаги времени.

Для голографической отрисовки мы решили не добавлять слишком много в функцию таймера, так как ее можно настроить как фиксированный шаг времени. Он может вызываться несколько раз для каждого кадра или вообще не вызываться для некоторых кадров, и обновления голографических данных должны выполняться один раз для каждого кадра.

Из AppMain::Update:

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

Положение и поворот голограмм в системе координат

Если вы работаете в одной системе координат, как это делает шаблон с SpatialStationaryReferenceFrame, этот процесс ничем не отличается от того, к чему вы привыкли в трехмерной графике. Здесь мы поворачиваем куб и задаем матрицу модели на основе положения в стационарной системе координат.

Из 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));

Примечание о расширенных сценариях: Вращающийся куб — это простой пример размещения голограммы в пределах одной опорной рамки. Кроме того, можно одновременно использовать несколько SpatialCoordinateSystems в одном отрисованном кадре.

Обновление данных буфера констант

Преобразования модели для содержимого обновляются как обычно. К настоящему моменту вычислим допустимые преобразования для системы координат, в которую будет выполняться отрисовка.

Из SpinningCubeRenderer::Update:

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

Как насчет преобразований представлений и проекций? Для достижения наилучших результатов мы хотим подождать, пока мы почти готовы к вызовам жеребьевки, прежде чем получить их.

Отрисовка текущего кадра

Отрисовка на Windows Mixed Reality мало чем отличается от отрисовки на двухD-моно-дисплее, но есть несколько отличий:

  • Голографические прогнозы кадров важны. Чем ближе прогноз к моменту представления кадра, тем лучше будут выглядеть голограммы.
  • Windows Mixed Reality управляет представлениями камеры. Отрисовка для каждого из них, так как голографический кадр будет представлять их позже.
  • Рекомендуется выполнять стерео отрисовку с использованием экземплярного рисования в целевом массиве отрисовки. В шаблоне голографического приложения используется рекомендуемый подход к рисованию экземпляров в целевом массиве отрисовки, при котором используется целевое представление отрисовки в Texture2DArray.
  • Если вы хотите выполнить отрисовку без использования стереостансинга, необходимо создать два представления RenderTargetView, не относящихся к массиву, по одному для каждого глаза. Каждый renderTargetViews ссылается на один из двух срезов в Texture2DArray , предоставленных приложению из системы. Это не рекомендуется, так как обычно это медленнее, чем использование instancing.

Получение обновленного прогноза голографического кадра

Обновление прогноза кадра повышает эффективность стабилизации изображения. Вы получаете более точное расположение голограмм из-за более короткого времени между прогнозом и тем, когда кадр становится видимым для пользователя. В идеале обновите прогноз кадра непосредственно перед отрисовкой.

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

Отрисовка для каждой камеры

Цикл на наборе поз камеры в прогнозе и отрисовка каждой камеры в этом наборе.

Настройка прохода отрисовки

Windows Mixed Reality использует стереоскопическую отрисовку для повышения иллюзии глубины и для стереоскопической отрисовки, поэтому активны как левый, так и правый дисплей. При стереоскопической отрисовке между двумя дисплеями есть смещение, которое мозг может согласовать как фактическую глубину. В этом разделе рассматривается стереоскопическая отрисовка с использованием кода из шаблона приложения Windows Holographic.

Каждая камера имеет собственную цель отрисовки (задний буфер), а также матрицы просмотра и проекции в голографическом пространстве. Вашему приложению потребуется создавать любые другие ресурсы на основе камеры, например буфер глубины, для каждой камеры. В шаблоне приложения Windows Holographic мы предоставляем вспомогательный класс для объединения этих ресурсов в DX::CameraResources. Начните с настройки целевых представлений отрисовки:

Из 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);

Использование прогноза для получения матрицы просмотра и проекции для камеры

Матрицы просмотра и проекции для каждой голографической камеры будут меняться с каждым кадром. Обновите данные в буфере констант для каждой голографической камеры. Сделайте это после обновления прогноза и перед выполнением каких-либо вызовов draw для этой камеры.

Из 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);

Здесь мы покажем, как матрицы получаются из положения камеры. В ходе этого процесса мы также получаем текущее окно просмотра для камеры. Обратите внимание на то, как мы предоставляем систему координат: это та же система координат, которую мы использовали для понимания взгляда, и та же, что и для размещения вращающегося куба.

Из 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))
    );
}

Окно просмотра должно быть задано для каждого кадра. Шейдер вершин (по крайней мере) обычно нуждается в доступе к данным представления или проекции.

Из 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()
);

Отрисовка обратного буфера камеры и фиксация буфера глубины:

Рекомендуется проверка, что TryGetViewTransform успешно выполнен, прежде чем пытаться использовать данные представления или проекции, так как если система координат не является locatable (например, отслеживание было прервано), приложение не сможет выполнить отрисовку для этого кадра. Шаблон вызывает отрисовку для вращающегося куба, только если класс CameraResources указывает на успешное обновление.

Windows Mixed Reality включает функции для стабилизации изображения, чтобы голограммы располагались там, где разработчик или пользователь помещает их в мир. Стабилизация изображения помогает скрыть задержку, присущую конвейеру отрисовки, чтобы обеспечить наилучшие голографические возможности для пользователей. Можно указать точку фокуса, чтобы еще больше повысить стабилизацию изображения, или буфер глубины может быть предоставлен для вычисления оптимизированной стабилизации изображения в режиме реального времени.

Для достижения наилучших результатов приложение должно предоставить буфер глубины с помощью API CommitDirect3D11DepthBuffer . Windows Mixed Reality затем могут использовать геометрическую информацию из буфера глубины для оптимизации стабилизации изображения в режиме реального времени. Шаблон приложения Windows Holographic по умолчанию фиксирует буфер глубины приложения, помогая оптимизировать стабильность голограммы.

Из 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);
    }
}

Примечание

Windows будет обрабатывать текстуру глубины в GPU, поэтому в качестве ресурса шейдера необходимо использовать буфер глубины. Создаваемый идентификатор ID3D11Texture2D должен иметь нетипизированный формат и быть привязан к представлению ресурсов шейдера. Ниже приведен пример создания текстуры глубины, которую можно зафиксировать для стабилизации изображения.

Код для создания ресурса буфера глубины для 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
    ));

Рисование голографического содержимого

Шаблон приложения Windows Holographic отрисовывает содержимое в стерео, используя рекомендуемый метод рисования экземпляра геометрии в Texture2DArray размера 2. Давайте посмотрим на часть этого и на то, как она работает на Windows Mixed Reality.

Из 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.
);

Каждый экземпляр обращается к отдельной матрице представления или проекции из буфера констант. Ниже приведена структура буфера констант, которая представляет собой просто массив из двух матриц.

Из Файла VertexShaderShared.hlsl, входящего в состав 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];
};

Индекс целевого массива отрисовки должен быть задан для каждого пикселя. В следующем фрагменте кода output.viewId сопоставляется с семантикой SV_RenderTargetArrayIndex . Для этого требуется поддержка необязательной функции Direct3D 11.3, которая позволяет задать семантику индекса целевого массива отрисовки на любом этапе шейдера.

Из 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;
};

Из Файла VertexShaderShared.hlsl, входящего в состав 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;
}

Если вы хотите использовать существующие методы рисования экземпляров с этим методом рисования в целевом массиве стереорисовки, нарисуйте в два раза больше экземпляров, которые у вас есть. В шейдере разделите input.instId на 2, чтобы получить исходный идентификатор экземпляра, который можно индексировать в (например) буфер данных для каждого объекта: int actualIdx = input.instId / 2;

Важное примечание о отрисовке стереосодержимого в HoloLens

Windows Mixed Reality поддерживает возможность задания индекса целевого массива отрисовки на любом этапе шейдера. Как правило, это задача, которую можно выполнить только на этапе геометрического шейдера из-за того, как определяется семантика для Direct3D 11. Здесь мы покажем полный пример настройки конвейера отрисовки только с заданными этапами вершины и пиксельного шейдера. Код шейдера, как описано выше.

Из 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.
);

Важное примечание о отрисовке на устройствах, отличных от HoloLens

Для установки индекса целевого массива отрисовки в вершинном шейдере требуется, чтобы графический драйвер поддерживал необязательную функцию Direct3D 11.3, которую поддерживает HoloLens. Приложение может безопасно реализовать именно этот метод для отрисовки, и все требования будут выполнены для выполнения на Microsoft HoloLens.

Возможно, вы также хотите использовать эмулятор HoloLens, который может быть мощным инструментом разработки для голографического приложения и поддерживать Windows Mixed Reality иммерсивные гарнитуры, подключенные к компьютерам с Windows 10. Поддержка пути отрисовки, отличного от HoloLens, для всех Windows Mixed Reality, также встроена в шаблон приложения Windows Holographic. В коде шаблона вы найдете код, позволяющий голографическим приложениям выполняться на GPU на компьютере разработки. Вот как класс DeviceResources проверяет поддержку этой необязательной функции.

Из 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;
}

Для поддержки отрисовки без этой необязательной функции приложение должно использовать геометрический шейдер для задания индекса целевого массива отрисовки. Этот фрагмент будет добавлен послеVSSetConstantBuffers и передPSSetShader в примере кода, показанном в предыдущем разделе, где объясняется, как отрисовка стереофона в HoloLens.

Из 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. В этом случае необходимо также загрузить слегка измененный вершинный шейдер, который передает индекс целевого массива отрисовки в геометрический шейдер с использованием всегда разрешенной семантики шейдера, например TEXCOORD0. Шейдер геометрии не должен выполнять никаких действий; геометрический шейдер шаблона проходит через все данные, за исключением индекса целевого массива отрисовки, который используется для задания SV_RenderTargetArrayIndex семантики.

Код шаблона приложения для 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);
    }
}

Присутствует

Включение голографического кадра для представления цепочки буферов

С помощью Windows Mixed Reality система управляет цепочкой буферов. Затем система управляет представлением кадров на каждую голографическую камеру, чтобы обеспечить высокое качество взаимодействия с пользователем. Он также обеспечивает обновление окна просмотра для каждого кадра для каждой камеры, чтобы оптимизировать аспекты системы, такие как стабилизация изображения или Смешанная реальность захвата. Таким образом, голографическое приложение, использующего DirectX, не вызывает Present в цепочке буферов DXGI. Вместо этого вы используете класс HolographicFrame , чтобы представить все цепочки буферов для кадра после завершения рисования.

Из DeviceResources::P resent:

HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();

По умолчанию этот API ожидает завершения фрейма перед возвратом. Голографические приложения должны дождаться завершения предыдущего кадра перед началом работы над новым кадром, так как это сокращает задержку и обеспечивает лучшие результаты при прогнозах голографических кадров. Это не жесткое правило, и если у вас есть кадры, для отрисовки которых требуется больше одного обновления экрана, можно отключить это ожидание, передав параметр HolographicFramePresentWaitBehavior в PresentUsingCurrentPrediction. В этом случае вы, скорее всего, будете использовать асинхронный поток отрисовки для поддержания непрерывной нагрузки на GPU. Частота обновления устройства HoloLens составляет 60 Гц, при этом длительность одного кадра составляет около 16 мс. Иммерсивные гарнитуры могут быть в диапазоне от 60 Гц до 90 Гц; при обновлении дисплея с частотой 90 Гц каждый кадр будет иметь длительность около 11 мс.

Обработка сценариев DeviceLost в сотрудничестве с HolographicFrame

Приложения DirectX 11 обычно хотят проверка HRESULT, возвращенный функцией Present цепочки буферов DXGI, чтобы узнать, не произошла ли ошибка DeviceLost. Класс HolographicFrame обрабатывает это за вас. Проверьте возвращенный объект HolographicFramePresentResult , чтобы узнать, нужно ли освободить и повторно создать устройство Direct3D и ресурсы на основе устройства.

// The PresentUsingCurrentPrediction API will detect when the graphics device
// changes or becomes invalid. When this happens, it is considered a Direct3D
// device lost scenario.
if (presentResult == HolographicFramePresentResult::DeviceRemoved)
{
    // The Direct3D device, context, and resources should be recreated.
    HandleDeviceLost();
}

Если устройство Direct3D было потеряно и вы создали его повторно, необходимо сообщить HolographicSpace , чтобы начать использовать новое устройство. Цепочка буферов будет воссоздана для этого устройства.

Из DeviceResources::InitializeUsingHolographicSpace:

m_holographicSpace.SetDirect3D11Device(m_d3dInteropDevice);

После представления кадра можно вернуться к циклу программы main и разрешить ему перейти к следующему кадру.

Гибридные графические компьютеры и приложения смешанной реальности

На компьютерах с Windows 10 Creators Update можно настроить как дискретные, так и интегрированные GPU. При использовании этих типов компьютеров Windows выберет адаптер, к которому подключена гарнитура. Приложения должны убедиться, что создаваемое устройство DirectX использует тот же адаптер.

В большинстве общих примеров кода Direct3D показано создание устройства DirectX с помощью аппаратного адаптера по умолчанию, который в гибридной системе может отличаться от используемого для гарнитуры.

Чтобы обойти все проблемы, используйте HolographicAdapterID из HolographicSpace. PrimaryAdapterId() или HolographicDisplay. AdapterId(). Затем этот adapterId можно использовать для выбора правильного DXGIAdapter с помощью IDXGIFactory4.EnumAdapterByLuid.

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

Код для обновления DeviceResources::CreateDeviceResources для использования 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.
);

Гибридная графика и Media Foundation

Использование Media Foundation в гибридных системах может привести к проблемам, из-за которых видео не будет отображаться или текстура видео повреждена, так как Media Foundation использует поведение системы по умолчанию. В некоторых сценариях для поддержки многопотоковой работы требуется создание отдельного id3D11Device и установлены правильные флаги создания.

При инициализации ID3D11Device флаг D3D11_CREATE_DEVICE_VIDEO_SUPPORT должен быть определен как часть D3D11_CREATE_DEVICE_FLAG. После создания устройства и контекста вызовите SetMultithreadProtected , чтобы включить многопоточность. Чтобы связать устройство с IMFDXGIDeviceManager, используйте функцию IMFDXGIDeviceManager::ResetDevice .

Код для связывания ID3D11Device с IMFDXGIDeviceManager:

// create dx device for media pipeline
winrt::com_ptr<ID3D11Device> spMediaDevice;

// See above. Also make sure to enable the following flags on the D3D11 device:
//   * D3D11_CREATE_DEVICE_VIDEO_SUPPORT
//   * D3D11_CREATE_DEVICE_BGRA_SUPPORT
if (FAILED(CreateMediaDevice(spAdapter.get(), &spMediaDevice)))
    return;                                                     

// Turn multithreading on 
winrt::com_ptr<ID3D10Multithread> spMultithread;
if (spContext.try_as(spMultithread))
{
    spMultithread->SetMultithreadProtected(TRUE);
}

// lock the shared dxgi device manager
// call MFUnlockDXGIDeviceManager when no longer needed
UINT uiResetToken;
winrt::com_ptr<IMFDXGIDeviceManager> spDeviceManager;
hr = MFLockDXGIDeviceManager(&uiResetToken, spDeviceManager.put());
if (FAILED(hr))
    return hr;
    
// associate the device with the manager
hr = spDeviceManager->ResetDevice(spMediaDevice.get(), uiResetToken);
if (FAILED(hr))
    return hr;

См. также раздел