了解如何在使用 Direct3D 生成的通用 Windows 平台(UWP)应用中使用多重采样。 多重采样(也称为多重采样抗锯齿)是一种图形技术,用于减少锯齿边缘。 它的工作原理是绘制比实际在最终呈现目标中更多的像素,然后求平均值,以保持某些像素中“部分”边缘的外观。 有关多重采样在 Direct3D 中实际工作原理的详细说明,请参阅 多重采样反锯齿光栅化规则。
多重采样和翻转模型交换链
使用 DirectX 的 UWP 应用必须使用翻转模型交换链。 翻转模型交换链不直接支持多重采样,但仍可以通过将场景渲染到多重采样渲染目标视图,然后在显示之前将多重采样渲染目标解析到后缓冲区,来实现多重采样。 本文介绍向 UWP 应用添加多重采样所需的步骤。
如何使用多重采样
Direct3D 功能级别保证对特定、最小样本计数功能的支持,并保证某些缓冲区格式支持多重采样。 图形设备通常支持比所需的最小格式和样本计数范围更广。 可以通过在运行时检查特定 DXGI 格式下的多重采样功能支持,然后核实每种受支持格式中可以使用的采样数,从而确定多重采样支持。
调用 ID3D11Device::CheckFeatureSupport,找出哪些 DXGI 格式可用于多重采样。 提供游戏可以使用的呈现目标格式。 呈现目标和解析目标必须使用相同的格式,因此请检查 D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET 和 D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE。
**功能级别 9:**尽管功能级别 9 设备 保证支持多采样呈现目标格式,但不保证支持多采样解析目标。 因此,在尝试使用本主题中所述的多重采样技术之前,必须进行此检查。
以下代码检查对所有DXGI_FORMAT值的多重采样支持:
// Determine the format support for multisampling. for (UINT i = 1; i < DXGI_FORMAT_MAX; i++) { DXGI_FORMAT inFormat = safe_cast<DXGI_FORMAT>(i); UINT formatSupport = 0; HRESULT hr = m_d3dDevice->CheckFormatSupport(inFormat, &formatSupport); if ((formatSupport & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE) && (formatSupport & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET) ) { m_supportInfo->SetFormatSupport(i, true); } else { m_supportInfo->SetFormatSupport(i, false); } }
对于每个受支持的格式,通过调用 ID3D11Device::CheckMultisampleQualityLevels来查询示例计数支持。
以下代码检查支持的 DXGI 格式的采样大小支持情况:
// Find available sample sizes for each supported format. for (unsigned int i = 0; i < DXGI_FORMAT_MAX; i++) { for (unsigned int j = 1; j < MAX_SAMPLES_CHECK; j++) { UINT numQualityFlags; HRESULT test = m_d3dDevice->CheckMultisampleQualityLevels( (DXGI_FORMAT) i, j, &numQualityFlags ); if (SUCCEEDED(test) && (numQualityFlags > 0)) { m_supportInfo->SetSampleSize(i, j, 1); m_supportInfo->SetQualityFlagsAt(i, j, numQualityFlags); } } }
注意 如果需要检查对平铺资源缓冲区的多采样支持,请改用 ID3D11Device2::CheckMultisampleQualityLevels1。
创建具有所需样本数量的缓冲区和呈现目标视图。 使用与交换链相同的DXGI_FORMAT、宽度和高度,但指定大于 1 的样本计数并使用多重采样纹理维度(例如D3D11_RTV_DIMENSION_TEXTURE2DMS)。 如有必要,可以使用最适合多重采样的新设置重新创建交换链。
以下代码创建一个多采样渲染目标:
float widthMulti = m_d3dRenderTargetSize.Width; float heightMulti = m_d3dRenderTargetSize.Height; D3D11_TEXTURE2D_DESC offScreenSurfaceDesc; ZeroMemory(&offScreenSurfaceDesc, sizeof(D3D11_TEXTURE2D_DESC)); offScreenSurfaceDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; offScreenSurfaceDesc.Width = static_cast<UINT>(widthMulti); offScreenSurfaceDesc.Height = static_cast<UINT>(heightMulti); offScreenSurfaceDesc.BindFlags = D3D11_BIND_RENDER_TARGET; offScreenSurfaceDesc.MipLevels = 1; offScreenSurfaceDesc.ArraySize = 1; offScreenSurfaceDesc.SampleDesc.Count = m_sampleSize; offScreenSurfaceDesc.SampleDesc.Quality = m_qualityFlags; // Create a surface that's multisampled. DX::ThrowIfFailed( m_d3dDevice->CreateTexture2D( &offScreenSurfaceDesc, nullptr, &m_offScreenSurface) ); // Create a render target view. CD3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc(D3D11_RTV_DIMENSION_TEXTURE2DMS); DX::ThrowIfFailed( m_d3dDevice->CreateRenderTargetView( m_offScreenSurface.Get(), &renderTargetViewDesc, &m_d3dRenderTargetView ) );
深度缓冲区必须具有相同的宽度、高度、样本计数和纹理维度,以匹配多重采样的呈现目标。
以下代码创建多采样深度缓冲区:
// Create a depth stencil view for use with 3D rendering if needed. CD3D11_TEXTURE2D_DESC depthStencilDesc( DXGI_FORMAT_D24_UNORM_S8_UINT, static_cast<UINT>(widthMulti), static_cast<UINT>(heightMulti), 1, // This depth stencil view has only one texture. 1, // Use a single mipmap level. D3D11_BIND_DEPTH_STENCIL, D3D11_USAGE_DEFAULT, 0, m_sampleSize, m_qualityFlags ); ComPtr<ID3D11Texture2D> depthStencil; DX::ThrowIfFailed( m_d3dDevice->CreateTexture2D( &depthStencilDesc, nullptr, &depthStencil ) ); CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2DMS); DX::ThrowIfFailed( m_d3dDevice->CreateDepthStencilView( depthStencil.Get(), &depthStencilViewDesc, &m_d3dDepthStencilView ) );
现在是创建视区的好时机,因为视区宽度和高度也必须与呈现目标匹配。
以下代码创建视口:
// Set the 3D rendering viewport to target the entire window. m_screenViewport = CD3D11_VIEWPORT( 0.0f, 0.0f, widthMulti / m_scalingFactor, heightMulti / m_scalingFactor ); m_d3dContext->RSSetViewports(1, &m_screenViewport);
将每一帧渲染到多重采样的渲染目标。 呈现完成后,请在呈现帧之前调用 ID3D11DeviceContext::ResolveSubresource 。 这指示 Direct3D 执行多重采样作,计算每个像素的值以显示并将结果放入后台缓冲区。 然后,后退缓冲区包含最终的抗锯齿图像,并可以显示。
以下代码在呈现帧之前解析子资源:
if (m_sampleSize > 1) { unsigned int sub = D3D11CalcSubresource(0, 0, 1); m_d3dContext->ResolveSubresource( m_backBuffer.Get(), sub, m_offScreenSurface.Get(), sub, DXGI_FORMAT_B8G8R8A8_UNORM ); } // The first argument instructs DXGI to block until VSync, putting the application // to sleep until the next VSync. This ensures that we don't waste any cycles rendering // frames that will never be displayed to the screen. hr = m_swapChain->Present(1, 0);