Renderização em DirectX

Nota

Este artigo diz respeito ao legado WinRT native APIs. Para novos projetos de aplicações nativas, recomendamos a utilização da API OpenXR.

Windows Mixed Reality é construído em DirectX para produzir experiências gráficas ricas e 3D para os utilizadores. A abstração de renderização situa-se um pouco acima do DirectX, o que permite que as aplicações raciocinem sobre a posição e orientação dos observadores de cena holográfica previstos pelo sistema. O desenvolvedor pode então localizar os seus hologramas com base em cada câmara, permitindo que a aplicação torne estes hologramas em vários sistemas de coordenadas espaciais à medida que o utilizador se desloca.

Nota: Este walkthrough descreve a renderização holográfica em Direct3D 11. Um modelo de aplicação direct3D 12 Windows Mixed Reality também é fornecido com a extensão de modelos de aplicativo de realidade mista.

Atualização para o quadro atual

Para atualizar o estado da aplicação para hologramas, uma vez por quadro a aplicação será:

  • Obtenha um HolographicFrame do sistema de gestão de exibição.
  • Atualize a cena com a previsão atual de onde estará a vista da câmara quando o renderização estiver concluído. Nota, pode haver mais de uma câmara para a cena holográfica.

Para renderizar para as vistas da câmara holográfica, uma vez por quadro a aplicação será:

  • Para cada câmara, torne a cena para a moldura atual, utilizando a visualização da câmara e as matrizes de projeção do sistema.

Crie um novo quadro holográfico e obtenha a sua previsão

O HolographicFrame tem informações de que a aplicação precisa de atualizar e renderizar o quadro atual. A aplicação começa cada novo quadro chamando o método CreateNextFrame. Quando este método é chamado, as previsões são feitas usando os dados mais recentes do sensor disponíveis, e encapsuladas no objeto CurrentPrediction.

Um novo objeto de moldura deve ser utilizado para cada quadro renderizado, uma vez que é válido apenas por um instante no tempo. A propriedade CurrentPrediction contém informações como a posição da câmara. A informação é extrapolada para o momento exato em que se espera que a moldura seja visível para o utilizador.

O seguinte código é excerto da AppMain::Update:

// The HolographicFrame has information that the app needs in order
// to update and render the current frame. The app begins each new
// frame by calling CreateNextFrame.
HolographicFrame holographicFrame = m_holographicSpace.CreateNextFrame();

// Get a prediction of where holographic cameras will be when this frame
// is presented.
HolographicFramePrediction prediction = holographicFrame.CurrentPrediction();

Atualizações de câmaras de processo

Os tampões traseiros podem mudar de quadro para quadro. A sua aplicação precisa de validar o tampão traseiro para cada câmara e lançar e recriar as vistas de recursos e os amortecedores de profundidade, conforme necessário. Note que o conjunto de poses na previsão é a lista autoritária de câmaras que estão a ser usadas no quadro atual. Normalmente, usas esta lista para iterar no conjunto de câmaras.

A partir de AppMain::Update:

m_deviceResources->EnsureCameraResources(holographicFrame, prediction);

A partir de DispositivosResources::Garantir Recursos DeCamera:

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

Obtenha o sistema de coordenadas para usar como base para a renderização

Windows Mixed Reality permite que a sua aplicação crie vários sistemas de coordenadas,como quadros de referência anexados e estacionários para locais de rastreio no mundo físico. A sua aplicação pode então utilizar estes sistemas de coordenadas para raciocinar sobre onde render os hologramas em cada frame. Ao solicitar coordenadas de uma API, passará sempre no Sistema EspacialCoordinate no qual pretende que essas coordenadas sejam expressas.

A partir de AppMain::Update:

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

Estes sistemas de coordenadas podem então ser utilizados para gerar matrizes de visualização estéreo ao renderizar o conteúdo na sua cena.

A partir de CameraResources::UpdateViewProjectionBuffer:

// Get a container object with the view and projection matrices for the given
// pose in the given coordinate system.
auto viewTransformContainer = cameraPose.TryGetViewTransform(coordinateSystem);

