잘못된 내용이 포함되어 있을 수 있으므로 이상하면 참고자료를 확인하세요


[ PBR 이란 무엇인가 ] 7. Light intensity 설정

개요

지금까지는 광도측정( photometry ) 관점에서 빛을 살펴 봤습니다. 사실 보면서 이게 "실무랑은 무슨 관계가 있나" 라는 의문을 가지시는 분들이 꽤 있었을 겁니다. 그러므로 딱히 이론적인 부분에 관심이 있는 분이 아니라면 지루하실 수도 있었을 겁니다. 이번 장부터는 앞에서 언급한 이론들을 UE4 를 사용해 실제로 적용해 보도록 하겠습니다.

[ 광원의 밝기( 광속 ) ] 에서는 광원의 밝기는 전방향으로 향하는 빛의 총량이라고 했습니다. 그리고 [ 조도( illuminance )측정 ] 에서 조도라는 것은 서피스에 들어 온 빛의 양을 의미한다고 했습니다. 그런데 PBR 로 오면서 조명의 개념이 약간 확장되었습니다. Area light 라는 개념이 생긴 것이죠. 말 그대로 범위를 가진 광원에서 나오는 빛을 의미합니다. 반대로 punctual light 는 point light, spot light 등 볼륨을 가지지 않는 기존 방식의 조명들을 의미합니다. 사실 우리가 매일 접하게 되는 실세계 조명들은 그림1 처럼 거의 다 area light 라 봐야겠죠. 

그림1. 실세계 조명들. 출처 : [1].

"Punctual" 이라는 것은 "시간을 엄수하는", "( 시간에 맞게 ) 정확한" 등의 뜻을 가지고 있습니다. 이것을 왜 area 의 반대 개념으로 사용하고 있는지는 저도 잘 모르겠습니다. Max, Maya, Frostbite 같은 프로그램/엔진들에서는 punctual light 를 photometric light 에 포함시켜 이야기하기도 합니다. 그림 2 에서 보이듯이 광원이 복잡한 IES( Illumination Engineering Society, 조명 공학회 ) profile 을 가지지 않는다면, 그냥 point light 나 spot light 가 되겠죠. 그런 것을 그냥 Simple/Isotropic profile 이라 부르는 것 같습니다. 

어쨌든 punctual light 를 사용하게 되면, ( GI 를 고려하지 않는다고 가정할 때 ) 한 광원에서 나오는 빛은 한 서피스에 한 번만 도달합니다.

그림2. Punctual light. 출처 : [1].

Puncutal light 와는 다르게 area light 는 그림3 에서 볼 수 있듯이 한 서피스에 대해 여러 번 빛을 방출하게 됩니다.

그림3. Area light. 출처 : [1].

여러 분은 그림2 의 punctual light 와 그림3 의 area light 를 보면서, "각각의 광원마다 빛의 세기( 밝기 )를 설정하는 방식이 달라야 한다" 고 느끼셨나요?

네 당연히 달라야 합니다. 왜냐하면 puncutal light 는 광도( luminous intensity, cd ) 관점으로 설정해야 하며, area light 는 광속( luminous flux, lumen ) 관점으로 설정해야 하기 때문입니다. Puncutal light 의 경우 특정 방향으로 가는 빛만이 서피스에 도달하므로, 전체 빛의 양을 의미하는 광속을 설정해서는 안 되겠죠. 광도와 광속의 차이가 이해가 안 가시면, [ 광원의 밝기( 광속 ) ]을 보고 오세요.

아티스트가 여러 단위를 써 가면서 이런 부분을 제어하는 것은 좀 스트레스를 받는 일이겠죠. 하지만 punctual light 의 경우에는 빛을 세기를 광도 단위로 설정하는 것이 좀 더 직관적일 수는 있습니다. 그냥 "얼마만큼의 빛이 특정 방향으로 전달되어야 해"라고 생각하는 것이 편하니까요. 그래서 프로스트바이트의 경우에는 단위를 직접 설정( 광속, 광도, 조도 )할 수 있도록 하고 있더군요[1]. CryEngine 3.6 같은 경우에는 조도 단위로 설정하도록 하고 있구요.

하지만 빛의 세기를 일괄적으로 광속 단위로 설정하더라도 이를 광도나 조도 단위로 변환하는 것은 엔진 내부에서 수행될 수 있습니다. 그러므로 이에 대해서 크게 걱정할 필요는 없습니다.

점광원의 광속을 기준으로 광도를 계산하는 경우를 생각해 봅시다. 구의 겉면적이 4π 이므로, 구의 단위 입체각을 통해서 나가는 빛의 양은 식1 과 같습니다.

식1. 점광원의 광도 계산.

