주의 : 잘못된 내용이 있을 수 있으므로 이상하면 참고자료를 확인하세요.


[ PBR 이란 무엇인가 ] 12. UE4 GI : Reflection Capture

기본 개념

오늘은 UE4 에서 가장 흔하게 사용되는 IBL 요소인 Reflection Capture 에 대해서 알아 보도록 하겠습니다.

아티스트들은 일반적으로 "반사( reflection )" 를 "정반사( specular reflection )" 라고 받아들입니다. 하지만 이것은 잘못된 것입니다. 반사는 난반사( diffuse reflection )와 정반사( specular reflection )으로 나뉩니다.

하지만 UE4 에서의 reflection capture 에서의 reflection 은 indirect specular reflection 을 의미합니다. 사실 indirect diffuse refleciton 도 반영해야 정확한 결과가 나오겠지만 UE4 에서는 specular 성분만을 반영합니다. 그렇게 하는 이유는 diffuse 성분까지 반영하기에는 메모리 부하와 성능 부하가 너무 커지기 때문인 것으로 보입니다.

UE4 의 reflection capture 는 지역적인 결과를 반영하고 있습니다. 즉 주변에 어떤 라이트와 메쉬가 배치되어 있느냐에 따라 결과가 달라집니다. 그러므로 매우 많은 reflection capture 를 사용해야만 아티스트가 원하는 결과에 근접한 결과가 나옵니다. 그래서 diffuse 성분까지 반영하기에는 메모리 측면이나 성능측면에서 부담이 될 수 있습니다. 

리플렉션 캡쳐는 indirect 성분을 저장한다고 했습니다. 그러므로 라이트매스를 빌드해야 제대로 된 결과가 나온다는 것을 잊지 마십시오.

Cubemap Array

이 섹션은 약간 기술적인 내용을 담고 있습니다. 이해를 못하셔도 사용하는 데는 지장이 없습니다.

하나의 씬에서는 총 341 개까지의 리플렉션 캡쳐를 렌더링할 수 있으며, 각 리플렉션 캡쳐의 큐브맵의 한 면의 해상도는 128 X 128 입니다. 리플렉션 캡쳐의 렌더링 개수가 341 개로 제한된 이유는 렌더링을 위해 큐브맵의 면을 D3D11 의 Texture Cubemap Array 를 사용하기 때문입니다. 일종의 texture pool 이라 생각하시면 됩니다. 저장은 개별 리플렉션 캡쳐의 큐브맵에다가 하지만 렌더링은 texture array 를 통해 하는거죠.

그림1. 리플렉션 캡쳐의 각 큐브맵의 면은 텍스쳐 배열에 올라갑니다.

D3D11 의 texture array 의 최대 요소 개수는 2048 개입니다. 그런데 큐브맵은 면을 6 개를 가지게 되죠. 그러면 341 X 6 = 2046 이기 때문에 341 개가 최대 캡쳐 개수가 되는 겁니다. 그냥 여러분은 그냥 "하드웨어 제약이다" 정도로 이해하시면 될 것 같습니다.

그런데 왜 UE4 는 번거롭게 하나의 texture array 에다가 큐브맵의 면들을 올리는 것일까요?

사실 ShaderModel 5( SM5 ) 아래 환경에서는 texture array 를 사용하지는 않습니다. 이 글을 보고 계신 분들은 적어도 SM5 이상을 사용한다고 가정하고 작성된 글입니다. SM5 에서는 Tiled-Deferred-Imaged-Based_Reflection 이라는 기법을 사용하는데, ComputeShader 를 사용하게 됩니다. 이때 texture array 를 넘겨서 빠르게 cubemap 들에 접근하게 됩니다. 리플렉션 캡쳐의 개수가 동적이기 때문에 쉐이더에 파라미터로 일일이 넘기는 것은 어렵습니다. 그러므로 cubemap texture array 의 index 로 각 리플렉션 캡쳐를 식별하게 되는 것이죠.

이 개념을 매우 단순하게 설명하면 아래 그림과 같습니다.

그림2. Tiled deferred imaged based refleciton 을 단순화함. 

먼저 씬의 렌더링 정보를 캡쳐합니다. 그리고 나서 씬을 여러 개의 타일 그룹( 기본값은 8 X 8 그룹 )으로 분할합니다. 그렇다면 각 타일에는 "width / 8height / 8" 개의 픽셀이 있겠죠. 각 픽셀의 중심 위치를 구하고 각각의 reflection capture 들과의 포함관계를 계산합니다. 그리고 나서 해당 reflection capture 의 데이터를 texture array 로부터 읽어 옵니다. 만약 cubemap texture 에 두 개의 요소를 넣었다면, 파란색 영역은 array index 0 이고 보라색 영역은 array index 1 이라고 할 수 있겠죠. 녹색은 겹치는 영역을 의미합니다.

