다음을 통해 공유


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