그러면 점광원을 위해 설정한 설정한 광속( luminous flux )을 I 라 할 때( 원래는 φ( phi [fai] -- 'f' 발음 잘 안되니 '파이' 와 구분하기 위해 '화이' 라고 읽거나 그리스 발음인 '피' 라고 읽기도 합니다 )로 표기해야 하는데 여기에서는 그냥 I 로 표기하겠습니다 ), 조도( illuminance ) E 는 식2 와 같습니다.

식2. 점광원의 조도 계산.

식2 에서 곱하기 앞쪽은 이해하기 어렵지 않습니다. 방금 이야기한 부분이니까요. 그런데 뒤의 식이 갑자기 붙어 있으니 머리가 살짝 아파지실 겁니다. r 은 광원에서 서피스까지의 거리이고, θ 는 광원과 서피스 노멀( normal )과의 각도입니다.  [ 조도( illuminance )측정 ] 에서 조도에 영향을 주는 부분은 거리 및 기울기라고 이야기했던 걸 기억하시나요?

광원과 서피스의 거리가 멀어지면 빛의 양이 1/r2 으로 줄어들고, 기울기가 달라지면 빛의 양이 cos(θ) 만큼 줄어듭니다. 이럼 감쇠항들을 식에다가 넣으면 식2 의 형태가 되는거죠.

공식이 나오니 좀 당황스럽겠지만, 차분하게 생각해 보세요. 앞의 시리즈에서 언급했던 것을 공식으로 나타낸 것 뿐입니다

주의 : 아래 그림에서 한 점이라고 표현했는데, 단위 입체각을 의미합니다. 그림을 수정해야 하는데 귀찮아서 그냥 둡니다.

그림4. Illuminance 공식의 구성. 주의 : 그림에서 한 점이라고 표현했는데, 단위 입체각을 의미합니다. 그림을 수정해야 하는데 귀찮아서 그냥 둡니다.

그런데 실제 UE4 에서의 설정은 어떨까요?

이노무 언리얼!!!

지금부터는 UE4 를 까는 이야기를 할 겁니다. 가끔 UE4 구현을 보면 "이게 뭔 삽질인가" 라는 느낌이 들 때가 있습니다. 뭔가 구현은 해 놨는데 엉망으로 해 놨다는 느낌입니다. Frostbite 보다 그래픽스 품질이 떨어지는 것은 어쩔 수 없는 것 같습니다. 제대로 검증도 안 하고 구현하는듯...

어쨌든 본론으로 들어가, UE4 의 경우에는 광속 단위로 빛의 밝기를 설정하고 있습니다. 뭐 여기까지는 OK 입니다. Punctual light 의 경우에는 이를 엔진 내부에서 광도로 변환해 주면 되기 때문입니다.

하지만! 버뜨! 아놔! UE4 는 4π 로 광속을 나누는 것을 빼 먹었습니다. 게다가 미친 삽질까지 하나 더 합니다. 4.9 버전에서 "Correction for lumen units" 라는 작업을 한 것 같습니다. Inverse square falloff( 거리 제곱분의 1 로 감쇠 )를 사용하는 경우에, 빛의 밝기에 16 을 곱합니다.

16 이라는 속임수 값은 point light 가 거리의 제곱분의 1 로 감쇠될 때 기본 노출( exposure )에서 합리적인 밝기로 보일 수 있도록 하기 위해 추가한 X 같은 거야. Point light 감쇠를 위해 1/(4pi) 는 계산 안 해.

- Brian Karis, Epic Game, Inc.

- 출처 : UDN.

정말 할 말이 없습니다. Physically based 인데 완전 지 맘대로 값을 설정하고 있습니다. 에너지 보존 법칙을 위배한 데다가, 임의의 값을 곱하기까지 합니다. 이것 때문에 혼란스러워하는 사람들이 있는데, 그냥 씹어버립니다.

우리는 당신이 검토하고 있는 부분에 대해서 그리 많은 노력을 들이지 않았어. ( 중략 ) 요걸 좀 명확하게 정리하는 시간을 가지고 싶은데, 내 할 일 목록에 있는 다른 것들을 처리하느라 바뻐.

- Brian Karis, Epic Games, Inc.

- 출처 : UDN.

벌써 4.17 이 다가 오고 있는데, 검토는 안 하나 봅니다.

이 때문에 촛 불의 밝기 12.57( 혹은 15 ) 을 설정해도 촛불같지가 않습니다. 물론 UE4 는 100 이 1m 이므로, 1257 을 설정해 주긴 해야겠죠. 그렇다고 해도 촛불같지 않습니다.

그리고 감쇠 거리 설정을 어떻게 해야 할지 감을 잡기가 힘들 겁니다.

그럼 어떻게?

