주의 : 잘못된 내용이 포함되어 있을 수 있으므로 이상하면 참고자료를 참조하세요.


[ PRB 이란 무엇인가 ] 13. UE4 GI : Lightmass & Mobility

개요

UE4 에서는 공식적으로는 Dynamic GI 를 지원하지 않습니다. LightPropagationVolume( LPV ) 이 있기는 하지만, 아직도 experimental 로 분류됩니다. 그러므로 여기에서는 다루지 않도록 하겠습니다. 그리고 이 문서는 Mobility 의 차이를 설명하는 데 중점을 두고 작성되었으므로 Lightmass 사용법이나 구현에 대해서는 다루지 않겠습니다.

UE4 를 처음 접하는 분들이 가장 헷갈려하는 개념이 Mobility 입니다; Static, Stationary, Movable.

기본적으로 Mobility 라는 것은 개체가 게임 실행중에 움직이냐 그렇지 않느냐를 의미합니다. 그렇다면 솔직히 Static 과 Dynamic 만 존재해도 됩니다. 하지만 UE4 는 굳이 Stationary 와 Movable 이라는 개념을 추가했습니다. 이렇게 구분한 이유는, 각 Mobility 마다 GI 처리 방식이 다르기 때문입니다.

그림1. UE4 Mobility.

[ Global Illumination & Indirect Lighting ] 에서는 GI 에서는 빛을 크게 direct lighting( 직접광 ) 과 Indirect lighting( 간접광 )으로 나눈다고 했습니다. 단순하게 표현하면 표면에 닿은 빛이 바로 눈에 들어 오면 direct lighting 이고, 그것이 다른 곳에 반사해서 눈에 들어 오면 indirect lighting 입니다( 눈으로 바로 들어 오는 빛도 direct lighting 이라 할 수 있겠죠. 사실 scattering 을 거쳐서 들어 오기는 하지만 여기에서는 무시하도록 하겠습니다 ).

그림2. Direct lighting( 붉은색 ) 과 Indirect lighting( 녹색 ).

자! 본론으로 돌아와, 이제 각 Mobility 설정에서 GI 가 어떤 식으로 처리되는지 알아 보도록 합시다.

씬 구성

아무것도 배치되지 않은 씬에다가 바닥( Static )과 6 개의 박스( 3 개는 Static, 3 개는 Stationary, 3 개는 Movable ), 그리고 3 개의 라이트( R 은 Movable, G 는 Stationary, B 는 Static )를 배치합니다. 그리고 그 주변에 "Lightmass Importance Volume" 을 적절히 설정해 줍니다. 최종 품질을 반영하기 위해서 Production 으로 빌드합니다. 오브젝트의 재질은 모두 color=white, metalic=0.2, roughness=0.5 로 설정했습니다.

그림3. 씬구성. 바닥과 왼쪽 열은 static, 중간 열은 stationary, 오른쪽 열은 movable. 붉은색 광원은 movable, 녹색 광원은 stationary, 파란색 광원은 static.

Object Moblity

여기에서 먼저 확인해 볼 것은 오브젝트의 mobility 입니다. 이를 명확히 확인하기 위해서는 라이트맵을 살펴 보는 것이 좋습니다. 뷰포트에서 "Optimiztion Viewmodes >> Lightmap Density" 를 선택하시면 라이트맵 밀도를 볼 수가 있습니다.

그림4. 라이트맵 밀도.

그림4 를 통해 여러분이 알 수 있는 것은 오브젝트가 static 이 아니라면 라이트맵을 가지지 않는다는 것입니다. 이게 무슨 말인가 하면 stationary/movable 오브젝트는 indirect lighting 성분을 indirect lighting cache 에서 가지고 온다는 것입니다.

확인을 위해서 뷰포트에서 "Lighting Features >> Indirect Lighting Cache" 를 off 합니다.

그림5. Indirect Lighting Cacahe 메뉴.

그러면 그림6 과 같이 stationary/movable 오브젝트에서 indirect 성분이 배제되는 것을 확인할 수 있습니다.

그림6. Indirect Lighting Cache 를 off 하면 Stationary/Movable 오브젝트의 Indirect 성분이 없어짐.

Light Mobility

