DirectX でのレンダリング

Note

この記事は、従来のWinRTネイティブAPIに関連します。 新しいネイティブ アプリ プロジェクトの場合は、OpenXR API を使用することをお勧めします。

Windows Mixed Reality は、ユーザーのリッチな 3D グラフィカル エクスペリエンスを生成するために、DirectX に基づいてビルドされます。 レンダリング アブストラクションは、DirectX の上に配置されます。これにより、アプリは、システムによって予測されるホログラフィック シーン オブザーバーの位置と方向を推測できます。 その後、開発者は各カメラに基づいてホログラムの位置を指定でき、それにより、ユーザーが動き回るときにさまざまな空間座標系でこれらのホログラムをアプリにレンダリングさせることができます。

注:このチュートリアルでは、Direct3D 11 でのホログラフィック レンダリングについて説明します。 Mixed Reality アプリ テンプレートの拡張機能と共に、Direct3D 12 Windows Mixed Reality アプリテンプレートも提供されています。

現在のフレームの更新

ホログラムの適用状態を更新するために、アプリはフレームごとに1回、以下を行います。

  • 表示管理システムから HolographicFrame を取得します。
  • レンダリング完了時のカメラビューの位置を現時点で予測することでシーンを更新します。 ホログラフィック シーンには複数のカメラが存在する可能性があることに注意してください。

ホログラフィック カメラ ビューにレンダリングするために、アプリはフレームごとに 1 回、以下を行います。

  • カメラごとに、システムからのカメラ ビューおよびプロジェクションのマトリックスを使用して、現在のフレームにシーンをレンダリングします。

新しいホログラフィック フレームを作成とその予測の取得

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 ホログラフィック アプリ テンプレートでは、DirectX 11 UWP アプリ テンプレートで提供される StepTimer と同様の StepTimer の実装が使用されています。 この StepTimer サンプルのヘルパー クラスは、固定タイムステップ更新、可変タイムステップ更新を提供できます。既定のモードは可変タイムステップです。

ホログラフィック レンダリングでは、タイマー関数の構成で固定タイム ステップを選択できるため、これにはあまり多くの設定を設けていません。 更新は、フレームごとに複数回呼び出される場合もあれば、一部のフレームではまったく呼び出されない場合もあります。当社のホログラフィックデータでは、フレームごとに1回更新されます。

AppMain::Update から:

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

座標系でのホログラムの位置決めと回転

テンプレートで SpatialStationaryReferenceFrame を実行する場合と同様に、1 つの座標系で操作を行う場合、この処理は 3D グラフィックスで行っていたものと変わりません。 ここでは、固定座標系の位置に基づいてキューブを回転させ、モデル マトリックスを設定します。

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

高度なシナリオに関する注意:回転キューブは、1 つの基準座標系内にホログラムを位置決めするためのシンプルな方法の一例です。 また、同じレンダリング対象のフレーム内で、同時に複数の 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 でのレンダリングは、2D モノ ディスプレイでのレンダリングと大差ありませんが、いつくかの違いがあります。

  • ホログラフィック フレームの予測が重要です。 予測がフレームを表示するタイミングに近いほど、ホログラムの見栄えが良くなります。
  • Windows Mixed Reality がカメラ ビューをコントロールします。 ホログラフィック フレームで、後々それらのビューが表示されるため、各ビューに対してレンダリングします。
  • レンダー ターゲット配列に対してインスタンス化された描画を使用してステレオ レンダリングを実行することをお勧めします。 ホログラフィック アプリ テンプレートは、レンダー ターゲット配列に対するインスタンス化された描画に、推奨されたアプローチを使用します。このアプローチでは、Texture2DArray に対してレンダー ターゲット ビューが使用されます。
  • ステレオ インスタンス化を使用せずにレンダリングを行う場合、非配列のRenderTargetView を 2 つ(目ごとに 1 つ)作成する必要があります。 各 RenderTargetView は、システムからアプリに提供されるTexture2DArray における 2 つのスライスのうちの 1 つを参照します。 これは通常インスタンス化を使用する場合よりも遅くなるため、お勧めしません。

