DirectX 中的呈現
注意
本文與舊版 WinRT 原生 API 相關。 針對新的原生應用程式項目,我們建議使用 OpenXR API。
Windows Mixed Reality 是以 DirectX 為基礎,為用戶產生豐富的 3D 圖形化體驗。 轉譯抽象概念位於 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 全像攝影應用程式範本會使用 StepTimer 實作,類似於 DirectX 11 UWP 應用程式範本中提供的 StepTimer。 這個 StepTimer 範例協助程式類別可以提供固定的時間步驟更新、可變時間步驟更新,而預設模式則是可變時間步驟。
針對全像攝影轉譯,我們選擇不要將太多放在定時器函式中,因為您可以將它設定為固定的時間步驟。 針對某些畫面,每個畫面可能會呼叫一次以上,或根本沒有呼叫一次,而且我們的全像攝影數據更新應該在每個畫面發生一次。
從 AppMain::Update:
m_timer.Tick([this]()
{
m_spinningCubeRenderer->Update(m_timer);
});
在座標系統中放置和旋轉全像投影
如果您是在單一座標系統中作業,如同範本使用 SpatialStationaryReferenceFrame,此程式與您在 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));
注意進階案例: 旋轉 Cube 是如何在單一參考框架內放置全像投影的簡單範例。 您也可以 同時在同一個轉譯框架中使用多個 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。 每個 RenderTargetView 都會參考從系統提供給應用程式的 Texture2DArray 中兩個配量中的其中一個。 不建議這麼做,因為它通常比使用實例慢。
取得更新的 HolographicFrame 預測
更新畫面格預測可增強影像防震的有效性。 由於預測與使用者看到框架之間的時間較短,因此您會取得更精確的全像投影定位。 在轉譯之前,最好先更新畫面格預測。
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);
在這裡,我們會示範如何從相機姿勢取得矩陣。 在此程式中,我們也會取得相機目前的檢視區。 請注意我們如何提供座標系統:這是我們用來了解註視的相同座標系統,而且它是我們用來放置旋轉立方體相同的座標系統。
從 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 沉浸式頭戴式裝置。 非 HoloLens 轉譯路徑的支援 - 適用於所有 Windows Mixed Reality - 也內建於 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,系統會控制交換鏈結。 系統接著會管理向每個全像攝影相機呈現畫面,以確保高品質的用戶體驗。 它也提供檢視區更新每個相機的每個畫面,以優化系統的各個層面,例如影像穩定或混合實境擷取。 因此,使用 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 來選取正確的 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 預設為系統行為。 在某些情況下,需要建立個別的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;