이제 라이트의 mobiltiy 를 살펴 보도록 하겠습니다. 일단 다시 Indirect Lighting Cache 를 on 합니다.

Static Light

Static 라이트는 Direct Lighting, Direct Shadowing, Indirect Lighting 성분을 모두 Lightmap 과 Indirect Lighting Cache 에 저장합니다.

뷰포트에서 "Lighting Components" 항목으로 간 다음 서브메뉴에서 "Dynamic Shadows", "Glboal Illumination" 을 모두 off 합니다.

그림7. Dynamic Shadows 와 Global Illumination 을 off.

그러면 그림8 처럼 Direct lighting 성분만 남습니다.

그림8. Direct Lighting 만 남겨 둠.

여기서 특이한 점을 발견하실 수 있습니다. Static 오브젝트에 Static 라이트는 반영되지 않고 그림자는 반영된다는 것입니다. 즉 Static 오브젝트의 그림자는 Direct 성분이라는 거죠.

그럼 이제는 반대로 "Glboal Illumination" 을 on 하고 "Direct Lighting" 을 off 합니다. 그러면 그림9 처럼 Indirect Lighting 성분만 남습니다.

그림9. Indirect Lighting 만 남겨 둠.

여기에서 알 수 있는 사실은 무엇입니까? Static 라이트의 direct/indirect lighting 성분은 모두 indirect lighting 으로 취급된다는 것입니다.

UE4 에서 이렇게 처리하는 이유는 무엇일까요? Lightmass 를 빌드하고 게임에 들어 가면 게임에서는 Static 라이트가 생성되지 않습니다. 그러므로 Static 라이트에 의한 direct 및 indirect 성분을 모두 indirect 로 취급하는 거죠. 이를 direct 와 indirect 로 구분하기 위해서는 라이트맵이 두 장씩 필요하고 direct lighting cache 같은 걸 만들어야겠죠. 개념적으로는 올바르지만 최적화 관점에서는 쓸데없는 짓입니다.

그래서 "LIGHTING NEEDS TO BE REBUILT" 라는 것이 나오면 성능이 저하됩니다.

왜냐! Lightmass 에서 Static 라이트와 Static 오브젝트가 빌드되기 전에는 Shadow 를 실시간 계산하고 Indirect 성분을 Indirect Lighting Cache 에서 가지고 오기 때문입니다.

Stationary Light

Stationary Light 는 Indirect Lighting 성분은 Lightmap 맵과 Indirect Lighting Cache 에 저장하고 Direct Shadowing 성분은 ShadowMap 에 저장하고 Direct Lighting 성분은 실시간에 계산합니다. 여기서 헷갈리지 말아야 할 것은, 대상 오브젝트가 static 이 아니라면( 라이트맵을 가지지 않는다면 ) direct shadowing 은 실시간에 계산된다는 것입니다.

Static 과 결정적인 차이는 Direct Lighting 성분을 실시간에 계산한다는 점인데, 이 때문에 specular reflection 처리가 가능합니다.

그림10. Specular reflection. R 은 Movable, G 는 Stationary, B 는 Static.

그림10 을 보면 Static 라이트에 대한 specular reflection 이 생기지 않는다는 것을 알 수 있습니다. 왜냐하면 diffuse 같이 뷰에 독립적인( view independent ) 성분은 어디에서 봐도 동일한 결과를 내기 때문에 미리 계산하는 것이 가능하지만, specular 와 같이 뷰에 종속적인( view dependent ) 성분들은 보는 위치에 따라 결과가 달라지기 때문에 미리 계산할 수가 없습니다.( 그림11 참조 ).

그림11. Diffuse 분산과 Specular 분산. Diffuse 는 모든 방향으로 동일하게 분산되지만, Specular 는 방향성을 가집니다.

물론 밸브( Valve )의 RNM( Radiosity Normal Map ) 라이트맵처럼 세 개의 기저벡터를 사용해서 라이트맵을 구우면 specular 를 표현할 수 있기는 하지만[2], 기저벡터마다 라이트맵을 만들어야 하므로 용량의 압박때문에 잘 사용하지는 않습니다. 

Movable Light