Processo olhar e entrada de gesto

O olhar e a entrada da mão não são baseados no tempo e não têm de atualizar na função StepTimer. No entanto, esta entrada é algo que a app precisa de olhar para cada frame.

Atualizações baseadas no tempo do processo

Qualquer aplicação de renderização em tempo real necessitará de alguma forma de processar atualizações baseadas no tempo - o modelo de aplicação Holográfica Windows utiliza uma implementação StepTimer, semelhante ao StepTimer fornecido no modelo de aplicação DirectX 11 UWP. Esta classe de ajudante de amostra StepTimer pode fornecer atualizações fixas do passo de tempo, atualizações variáveis do passo de tempo, e o modo padrão são passos de tempo variáveis.

Para a renderização holográfica, optámos por não colocar muito na função do temporizador porque pode configurá-lo para ser um passo fixo. Pode ser chamado mais do que uma vez por frame – ou não, para alguns frames – e as nossas atualizações holográficas de dados devem acontecer uma vez por frame.

A partir de AppMain::Update:

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

Posicione e rode hologramas no seu sistema de coordenadas

Se estiver a operar num único sistema de coordenadas, como o modelo faz com o SpatialStationaryReferenceFrame,este processo não é diferente do que está habituado em gráficos 3D. Aqui, rodamos o cubo e definimos a matriz do modelo com base na posição no sistema de coordenadas estacionária.

De SpinningCubeRenderer::Update:

// Rotate the cube.
// Convert degrees to radians, then convert seconds to rotation angle.
const float    radiansPerSecond = XMConvertToRadians(m_degreesPerSecond);
const double   totalRotation = timer.GetTotalSeconds() * radiansPerSecond;
const float    radians = static_cast<float>(fmod(totalRotation, XM_2PI));
const XMMATRIX modelRotation = XMMatrixRotationY(-radians);

// Position the cube.
const XMMATRIX modelTranslation = XMMatrixTranslationFromVector(XMLoadFloat3(&m_position));

// Multiply to get the transform matrix.
// Note that this transform does not enforce a particular coordinate system. The calling
// class is responsible for rendering this content in a consistent manner.
const XMMATRIX modelTransform = XMMatrixMultiply(modelRotation, modelTranslation);

// The view and projection matrices are provided by the system; they are associated
// with holographic cameras, and updated on a per-camera basis.
// Here, we provide the model transform for the sample hologram. The model transform
// matrix is transposed to prepare it for the shader.
XMStoreFloat4x4(&m_modelConstantBufferData.model, XMMatrixTranspose(modelTransform));

Nota sobre cenários avançados: O cubo de fiação é um exemplo simples de como posicionar um holograma dentro de um único quadro de referência. Também é possível usar vários SpatialCoordinateSystems no mesmo quadro renderizado, ao mesmo tempo.

Atualizar dados de tampão constantes

As transformações de modelos para conteúdo são atualizadas como de costume. Por esta altura, já terás calculado transformações válidas para o sistema de coordenadas que vais renderizar.

De SpinningCubeRenderer::Update:

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

E as transformações de visualização e projeção? Para obter os melhores resultados, queremos esperar até estarmos quase prontos para as nossas chamadas antes de recebermos estas.

Rendere o quadro atual

Renderizar em Windows Mixed Reality não é muito diferente de renderizar em um ecrã mono 2D, mas há algumas diferenças:

  • As previsões do quadro holográfico são importantes. Quanto mais perto a previsão for quando a sua moldura for apresentada, melhor os hologramas irão parecer.
  • Windows Mixed Reality controla as vistas da câmara. Rendere para cada um porque a moldura holográfica irá apresentá-las para você mais tarde.
  • Recomendamos fazer renderização estéreo utilizando o desenho de instâncias para um conjunto de alvos de renderização. O modelo de aplicação holográfica usa a abordagem recomendada de desenho de instância para um conjunto de alvo de renderização, que usa uma visão alvo de renderização para uma Texture2DArray.
  • Se quiser renderizar sem usar a instancing estéreo, terá de criar duas RenderTargetViews não-array, uma para cada olho. Cada RenderTargetViews faz referência a uma das duas fatias da Texture2DArray fornecidas à aplicação a partir do sistema. Isto não é recomendado, pois é tipicamente mais lento do que usar a instancing.

