그래픽스에서 라이팅을 많이 다루는 이유
유니티나 언리얼 같은 3D 엔진에서 라이팅은 매우 중요하다. 라이팅 하나만으로도 게임의 분위기를 반대로 바꿀 수도 있다. 라이팅에 대해 구체적으로 알아보고자 한다.
(사용방법 관한 내용보다는 비교 위주로 포스팅하도록 하겠다. 사용방법은 공식문서나 공식 블로그에 많이 나와있기 때문.)
라이팅을 잘 다루려면 다음과 같은 정보를 알아야 한다.
- 직접 조명과 간접 조명
- 라이트 프로브와 리플렉션 프로브
- 라이트 매핑과 셰도우 매핑
- 조명(빛)과 그림자
라이팅 기초 배경 지식
빛의 물리적 특징
게임엔진에서 라이팅은 현실세계의 라이트 물리 일부를 그대로 구현한다.
그렇다면 우리가 살아가는 세상의 빛 특징은 어떻게 있을까? 큰 특성은 다음 표와 같다.
투과(transmission) | 굴절(refraction) | 반사(reflection) |
산란(scatter) | 회절(difraction) | 흡수(Absorption) |
현실세계는 이처럼 복잡한 구조를 띄고 있지만, 게임엔진과 같은 컴퓨터 그래픽에서는 라이팅을 크게 직접광과 간접광으로 나누어 표현한다. 두 가지만 사용해도 사실적인 표현이 가능하기 때문이다. 물론 위의 표에 나타난 모든 특징들은 셰이더를 사용하여 추가적으로 나타낼 수 있다.
직접광과 간접광
직접광과 간접광은 라이팅에서 가장 기본적이고 필수적인 세팅값이다. 이 둘에 대해 더 자세히 알아보자면,
직접광(direct lighting) | 빛이 방출되어 표면에 한번 반사된 후 직접적으로 들어오는 빛 |
간접광(indirect lighting) | 빛이 방출되고 표면에 여러번 반사된 빛 혹은 스카이 라이트 같은 빛 |
결국 라이팅은 이 두 가지를 잘 표현하는 것에서부터 시작한다. 하지만 단순히 라이트와 관련된 세팅을 이것저것 조작한다고 해결되지는 않는다. 게임엔진에서 빛의 표현은 컴퓨터에게 많은 연산을 요구하고, 이는 최적화와 직결되기 때문이다. 그래서 유니티에서는 다음과 같은 기능을 제공한다.
실시간 라이팅과 베이크 된 라이팅
이 두 가지가 무엇이고 어떻게 해결해 준다는 것일까?
실시간 라이팅과 베이크드 라이팅을 간단히 정리하면 다음과 같다.
실시간 라이팅(real-time lighting) | 런타임 동안에 빛과 관련된 연산을 수행 |
베이크된 라이팅(baked lighting) | 라이트 정보를 미리 계산해두고 런타임 시점에 적용 |
실시간 라이팅은 주로 직접광과 관련된 계산을 수행한다. 직선광은 한번 반사되어 우리 눈에 들어오는 빛이다. 그래서 연산도 한 번만 하면 된다. 따라서 상대적으로 가볍다! 디테일하진 않더라도 그림자를 나타내거나 스페큘러를 나타내기에 충분하기 때문에 게임에서 많이 사용한다.(매 프레임마다 처리해야 하는 정보가 한둘이 아닌 게임에서는 가장 효율적인 처리과정일 것이다.)
베이크드 라이팅은 직접광과 간접광 모두와 관련된 연산을 한다. 간접광은 빛이 물체 표면에 부딪쳐 이리저리 산란되고 반사되는 과정을 계산한다. 따라서 주변의 광원 개수와 디테일 정도에 따라서 아주 많은 연산을 할 수도 있다. 리얼타임에 비해서는 확실히 무겁기 때문에 유니티에서는 간접광을 실시간으로 연산하지 않는다(하지만 최근에 HDRP에서 레이트레이싱을 지원하기 시작했다). 대신 유니티 같은 게임엔진에서는 베이킹이라는 것을 한다. 간접광과 관련된 정보를 라이트 맵이라는 텍스쳐 형태로 저장(베이크)하는 것이다. 그리고 이 정보를 인게임에 적용시킨다. 이렇게 하면 미리 연산을 다 해놓은 결괏값을 맵에 붙이기만 하면 되므로 많은 부하를 줄일 수 있다. 이처럼 라이팅 정보를 베이크 하여 텍스쳐 형태로 저장해 두는 과정을 유니티에서는 라이트매핑이라고 한다.
라이트매핑(베이크드 라이팅)만이 간접광을 계산하여 적용하는 것은 아니다.
유니티는 인라이튼 실시간 조명(Enlighten Realtime Global Illumination)을 제공한다. 실시간으로 간접광을 계산하여 게임에 적용시켜 주는 기술이다. 그러나 이 방법은 Baked에 비해 현저히 성능이 떨어지며 복잡한 연산을 미리 해두어 성능에서 이점을 볼 수 없기 때문에 잘 사용하지 않는다. 위에서 언급했듯이 HDRP의 레이트레이싱 역시 실시간으로 간접광을 계산하여 적용해 주는 기법이다. 이 외에도 동적인 오브젝트나 작은 오브젝트에 사용하는 라이트 프로브도 라이트 매핑을 대신하여 GI에 많이 사용된다.
실시간 라이팅과 베이크드 라이팅의 차이점
이 둘의 가장 큰 차이점은 빛과 관련된 연산을 하는 시점의 차이이다. Baked lighting은 미리 연산을 수행해 놓기 때문에 고품질의 빛 효과를 연출할 수 있다(물론 얼마나 고품질로 하냐에 따라 시간 차이가 상이하겠지만...). Baked lighting의 장점은 Real-time으로 연산을 수행했을 때 보다 더 수준 높은 그래픽과 불필요한 라이팅 계산을 줄여 컴퓨터 부하도 적게 걸리게 한다는 점이다.
그러나 Baked lighting의 단점 역시 존재한다. 베이크드 라이팅은 지형과 관련된 라이팅 정보를 텍스쳐 형태로 저장한다. 마치 3D 오브젝트의 노멀맵이나 하이트맵, 앰비언트 오클루전 맵과 같다고 생각하면 된다. 이러한 3D 오브젝트 맵의 크기는 메모리에 영향을 줄 정도로 크진 않다.
그러나 3D 오브젝트가 아닌 베이크드 맵이라면? 그리고 그것이 오픈월드처럼 방대하다면? 이러한 맵의 라이팅 정보를 모두 텍스쳐로 저장하게 된다면 꽤나 큰 메모리 이슈를 불러올 수 있다. 그것이 디테일하면 할수록 더욱 큰 영향을 끼칠 것이다. 물론 PC 같은 고사양 기기에서는 문제 될 리 만무하다. 반면 모바일의 경우 메모리 이슈를 일으키기에 충분하다.
또한 Baked lighting은 라이팅 효과가 정적인 오브젝트에만 적용 가능하다는 한계점이 있다. 이를 위해 Mixed lighting이 있지만 static 오브젝트(Contribute GI)를 수정했을 경우 다시 베이크를 해야 하는 수고로움은 해결할 방법이 없다. 이는 맵의 크기와 컴퓨터 사양에 따라서 유의미한 시간 소요를 불러올 수 있다.
다음은 실시간 라이팅과 베이크드 라이팅의 차이이다.
리얼타임 라이팅 | 베이크드 라이팅 |
리얼타임 라이팅에 비해 베이크드 라이팅이 훨씬 자연스러운 분위기를 연출한다. 간접광도 함께 계산하기 때문이다.
간접광 정보를 텍스쳐 형태로 저장하기 때문에 씬 폴더에 베이크드 맵 정보가 함께 들어가게 된다.
만약 해상도가 높고 맵의 크기가 크다면 메모리 이슈가 발생할 수 있으므로 모바일 같은 저사양 디바이스일 경우에 유의해야 한다.
그렇다면 언제 사용할 수 있을까?
정적, 다시 말해 움직이지 않는 물체에 사용하면 매우 효과적이다. 집안의 램프, 전등 혹은 가로등 주변의 정적인 물체에 사용하면 미리 연산해 놓은 고품질의 라이트 데이터를 곧바로 적용시킬 수 있다. 반면 캐릭터 같은 움직이는 물체는 real-time lighting을 사용해야 한다. 움직이는 형태에 따라 빛과 그림자가 바뀌어야 하기 때문이다.
중요한 것은 맵의 모든 영역을 다 베이크 하는 것이 아니라 간접광에 의한 디테일한 라이팅 표현이 필요한 구간에만 써야 한다는 점이다. 성능과 메모리 사이에 적절한 지점을 개발자가 고려하면서 베이크 할 필요가 있다.
(실외는 Directional Light를 사용하고 실내의 경우 간접광과 함께 Bake 하는 경우가 예시이다.)
사용방법은 어떻게 될까?
baked lighting은 정적인 물체에 쓴다는 것에 주목하자. 정적은 영어로 static이다. 따라서 유니티에서 해당 오브젝트가 정적이라는 것을 먼저 알려줘야 한다.
Baked lighting을 적용시키고자 하는 물체의 인스펙터 창에서 Static을 Contribute GI로 변경해주어야 한다.
이는 해당 물체를 고정된 물체로 만들어 간접광의 영향을 받게 하겠다는 의미이다.
혹은 아래와 같이 MeshRenderer에서 Contribute GI를 체크해 준다. (Contribute GI를 Static으로 하는 것이기 때문에 같다.)
물체를 정적으로 만들어 줬다면 어떤 광원을 간접광까지 연산하게 할지 선택해주어야 한다.
광원에 들어가서 Mode를 Baked로 변경해 주자.
모드가 변경된 광원은 GI Static 오브젝트에만 영향을 준다.
그러고 나서 window-rendering-lighting-generate lighting을 하면 해당 광원들의 정보가 GI(global illumination-전역 광원) 데이터로 저장된다. 이후에 씬을 실행하면 이 데이터를 불러와서 맵에 적용된다.
Emissive Material을 사용한 라이트매핑
만약 발광 머테리얼을 사용한다면 머테리얼의 Emission에 체크가 되어 있고 GI가 Baked인 상태여야 한다.
당연히 매쉬 렌더러는 GI Static(Contribute Global Illumination)이어야 하며 Baked모드의 광원으로부터 빛을 받아야 한다.
아래 표는 GI와 포스트 프로세싱에 따른 렌더링 차이이다.
GI (OFF), Post Processing (ON) | GI (ON), Post Processing (OFF) | GI (ON), Post Processing (ON) |
만약 결과가 어딘가 부족하게 나온다면 다음을 확인해 보자.
- 오브젝트의 Contribute GI가 Static으로 되어있는가? (혹은 매쉬렌더러에 Contribute Global Illumination이 체크가 되어 있는가)
- 광원(빛)의 Mode가 Baked(혹은 Mixed)로 되어 있는가?
- Volume(URP/HDRP) 혹은 Post Processing(Built-in)을 씬에 추가했고 Post-processing의 Bloom이 추가되었으며 카메라에 적용이 되었는가?
- (만약 Emission을 적용했다면) 머테리얼의 Emission이 체크되어 있고 Baked로 되어있는가?
위 방법대로 했음에도 적용이 되지 않는다면 커스텀 셰이더를 사용했을 가능성이 있다.(일부 커스텀 셰이더는 Bake 되지 않는다!) 문제가 발생할 경우 라이트 프로브를 적용하는 방법을 고려해야 한다.
'유니티 > 그래픽스' 카테고리의 다른 글
[Unity] HDRP (4) - 포스트 프로세싱 (0) | 2022.08.11 |
---|---|
[Unity] HDRP (3) - 렌더링 보정 (0) | 2022.08.10 |
[Unity] HDRP (2) - 빛과 그림자 (0) | 2022.08.09 |
[Unity] HDRP (1) - 프로젝트 세팅 및 볼륨 (0) | 2022.08.09 |
[Unity] 라이트 세팅 (2) - 라이트 프로브와 리플렉션 프로브 (0) | 2022.05.07 |