Movable 라이트는 Direct lighting 성분만을 가집니다. 실시간 GI 알고리즘을 사용하지 않는다면 Indirect 성분은 없습니다. Light Propagation Volume 을 사용한다면 라이트의 "Dynamic Indirect Lighting" 속성을 on 시켜 indirect 성분을 처리할 수 있습니다.

Movable 라이트는 GI 처리를 하지 않기 때문에 품질이 떨어진다는 단점을 가지고 있기는 하지만, 라이트를 움직일 수 있다는 장점이 있으므로 제한적으로 사용됩니다.

결론

지금까지 언급했던 것들을 정리하면 다음과 같습니다. 물론 HQ 라이트맵에서의 조건입니다. LQ 에서는 모든 것을 라이트맵에 넣습니다. "Indirect Lighting Cache" 는 "ILC" 라는 머리글자로 표기하도록 하겠습니다.

 

 Static Light

 Stationary Light 

 Movable Light

 Static Object

 Direct Lighting : Ligthmap.

 Direct Shadowing : ShadowMap.

 Indirect Lighting : Lightmap.

 Direct Lighting : Realtime.

 Direct Shadowing : ShadowMap.

 Indirect Lighting : Lightmap.

 Direct Lighitng : Realtime.

 Direct Shadowing : Realtime.

 Indirect Lighting : None.

 Staionary Object

 Direct Lighting : ILC.

 Direct Shadowing : None.

 Indirect Lighting : ILC.

 Direct Lighting : Realtime.

 Direct Shadowing : Realtime.

 Indirect Lighting : ILC.

 Direct Lighitng : Realtime.

 Direct Shadowing : Realtime.

 Indirect Lighting : None.

 Movable Object

 Direct Lighting : ILC.

 Direct Shadowing : None.

 Indirect Lighting : ILC.

 Direct Lighting : Realtime.

 Direct Shadowing : Realtime.

 Indirect Lighting : ILC.

 Direct Lighitng : Realtime.

 Direct Shadowing : Realtime.

 Indirect Lighting : None.

참고자료

[1] 간접광 캐시, 언리얼 문서.

[2] Half-Life(R) 2 / Valve Source(TM) Shading, GDC2004.

 

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