자세한 내용을 언급하지는 않았지만 대충 개념은 이해하실 수 있을거라 생각합니다. 분석을 원하는 프로그래머들을 위해 첨언하자면, TReflectionEnvironmentTiledDeferredCS 템플릿을 참조하시기 바랍니다.

UE4 에는 두 종류의 reflection capture 가 있어서 사람을 혼란스럽게 합니다. 그것은 "Box Reflection Capture" 와 "Sphere Reflection Capture" 인데요, 이름이 말해 주듯이 리플렉션 캡쳐 바운딩의 모양을 의미합니다. 그 이외에는 큰 차이는 없다고 보시면 됩니다.

Reflection Capture 블렌딩

"Default" 레벨 템플릿으로부터 씬을 하나 생성해 크롬 바닥을 배치해 보도록 하죠.

좀 더 넓은 영역을 확보하기 위해 바닥 메쉬의 XY 스케일을 10배로 만듭니다. 그리고 지난 시간에 만들었던 크롬 재질을 반영합니다. 머티리얼을 바닥에 드래그&드랍하시면 됩니다. 크롬재질은 basecoor = white, metalic = 1, roughness = 0 을 설정한 것을 의미합니다. 혼란을 방지하기 위해서 일단 ScreenSpaceReflection 은 끕니다; Viepwort 의 "Show >> Lighting Features >> Screen Space Reflection" 을 off 시킵니다.

그러면 여러분은 그림3 과 같은 놀라운 결과를 확인하게 됩니다.

그림3. 아무런 설정없이 크롬 바닥을 배치했을 경우의 결과.

그림3 에서 완전히 검은색이 나오는 것은 metalic 이 1 인 경우에는 diffuse 가 0 이 되기 때문입니다. 금속은 모든 빛을 흡수하고 방출하기 때문인데, 이에 대해서는 다음 시리즈에서 설명하도록 하겠습니다.

여기에 Box Reflection Capture 를 배치해 봅시다. 와우!!! 뭔가 반사가 나옵니다.

그림4. BoxReflectionCapture 배치.

그런데 그림4 에 보면 바운딩 박스가 두 개 나옵니다. 1 번은 전체 바운딩 박스를 의미하고 2 번은 블렌딩 영역의 바운딩 박스를 의미합니다. 2 번을 조정하려면 "Reflection apture" 카테고리의 "Box Transition Distance" 를 조정하시면 됩니다.

그림5. "Box Transition Distance" 속성.

이것을 0500 으로 설정해 차이를 확실하게 확인해 봅시다.

그림6. "Box Transition Distance" 가 0 일때( 상 )와 500 일때( 하 )의 차이.

 

이것만 봐서는 잘 구분이 안 가시죠? 이 리플렉션 캡쳐의 "Box Transition Distance" 가 0 인 것과 500 인 것 두 개를 배치해서 겹쳐봅시다. 그 결과가 그림7 에 나와 있습니다.

그림7. 두 개의 리플렉션 캡쳐를 블렌딩. 오른쪽이 0 왼쪽이 500.

0 인 것의 리플렉션 캡쳐가 위쪽에 나오는 것을 볼 수 있습니다. 그러면 0 이던 것을 700 으로 바꿔볼까요?

그림8. 두 개의 리플렉션 캡쳐를 블렌딩. 오른쪽이 700 왼쪽이 500.

그림8 을 보면 "Box Transition Distance" 가 높은 것이 밑에 깔리는 것을 볼 수 있습니다. 그러면 "Box Transition Distance" 의 값이 Order 를 설정하는 것일까요?

질문을 하면 언제나 그렇듯이... 답은 "NO" 입니다.

그림8 에 배치된 것 중에서 오른쪽의 스케일 X 를 400 으로 바꿔봅시다.

그림9. 두 개의 리플렉션 캡쳐를 블렌딩. 오른쪽이 BoxTranstionDistance = 700, ScaleX = 400. 왼쪽이 각각 500, 1000.

그림9 의 결과를 보면서 뭔가 감을 잡은 분들이 계실 겁니다. 박스의 부피와 BoxTransitionDistance 의 관계는 반비례합니다. 이러한 것을 잘 고려해서 리플렉션 캡쳐의 우선순위를 결정할 필요가 있습니다.