Obtenha uma previsão do HolographicFrame atualizada

A atualização da previsão do quadro aumenta a eficácia da estabilização da imagem. Obtém-se um posicionamento mais preciso dos hologramas devido ao menor tempo entre a previsão e quando a moldura é visível para o utilizador. Atualização ideal da previsão do seu quadro pouco antes de renderizar.

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

Renderizar a cada câmara

Loop no conjunto de poses de câmara na previsão, e render para cada câmara neste conjunto.

Configurar o seu passe de renderização

Windows Mixed Reality utiliza renderização estereoscópica para aumentar a ilusão de profundidade e para tornar estereoscopicamente, de modo que tanto o ecrã esquerdo como o direito estão ativos. Com a renderização estereoscópica, há uma compensação entre as duas exibições, que o cérebro pode reconciliar como profundidade real. Esta secção abrange a renderização estereoscópica utilizando a instancing, utilizando código do modelo de aplicação holográfica Windows.

Cada câmara tem o seu próprio alvo de renderização (tampão traseiro), e matrizes de vista e projeção, para o espaço holográfico. A sua aplicação terá de criar quaisquer outros recursos baseados em câmaras - como o tampão de profundidade - numa base por câmara. No modelo de aplicação Holográfica Windows, fornecemos uma classe de ajudante para juntar estes recursos em DX::CameraResources. Comece por configurar as vistas-alvo de renderização:

A partir de 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);

Use a previsão para obter as matrizes de visualização e projeção para a câmara

As matrizes de visualização e projeção de cada câmara holográfica mudarão a cada quadro. Refresque os dados no tampão constante para cada câmara holográfica. Faça isto depois de atualizar a previsão, e antes de fazer qualquer chamada de sorteio para a câmara.

A partir de 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);

Aqui, mostramos como as matrizes são adquiridas a partir da pose da câmara. Durante este processo, também obtemos o viewport atual para a câmara. Note como fornecemos um sistema de coordenadas: este é o mesmo sistema de coordenadas que usávamos para entender o olhar, e é o mesmo que usávamos para posicionar o cubo giratório.

A partir de CameraResources::UpdateViewProjectionBuffer:

// The system changes the viewport on a per-frame basis for system optimizations.
auto viewport = cameraPose.Viewport();
m_d3dViewport = CD3D11_VIEWPORT(
    viewport.X,
    viewport.Y,
    viewport.Width,
    viewport.Height
);

// The projection transform for each frame is provided by the HolographicCameraPose.
HolographicStereoTransform cameraProjectionTransform = cameraPose.ProjectionTransform();

// Get a container object with the view and projection matrices for the given
// pose in the given coordinate system.
auto viewTransformContainer = cameraPose.TryGetViewTransform(coordinateSystem);

// If TryGetViewTransform returns a null pointer, that means the pose and coordinate
// system cannot be understood relative to one another; content cannot be rendered
// in this coordinate system for the duration of the current frame.
// This usually means that positional tracking is not active for the current frame, in
// which case it is possible to use a SpatialLocatorAttachedFrameOfReference to render
// content that is not world-locked instead.
DX::ViewProjectionConstantBuffer viewProjectionConstantBufferData;
bool viewTransformAcquired = viewTransformContainer != nullptr;
if (viewTransformAcquired)
{
    // Otherwise, the set of view transforms can be retrieved.
    HolographicStereoTransform viewCoordinateSystemTransform = viewTransformContainer.Value();

    // Update the view matrices. Holographic cameras (such as Microsoft HoloLens) are
    // constantly moving relative to the world. The view matrices need to be updated
    // every frame.
    XMStoreFloat4x4(
        &viewProjectionConstantBufferData.viewProjection[0],
        XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Left) *
            XMLoadFloat4x4(&cameraProjectionTransform.Left))
    );
    XMStoreFloat4x4(
        &viewProjectionConstantBufferData.viewProjection[1],
        XMMatrixTranspose(XMLoadFloat4x4(&viewCoordinateSystemTransform.Right) *
            XMLoadFloat4x4(&cameraProjectionTransform.Right))
    );
}