[ 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.

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


[ PBR 이란 무엇인가 ] 11. UE4 GI : Sky Light

UE4 IBL 의 종류

일단 이미지 소스를 광원으로 사용한다면 그것은 IBL 이라 할 수 있습니다. UE4 에서의 IBL 은 Sky Light, Reflection Environment( Box Reflection Capture, Spehre Reflection Capture ), Ambient Cubemap( Post Process Volume ) 등이 있습니다. 오늘은 Sky Light 에 대해서 다뤄 보도록 하겠습니다.

주의 : 여기에서는 Stationary 환경을 중심으로 설명하도록 하겠습니다.

먼저 각 light 요소들의 영향력을 평가하기 위해 사용할 옵션들에 대해서 설명드리도록 하겠습니다.

Viewport 의 "Show" 풀다운 메뉴에는 "All Show Flags" 라는 섹션이 있습니다. 거기에 있는 "Light Types" 메뉴와 "Lighting Components" 메뉴를 사용해서 기능을 on/off 하면서 결과를 살펴 볼 것입니다.

그림1. "Light Types" 옵션과 "Lighting Components" 옵션.

Sky Light 란

아웃도어 환경을 고려하는 개발자라면 가장 먼저 배치하는 것이 direcitonal light, sky sphere( atmostpheric sky ) 같은 요소일 것입니다.

Sky sphere 같은 경우에는 directional light( 일반적으로 태양 ) 에 의한 Rayleigh scattering 과 Mie scattering 을 처리합니다. 이것을 IBL 소스로 쓴다면 매우 좋겠죠. 하지만 보통 그런 개체를 구현할 때 IBL 을 고려하고 만들지는 않습니다.

그래서 Sky sphere 를 IBL 로서 사용할 수 있는 방법이 필요합니다. 이를 도와 주는 액터가 바로 Sky Light 액터입니다. Mode 툴바에 보면 "Lights" 카테고리에 "Sky Light" 액터가 있습니다. 이를 씬에 배치하면 됩니다. 이때 액터의 위치는 대충 설정해도 됩니다. 그 이유는 뒤쪽에서 설명하도록 하겠습니다.

SkyLight 를 배치했을 때와 그렇지 않았을 때의 차이는 매우 큽니다. "Light Types >> Sky Lighting" 을 껐다가 켜 보면 그 차이를 볼 수 있습니다. 그림자가 진 영역에 대해서 "Sky Light" 성분이 반영되는 것을 알 수 있습니다.

 

 그림2. 이미지 슬라이더가 보이는 경우에는 왼쪽이 SkyLight 없을 때이고 오른쪽이 SkyLight 있을 때임. 이미지 슬라이더가 안 보이는 경우에는 위쪽이 SkyLight 있을 때이고 아래쪽이 SkyLight 없을 때임.

Sky Occlusion

그런데 SkyLight 를 배치할 때 한 가지 주의할 점이 있습니다. SkyLight 의 차폐 성분은 Lightmass 빌드시에 SkyOcclusion 이라는 아틀라스 텍스쳐에 포함되므로 Lightmass 를 빌드하기 전까지는 차폐 성분이 반영되지 않습니다.

그림3. Lightmass 의 SkyOcclusion.

그러므로 Lightmass 빌드 전에 박스를 옮겨 보면 그림4 처럼 차폐 영역에 SkyLight 가 그대로 뭍어 나오는 것을 알 수 있습니다.

그림4. Lightmass 빌드 전에는 SkyLight 에 대한 차폐가 발생하지 않음.

하지만 Lightmass 를 빌드하면 그림5 처럼 차폐된 영역에 SkyLight 가 들어 오지 않습니다.

그림5. Lightmass 빌드 후에는 SkyLight 에 대한 차폐가 발생함. 주의 : Preview 빌드에서는 샘플 개수의 부족으로 차폐가 제대로 반영되지 않을 수도 있습니다.

UE4 에서 SkyOcclusion 은 벤트노멀( Bent-Normal )과 입체각( Solid-Angle )을 사용해서 계산됩니다. 입체각이 뭔지 기억이 안 나시는 분은 [ 3. 빛의 감쇠 ] 를 참조하세요. 벤트노멀이라는 것은 어떤 벡터들의 평균벡터를 의미합니다.

그림6. Bent Normal. 출처 : How could I use bent normal map.

그림6 에서 보이듯이 서피스의 어떤 점으로부터 반구내에서 사방으로 레이를 쏘면 다른 서피스와 충돌하지 않고 나아가는 레이들이 있습니다. 그러한 레이들의 벡터들을 모두 더해서 평균을 내면 BentNormal 이라는 것이 나옵니다. 그리고 BentNormal 을 중심으로 얼마나 열려있는지를 구할 수 있고 그것을 입체각 단위로 저장합니다.

그러면 그림7 에서와 같이 어떤 점에서 하늘에 대한 차폐 정도를 알 수 있죠. 그러면 BentNormal 은 specular reflection 과 diffuse reflection 을 위해 이미지를 샘플링하기 위한 중심벡터가 되고, 입체각은 차폐 정도를 판단할 수 있는 기준이 되죠.

그림7. 여러 지점에서 BentNormal 과 SolidAngle 을 구함. 붉은색, 녹색, 보라색 순으로 차폐 정도가 덜함을 알 수 있음.

SolidAngle 을 알게 되면 반구의 면적이 pi 이므로 SolidAngle / 2pi 를 하면 [0, 1] 범위의 값이 나오며, 그것이 SkyVisibility 가 되는 것입니다 .SkyOcclusion 아틀라스 텍스쳐의 rgb 채널에는 BentNormal 이 a 에는 1 - SkyVisibility 가 들어 갑니다( 사실 실제 Lightmass 구현에서는 일일이 SolidAngle 을 구하지는 않고, 차폐된 ray 의 개수를 사용해 occlusion 을 구합니다. 여기에서는 설명의 목적으로 SolidAngle 을 사용했으나 개념상으로는 큰 차이가 없습니다. 정확도의 차이는 있겠지만 비슷한 비율이 나옵니다 ).

Indirect specular reflection & indirect diffuse reflection

SkyLight 의 경우에는 indirect specular reflection 과 indirect diffuse reflection 을 모두 반영합니다. 씬에 크롬볼과 화이트볼을 하나 배치해 보도록 합시다. 그리고 SkyLight 성분을 확실하게 보기 위해서 "Light Types >> Directional Light" 를 off 합니다. 그리고 나서 "Light Types >> Sky Lighting" 을 on/off 해 봅니다.

그림8. SkyLight 가 반영된 상태.

그림9. SkyLight 가 반영되지 않은 상태. SkyLight 를 제외한 reflection 성분은 나중에 이야기할 Reflection Capture 로부터 온 것입니다.

다음으로는 SkyLight 를 켠 상태에서 "Light Components >> Diffuse" 와 "Light Components >> Specular" 를 on/off 해 봅니다.

그림10. Diffuse 만 활성화. 크롬볼은 metalic 이 1 이고 roughness 가 0 이므로 전혀 diffuse 성분을 안 먹습니다. 원래 완전한 금속은 diffuse 가 0 이기 때문인데 그 이유에 대해서는 시리즈의 뒷부분에서 설명하도록 하겠습니다.

그림11. Specular 만 활성화. 화이트볼은 metalic 이 0 이고 roughness 가 1 이지만 fresnel 효과에 의해 약간의 specular 를 먹습니다. 그 이유에 대해서는 시리즈의 뒷부분에서 설명하도록 하겠습니다.

Capture

이제 directional light 와 sky sphere 의 설정을 변경해서 초저녁 환경을 만들어 봅시다. 하지만 Sky Light 의 capture 는 변경되지 않습니다.

그림12. 환경을 변화시켜도 SkyLight 에 반영되지 않음.

 이때 빠르게 분위기를 확인하고자 한다면 SkyLight 액터의 "Sky Light" 카테고리에서 "Recapture" 를 누르시면 됩니다.

그림13. Recapture 를 통해 환경 반영.

하지만 그림13 에서 크롬볼에 반사되는 일부 메쉬들이 여전히 파란색 스카이 컬러를 먹고 있는 것을 알 수 있습니다. 앞에서 언급했듯이 specular 성분 중에서 Sky 를 제외한 다른 부분은 Reflection Capture 로부터 온 것입니다. Reflection Catpure 에서 "Update Captures"` 를 실행해 줘야 모든 specular 성분에 Sky 색상이 제대로 반영됩니다.

Capture 대상

여러분은 이 시점에 "어떤 대상을 SkyLight 로 캡쳐하는 것일까" 궁금해 하실 겁니다. 사실 대상이 정해져 있는 것은 아닙니다. SkyLight 액터가 배치된 위치로부터 일정 거리 이상에 존재하는 요소들을 Sky 로 취급합니다. SkyLight 액터의 "Light" 카테고리에는 "Sky Distance Threshold" 라는 속성이 있습니다. 이 값을 조정하시면 됩니다.

SkyLightActor 를 액터를 벽에 붙여 놓고 "Sky Distance Threshold" 를 1.0 으로 설정하고 "Recapture" 를 하면 그림14 와 같은 결과를 얻을 수 있습니다.

그림14. Sky Distance Threshold 를 작게 잡았을 때의 결과. 하늘이 나와야 할 부분에 벽이 나오는 것을 확인할 수 있습니다.

이 값은 기본적으로 150000 센티미터( 1.5 KM )로 잡혀 있습니다. 매우 큰 값이므로 SkyLight 액터를 아무 곳에나 배치해도 상관이 없는 것입니다. 만약 여러 분이 SkyLight 에 하늘 이외의 원경이 반영되었으면 좋겠다고 생각하신다면 이 문턱값을 조정하시면 됩니다. 물론 SkyLight 액터도 적절한 위치에 배치해야겠죠.

Sky Light 의 개수

이제 이 시점에서 여러 개의 Sky Light 를 배치해 다양한 효과를 내 보겠다는 생각을 하시는 분이 있을 수 있습니다. 하지만 현재 구현에서는 하나의 월드에 여러 개의 SkyLight 를 사용하는 것이 불가능합니다. Lightmass 빌드를 하면 다음과 같은 에러를 확인할 수 있습니다.

그림15. 여러 개의 SkyLight 사용시 lightmass 에러 발생.

+ Recent posts