Graphics Techniques

[Rendering Pipeline] 후처리 시 스왑체인과 백버퍼에 대한 이해

doyyy_0 2024. 12. 12. 11:42

후처리란 그래픽스 렌더링 파이프라인에서 최종 화면 이미지(Frame Buffer)에 다양한 효과를 적용해 시각적 품질을 개선하거나 특정 분위기를 연출하는 과정입니다. 이는 렌더링된 기본 이미지 위에 추가적인 작업을 수행하는 것을 의미합니다.

 

즉 렌더링 된 이미지에 추가 작업을 해야하는데요. 프론트 버퍼는 스크린에 출력된 상태이고, 백버퍼는 다음 프레임에 나올 렌더링 상태를 저장하고 있습니다. 흔히 우리가 Render함수에서 렌더되는 과정은 백버퍼에 그리는 과정입니다. 후처리는 백버퍼에 다 그려진 렌더링 상태를 가지고 추가 작업을 수행해 주는 것 입니다.

 

이 과정을 수행하기 위해, 첫 초기화 시 백버퍼에 RTV(Render Target View) 뿐만이 아니라 SRV(Shader Resource View)도 연결해 줘야합니다. 다음 코드와 같이 말이죠.

bool AppBase::CreateRenderTargetView() {

    ComPtr<ID3D11Texture2D> backBuffer;
    m_swapChain->GetBuffer(0, IID_PPV_ARGS(backBuffer.GetAddressOf()));
    if (backBuffer) {
        m_device->CreateRenderTargetView(backBuffer.Get(), nullptr,
                                         m_renderTargetView.GetAddressOf());
        m_device->CreateShaderResourceView(backBuffer.Get(), nullptr,
                                           m_shaderResourceView.GetAddressOf());
    } else {
        std::cout << "CreateRenderTargetView() failed." << std::endl;
        return false;
    }

    return true;
}

 

그리고 Update문의 Render함수를 실행하기 이전에 초기화 함수에서

void ExampleApp::BuildFilters() {

    m_filters.clear();
      
    auto copyFilter = 
        make_shared<ImageFilter>(m_device, m_context, L"Sampling", L"Sampling",
                                 m_screenWidth, m_screenHeight);
    // m_shaderResourceView는 이미 backbuffer에 연결돼있음
    copyFilter->SetShaderResources({this->m_shaderResourceView}); 
    m_filters.push_back(copyFilter);

    
    auto finalFilter =
        make_shared<ImageFilter>(m_device, m_context, L"Sampling", L"Sampling",
                                 m_screenWidth, m_screenHeight);
    finalFilter->SetShaderResources({m_filters.back()->m_shaderResourceView});
    finalFilter->SetRenderTargets({this->m_renderTargetView});    
    m_filters.push_back(finalFilter);
    
   
}

 이렇게 필터를 연동해줍니다.  copyFilter에 this->m_shaderResourceView 는 백버퍼를 참조하고있는 SRV를 넘겨주는 것입니다. 이렇게하면 추후 Render가 끝난후 후처리 Render에서 copyFilter내부에서 백버퍼를 참조 하여 후처리 렌더를 진행해줍니다. 

 

그런데 finalFilter는 굳이 왜 필요할까요? 

한 텍스춰를 입력과 출력으로 동시에 사용할 수 없기 때문입니다.

 

한 텍스춰를 입력과 출력으로 사용된 예시입니다.

SRV를 넘겨주고 그것(BackBuffer)을 통해 (BackBuffer를 참조하고 있는)RTV에 그려줍니다.

(this->m_shaderResourceView와 this->m_renderTargetView모두 백버퍼를 참조한 상태)

void ExampleApp::BuildFilters() {

    m_filters.clear();
      
    auto copyFilter = 
        make_shared<ImageFilter>(m_device, m_context, L"Sampling", L"Sampling",
                                 m_screenWidth, m_screenHeight);
    // m_shaderResourceView는 이미 backbuffer에 연결돼있음
    copyFilter->SetShaderResources({this->m_shaderResourceView}); 
    copyFilter->SetRenderTargets({this->m_renderTargetView});   
    m_filters.push_back(copyFilter);
}

잘 작동할 것 같지만, 작동되질 않습니다. RTV가 백버퍼를 가리키고 있다면 후처리 시 SRV가 백버퍼를 참조하여 RTV에 그려야 하는 것인데 RTV가 백버퍼를 참조하고 있다면, 순환참조 문제가 생겨 그냥 검은 화면이 떠 버리기 때문입니다.

 

정상 동작이 되려면 백버퍼를 this->m_shaderResourceView로 받아서 RTV(temp)에 그려야합니다. 이 때 RTV는 Filter내부의 빈 타겟(텍스춰)여야 합니다.

 

따라서 필터 내부의 RTV를 이용하여, 그 생성된 RTV를 필터 내부의 SRV가 참조하고, FinalFilter는 그 SRV를 참조하여 그리는 것입니다. 마치 int a와 int b가 있을 때 서로를 교체하려면 int temp가 필요하듯이 필터 내부의 RTV와 SRV가 temp의 역할을 하는 것이죠.