O viewport deve ser definido em cada quadro. O seu shader vértice (pelo menos) necessitará geralmente de acesso aos dados de visualização/projeção.

A partir de CameraResources::AttachViewProjectionBuffer:

// Set the viewport for this camera.
context->RSSetViewports(1, &m_d3dViewport);

// Send the constant buffer to the vertex shader.
context->VSSetConstantBuffers(
    1,
    1,
    m_viewProjectionConstantBuffer.GetAddressOf()
);

Entregue ao tampão traseiro da câmara e comprometa o tampão de profundidade:

É uma boa ideia verificar se o TryGetViewTransform conseguiu antes de tentar utilizar os dados de visualização/projeção, porque se o sistema de coordenadas não é local (por exemplo, o rastreio foi interrompido) a sua aplicação não pode renderizar com ele para esse quadro. O modelo só chama Render no cubo de fiação se a classe CameraResources indicar uma atualização bem sucedida.

Windows Mixed Reality inclui funcionalidades de estabilização de imagem para manter os hologramas posicionados onde um desenvolvedor ou utilizador os coloca no mundo. A estabilização da imagem ajuda a esconder a latência inerente a um gasoduto de renderização para garantir as melhores experiências holográficas para os utilizadores. Um ponto de foco pode ser especificado para melhorar ainda mais a estabilização da imagem, ou um tampão de profundidade pode ser fornecido para calcular a estabilização de imagem otimizada em tempo real.

Para obter melhores resultados, a sua aplicação deve fornecer um tampão de profundidade utilizando a API CommitDirect3D1DepthBuffer. Windows Mixed Reality pode então usar informações de geometria do tampão de profundidade para otimizar a estabilização da imagem em tempo real. O modelo de aplicação Holográfico Windows compromete o tampão de profundidade da aplicação por padrão, ajudando a otimizar a estabilidade do holograma.

A partir de AppMain::Render:

// Only render world-locked content when positional tracking is active.
if (cameraActive)
{
    // Draw the sample hologram.
    m_spinningCubeRenderer->Render();
    if (m_canCommitDirect3D11DepthBuffer)
    {
        // On versions of the platform that support the CommitDirect3D11DepthBuffer API, we can 
        // provide the depth buffer to the system, and it will use depth information to stabilize 
        // the image at a per-pixel level.
        HolographicCameraRenderingParameters renderingParameters =
            holographicFrame.GetRenderingParameters(cameraPose);
        
        IDirect3DSurface interopSurface =
            DX::CreateDepthTextureInteropObject(pCameraResources->GetDepthStencilTexture2D());

        // Calling CommitDirect3D11DepthBuffer causes the system to queue Direct3D commands to 
        // read the depth buffer. It will then use that information to stabilize the image as
        // the HolographicFrame is presented.
        renderingParameters.CommitDirect3D11DepthBuffer(interopSurface);
    }
}

Nota

Windows irá processar a sua textura de profundidade na GPU, por isso deve ser possível usar o seu tampão de profundidade como um recurso shader. O ID3D11Texture2D que cria deve estar num formato sem tipo e deve ser ligado como uma visão de recursos shader. Aqui está um exemplo de como criar uma textura de profundidade que pode ser comprometida para a estabilização da imagem.

Código para a criação de recursos tampão de profundidade para 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
    ));

Desenhar conteúdo holográfico

O modelo de aplicação holográfica Windows torna o conteúdo em estéreo utilizando a técnica recomendada de desenhar geometria instânciad para uma Texture2DArray do tamanho 2. Vamos ver a parte da instancing disto, e como funciona em Windows Mixed Reality.

De SpinningCubeRenderer::Render:

