multisampling i appar för UWP (Universal Windows Platform)

Lär dig hur du använder multisampling i UWP-appar (Universal Windows Platform) som skapats med Direct3D. Multisampling, även kallat antialias med flera exempel, är en grafikteknik som används för att minska utseendet på aliaskanter. Det fungerar genom att rita fler bildpunkter än vad som faktiskt finns i det slutliga återgivningsmålet, och sedan medelvärdesvärden för att upprätthålla utseendet på en "partiell" kant i vissa bildpunkter. En detaljerad beskrivning av hur multisampling faktiskt fungerar i Direct3D finns i Rasteriseringsregler för flersampling mot alias.

Multisampling och byteskedjan för flip-modellen

UWP-appar som använder DirectX måste använda flipmodell-växlingskedjor. Flipmodells växlingskedjor stöder inte multisampling direkt, men multisampling kan fortfarande tillämpas på ett annat sätt genom att rendera scenen till en flersamplerad renderingsmålsvy och sedan lösa det flersamplade renderingsmålet till bakbufferten innan den presenteras. I den här artikeln beskrivs de steg som krävs för att lägga till multisampling i UWP-appen.

Så här använder du multisampling

Direct3D-funktionsnivåer garanterar stöd för specifika, minsta exempelantalsfunktioner och garanterar att vissa buffertformat är tillgängliga som stöder multisampling. Grafikenheter stöder ofta ett bredare utbud av format och exempelantal än det minsta som krävs. Stöd för multisampling kan fastställas vid körning genom att kontrollera funktionsstöd för multisampling med specifika DXGI-format och sedan kontrollera de exempelantal som du kan använda med varje format som stöds.

  1. Anropa ID3D11Device::CheckFeatureSupport för att ta reda på vilka DXGI-format som kan användas med multisampling. Ange de återgivningsmålformat som spelet kan använda. Både återgivningsmålet och matchningsmålet måste använda samma format, så sök efter både D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET och D3D11_FORMAT_SUPPORT_MULTISAMPLE_RESOLVE.

    **Funktionsnivå 9: ** Även om funktionsnivå 9-enheter garanterar stöd för målformat för multisamplad rendering, garanteras inte stöd för mål för flersamplingslösning. Därför är den här kontrollen nödvändig innan du försöker använda multisamplingstekniken som beskrivs i det här avsnittet.

    Följande kod kontrollerar stöd för flerasamplingar för alla DXGI_FORMAT värden:

    // 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. För varje format som stöds frågar du efter stöd för antal prover genom att anropa ID3D11Device::CheckMultisampleQualityLevels.

    Följande kod kontrollerar stöd för exempelstorlek för DXGI-format som stöds:

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

    Observera Använd ID3D11Device2::CheckMultisampleQualityLevels1 i stället om du behöver kontrollera multisample-stöd för tillagda resursbuffertar.

     

  3. Skapa en buffert och återge målvyn med önskat antal exempel. Använd samma DXGI_FORMAT, bredd och höjd som växlingskedjan, men ange ett provantal större än 1 och använd en flersamplingstexturdimension (D3D11_RTV_DIMENSION_TEXTURE2DMS till exempel). Om det behövs kan du återskapa växlingskedjan med nya inställningar som är optimala för multisampling.

    Följande kod skapar ett flersampelmål för återgivning:

    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. Djupbufferten måste ha samma bredd, höjd, antal exempel och strukturdimension för att matcha återgivningsmålet med flera exempel.

    Följande kod skapar en multisamplad djupbuffert:

    // 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. Nu är det dags att skapa visningsporten, eftersom visningsportens bredd och höjd också måste matcha återgivningsmålet.

    Följande kod skapar ett visningsområde:

    // 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. Rendera varje bildruta till det multisamplade återgivningsmålet. När renderingen är klar, anropa ID3D11DeviceContext::ResolveSubresource innan du presenterar bildrutan. Detta instruerar Direct3D att utföra multisamplingsåtgärden, beräkna värdet för varje pixel för visning och placera resultatet i den bakre bufferten. Den bakre bufferten innehåller sedan den slutliga antialiasbilden och kan visas.

    Följande kod löser underresursen innan du presenterar ramen:

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