다음을 통해 공유


UWP(유니버설 Windows 플랫폼) 앱에서 멀티샘플링

Direct3D로 빌드된 UWP(유니버설 Windows 플랫폼) 앱에서 다중 샘플링을 사용하는 방법을 알아봅니다. 다중 샘플 앤티앨리어싱이라고도 하는 다중 샘플링은 별칭 가장자리의 모양을 줄이는 데 사용되는 그래픽 기술입니다. 실제 최종 렌더링 대상보다 더 많은 픽셀을 먼저 드로잉한 후, 값을 평균화하여 특정 픽셀에서 "부분" 가장자리의 모양을 유지시키기 위해 작동합니다. Direct3D에서 멀티샘플링이 실제로 작동하는 방식에 대한 자세한 설명 은 다중 샘플 앤티앨리어싱 래스터화 규칙을 참조하세요.

다중 샘플링 및 플립 모델 스왑체인

DirectX를 사용하는 UWP 앱은 플립 모델 스왑 체인을 사용해야 합니다. 플립 모델 스왑 체인은 멀티샘플링을 직접 지원하지 않지만, 장면을 멀티샘플링된 렌더링 대상 보기로 렌더링한 다음, 이를 백 버퍼로 해결한 후 제시하는 방식으로 멀티샘플링을 다른 방법으로 적용할 수 있습니다. 이 문서에서는 UWP 앱에 다중 샘플링을 추가하는 데 필요한 단계를 설명합니다.

다중 샘플링을 사용하는 방법

Direct3D 기능 수준은 특정 최소 샘플 개수 기능에 대한 지원을 보장하고 다중 샘플링을 지원하는 특정 버퍼 형식을 사용할 수 있도록 보장합니다. 그래픽 디바이스는 필요한 최소 개수보다 더 광범위한 형식 및 샘플 수를 지원하는 경우가 많습니다. 특정 DXGI 형식의 다중 샘플링에 대한 기능 지원을 확인한 다음 지원되는 각 형식에 사용할 수 있는 샘플 수를 확인하여 런타임에 다중 샘플링 지원을 확인할 수 있습니다.

  1. ID3D11Device::CheckFeatureSupport 호출하여 다중 샘플링과 함께 사용할 수 있는 DXGI 형식을 확인합니다. 게임에서 사용할 수 있는 렌더링 대상 형식을 제공합니다. 렌더링 대상과 멀티샘플 해상 대상은 모두 동일한 형식을 사용해야 하므로 D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGETD3D11_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);