// Draw the objects.
context->DrawIndexedInstanced(
    m_indexCount,   // Index count per instance.
    2,              // Instance count.
    0,              // Start index location.
    0,              // Base vertex location.
    0               // Start instance location.
);

Cada instância acede a uma matriz de visão/projeção diferente do tampão constante. Aqui está a estrutura tampão constante, que é apenas uma matriz de duas matrizes.

Da VertexShaderShared.hlsl,incluído por 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];
};

O índice de matriz de destino de renderização deve ser definido para cada pixel. No seguinte corte, output.viewId é mapeado para o SV_RenderTargetArrayIndex semântico. Isto requer suporte para uma funcionalidade opcional Direct3D 11.3, que permite que o índice de matriz de ponto alvo de renderização seja definido a partir de qualquer fase de sombreamento.

De VPRTVertexShader.hlsl:

// Per-vertex data passed to the geometry shader.
struct VertexShaderOutput
{
    min16float4 pos     : SV_POSITION;
    min16float3 color   : COLOR0;

    // The render target array index is set here in the vertex shader.
    uint        viewId  : SV_RenderTargetArrayIndex;
};

Da VertexShaderShared.hlsl,incluído por VPRTVertexShader.hlsl:

// Per-vertex data used as input to the vertex shader.
struct VertexShaderInput
{
    min16float3 pos     : POSITION;
    min16float3 color   : COLOR0;
    uint        instId  : SV_InstanceID;
};

// Simple shader to do vertex processing on the GPU.
VertexShaderOutput main(VertexShaderInput input)
{
    VertexShaderOutput output;
    float4 pos = float4(input.pos, 1.0f);

    // Note which view this vertex has been sent to. Used for matrix lookup.
    // Taking the modulo of the instance ID allows geometry instancing to be used
    // along with stereo instanced drawing; in that case, two copies of each 
    // instance would be drawn, one for left and one for right.
    int idx = input.instId % 2;

    // Transform the vertex position into world space.
    pos = mul(pos, model);

    // Correct for perspective and project the vertex position onto the screen.
    pos = mul(pos, viewProjection[idx]);
    output.pos = (min16float4)pos;

    // Pass the color through without modification.
    output.color = input.color;

    // Set the render target array index.
    output.viewId = idx;

    return output;
}

Se quiser utilizar as suas técnicas de desenho de instâncias existentes com este método de desenho para um conjunto de alvos de renderização estéreo, desenhe o dobro do número de ocorrências que normalmente tem. No shader, divida input.instId por 2 para obter o ID de instância original, que pode ser indexado em (por exemplo) um tampão de dados por objeto:

Nota importante sobre a prestação de conteúdos estéreo na HoloLens

Windows Mixed Reality suporta a capacidade de definir o índice de matriz de destino de renderização a partir de qualquer estágio shader. Normalmente, esta é uma tarefa que só poderia ser feita na fase de sombreado de geometria devido à forma como a semântica é definida para Direct3D 11. Aqui, mostramos um exemplo completo de como configurar um pipeline de renderização apenas com o conjunto de estágios de sombreamento de vértice e pixel. O código shader é como descrito acima.

De SpinningCubeRenderer::Render:

const auto context = m_deviceResources->GetD3DDeviceContext();

// Each vertex is one instance of the VertexPositionColor struct.
const UINT stride = sizeof(VertexPositionColor);
const UINT offset = 0;
context->IASetVertexBuffers(
    0,
    1,
    m_vertexBuffer.GetAddressOf(),
    &stride,
    &offset
);
context->IASetIndexBuffer(
    m_indexBuffer.Get(),
    DXGI_FORMAT_R16_UINT, // Each index is one 16-bit unsigned integer (short).
    0
);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
context->IASetInputLayout(m_inputLayout.Get());

// Attach the vertex shader.
context->VSSetShader(
    m_vertexShader.Get(),
    nullptr,
    0
);
// Apply the model constant buffer to the vertex shader.
context->VSSetConstantBuffers(
    0,
    1,
    m_modelConstantBuffer.GetAddressOf()
);

// Attach the pixel shader.
context->PSSetShader(
    m_pixelShader.Get(),
    nullptr,
    0
);