更新された HolographicFrame の予測の取得

フレーム予測を更新することで、画像安定化の有効性を向上させることができます。 予測からフレームがユーザーに表示されるまでの時間が短くなるため、ホログラムをより正確に位置決めできます。 レンダリングの直前にフレーム予測を更新するのが理想的です。

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

カメラごとにレンダリング

予測においてカメラ ポーズのセットをループし、このセット内のカメラごとにレンダリングします。

レンダリング パスを設定します

Windows Mixed Reality では、深さの錯覚を強化し、立体的にレンダリングするうえで、ステレオ スコピック レンダリングが使用されるため、左と右の両方のディスプレイがアクティブになります。 ステレオ スコピック レンダリングでは、2 つのディスプレイの間にオフセットがあります。脳はこれを実際の奥行として認識できます。 このセクションでは、インスタンス化を使用し、Windows ホログラフィック アプリ テンプレートからのコードを使用したステレオ スコピック レンダリングについて説明します。

各カメラには、ホログラフィック空間に対する独自のレンダー ターゲット(バック バッファー)と、ビューおよびプロジェクションのマトリックスがあります。 アプリは、深度バッファーなど、その他のカメラ ベースのリソースをカメラごとに作成する必要があります。 Windows ホログラフィック アプリ テンプレートでは、これらのリソースを互いに 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);

予測を使用して、カメラのビューおよびプロジェクションのマトリックを取得します。

各ホログラフィック カメラのビューおよびプロジェクションのマトリックスは、フレームごとに変更されます。 ホログラフィック カメラごとに定数バッファー内のデータを更新します。 これは、予測を更新した後、そのカメラのいずれかの描画呼び出しを行う前に行います。

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 が成功しているかどうかを確認することをお勧めします。 CameraResources クラスが更新の成功を示す場合、このテンプレートは、回転キューブに対してのみ Render を呼び出します。

Windows Mixed Reality には、開発者やユーザーが実世界でホログラムを配置した場所にそれらのホログラムを常に位置決めするための画像安定化機能が含まれます。 画像安定化は、レンダリング パイプライン固有の遅延を隠し、ユーザーにとって最高のホログラフィック エクスペリエンスを保証することに役立ちます。 画像安定化をさらに強化するには、フォーカス ポイントを指定するか、または深度バッファーを提供して最適な画像安定化をリアル タイムで計算することができます。

最良の結果を得るために、アプリでは CommitDirect3D11DepthBuffer API を使用して深度バッファーを提供する必要があります。 Windows Mixed Reality では、深度バッファーからのジオメトリ情報を使用して、リアルタイムで画像安定化を最適化できます。 Windows ホログラフィック アプリ テンプレートは、既定でアプリの深度バッファーをコミットし、ホログラム安定化の最適化を支援します。

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

Note

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 ホログラフィック アプリ テンプレートは、インスタンス化されたジオメトリをサイズ 2 の Texture2DArray に描画する推奨手法を使用して、コンテンツをステレオでレンダリングします。 これのインスタンス化の部分と、それが 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.
);

各インスタンスは、定数バッファーから異なるビュー/プロジェクション マトリックスにアクセスします。 定数バッファーの構造は次のとおり、2 つのマトリックスの配列にすぎません。

VPRTVertexShader.hlsl に含まれる VertexShaderShared.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;
};

VPRTVertexShader.hlsl に含まれる VertexShaderShared.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;
}

ステレオ レンダー ターゲット配列に描画するこの方法と共に、既存のインスタンス化描画手法を使用する場合、通常あるインスタンスの 2 倍の数のインスタンスを描画します。 シェーダーにおいて、input.instId を 2 で除算することで元のインスタンス ID を取得し、その ID をインデックス付けして、(たとえば)オブジェクトごとのデータのバッファーに格納できます。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 10 PC に接続されている Windows Mixed Reality イマーシブ ヘッドセット デバイスをサポートしています。 非 HoloLens レンダリング パスのサポート(すべての Windows Mixed Reality に対して)も、Windows ホログラフィック アプリ テンプレートに組み込まれます。 テンプレート コードには、開発用 PC の 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;
}

