DirectX 中的呈現
注意
本文與舊版 WinRT 原生 API 相關。 針對新的原生應用程式專案,我們建議使用 OpenXR API。
Windows Mixed Reality是以 DirectX 為基礎,為使用者產生豐富的 3D 圖形化體驗。 轉譯抽象概念位於 DirectX 上方,可讓應用程式推斷系統所預測全像攝影場景觀察者的位置和方向。 開發人員接著可以根據每個相機找出全像投影,讓應用程式在使用者四處移動時,在各種空間座標系統中轉譯這些全像投影。
注意:本逐步解說說明 Direct3D 11 中的全像攝影轉譯。 Direct3D 12 Windows Mixed Reality應用程式範本也隨附Mixed Reality應用程式範本延伸模組。
目前框架的更新
若要更新全像投影的應用程式狀態,每個畫面一次,應用程式將會:
- 從顯示管理系統取得 全像攝影框架 。
- 使用呈現完成時,相機檢視的目前預測來更新場景。 請注意,全像攝影場景可以有多個相機。
若要轉譯至全像攝影相機檢視,每個畫面一次,應用程式將會:
- 針對每個相機,使用來自系統的相機檢視和投影矩陣,呈現目前畫面的場景。
建立新的全像攝影畫面並取得其預測
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 全像攝影應用程式範本會使用 StepTimer 實作,類似于 DirectX 11 UWP 應用程式範本中提供的 StepTimer 。 這個 StepTimer 範例協助程式類別可以提供固定的時間步驟更新、可變時間步驟更新,而預設模式是可變時間步驟。
針對全像攝影轉譯,我們選擇不要將太多放在計時器函式中,因為您可以將它設定為固定時間步驟。 針對某些畫面,每個畫面可能會呼叫一次以上,而我們的全像攝影資料更新應該在每個畫面上執行一次。
從 AppMain::Update:
m_timer.Tick([this]()
{
m_spinningCubeRenderer->Update(m_timer);
});
在您的座標系統中放置和旋轉全像投影
如果您是在單一座標系統中運作,因為範本與 SpatialStationaryReferenceFrame一樣,此程式與您在 3D 圖形中使用的程式不同。 在這裡,我們會旋轉 Cube,並根據固定座標系統中的位置來設定模型矩陣。
從 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上的轉譯與 2D 單聲道顯示器上的轉譯不同,但有幾個差異:
- 全像攝影畫面預測很重要。 預測越接近顯示畫面時,全像投影看起來越好。
- Windows Mixed Reality控制相機檢視。 轉譯至每一個畫面,因為全像攝影畫面稍後會為您呈現。
- 我們建議使用實例繪圖對轉譯目標陣列執行立體轉譯。 全像攝影應用程式範本會使用具現化繪圖的建議方法到轉譯目標陣列,其會使用轉譯目標檢視到 Texture2DArray。
- 如果您想要在不使用立體實例的情況下轉譯,您必須為每個眼睛建立兩個非陣列 RenderTargetViews。 每個 RenderTargetViews 都會參考從系統提供給應用程式的 Texture2DArray 中兩個配量之一。 不建議這麼做,因為它通常比使用實例慢。
取得更新的全像攝影框架預測
更新畫面預測可增強影像防震的有效性。 由於預測與使用者可以看到框架之間的較短時間,所以您會取得更精確的全像投影位置。 在轉譯之前,最好先更新畫面預測。
holographicFrame.UpdateCurrentPrediction();
HolographicFramePrediction prediction = holographicFrame.CurrentPrediction();
轉譯至每個相機
迴圈顯示預測中的相機集合,並轉譯至此集合中的每個相機。
設定您的轉譯階段
Windows Mixed Reality使用立體轉譯來增強深度的錯覺,並以立體方式轉譯,因此左右顯示器都是使用中。 使用立體轉譯時,兩個顯示器之間會有位移,而大腦可以協調為實際深度。 本節涵蓋使用實例的立體轉譯,以及使用 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);
在這裡,我們會示範如何從相機姿勢取得矩陣。 在此程式中,我們也會取得相機的目前檢視區。 請注意我們如何提供座標系統:這是我們用來瞭解注視的相同座標系統,而且是用來放置旋轉 Cube 的相同座標系統。
從 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類別指出更新成功,範本只會在旋轉 Cube 上呼叫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);
}
}
注意
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.
);
每個實例都會從常數緩衝區存取不同的檢視/投影矩陣。 以下是常數緩衝區結構,這只是兩個矩陣的陣列。
從 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 裝置上轉譯的重要注意事項
在頂點著色器中設定轉譯目標陣列索引,需要圖形驅動程式支援 HoloLens 支援的選擇性 Direct3D 11.3 功能。 您的應用程式可以安全地實作該技術來轉譯,而且所有需求都將符合在Microsoft HoloLens上執行。
您可能也想要使用 HoloLens 模擬器,這可以是全像攝影應用程式的強大開發工具,並支援附加至Windows 10電腦的Windows Mixed Reality沉浸式頭戴式裝置。 所有Windows Mixed Reality的非 HoloLens 轉譯路徑支援也內建于 Windows 全像攝影應用程式範本中。 在範本程式碼中,您會發現程式碼可讓您的全像攝影應用程式在開發電腦上的 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,系統會控制交換鏈結。 系統接著會管理將畫面呈現給每個全像攝影機,以確保高品質的使用者體驗。 它也提供檢視區更新每個相機的每個畫面,以優化系統的各個層面,例如影像防震或Mixed Reality擷取。 因此,使用 DirectX 的全像攝影應用程式不會在 DXGI 交換鏈結上呼叫 Present 。 相反地,您會使用 HolographicFrame 類別,在完成繪圖後,呈現框架的所有交換鏈。
從 DeviceResources::P resent:
HolographicFramePresentResult presentResult = frame.PresentUsingCurrentPrediction();
根據預設,此 API 會等候框架在傳回之前完成。 全像攝影應用程式應該等到上一個畫面完成,再開始在新畫面上工作,因為這可減少延遲,並允許從全像攝影畫面預測取得更好的結果。 這不是硬式規則,而且如果您有超過一個螢幕重新整理才能轉譯的畫面,您可以將 HolographicFramePresentWaitBehavior 參數傳遞至 PresentUsingCurrentPrediction來停用此等候。 在此情況下,您可能會使用非同步轉譯執行緒來維護 GPU 上的連續負載。 HoloLens 裝置的重新整理速率為 60 hz,其中一個畫面的持續時間大約為 16 毫秒。 沉浸式頭戴裝置的範圍可以從 60 hz 到 90 hz;在 90 hz 重新整理顯示器時,每個畫面的持續時間大約為 11 毫秒。
處理與 HolographicFrame 合作的 DeviceLost 案例
DirectX 11 應用程式通常會想要檢查 DXGI 交換鏈結的 Present 函式傳回的 HRESULT,以瞭解是否有 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);
顯示框架之後,您可以返回主要程式迴圈,並允許它繼續下一個畫面。
混合式圖形電腦和混合實境應用程式
Windows 10 Creators Update電腦可以使用離散和整合式 GPU 來設定。 透過這些類型的電腦,Windows 會選擇頭戴式裝置所連接的介面卡。 應用程式必須確保所建立的 DirectX 裝置使用相同的介面卡。
大部分的 Direct3D 範例程式碼示範如何使用預設硬體介面卡建立 DirectX 裝置,而混合式系統上的裝置可能與頭戴式裝置所使用的裝置不同。
若要解決任何問題,請使用HolographicSpace中的HolographicAdapterID。PrimaryAdapterId () 或HolographicDisplay。AdapterId () 。 接著,您可以使用此 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();
}
更新 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 預設為系統行為。 在某些情況下,需要建立個別的 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;