// Draw the objects.
context->DrawIndexedInstanced(
    m_indexCount,   // Index count per instance.
    2,              // Instance count.
    0,              // Start index location.
    0,              // Base vertex location.
    0               // Start instance location.
);

Nota importante sobre a prestação em dispositivos não HoloLens

A definição do índice de matriz de destino de renderização no shader vértice requer que o controlador gráfico suporte uma funcionalidade opcional Direct3D 11.3, que HoloLens suporta. A sua aplicação pode implementar com segurança apenas essa técnica de renderização, e todos os requisitos serão cumpridos para correr no Microsoft HoloLens.

Pode ser que queira utilizar o emulador HoloLens também, que pode ser uma poderosa ferramenta de desenvolvimento para a sua aplicação holográfica - e apoiar Windows Mixed Reality dispositivos de auscultadores imersivos que estão ligados a computadores Windows 10. O suporte para o caminho de renderização não HoloLens - para todos os Windows Mixed Reality - também está integrado no modelo de aplicação holográfica Windows. No código de modelo, encontrará código para permitir que a sua aplicação holográfica seja executada na GPU no seu PC de desenvolvimento. Eis como a classe DeviceResources verifica este suporte opcional de funcionalidade.

A partir de DispositivosResources::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;
}

Para suportar a renderização sem esta funcionalidade opcional, a sua aplicação deve utilizar um shader de geometria para definir o índice de matriz de destino de renderização. Este snippet seria adicionado apósVSSetConstantBuffers, e antesdo PSSetShader no exemplo de código mostrado na secção anterior que explica como tornar a estéreo no HoloLens.

De SpinningCubeRenderer::Render:

if (!m_usingVprtShaders)
{
    // On devices that do not support the D3D11_FEATURE_D3D11_OPTIONS3::
    // VPAndRTArrayIndexFromAnyShaderFeedingRasterizer optional feature,
    // a pass-through geometry shader is used to set the render target 
    // array index.
    context->GSSetShader(
        m_geometryShader.Get(),
        nullptr,
        0
    );
}

Nota HLSL: Neste caso, deve também carregar um shader vértuoso ligeiramente modificado que passa o índice de matriz de características de renderização para o shader de geometria utilizando uma semântica shader sempre permitida, como o TEXCOORD0. O shader de geometria não tem que fazer nenhum trabalho; o shader de geometria do modelo passa por todos os dados, com exceção do índice de matriz de destino de renderização, que é usado para definir o SV_RenderTargetArrayIndex semântico.

Código de modelo de aplicativo para GeometryShader.hlsl:

// Per-vertex data from the vertex shader.
struct GeometryShaderInput
{
    min16float4 pos     : SV_POSITION;
    min16float3 color   : COLOR0;
    uint instId         : TEXCOORD0;
};

// Per-vertex data passed to the rasterizer.
struct GeometryShaderOutput
{
    min16float4 pos     : SV_POSITION;
    min16float3 color   : COLOR0;
    uint rtvId          : SV_RenderTargetArrayIndex;
};

// This geometry shader is a pass-through that leaves the geometry unmodified 
// and sets the render target array index.
[maxvertexcount(3)]
void main(triangle GeometryShaderInput input[3], inout TriangleStream<GeometryShaderOutput> outStream)
{
    GeometryShaderOutput output;
    [unroll(3)]
    for (int i = 0; i < 3; ++i)
    {
        output.pos   = input[i].pos;
        output.color = input[i].color;
        output.rtvId = input[i].instId;
        outStream.Append(output);
    }
}

Presente

Permita que o quadro holográfico apresente a cadeia de swaps

Com Windows Mixed Reality, o sistema controla a cadeia de swaps. Em seguida, o sistema gere a apresentação de quadros em cada câmara holográfica para garantir uma experiência de utilizador de alta qualidade. Também fornece uma atualização do viewport em cada quadro, para cada câmara, para otimizar aspetos do sistema como a estabilização de imagem ou a Captura de Realidade Mista. Assim, uma aplicação holográfica usando DirectX não chama Present numa cadeia de swaps DXGI. Em vez disso, usa-se a classe HolographicFrame para apresentar todas as poltronas para uma moldura uma vez que tenha terminado de desenhá-la.

