通用 Windows 平台 (UWP) 应用中的多重采样

了解如何在使用 Direct3D 生成的通用 Windows 平台 (UWP) 应用中使用多重采样。 多重采样(也称为多重采样抗锯齿)是一种图形技术,用于减少锯齿边缘的显示。 它的工作方式是绘制比最终渲染目标中包含的实际像素更多的像素,然后取平均值以保留某些像素的“局部”边缘的显示。 有关多重采样在 Direct3D 中的实际工作方式的详细说明,请参阅多重采样抗锯齿光栅化规则

多重采样和翻转模型交换链

使用 DirectX 的 UWP 应用必须使用翻转模型交换链。 翻转模型交换链不直接支持多重采样,但仍然可以使用不同方式应用多重采样,方法是将场景渲染到多重采样的渲染目标视图,然后在呈现前将多重采样的渲染目标解析到后台缓冲区。 本文介绍将多重采样添加到 UWP 应用所需的步骤。

如何使用多重采样

Direct3D 功能级别保证支持特定的最小样本计数功能,并保证某些支持多重采样的缓冲区格式可用。 图形设备支持的格式和样本计数通常多于最低要求。 通过检查具有特定 DXGI 格式的多重采样支持功能,然后检查可以与每个支持的格式一起使用的样本计数,你可以在运行时确定多重采样支持。

  1. 调用 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);
        }
    }
    
  2. 对于每个支持的格式,通过调用 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

     

  3. 创建具有所需样本计数的缓冲区和呈现目标视图。 使用与交换链相同的 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
        )
        );
    
  4. 深度缓冲区必须具有相同的宽度、高度、样本计数和纹理大小以匹配多重采样渲染目标。

    以下代码将创建多重采样深度缓冲区:

    // 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
        )
        );
    
  5. 现在是创建视口的最佳时间,因为视口宽度和高度必须也匹配渲染目标。

    以下代码将创建视口:

    // 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);
    
  6. 将每个帧渲染到多重采样渲染目标。 完成渲染后,先调用 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);