このオプション機能を使用しないでレンダリングをサポートするには、アプリではジオメトリ シェーダーを使用してレンダー ターゲット配列インデックスを設定する必要があります。 このスニペットは、HoloLens でステレオ レンダリングを実行する方法について説明した前のセクションで示したコード例における VSSetConstantBuffersPSSetShaderに追加します。

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を使用すると、システムでスワップ チェーンがコントロールされます。 その後、システムでは、高品質のユーザー エクスペリエンスを保証するために、各ホログラフィック カメラへのフレームの表示が管理されます。 また、各カメラに対してフレームごとにビュー ポートの更新を提供して、画像安定化や Mixed Reality キャプチャなどのシステムの態様を最適化します。 そのため、DirectX を使用するホログラフィック アプリは、DXGI スワップチェーンで Present を呼び出しません。 代わりに、HolographicFrame クラスを使用して、フレームの描画を行った後にフレームのすべてのスワップ チェーンを表示します。

DeviceResources::Present から:

HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();

既定では、この API はフレームが終了するまで待機してから戻ります。 ホログラフィック アプリは、新しいフレームに対する作業を開始する前に、前のフレームが終了するまで待機する必要があり、これによって遅延を短縮し、ホログラフィック フレームの予測からより正確な結果を得ることが可能になります。 これは厳密なルールではありません。レンダリングのために 1 つの画面をリフレッシュするよりも長い時間がかかるフレームがある場合、HolographicFramePresentWaitBehavior パラメーターをPresentUsingCurrentPrediction に渡すことで、この待機を無効にできます。 この場合では、GPU の継続的な負荷を維持するために非同期レンダリング スレッドを使用する可能性があります。 HoloLens デバイスの更新速度は 60 hzであり、この場合、1 つのフレームの持続時間は約 16 ミリ秒です。 イマーシブ ヘッドセット デバイスは、60 hz から 90 hz の範囲であり、90 hz でディスプレイを更新する場合、各フレームの持続時間は約 11 ミリ秒になります。

HolographicFrame と連携して DeviceLost シナリオを処理する

DirectX 11 アプリは、通常、DeviceLost エラーが発生したかどうかを見つけるするために、DXGI スワップ チェーンの Present 関数によって返された HRESULT を確認します。 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);

フレームが表示されると、メイン プログラム ループに戻り、次のフレームに進むことを許可できます。

ハイブリッド グラフィックス PC および Mixed Reality アプリケーション

Windows 10 Creators Update PC は、個別の GPU と統合 GPU の両方で構成できます。 これらのタイプのコンピューターでは、Windows によってヘッドセットが接続されているアダプターが選択されます。 アプリケーションで作成される DirectX デバイスには、同じアダプターが使用されるようにする必要があります。

最も一般的な Direct3D サンプル コードは、既定のハードウェア アダプターを使用して DirectX デバイスを作成することを示し、ハイブリッド システムの既定のハードウェア アダプターは、ヘッドセットに使用されるものとは同じではない場合があります。

問題を回避するために、HolographicSpace.PrimaryAdapterId() または HolographicDisplay.AdapterId() のいずれかからの Holographic​Adapter​ID を使用します。 この adapterId を使用することで、IDXGIFactory4.EnumAdapterByLuid を使用する適切な DXGIAdapter を選択できます。

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

IDXGIAdapter を使用するように DeviceResources::CreateDeviceResources を更新するためのコード

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

ハイブリッド グラフィックスおよびメディア ファンデーション

ハイブリッド システムでメディア ファンデーションを使用すると、メディア ファンデーションがデフォルトでシステム挙動を行うため、ビデオがレンダリングされないか、またはビデオ テクスチャが破損するという問題が発生する可能性があります。 シナリオによっては、マルチ スレッドをサポートするには、個別の 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;

関連項目