A partir de DeviceResources::P resent:

HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();

Por predefinição, esta API aguarda que o quadro termine antes de regressar. As aplicações holográficas devem esperar que o quadro anterior termine antes de iniciar os trabalhos num novo quadro, porque isso reduz a latência e permite melhores resultados a partir das previsões do quadro holográfico. Esta não é uma regra difícil, e se tiver quadros que demoram mais do que uma atualização de ecrã para tornar-se possível desativar esta espera passando o parâmetro HolographicFramePresentWaitBehavior para PresentUsingCurrentPrediction. Neste caso, é provável que utilize um fio de renderização assíncronos para manter uma carga contínua na GPU. A taxa de atualização do dispositivo HoloLens é de 60 hz, onde uma moldura tem uma duração de aproximadamente 16 ms. Os dispositivos imersivos dos auscultadores podem variar entre 60 hz e 90 hz; ao refrescar o ecrã a 90 hz, cada armação terá uma duração de aproximadamente 11 ms.

Lidar com dispositivosLost cenários em cooperação com o HolographicFrame

As aplicações DirectX 11 normalmente gostariam de verificar o HRESULT devolvido pela função Presente da cadeia de swaps DXGI para saber se houve um erro do DeviceLost. A classe HolographicFrame trata disto para si. Inspecione o HolographicFramePresentResult devolvido para saber se precisa de lançar e recriar o dispositivo Direct3D e os recursos baseados no dispositivo.

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

Se o dispositivo Direct3D foi perdido e o recriou, tem de dizer ao HolographicSpace para começar a usar o novo dispositivo. A cadeia de swaps será recriada para este dispositivo.

A partir de DispositivosResources::InitializeUsingHolographicSpace:

m_holographicSpace.SetDirect3D11Device(m_d3dInteropDevice);

Uma vez apresentada a sua moldura, pode voltar ao circuito principal do programa e permitir que continue até ao quadro seguinte.

PCs gráficos híbridos e aplicações de realidade mista

Atualização para Criativos do Windows 10 PCs podem ser configurados com GPUs discretos e integrados. Com este tipo de computadores, Windows escolherá o adaptador ao qual os auscultadores estão ligados. As aplicações devem garantir que o dispositivo DirectX que cria utiliza o mesmo adaptador.

A maioria do código de amostra direct3D geral demonstra a criação de um dispositivo DirectX utilizando o adaptador de hardware predefinido, que num sistema híbrido pode não ser o mesmo que o utilizado para os auscultadores.

Para resolver quaisquer problemas, utilize o HolographicAdapterID de qualquer espaço holográfico. PrimaryAdapterId() ou HolographicDisplay. AdapterId(). Este adaptadorId pode então ser utilizado para selecionar o DXGIAdapter direito utilizando o IDXGIFactory4.EnumAdapterByLuid.

A partir de DispositivosResources::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();
}

Código para atualizar DispositivosResources::CreateDeviceResources para utilizar 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.
);

Gráficos híbridos e Media Foundation

A utilização da Media Foundation em sistemas híbridos pode causar problemas em que o vídeo não rendere ou a textura de vídeo são corruptas porque a Media Foundation está a falhar com um comportamento do sistema. Em alguns cenários, é necessário criar um ID3D11Device separado para suportar o multi-threading e as bandeiras de criação corretas são definidas.

Ao rubricar o ID3D11Device, D3D11_CREATE_DEVICE_VIDEO_SUPPORT bandeira deve ser definida como parte do D3D11_CREATE_DEVICE_FLAG. Assim que o dispositivo e o contexto estiverem criados, ligue para o SetMultithreadProtected para ativar a multi-leitura. Para associar o dispositivo ao IMFDXGIDeviceManager,utilize a função IMFDXGIDeviceManager::ResetDevice.

Código para associar um ID3D11Device ao 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;

Ver também