이런 상황에 대응하는 세 가지 자세가 있습니다.

    1. 현실을 인정하고, 감으로 값을 때려 넣는다.
    2. 현실을 인정하고, 적절한 값을 넣는다.
    3. 현실을 부정하고, 제대로 고친다.

뭐 1 번이야 알아서 하고 계실테고, 2 번과 3 번에 대해서 이야기하도록 하겠습니다.

현실 인정 -> 적절한 값 넣기

먼저 2 번 자세입니다. 앞에서 UE4 가 4π 로 나누는 것을 누락시키고 16 을 곱했다고 했습니다. 그러므로 값을 넣을 때 한 번 더 계산을 해 주는거죠.

내가 어떤 차트에서 I 루멘이라는 값을 가지고 왔다고 칩시다. 그러면 입력할 때 거리 팩터를 곱해주면 100 * I 루멘이 됩니다. 이걸 엔진 내부에서 다시 16 을 곱해서 최종적으로 1600 * I 루멘이 되죠. 그러면 식3 과 같이 현재값과 기대값 사이의 변환을 할 수 있습니다.

식3. 현재값과 기대값의 변환. 현재 넣는 값에 0.004973 을 곱해주면 됩니다. 물론 감쇠거리 계산도 적절히 해야겠죠.

현실 부정 -> 제대로 고치기

3 번 자세입니다. 그냥 제대로 고치는 겁니다. 물론 엔진 프로그래머가 고쳐줘야겠죠. [2] 번과 동일하게 고치면 별 문제가 없을 것이라 봅니다.

그림5 : 다양한 광속( luminous flux, luminous power ). 출처 : [2]. 태양의 경우에는 거리감쇠가 없으므로 바로 조도( lux )로 표시. UE4 에서는 거리 팩터( 100 = 1m )를 적용해 각각 1500, 120000, 260000 으로 넣어 줘야 합니다. 물론 그냥 루멘값을 넣으면 내부적으로 100 을 곱해주는 것도 방법이겠죠( 그림6 참조 ).

그러면 여러 차트에 있는 값들을 그냥 가져다 쓸 수도 있고, 감쇠 거리도 계산하기 편해집니다. 예를 들어 10 미터까지 도달하는 라이트를 설정하고 싶다면 제곱하면 됩니다. 즉 10 X 10 = 100 루멘을 설정하는 거죠. 반대로 루멘을 통해 감쇠거리를 계산하고 싶다면 root 를 씌우면 됩니다( 제곱근을 구하면 됩니다 ). 앞의 예로 보면 100 루멘짜리 라이트는 10 미터를 가는 것이죠.

그림6. UE4 엔진 코드를 수정하고, 두 개의 촛불을 넣음( 천장에 있는 등불도 촛불 밝기 ). 각각 15 루멘( 촛불 하나의 밝기 ) 설정. 15 루멘 입력하면 100 / ( 4 * pi ) 를 자동으로 적용해서 칸델라로 변환합니다. Eye-Adaptation 이 완전히 적용된 상태입니다.

그림7. 비교 자료. 실제 촛불 하나의 느낌. 출처 : DevianArt.

정리

현재 UE4 엔진은 조도계산 시에  4π 로 나누는 것을 누락시키고 16 을 곱했습니다. 그래서 외부 차트의 값을 가져다 쓰기도 애매하고 감쇠거리를 설정하기도 애매합니다. 뭘 기준으로 계산을 해야 할지 부정확하기 때문입니다.

게다가 directional light, punctual light, area light 의 라이트 세기 설정 방식이 전부 다릅니다. 그냥 이쪽 프로그래밍한 사람 맘대로입니다. 정말 한 대 쥐어 박고 싶군요.

개인적으로 PBR 을 도입하는 데 있어서 중요한 부분이 "단순성과 일관성" 에 있다고 봅니다. 아티스트가 값을 결정하는 데 있어서 의구심을 가질만한 요소가 적어야 한다고 봅니다. 그리고 아티스트가 외부 자료들을 보고 쉽게 자신만의 설정값 데이터베이스를 구축할 수 있어야 하겠죠.

그러므로 엔진을 고치든지 제대로 된 값을 계산해서 넣든지 해야 할 것이라 생각합니다. 아니면... 감쇠거리로부터 루멘을 계산해 주는 유틸리티를 추가하는 것도 방법이겠죠. 일반적으로 아티스트들에게는 일정 거리까지 도달하는 빛을 만드는 것이 목적일테니 말이죠.

참고 자료

[1] Moving FROSTBITE to PBR, Sebastien Lagarde & Charles de Rousiers.

[2] Moving Frostbite to Physically Based Rendering 3.0, Sebastien Lagarde & Charles de Rousiers.

+ Recent posts