[Rendering Pipeline] 후처리 시 스왑체인과 백버퍼에 대한 이해
후처리란 그래픽스 렌더링 파이프라인에서 최종 화면 이미지(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의 역할을 하는 것이죠.