Float Render Target
PBR 적용 시 중요한 요소 중 하나
- 환경맵을 HDRI로 이용하는 것
- HDRI : High Dynamic Range Image
- HDRI : High Dynamic Range Image
- 조명을 HDRI로 이용하겠다는 뜻이며
IBL을 간접 조명(Ambient)으로 사용한다는 의미
결국 렌더링 전체가 HDR을 사용해야 함
- LDR은 0.0 ~ 1.0으로 색을 표현
- UNORM 0 ~ 255 역시 쉐이더에서 0.0 ~ 1.0 으로 변환하여 사용함
- UNORM 0 ~ 255 역시 쉐이더에서 0.0 ~ 1.0 으로 변환하여 사용함
Float Render Target
렌더링을 진행할 때
원하는 장면(Scene)을 렌더링(Rasterizer)할 때
픽셀 포맷이 Floating Point 여야 HDR로 렌더링이 가능
-
- Float Render Target
- 더 넓은 밝기 범위를 담을 수 있는 고정밀도 렌더 타겟
- Float Render Target
-
일반적인 8Bit로만 색을 표현하는 것은
Bloom, Exposure, Tone Mapping, Luminance 등을 계산할 때
데이터 손실이 발생 - 상황에 따라 Render->FP Render Target 까지는 HDR 로 진행하나
ToneMapping 에 따라 LDR로 다시 바꾸어주는 경우도 존재
(모든 모니터와 기능이 HDR로 구성될 필요가 없을 수 있음)
- ToneMapping : HDR을 사람이 인식할 수 있는 LDR로 변환
- 밝기를 적절한 수준으로 조절하여 여러 보정과 색상에 대한 조절등을 실행
- ToneMapping : HDR을 사람이 인식할 수 있는 LDR로 변환
-
후처리 + 안티 앨리어싱 통합 파이프라인
- MSAA Render Target : 3차원 물체 렌더링 결과
- MSAA는 하드웨어 가속을 받는게 좋음
하나의 픽셀에 여러 샘플을 가지는 형태
- MSAA는 하드웨어 가속을 받는게 좋음
- 위의 결과를 Resolve하여 한 픽셀에 하나의 결과를 가지는
FP Render Target 으로 변화
- 다만 Floating Point로 변화하려면 MSAA에서부터 FP 기반이여야 함
(동시에, 해당 단계에서 반드시 HDR이 아닐수도 있음)
(정확히는 Resolve의 Render Target의 포맷이 LDR이면 사라짐) - 우리의 예제에선 이 타이밍에 Bloom 적용 (Post Process)
- 다만 Floating Point로 변화하려면 MSAA에서부터 FP 기반이여야 함
-
- SDR/LDR Render Target
- 0~1 범위를 갖는 렌더타겟
- 다시 Post Process 사용 가능
- SDR/LDR Render Target
- UI 렌더링은 별도로 진행
(HDR이 필요하진 않으므로)
요약하자면
처음에 MSAA를 이용하여 HDR 렌더링을 진행하고
이후로는 각 렌더타겟의 포맷팅에 맞춘다
예제 - HDRI Pipeline
void ExampleApp::Render() {
...
// MSAA로 Texture2DMS에 렌더링 된 결과를 Texture2D로 변환(Resolve)
m_context->ResolveSubresource(m_resolvedBuffer.Get(), 0,
m_floatBuffer.Get(), 0,
DXGI_FORMAT_R16G16B16A16_FLOAT);
}
MSAA는 픽셀 내부를 여러 샘플로 쪼개고
경계선의 커버리지 비율을 구하여 계단 현상을 완화하는 방식
- MSAA 용으로 렌더링된 텍스쳐들을 합쳐
하나의 Non Msaa 로 변환하는 과정
bool AppBase::InitDirect3D() {
...
DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory(&sd, sizeof(sd));
sd.BufferDesc.Width = m_screenWidth;
sd.BufferDesc.Height = m_screenHeight;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferCount = 2;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = m_mainWindow;
sd.Windowed = TRUE;
sd.Flags =
DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // allow full-screen switching
// sd.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; //ImGui 폰트가 두꺼워짐
sd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sd.SampleDesc.Count = 1; // _FLIP_은 MSAA 미지원
sd.SampleDesc.Quality = 0;
...
}
-
- 포맷이 DXGI_FORMAT_R8G8B8A8_UNORM
- 플로팅 포인트가 아님
- 일반적인 모니터 등이 표시하는 포맷팅 방식
- 굳이 스왑 체인용으로 더 많은 용량의 포맷팅을 잡을 이유는 없음
-> 내부에서 HDR 렌더링에 톤매핑 까지 하여 화면에 뿌린다면 이런 방식이 정석적인 구조
(FP 포맷팅을 사용할 필요가 없다면 일반적인 포맷팅 사용을 권장)
- 포맷이 DXGI_FORMAT_R8G8B8A8_UNORM
- 그렇기에 백버퍼용 Swapchain 부분에 MSAA 미지원 방식을 적용 가능
void AppBase::CreateBuffers() {
...
ThrowIfFailed(m_device->CheckMultisampleQualityLevels(
DXGI_FORMAT_R16G16B16A16_FLOAT, 4, &m_numQualityLevels));
D3D11_TEXTURE2D_DESC desc;
backBuffer->GetDesc(&desc);
desc.MipLevels = desc.ArraySize = 1;
desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
desc.Format = DXGI_FORMAT_R16G16B16A16_FLOAT;
desc.Usage = D3D11_USAGE_DEFAULT; // 스테이징 텍스춰로부터 복사 가능
desc.MiscFlags = 0;
desc.CPUAccessFlags = 0;
if (m_useMSAA && m_numQualityLevels) {
desc.SampleDesc.Count = 4;
desc.SampleDesc.Quality = m_numQualityLevels - 1;
} else {
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
}
ThrowIfFailed(
m_device->CreateTexture2D(&desc, NULL, m_floatBuffer.GetAddressOf()));
...
}
- MSAA 용 텍스쳐 버퍼 만들기
댓글남기기