Sphere Reflection Capture 같은 경우에는 일반적으로 Scale 을 사용하지 않습니다. 대신에 "Influence Radius" 속성을 사용합니다.

그림10. Sphere Reflection Capture 의 "Influence Radius" 속성.

Scale 이 동일하다면 "Influence Radius" 값이 낮은 것이 위에 나온다고 보시면 됩니다.

그림11. "Influence Radius" 에 따른 우선순위. 오른쪽이 1000, 왼쪽이 1001.

이런 결과를 볼 때 UE4 가 의도하는 것은 명백합니다. 볼륨이 작을수록 그리고 블렌딩 팩터가 작을 수록 더 메인으로 렌더링되게 하겠다는 것입니다.

한계 및 대안

자 이제는 구형 리플렉션 캡쳐를 하나 배치하고 그 영역 안에서 바닥 위에 박스 메쉬를 하나 올려보도록 하겠습니다. 그런데 결과가 안 나옵니다.

그림12. 구형 리플렉션 영역 안에 박스 메쉬 배치했지만 결과가 반영이 안 됨.

여기에서 "저는 나오는데요?" 하시는 분들은 Screen Space Reflection 안 끄신 분들입니다.

본론으로 돌아가서... 왜 안 나오는 걸까요? 대부분의 IBL 씬 캡쳐들은 주변 환경이 바뀌었을 때 캡쳐를 갱신해 줘야 합니다. 리플렉션 캡쳐의 TM 을 변경하면 자동으로 캡쳐가 갱신되지만 그렇지 않고 외부 요인을 바꿨을 때는 반드시 "Update Captures" 를 해 줘야 합니다.

그림13. "Update Captures" 버튼.

하지만 그림14 처럼 별로 좋은 결과가 나오지는 않을 겁니다.

그림14. "Update Captures" 결과.

그러면 그림14 의 결과가 매우 잘못된 걸까요? 이 리플렉션 캡쳐라는 것은 사용할만한 물건이 아닐까요?

여기에서는 리플렉션 캡쳐를 바라보는 관점을 전환시킬 필요가 있습니다. IBL 은 Planar reflection capture 와는 다르게 사용자가 오브젝트를 바라 봤을 때 반대쪽 결과를 보는데 특화된 것입니다.

그림15. 리플렉션 캡쳐의 결과가 그나마 올바르게 나오기 위한 조건.

사실 Parallax Correction 이라는 알고리즘[1]을 사용해서 이를 개선하는 것은 가능합니다. 하지만 UE4 에서는 비용이 비싸서 그런지 다른 이유때문인지 이를 구현하지는 않았습니다.

이를 보정해 주는 것이 "Screen Space Reflection( SSR )" 입니다. 아까 껐던 SSR 을 다시 켜 봅시다.

그림16. Screen Space Reflection 과 Reflection Capture 의 조합. 

물론 이것도 한계가 있습니다. 왜냐하면 ScreenSpace 기법들은 화면을 벗어난 것에 대한 처리를 할 수 없기 때문에 그림16 에서 보듯이 뷰 방향에 따라 다른 결과가 나오기 때문입니다. 하지만 이 샘플에서처럼 극단적으로 반사를 하는 경우는 많지 않기 때문에 큰 문제는 되지 않습니다.

하지만 샘플처럼 바닥이 거의 평평한데 거울과 비슷하게 반사한다면 어떻게 해야 할까요? 이때는 "Planar Reflection"[2] 을 사용할 수 있습니다.

Indirect Specular Reflection

위에서도 언급했듯이 리플렉션 캡쳐는 간접광( indirect lighting )을 다루기 때문에 반드시 lightmass 를 빌드해야 합니다. 이제 포인트라이트 하나를 배치하고 "Update Captures" 를 해 보도록 하겠습니다. 그러면 그림17 과 같은 결과를 볼 수 있습니다. 이것은 옳은 결과인가요?

그림17. Direct 성분만 반영된 캡쳐 결과.

그렇지 않습니다. Point light 에 대한 indirect 성분이 전혀 반영되지 않았기 때문에 박스 바닥이 검게 나오는 것을 알 수 있습니다. Lightmass 빌드를 하면 그림18 과 같은 결과를 얻을 수 있습니다.

그림18. Indirect 성분까지 반영된 캡쳐 결과.

참고자료

[1] Image-based Lighting approaches and parallax-corrected cubemap, Sebastien Lagarde.

[2] 플레이너 리플렉션, UE4 Document.

+ Recent posts