회사에서 거리에 따른 texture streaming 을 하는 경우에 대해 이야기하다가 논쟁거리가 하나 생겼습니다.

 

저는 "직접 texture streaming 을 하면 더 작은 크기의 texture 를 적용하는 데다가 GPU 에서 계산되는 LOD 까지 영향을 받아 LOD bias 를 둔 것과 같은 효과가 나와서 품질 조절이 힘들다" 라는 주장을 했습니다.

 

하지만 같이 일하는 분은 "texture 의 해상도를 고려해서 LOD 계산이 되기 때문에 서로 다른 크기의 texture 를 binding 해도 참조하는 LOD 는 결과적으로 동일하다" 는 주장을 했습니다.

 

제가 이쪽 LOD 계산에 대해서 구현 문서를 실제로 본 적은 없고 ddxddy 를 이용한다고만 알고 있었기 때문에, 그럴 수도 있다는 생각이 들었습니다. 만약 texture 크기가 고려된다면 texture streaming 시에 LOD 문제에 대해서 걱정할 필요가 없어지겠죠. 그래서 실제로 그런 결과가 나오는지 테스트해 보기로 했습니다.

 

무지개색을 가진 mipmap chain 을 가진 텍스쳐를 생성했습니다. 1024 texture 와 512 texture 를 만들어서 테스트해 봤습니다. 둘 다 붉은색이 가장 낮은 LOD( Mipmap 0 )이며 보라색이 가장 높은 LOD 입니다. 결과는 다음과 같았습니다.

 

그림 1

 

여기에서 성급하게 결론을 내리려다가, 직접 texture streaming 이 들어 가는 경우에는 작은 texture 의 LOD0 은 큰 texture 의 LOD1 에서 시작해야 된다는 것을 상기했습니다. 그래서 512 texture 의 LOD0 이 주황색에서 시작하도록 texture 를 새로 만들어서 테스트해 보았습니다.

 

 

그림 2

 

결국 거리별로 texture streaming 단계를 조정해도 실제 참조해야 하는 LOD 는 거의 같다는 결론이 나왔습니다. 제가 틀렸습니다 ㅠㅠ.

 

물론 더 낮은 LOD 가 나와야 하는 곳에서 필요한 해상도의 texture 가 공급되어 있지 않으면 그림 3 과 같이 이상한 결과가 나오겠지만 말이죠. 그건 어쩔 수 없는 문제인 것 같습니다.

 

그림 3

 

정리하자면 mipmap level 결정에는 texture 크기가 반영되므로 texture streaming 을 직접 하는 경우에도 LOD bias 가 먹을 것을 걱정할 필요가 없습니다

 

주의할 점은 이게 pixel 단위 해상도 보장이 아니라는 것입니다. 밑에 코드에서 알 수 있듯이, 같은 기울기와 같은 uv 변화량을 공유하는 경우에만 동일한 pixel 단위 해상도가 보장됩니다. 만약 uv 가 다르면 texture size 에 따라 pixel 단위 해상도가 틀려집니다. 즉 같은 메쉬 내에서도 pixel 단위 해상도는 달라질 수 있다는 것입니다.

 

===============================

 

실제 virtual texturing 을 위해서 mipmap level 을 계산하는 샘플을 찾아 봤습니다( 출처 : Texture LOD calculation( useful for atlasing ).

 

 

위와 같이 한 뒤에 mipLevel 을 사용해 tex2Dlod() 를 호출해 주면 된다고 하네요. 물론 위의 코드는 일부이고 이런 저런 이슈가 있다고 하네요.

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

주의 : 여기에서 Specular BRDF 공식에 대해서는 자세하게 다루지 않을 계획입니다. 공부를 더 하고 싶은 분들은 참고자료들을 확인하세요.


[ PBR 이란 무엇인가 ] 20. Specular BRDF

19. Diffuse BRDF ] 에서 Diffuse 를 위해서는 일괄적으로 Lambert cosine law 를 사용한다고 했습니다. 그렇기 때문에 금속이 아니라면 Diffuse 는 모든 재질에 대해서 동일하게 적용된다고 보면 됩니다.

Disney 의 Burley 같은 경우에는 새로운 diffuse model 을 만들었습니다. 그러나 그것은 그림1 처럼 외형상 큰 차이를 보여주지 못하고 계산 비용만 더 들기 때문에 실시간 그래픽스에서 잘 사용되지 않습니다.

그림1. Lambert 모델과 Burley 모델의 비교. 큰 차이를 보이지는 않음. 출처 : [ 1 ].

하지만 Specular 의 경우는 다릅니다. 물체가 어떤 재질로 구성되어 있느냐에 따라 달라집니다( 물론 Diffuse 도 그래야 하지만 대충 넘어갑시다. 두 마리 토끼를 잡기는 쉽지가 않으니... ). Specular 는 물체의 재질특성을 보여주는 매우 중요한 요소입니다. 그러므로 여러분이 한 번이라도 들어 본 대부분의 BRDF 모델들은 거의 다 specular 와 관련된 것일 겁니다. 그렇기 때문에 조금 비용을 들이더라도 재질 차이를 확실하게 보여 줄 만한 모델을 사용하죠.

지금까지 우리는 PBR 의 대원칙들에 대해서 살펴 봤습니다. 처음에 글을 시작하면서 PBR 이라는 것은 다음과 같은 요소를 포함해야 한다고 했습니다.

    • 전역 조명( Global Illumination ) : 이미지 기반 라이팅( Image Based Lighting, IBL ).
    • 에너지 보존 법칙( Energy Conservation ).
    • 반사도( Reflectivity ) : 디퓨즈 및 스펙큘러( Diffuse & Specular ).
    • 미세면( Microsurface ) : 러프니스( Roughness ).
    • 프레넬 법칙( Fresnel's Law ).
    • 금속성( Metalicity ).

[ 1. 인간과 빛 ], [ 2. 조도와 휘도 ], [ 3. 빛의 감쇠 ], [ 4. 광원의 밝기( 광속 ) ], [ 5. 조도( illuminance ) 측정 ], [ 6. 휘도( luminance ) 측정 ] 에서는 빛의 기본 성질에 대해서 중점적으로 이야기했습니다.

[ 7. Light Intensity 설정 ], [ 8. Attenuation Radius 설정 ] 에서는 에너지 보존 법칙에 대해서 중점적으로 이야기했습니다.

[ 9. Global Illumination & Indirect Lighting ], [ 10. Image Based Lighting ], [ 11. UE4 GI : Sky Light ], [ 12. UE4 GI : Reflection Capture ], [ 13. UE4 GI : Lightmass & Mobility ] 에서는 전역 조명에 대해서 중점적으로 이야기했습니다.

[ 14. 모든 것은 빛납니다 ], [ 15. 모든 것은 프레넬을 가집니다 ], [ 17. Fresnel 이란 ] 에서는 프레넬 법칙에 대해서 중점적으로 이야기했습니다.

[ 실제 이미지에서 specular 와 Diffuse 분리하기 ], [ 16. Reflection 에 대한 잘못된 상식들 ], [ 18. Distribution Function ] 에서는 반사도와 Diffuse  및 specular 개념에 대해서 중점적으로 이야기했습니다.

[ 19. Diffuse BRDF ] 에서는 Diffuse BRDF 에 대해서 중점적으로 이야기했습니다.

마지막으로 이번 챕터에서는 미세면과 금속성에 대해서 다루게 됩니다. 아무런 순서도 없이 두서없이 글을 진행한 것처럼 보였을지 모르겠지만 나름대로 생각하고 진행했답니다.

이제 PBR 에서의 specular BRDF 에 대해 중점적으로 살펴 보도록 하겠습니다.

MERL 100

Diffuse 및 Specular 모델을 만들 때 그냥 내키는 대로 만드는 것은 아닙니다. 어떤 기준이 있어야 하며 실제로 측정된 BRDF 모델을 사용하는 경우가 많습니다. 그 중 하나가 MERL 100 이라는 것이죠.

그림2. MERL 100. 출처 : [ 2 ].

이것은 100 개의 등방성 재질을 캡쳐한 것으로 페인트, 나무, 금속, 섬유, 돌, 플라스틱, 그리고 다른 합성 재질들을 포함합니다. 이것은 새로운 BRDF 모델들이 나오면 그것의 결과가 얼마나 맞는지 확인하기 위해서 사용됩니다. 자세한 내용에 대해서 알고자 한다면 [ 2 ] 를 확인하시기 바랍니다.

이 MERL 100 을 반드시 이해하실 필요는 없습니다. 지금 우리가 다루고 있는 영역이 매우 물리적인 측정값에 기반하고 있다는 것을 강조하기 위해서 언급했을 뿐입니다.

미세면 분포 함수 : D 함수

미세면( Microfacet ) 모델이라는 것은 폴리곤의 거대면( Mcrosurface )은 그림3 처럼 미세면으로 구성되어 있다는 전제에서 반사나 굴절을 모델링하는 것을 의미합니다.

그림3. Microsurface vs Macrosurface. 출처 : [ 4 ].

여러분은 반짝반짝 빛나는 면들은 매끈하고 빛나지 않는 면들은 거칠다는 것을 경험적으로 알고 계실 겁니다. 물론 코팅을 해 놓아서 빛날 수도 있지만 그 부분은 예외로 두고 이야기하도록 하겠습니다.

하지만 이러한 미세면의 분포는 재질마다 다를 겁니다. 그것을 Albedo 설정하듯이 아티스트가 일일이 설정할 수는 없겠죠. 게다가 미세면이라는 것은 거의 눈에 보이지도 않는 면을 이야기합니다. 

그러므로 몇 가지 매개변수만으로 분포를 계산할 수 있는 함수가 필요하겠죠? 그것이 바로 미세면 분포 함수입니다. Distribution 의 머리글자를 사용해 이 함수를 D 라고 부릅니다. 일반적으로 이 D 함수는 통계적인 기법을 이용하게 됩니다. 여기에서 세부 사항에 대해서 구체적으로 다루지는 않겠습니다. 더 구체적으로 알고 싶은 분들은 참고자료를 확인하세요.

어쨌든 이 D 함수는 Specular Peak 가 어느 방향을 향하게 되는지를 결정하게 됩니다. 우리는 Specular 를 거울면 반사라고 생각하고 있지만, 사실은 입사각과 반사각이 정확하지는 않습니다. 이는 표면의 거칠기( 미세면의 분포 )에 영향을 받게 되죠. 거울면 반사에서 벗어나서 생기는 반사를 "Off-specular reflection" 이라고 부릅니다. 우리 말로 굳이 해석하자면 "거울면 반사( 정반사 )를 벗어난 반사"라고 할 수 있겠습니다.  

그림4 는 서로 다른 미세면 분포를 가진 표면에서 이 off-specular reflection 이 어떻게 달라지는 지 보여 줍니다.

그림4. Off-specular reflection 의 예: 지표각에 가까워질 수록 specular 의 방향( M ) 이 반사각과 달라지는 모습을 보여줌.

D 함수는 거대면 내에서 우리에게 거울면 반사를 보여줄 수 있는 미세면의 양이 얼마나 되는지를 알려주는 것입니다( 여기에 대해서 제가 이해한 바를 정리해 봤는데, 자세히 알고 싶으신 분들은 [ PBR Specular D 의 기하학적 의미 ] 를 참고하세요 ).

미세면 그림자 마스킹 함수 : G 함수

자, 여러분은 렌더링할 때 그림자가 없으면 매우 어색하다는 것을 알수 있습니다. 미세면의 그림자의 경우에는 그 정도 영향력을 가지지는 않습니다. 눈에 거의 보이지도 않는데 알게 뭡니까.

그림5. 그림자 마스킹의 기하학. 같은 미세면 노멀 m 을 사용하는 세 지점을 비교.

두 개는 io 방향에서 보이며, 한 개는 i 방향에서 차폐됨.

하지만! 에너지 보존 법칙을 위해서는 반드시 고려되어야 합니다. 그런데 미세면 그림자 마스킹도 아티스트가 작업할 수 있을만한 건 아니겠죠. 그래서 이것도 함수로 만듭니다. 기하와 관련된 것이므로 Geometry 의 머리글자를 사용해 G 함수라고 부릅니다. 

프레넬 함수 : F 함수

모든 물체의 반사에는 프레넬 법칙이 적용된다고 했습니다. 그러므로 당연히 이것과 관련한 함수도 있겠죠? Fresnel 의 머리글자를 사용해 F 함수라고 부릅니다. 프레넬이 뭔지 기억이 가물가물한 분들은 [ 17. Fresnel 이란 ] 을 참고하세요.

금속일수록 거의 거의 대부분의 각도에서 높은 반사율을 보이며, 비금속일수록 지표각에서 높은 반사율을 가집니다.

모아봅시다

자 위에서 언급한 세 개의 함수를 모아 봅시다. 어떤 빛이 표면에 들어 오면 미세면 분포 함수 D 에 의해서 off-specular reflection 이 결정될 것이고, 기하학적 감쇠 G 함수에 의해 에너지 보존 법칙이 적용됩니다. 그리고 프레넬 함수 F 에 의해 반사율이 결정됩니다.

눈치가 빠른 분들은 DG 가 표면의 거칠기( roughness )와 관련이 있고 F 가 금속성( metallicty )과 관련이 있음을 알 수 있을 겁니다. 이걸 제대로 표현하기 위해서는 매우 많은 공식이 필요하지만 여기에서는 구체적으로 언급하지 않겠습니다. 참고자료를 확인하시기 바랍니다.

그러므로 미세면과 프레넬을 고려한 Specular BRDF 는 기본적으로 식1 과 같은 형태를 띄고 있습니다. 우리가 위에서 언급하지 않은 것들이 분모에 들어 가 있지만 별로 신경쓰실 것은 없습니다. 빛과 표면의 관계 및 에너지 보존을 위해 들어 가 있습니다 :

식1. Specular BRDF.

식1 은 Cook-Torrance 의 BRDF 모델이며, D, F, G 를 위해 어떤 알고리즘을 사용하느냐에 따라 결과가 조금씩 달라집니다. UE4 의 경우에는 D 를 위해 GGX 를, G 를 위해 Smith Approximation 을, F 를 위해 Shilick's approximation 을 사용합니다. 여러 가지 변종들에 대해서 궁금하다면, Graphic Rants 블로그의 [ Specular BRDF Reference ] 를 참조하세요.

여기에서 식을 언급하기는 했지만 굳이 이해하실 필요는 없습니다. 위에서 언급했듯이 roughness 값은 DG 에 영향을 주고, metallic 값은 F 에 영향을 준다는 것만 알고 계시면 됩니다.

Roughness 는 반사의 선명도 및 확산과 관련이 있으며, metallic 은 반사의 세기 및 각도와 관련이 있습니다.

UE4 에서 Metallic

UE4 에서의 Metallic 에 대해서 이야기해 보도록 하겠습니다. 이것은 프레넬과 관련이 있다고 했습니다. 정확하게 말하자면 F0 값입니다. 굴절률( Index Of Refraction, IOR ) 값으로부터 결정됩니다. 이에 대해서는 [ 17. Fresnel 이란 ] 에 자세히 언급했으니 기억이 안 나면 참고하세요. 앞의 링크에는 IOR 을 F0 로 변경하는 공식과 IOR 정보를 알 수 있는 사이트에 대한 링크가 있습니다.

그림6. 프레넬 공식 그래프. 출처 : Real-Time Rendering, 3rd Edition.

금속들은 수직으로 내려다 봤을 때( 즉 0 도일 때 ) 0.5 이상의 F0 값을 가지고 있으며, 보석같은 경우에는 0.2 이하의 F0 의 값을 가지고 있으며, 우리가 자연에서 볼 수 있는 많은 물체들은 0.05 이하의 값을 가지고 있습니다.

낮은 F0 값을 가질 수록 지표각에 가까워져야 반사가 제대로 보입니다. 이 F0 값이 Metallic 값으로 사용됩니다. 즉 높은 metallic 값을 가진다면 아무데서나 봐도 반사가 잘 보이고, 낮은 metallic 값을 가진다면 지표각에 가까워야 반사가 잘 보인다는 의미입니다.

이것의 활용법에 대해서는 아래에서 자세히 다루도록 하겠습니다.

UE4 의 SpecularColor

그런데 여기에서 하나 알고 넘어 가야 할 것이 있습니다. UE4 에서의 SpecularColor 의 개념입니다. 원래 SpecularColor 라는 것은 재질의 색상이 아니라 빛의 색상을 의미해야만 합니다. 하지만 완벽하게 Metallic 이 0 이거나 1 인 경우는 거의 없기 때문에 이 색상이 섞이게 됩니다. 그리고 전에 말씀드렸듯이 금속은 모든 빛을 흡수해서 방출하기 때문에 Diffuse 가 존재하지 않고 Specular 는 금속 특유의 색상이 나오게 됩니다. 그런데 SpecularColor 핀을 추가하는 것은 ( 성능 및 메모리에 ) 부담이 되므로 BaseColor 를 마치 금속 특유의 방출 색상인 것처럼 사용하게 됩니다.

이를 위해 UE4 는 다음과 같이 SpecularColor 와 DiffuseColor 를 적용합니다.

SpecularColor = lerp( 0.08 * Specular.xxx, BaseColor, Metallic.xxx ); 

공식이 어려워 보이겠지만 잘 생각해 보면 그렇게 어렵지는 않습니다. 먼저 SpecularColor 를 결정하는 공식을 보죠. lerp( x, y, z ) 는 x 와 y 값을 z 값으로 선형보간합니다. 알파블렌딩과 유사하다고 보시면 됩니다. z 가 0 이면 x 값이 1 이면 y 값이 나오는 것이죠.

만약 완전한 금속( Metallic = 1 )이라면 BaseColor 를 사용하고, 완전한 비금속( Metallic = 0 )이라면 0.08 * SpecularColor.xxx 를 사용합니다. 여기에서 xxx 라는 것은 rgb 각 채널에 한 값을 반복해서 넣겠다는 의미입니다. 즉 값이 vector 가 아니라 scalar 라는 의미죠. 저 SpecularColor 라는 것은 material node 에 존재하는 "Specular" 핀을 의미합니다.

그림7. Material Specular 핀.

어떤 분들은 여기에다가 Color 를 넣는 경우가 있는데, 핀 이름이 "Specular Color" 가 아니라 "Specular" 인 이유가 있습니다. 이 경우에는 scalar 값이므로 Color 를 넣어도 의미가 없습니다. 만약 color 를 넣게 된다면 R 채널만을 사용하게 됩니다.

그림8. 잘못된 Specular 값 설정. 이 경우에는 R 채널값이 0.839 가 사용됨.

자 이제 다시 본론으로 돌아와서 Specular 값에 0.08 이 곱해져 있으므로 이건 상당히 미세한 값을 의미합니다. Specular 를 설정하지 않으면 기본값이 0.5 이므로 실제로는 0.04 의 값이죠. 다시 말해 Metallic 이 0 이어도 specular 가 0.4 정도의 비율로 반영된다는 의미입니다.

SpecularColor 값은 다음과 같이 결정됩니다.

 Metallic

 Equation

 Result

 0

 0.08 * Specular.xxx * ( 1 - 0 ) + BaseColor.xyz * ( 0 ) 

 0.08 * Specular.xxx

 0.3

 0.08 * Specular.xxx * ( 1 - 0.3 ) + BaseColor.xyz * 0.3

 0.056 * Specular.xxx + 0.3 * BaseColor.xyz

 0.6

 0.08 * Specular.xxx * ( 1 - 0.6 ) + BaseColor.xyz * 0.6

 0.032 * Specular.xxx + 0.6 * BaseColor.xyz

 1.0

 0.08 * Specular.xxx * ( 1 - 1 ) + BaseColor.xyz * 1

 BaseColor.xyz

BaseColor 는 RGB 채널을 모두 쓰고 SpecularColor 는 scalar( 즉 회색 ) 입니다. 그래서 Metallic 이 1 에 가까워지면 SpecularColor 의 색상에 BaseColor 가 많이 묻어 나오고, 0 에 가까워지면 BaseColor 의 색상이 덜 묻어 나옵니다. BaseColor 의 색상이 덜 묻어 나온다는 것은 순수한 라이트의 색상에 가깝다는 의미죠. 대신 SpecularColor 에 0.08 이 곱해져 있으므로 specular higthlight 가 약해집니다.

그림9. Metallic 에 따른 specular color. Diffuse 를 off 시킴.

광원이 흰색이라 차이가 잘 안 드러나죠? 보라색 광원을 하나 배치해 보겠습니다.

그림10. 색상이 있는 광원을 추가했을 때의 결과. 순서는 그림 9와 같음. Diffuse 를 off 시킴.

그림10 을 보면 metallic 이 낮을수록 원래 광원의 색상이 더 반영된다는 것을 확실히 알 수 있습니다.

그런데  비금속( metallic 이 0 이 아님 )인 경우에 specular 를 올리거나 내리고 싶은 경우가 있습니다. 이럴때 바로 "Specular" 핀을 사용하는 것입니다. 그러면 그림11 과 같은 결과가 나옵니다.

그림11. Metallic = 0 일때 Specular 설정 결과 비교. Diffuse 를 off 시킴.

만약 비금속재질에서 반사에 라이트 색상을 보존하고 싶으시다면 SpecularColor 를 구하는 공식을 프로그래머에게 변경해 달라고 요청하시면 됩니다.

UE4 의 Roughness

아마 roughness 는 ( 눈으로 보기에 ) 가장 이해하기 쉬운 개념일 것입니다. 물론 내부적으로는 DG 를 위해서도 사용되기는 하지만 별로 신경을 쓸 필요는 없겠죠.

그냥 아티스트 입장에서는 "거칠면 반사가 뭉개지고 안 거칠면 반사가 또렷해진다" 정도로 이해하면 그만이겠습니다.


그림12. Metallic 이 0.5 일때 Roughness 변화에 의한 결과. Diffuse 를 off 시킴.

에너지 보존 법칙

위에서 specular G 에 대해 언급하면서 에너지 보존 법칙을 이야기했습니다. 하지만 여기에서는 Diffuse 와 Specular 의 관계에 있어서 에너지 보존 법칙을 이야기하고자 합니다.

표면에 들어 온 빛은 일부는 흡수되고, 일부는 반사되고, 일부는 투과됩니다. 이 반사되는 빛은 Diffuse 와 Specular 로 나뉜다는 것은 익히 알고들 계실 겁니다. 그런데 문제는 Diffuse 와 Specular 가 어느 비율로 반사되느냐입니다. 예전 Phong 과 같은 모델에서는 그냥 아티스트가 임의로 설정하게 했습니다. Kd 나 Ks 같은 항이 있었죠. 아티스트가 원하는 결과를 만들려고 하다 보니 과도한 라이팅이 들어 가는 경우가 많았습니다.

사실 조명에 대한 이해가 높지 않으면 적정 값을 설정하기 힘들죠. 그것도 한씬이 아니라 다양한 라이팅 환경이 존재하는 씬에서 맞추는 것은 쉽지가 않은 일입니다.

그림13. 에너지 보존 적용하지 않은 경우. 가장 왼쪽은 과도하게 빛나고 가장 오른쪽은 선명하지 않음. 출처 : Energy Conservation In Game.

그림14. 에너지 보존 법칙이 적용된 경우. 출처 : Energy Conservation In Game.

하지만 PBR 에서는 들어 온 빛의 양보다 나가는 빛의 양이 많아서는 안 됩니다. 그러므로 흡수되거나 투과되는 빛이 없다고 가정할 때 Diffuse + Specular 의 양은 항상 들어 온 빛의 양과 같아야만 하죠. 그래서 UE4 에서는 Metallic 을 가지고 Diffuse 의 양을 조절합니다.

DiffuseColor = BaseColor - BaseColor * Metallic;

Metallic 이 세지면 specular 가 세진다는 의미이고 Diffuse 의 양이 줄어들게 됩니다. 위에서 SpecularColor 를 구할 때 lerp 의 파라미터로 Metallic.xxx 를 넘기고 있기 때문에 Metallic 이 크면 Specular 가 늘어나고 Metallic 이 작으면 Diffuse 가 늘어나는 결과를 내게 되는 것입니다.

이것은 기존의 non-PBR 환경의 작업자들에게는 좀 어색하게 느껴질 수 있습니다만, 물리적으로 올바른 결과이고 라이팅 환경이 달라도 어색함이 적어집니다. 게다가 그 비율을 아티스트가 신경쓰지 않아도 되니 금상첨화입니다.

결론

Specular BRDF 공식은 매우 복잡하지만, 아티스트 입장에서는 metallic 과 roughness 만 알면 대충 재질을 설정할 수 있습니다. 그리고 그 재질들을 위한 물리적 값은 이미 정해져 있고 그것을 데이터베이스로 만드는 것이 아티스트나 팀의 역량이 되겠죠.

막말로 metallic 같은 경우에는 비금속은 0 금속은 1 이라고 설정해도 문제될 것은 없습니다. 대부분 roughness 가 중요한 factor 가 됩니다. 그러므로 특이한 경우( 매우 정교하게 섞인 복합재질 )가 아니라면 metallic 을 위해서 큰 리소스를 할당할 이유가 없습니다. 맵이 거의 단색으로 나온다고 해도 과언이 아닙니다. 만약 맵을 따로 만든다면 metallic 과 관련한 맵만 작게 만드는 것도 최적화를 위해 좋을 것입니다.

시리즈를 마무리하며...

Specular BRDF 는 [ PBR 이란 무엇인가 ] 시리즈에서 이야기하고자 하는 최종 보스라 할 수 있습니다. 이제 외전격의 이야기 몇 개만 제외하면 실질적으로 마지막 글이라고 생각하셔도 무방합니다. 이제 대단원( ? )의 막을 내리게 되겠네요. 제대로 이야기를 못 풀어 나간 것 같아서 아쉽기도 하지만, 이제 저작의 고통에서 벗어날 수 있다는 생각에 시원하기도 하네요. 이런 걸 시원섭섭하다고 하죠 ㅋ.

아티스트도 쉽게 이해할 만한 내용을 쓰고 싶었는데 많은 내용을 담으려고 욕심을 내다가 보니 밸런스를 잘 못 맞춘 것 같아서 아쉽네요. 아티스트도 만족을 못하고 프로그래머나 TA 도 만족을 못하는 그런 글이 되지 않았나하고 자평을 해 봅니다.

다음에 기회가 되면 보충글을 더 올리도록 하겠습니다. 지금까지 긴 시리즈를 읽어 주셔서 감사합니다.

추가 : [ 21. PBR 셰이더 구성 ].

참고자료

[ 1 ] Real Shading in Unreal Engine 4, Biran Karis.

[ 2 ] A Reflectance Model for Computer Graphics, Robert L. Cook and Kenneth E. Torrance.

[ 3 ] Physically-Based Shading at Disney, Brent Burley.

[ 4 ] Microfacet Models for Refraction through Rough Surfaces, Bruce Walter, Stephen R. Marschner, Hongsong Li, Kenneth E. Torrance,.


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


[ PBR 이란 무엇인가 ] 19. BRDF

오늘은 BRDF 에 대해서 이야기해 보려 합니다. BRDF 는 "Bidirectional Reflectance Distribution Function" 의 머리글자입니다. 우리 말로는 "양방향 반사율 분포 함수"입니다. 이 BRDF 는 Diffuse BRDF 와 Specular BRDF 로 나뉩니다. 한번에 모든 것을 설명할 수 있는 모델이 있으면 좋겠지만, 안타깝게도 현재까지는 ( 필자가 알기로는 ) 상용화된 모델이 존재하지 않습니다.

어쨌든 BRDF 는 그림1 의 파라미터들을 사용해 식1 과 같은 형태의 함수 fr 로 정의됩니다[ 1 ] :

식1. BRDF 함수 형태. 출처 : [ 2 ].

 

그림1. BRDF 함수를 정의하는 다이어그램. 출처 : [ 2 ].

식이 나오니 뭔가 어렵게 느껴지실 수도 있지만 규칙을 알면 어려울 것이 없습니다.

식1 에서 r 이라는 아래첨자( subscrip )는 reflectance 의 머리글자입니다. 함수가 r 에 대한 함수라는 의미죠. ω( 오메가 ) 라는 파라미터( 매개변수, parameter )는 함수의 입력값을 의미하죠. 여기에서는 i 라는 아래첨자는 incident( 입사하는 ) 의 머리글자입니다. 즉 ωi 는 입사광 벡터( 광원을 향하는 벡터 )을 의미하고 ωr 은 반사광 벡터( 관찰자를 향하는 벡터 )를 의미합니다.

자, 정리해 봅시다. 함수 f 는 입사 방향과 반사 방향을 입력으로 받으면, 그 관계에 따라 반사율( 입사광과 반사광의 비율 = 반사광 / 입사광 )을 출력으로 내뱉어 준다는 것입니다.

여기에서 f 는 실제 식을 가지고 있지 않습니다. 형태만을 정의한 것이죠. 다시 말해 다음과 같은 조건을 만족하면 BRDF 라 할 수 있는 것입니다.

    • 반사율 분포( Reflectance Distribution )를 출력으로 내뱉음.
    • 양방향성( Bidirectional )을 가짐: 광원과 관찰자의 관계가 동일하다면 반사율 분포가 변하지 않음.
    • 광원과 관찰자를 입력 파라미터로 받음.
    • 함수( Function )여야 함: 같은 입력에는 항상 하나의 같은 결과가 나와야 함.

그렇다면 여러분은 위의 조건을 만족하기만 하면 구현하는 사람마다 다른 결과를 낼 수 있을 것이라는 사실을 유추하실 수 있을 겁니다. 그래서 BRDF 모델은 참 다양합니다( 그림2 참조 ).

그림2. 다양한 BRDF 모델들. 출처 : [ 1 ].

우리가 많이 들어 본 Cook-Torrance, Oren-Nayar, Phong, Blinn 등 여러 가지 BRDF 모델들이 있군요. Theroical( 이론적인 ) 은 이론적으로는 더 올바른 결과를 내는 모델들을 의미하고, Empirical( 경험적인 ) 은 덜 이론적이지만 실증된 결과를 반영한 것을 의미합니다. 마지막으로 Experimental( 실험적인 ) 은 말 그대로 실험적인 결과를 보여 줍니다. 살색은 결과가 Isotropic( 등방성 ) 이라는 것이고 녹색은 결과가 Anisotropic( 비등방성 ) 이라는 것입니다.

Diffuse BRDF

일반적으로 Diffuse BRDF 는 Lambertian reflectance( 램버트 반사율 ) 를 사용합니다. 복잡한 모델들도 있을 수 있겠지만 diffuse 모델의 경우에는 별 차이가 나지 않습니다.

램버트 반사율은 표면이 이상적인 확산 반사를 보여준다고 가정합니다. 소위 "무광( matte )" 재질을 의미합니다. 모든 방향으로 균일하게 빛을 반사하고 있다는 것이죠.

전에 [ 16. Reflection 에 대한 잘못된 상식들 ] 에서 언급했듯이 diffuse 는 표면에 흡수된 후에 다시 방출되는 것이라서 재질의 색상을 보여 준다고 했습니다.

그림6. Diffuse 의 발생과정. 출처 : [ 4 ].

그런데 이게 어떻게 튈지는 구성요소의 성분과 밀도에 따라 달라지기에 한정된 자원을 가진 현세대 컴퓨터로 이를 제대로 표현하는 것은 힘듭니다. 그래서 전방향으로 균일하게 반사한다고 가정하는 것이 바로 램버트 반사율입니다( 그런 재질을 가졌다고 가정한 상태에서의 표면을 Lambertian surface 라고 부르기도 합니다 ).

하여간 이를 그림으로 표현하면 그림7 과 같습니다.

그림7. Lambertian reflectance. 붉은색이 diffuse. 출처 : [ 4 ].

그런데 글을 차분히 읽으신 분은 여기에서 강한 의문을 표하게 될 것입니다 : "모든 방향으로 균일하게 반사한대며?".

그렇다면 그림7 은 잘못된 것일까요? 그림8 이 맞는 걸까요?

그림8. Lambertian reflectance. 전방향으로 동일하게 반사. 출처 : Optical PTFE from Lake Photonics.

제가 "안 누구누구"는 아니지만 "그림7 일수도 있고 그림8 일수도 있습니다" 라고 대답하도록 하겠습니다.

빛은 그림8 처럼 표면에서 균일하게 반사되어 나가는 것이 맞습니다만, 표면에 들어 온 조도( 빛의 양 )가 다릅니다. [ 5. 조도( illuminance ) 측정 ] 에서 빛을 받는 서피스의 기울기에 따라서 빛을 받는 양이 달라진다고 했던 것이 기억나시나요? 표면에 도달한 빛은 그림8 처럼 모든 방향으로 균일하게 방출되지만, 눈으로 볼 때는 그림7 처럼 기울기에 따라 다른 양의 빛을 받게 되는 겁니다. 램버트 모델에서는 관찰자의 위치에 따라 반사율이 달라질 수는 없지만 조도가 다르기 때문에 기울기가 커지면 점점 어두워 보이게 되는 것입니다.

그림9. 기울기와 조도의 관계. 램버트 코사인 법칙. 출처 : Lambert's Cosine Law, Ocean Optics.

그림9 에서 박스가 눈의 면적만큼의 빛덩이라고 가정합시다. 기울기가 작아지면 같은 면적이라고 할지라도 더 적은 양의 빛이 들어 오게 되는 것을 알 수 있습니다. 이  이것을 수학적으로 정리한 사람이 Lambert 이고 그 결과가 cosine 법칙과 같기 때문에, 이를 램버트 코사인 법칙( lambertian cosine law )라고 부릅니다.

빛을 향하는 벡터와 표면의 노멀을 내적(  dot product )하면 그 결과는 cosine 과 같아집니다. 왜냐하면 N 과 L 은 정규화된( normalized ) 벡터이므로 그 길이가 1 이기 때문입니다.

식2. Diffuse BRDF. 램버트 코사인 법칙.

이것이 소위 말하는 "엔닷엘"입니다. 식2NL 중간에 있는 mid dot( · ) 연산자가 dot product( 내적 )을 의미합니다. N 은 표면의 노멀( Normal ) 벡터이고 L 은 라이트( Light ) 벡터입니다.

그런데 이 시점에서 민감하신 분들은 왜 식1 의 형식과 다르냐고 지적하실 수 있습니다. 여기에서는 ωi 를 입력값으로 받지 않죠. 왜냐하면 전술했듯이, 그림8 처럼 관찰자의 위치와 관계없이 모든 방향으로 균일하게 빛을 반사하기 때문에 관찰자의 위치는 필요하지 않습니다. ωi 를 입력값으로 넣는다고 해도 아무런 변화가 없다는 것입니다. N·L 은 조도계산을 위해서 필요한 것입니다. 

에너지 보존( Energy Conservation ) 법칙

아티스트들과 연구자들은 이 Diffuse BRDF 가 뭔가 이상하다는 것을 깨닫게 됩니다( 사실 물리적으로 올바르지 않으면 자연스럽지도 않습니다. 그것이 얼마나 많이 티가 나느냐는 차이가 있을 뿐이죠. 왜냐하면 우리 눈은 실세계에서 물리적 현상들에 익숙해져있기 때문입니다. 일부러 왜곡하는 경우는 배제하고 이야기하는 것입니다 ).

뭐가 이상한지 찾아 보았더니 들어온 빛의 세기와 나가는 빛의 세기의 합을 해보니 들어온 빛보다 나가는 빛이 더 많다는 것을 알게 된 것입니다.

그림8 을 보시면 모든 방향으로 빛이 나가는 것을 알 수 있습니다. 다른 방향으로 가고 있는 빛은 관찰자에게 도달할 수 없습니다. 그런데 N·L 은 다른 방향으로 가고 있는 빛을 고려하고 있지 않습니다. 단지 빛과 표면의 노멀의 관계만을 고려한 것이죠.

그래서 나가는 빛의 양을 모두 합산한 다음에 그것으로 나눠줍니다. 예를 들어 피자 한 판을 10 조각으로 나누고 내가 한 조각을 먹었다면, 내가 먹은 비율은 1 / 10 이라고 계산하는 것과 같습니다. [ 7. Light intensity 설정 ] 에서 점광원의 광속( lumen )으로부터 광도( lux )를 계산할 때 전체 입체각의 크기인 4π 로 나눠야 정확한 광도가 나온다는 것을 기억하시나요? 그것과 유사합니다.

이걸 증명하려면 미적분이 들어가므로 여기에서 구체적으로 설명을 하지는 않겠습니다. π 로 나눠야 한다는 것만 알려드리도록 하겠습니다. 실제 유도하는 과정이 궁금하시다면 [ 5 ] 의 "Relating peak luminous intensity and luminous flux" 를 참조하시기 바랍니다.

최종 Diffuse BRDF 식은 식3 과 같이 결정됩니다. 이번에는 식1 과 같은 형태로 정리해 봤습니다.

식3. Diffuse BRDF. 에너지 보존 법칙을 고려.

그렇기 때문에 여러분은 PBR 이 적용된 엔진에서는 Diffuse 가 1/3( π = 3.141592... ) 정도 줄어드는 것을 느끼게 될 것입니다.

우리는 Diffuse 맵( 언리얼에서는 Base texture )을 알베도( albedo, RGB 채널당 표면 반사율 )의 개념으로 사용합니다. 알베도는 절대값인 색상이 아니라 비율입니다. 색상이라기보다는 데이터라는 의미죠. 그러므로 PBR 환경에서는 다른 파라미터를 통해서 밝기를 조정하시는 것이 좋습니다( 예를 들면 광원의 밝기 ).

참고자료

[ 1 ] An Overview of BRDF Models, Rosana Montes and Carlos Urena.

[ 2 ] Bidirectional reflectance distribution function, Wikipedia.

[ 3 ] Lambertian reflectance, Wikipedia.

[ 4 ] Diffuse reflection, Wikipedia.

[ 5 ] Lambert's cosine law, Wikipedia.

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


[ PBR 이란 무엇인가 ] 18. Distribution Function

그래픽스에 익숙하신 분들이라면 BSDF, BRDF, BTDF 와 같은 용어들을 한 번쯤은 보셨을 겁니다. 뭔진 잘 모르겠지만 DF 라는 머릿글자가 동일하다는 것을 알 수 있습니다. 제목에서 이야기하고 있는 "Distribution Function" 의 머리글자가 바로 "DF" 입니다. "Distribution Function" 은 우리말로는 "분포 함수" 정도로 표현할 수 있습니다.

우리는 여러 3D 저작 도구들에서 BRDF 라는 용어를 볼 수 있습니다.

그림1. 3DS Max 의 BRDF 롤아웃.

그림2. Maya 의 Specular 패널.

그림3. V-RAY for Maya 의 Reflection 패널.

그림4. Anorld for Maya 의 Specular 패널. 여기에서는 "Microfacet Distribution" 이라고 되어 있는데 이것도 BRDF 입니다.

명식적으로 BRDF 라는 용어를 사용하지 않더라도 BRDF 관련 파라미터들을 사용하는 경우가 많습니다. 여러분에게 익숙한 "Glossiness" 라든가 "Specular Power" 같은 용어들을 보셨을 겁니다. 보통 그것은 "Phong" 혹은 "Blinn Phong" 을 위한 BRDF 파라미터죠.

딱히 BRDF 에 대해서 알지 못한다고 하더라도 여러 가지 쉐이딩 모델을 비교하는 글들을 본 적이 있었을 겁니다( 그림5 참조 ).

그림5. BRDF 비교. 출처 : Experimental Analysis of BRDF Models.

이런 쉐이딩 모델들의 차이는 어디에서 발생하는 것일까요? "답정너" 같지만 바로 분포 함수( Distribution Function )의 차이입니다.

함수란?

함수라는 것은 무엇입니까? 그것은 입력이 들어 오면 출력을 내뱉어 주는 기계상자로 비유할 수 있습니다.

그림6. 함수를 기계에 비유함. 출처 : [ 2 ].

그림6 에서 볼 수 있듯이 "x" 라는 입력을 넣을 때 "f(x)" 라는 출력을 줍니다. "f" 는 "function" 의 머리글자입니다. 이때 x 의 값이 될 수 있는 값들의 집합을 정의역이라 하고, f(x) 의 값이 될 수 있는 값들의 집합을 치역이라고 합니다.

그런데 여기에서 제약이 있습니다. 반드시 x 와 f(x) 의 값은 1:1 로 대응해야만 한다는 것입니다. 만약 1:1 로 대응하지 않으면 어떤 일이 발생할까요? 여러분이 쿠킹머신에 재료 "A" 를 넣었는데 음식이 "B" 일 수도 있고 "C" 일 수도 있습니다. 이것을 의도한 것이 아니라고 한다면, 이러한 기계를 신뢰할 수 있을까요?

그래서 x 와 f(x) 의 값은 반드시 1:1 로 대응해야 수학적으로 올바른 함수라고 할 수 있습니다. 이 때 이런 f(x) 값들만 모아 놓은 것을 공역이라고 합니다. 그러므로 공역은 치역의 부분집합이죠( 그림 7 참조 ).

그림7. 정의역, 치역, 공역의 관계. X( 붉은색 ) 는 정의역, Y( 파란색 ) 는 치역, 노란색은 공역.

자 이제 "분포 함수" 에서 함수의 의미를 아시겠죠? 특정 입력에 대해서 특정 결과를 내 주는 것이 바로 함수입니다.

함수 f 가 있고 그것은 "f(x) = x + 1" 라고 정의되었다고 합시다. 그러면 반드시 "f(1) = 2" 여야합니다. "f(1) = 3" 일 수도 있고 "f(1) = -1" 일 수도 있으면 안 되겠죠?

그런데 세상에 안 되는 것이 어디있겠습니까? 음함수( Implicit Function ) 이라는 개념이 있습니다. 대표적인 음함수는 원이죠.

식1. 원을 위한 음함수.

이것을 그래프로 그리면 어떻게 될까요?

그림8. 원의 그래프.

식1 을 보시면 알겠지만 하나의 x 값에 두 개의 y 값이 대응됩니다. 그래서 이를 함수로 만들 수가 없죠. 그러므로 이것을 두 개의 함수( explicit function )로 나눠서 그리게 됩니다( 원을 하나의 함수로 그리는 방법이 있긴 합니다. 바로 매개변수를 도입하는 방법인데요, 이것에 대해서는 나중에 기회가 되면 설명하도록 하겠습니다 ).

어쨌든 중요한 것은 어떤 식의 입력값과 출력값이 반드시 1:1 로 대응해야만 그 식을 함수라고 부를 수 있다는 것입니다.

분포란?

"분포"라는 것은 무엇일까요? 확률이나 통계에서 많이 나오죠? 분포는 어떤 요소들이 일정한 범위에 퍼져 있는 모양을 의미합니다. 이것을 함수로 나타내면 분포함수가 되겠죠.

그림9. 정규분포 그래프. 출처 [ 3 ].

그림10. 주사위 두개를 던질 때의 두 눈의 합 S 에 대한 확률분포. 출처 : [ 3 ].

분포라는 것은 어렵게 생각할 필요가 없습니다. 그냥 어떤 패턴이라고 생각하시면 됩니다. 사실 그래픽스에서의 분포는 해석학 영역에 속하기 때문에 조금 다른 의미를 가지고 있지만, 그냥 "분포는 패턴" 이라고 이해하시는 것이 좋습니다( 여기에서 디랙델타함수[ 4 ]를 다룸으로써 미적분과 양자론에 대해 이야기하고 싶지는 않군요 ㅠㅠ ).

그래픽스에서 분포는 그림11 과 같이 로브( lobe )로 표현되는 경우가 많습니다. 로브라는 것은 어떤 물체의 둥글거나 평평한 부분을 일컽는 말입니다. 쉐이더의 차이점이 궁금해서 검색을 해 본 경험이 있는 분들은 그림11 과 같은 그림을 보신 적이 있을 겁니다.

그림11. 여러 reflection lobe 들에 대한 표현. 출처 : Explain the shape of a specular lobe

위의 그림을 어떤 식으로 이해해야 하는지 알아 볼까요? 그림12 에는 붉은색으로 A, B, C 가 있습니다. 이것은 관찰자의 위치를 나타내는 것이죠. 일단 다른 것은 놔두고 "Standard Phong specular lobe" 에 대해서만 알아봅시다( 녹색 로브 ). 원점을 O 라 할 때 OA, OB, OC 가 녹색로브와 만나게 될때까지의 길이가 바로 specular 의 세기를 의미합니다. OA 와 OB 를 비교해 보면 녹색로브와 만날 때까지의 길이는 OB 쪽이 더 길다는 것을 알 수 있습니다. 이는 관찰자가 A 에 있을 때보다 B 에 있을 때 specular 를 더 세게 받는다는 것을 의미하죠. OC 같은 경우에는 녹색 로브와 만나지를 않는군요. 결국 specular 세기는 0 이라는 의미입니다.

그림12. Lobe 와 관찰자 A, B, C 의 관계.

여러분은 specular lobe 의 모양만 보고도 specular 가 뾰족하게 보일지 퍼져 보일지를 알 수 있습니다.

컴퓨터 그래픽스와 PBR 의 선구자인 Disney 같은 경우에는 툴까지 만들어서 BRDF 를 연구하더군요. BRDF Explorer 라는 툴이 있습니다. 링크에 가면 받을 수 있습니다. 그림13 처럼 2D 와 3D 에서 로브를 확인하거나 할 수가 있습니다. 실제로 BRDF 모델을 만들 수도 있습니다.

어떤 쉐이더를 만들기 전에는 BRDF Explorer 를 통해서 아티스트와 그 결과를 미리 확인해 보는 것도 좋겠죠.

그림13. Disney BRDF Explorer 스크린샷. 출처 : BRDF Explorer.

BRDF 와 BTDF

예전에 [ 6. 휘도 측정 ] 에서 BRDF, BTDF 그림을 보여 준 적이 있습니다.

<p

그림14. BRDF 와 BTDF. 출처 : [ 1 ]

반사되서 나가는 빛의 분포를 다루는 것이 RDF( Reflectance Distribution Function, 반사율 분포 함수 ) 이고 투과되서 나가는 빛의 분포를 다루는 것이 TDF( Transmittance Distribution Function, 투과율 분포 함수 ) 입니다.

쉽게 이야기하면 표면에 들어 온 빛이 얼마만큼 반사되었고 얼마만큼 투과되었느냐를 함수로 나타낸 것이죠. 물론 반사는 diffuse 와 specular 가 존재하기 때문에 Diffuse RDF 와 Specular RDF 로 나뉩니다.

자 우리가 자주 접하는 분산함수들은 앞에 "B" 가 붙었죠? 이것은 "Bidirectional" 의 머리글자입니다. 이게 무슨 말이냐? 그림15 처럼 빛과 관찰자의 위치가 변경되어도 관계만 같으면 결과( 반사율 혹은 투과율 )가 동일하다는 것입니다. 반사로 치면 입사각과 반사각이 같으면 어디에 있어도 반드시 같은 결과가 나온다는 의미입니다.

그림15. Bidirectional 의 의미. 입사각과 반사각의 관계가 동일하면 항상 동일한 결과가 나옴.

그런데 왜 bidirectional 이 필요할까요? 여기에 대해서는 명확하게 정리된 글이 없더군요. 그냥 제 생각을 말씀드리도록 하겠습니다.

컴퓨터 그래픽스에서는 성능( 혹은 속도 )을 상당히 중요한 요소로 보고 있습니다. 특히나 실시간 그래픽스라면 그것의 중요도는 말할 필요도 없겠죠. 만약 관찰자와 광원의 관계에 의해 일정한 결과를 도출할 수 없다면, 매우 복잡한 공식이 필요할 겁니다. 예를 들어 그림16처럼 서피스에 방향성이 있는 결이 있다고 하죠.

그림16. 서피스의 방향성 있는 미세한 결.

그림16 와 같은 서피스를 고려한다면 그림15 와 같은 결과를 기대할 수 있을까요? 사용자가 원하는 결과를 얻을 수는 있겠지만 재질이나 기하학적 위치에 따라 공식이 틀려져야 할 것입니다. 아티스트가 이런 것을 모두 관리하기도 힘들 뿐더러 공식이 복잡해지면서 성능도 저하되겠죠.

물론 PBR 에서는 눈에 거의 보이지 않는 미세면( microfacet )이라는 것을 고려하기는 하지만 그림16 처럼 특정 방향성으로 쏠려 있는 미세면이 아닙니다. 일정한 분포를 가지고 있는 미세면들입니다. 그것을 "Microfacet Model" 이라고 부릅니다. 이것에 대해서는 나중에 다루도록 하겠습니다.

어쨌든 일정한 분포를 가진 미세면들을 고려하면 빛과 관찰자의 관계에 의한 반사율은 일관된 방식으로 정의됩니다. 즉 bidirectional 한 성격을 띄게 되며, 하나의 재질에 대해 빛과 관찰자의 관계에 따른 여러 가지 공식을 가지고 있을 필요가 없겠죠.

P.S. "그렇다면 BRDF 로 방향성을 가진 결을 만들 수는 없나요?" 라는 질문을 하실 수 있습니다. 안 되는 게 어디 있겠습니까? NormalMap 등을 사용해서 Normal 을 왜곡시킴으로써 결을 만들어낼 수 있습니다. 하지만 이것은 관찰자와 빛의 관계에 의해 일관된 결과가 나온다는 대전제를 깨는 것은 아닙니다.

참고자료

[ 1 ] Bidirectional scattering distribution function, Wikipedia.

[ 2 ] 함수, 위키백과.

[ 3 ] 확률분포, 위키백과.

[ 4 ] 분포 (해석학), 위키백과.

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


[ PBR 이란 무엇인가 ] Fresnel 이란?

프레넬 공식이란?

앞서 [ [ 번역 ] 모든 것은 프레넬을 가집니다 ] 와 [ Reflection 에 대한 잘못된 상식들 ] 에서 프레넬( Fresnel )이라는 용어가 자주 나옵니다. 대체 이게 무엇이길래 PBR 에서는 그토록 중요한 것일까요?

모든 빛은 매질( media )을 만나면 반사, 흡수, 굴절이라는 과정을 거치게 됩니다. 이때 프레넬 공식을 사용해서 빛의 움직임을 계산하게 됩니다.

"프레넬"이라는 것은 "오귀스탱 장 프레넬" 이라는 프랑스 과학자의 이름을 의미합니다( 그림1 ).

그림1. Auguistin-Jean Fresnel. 출처 : [ 2 ].

이 프레넬이라는 사람은 서로 다른 굴절률( refractive index )를 가진 매질( media ) 사이에서 빛이 움직일 때의 동작을 설명했습니다. 빛의 반사를 예측하는 공식을 만들었고 이를 프레넬 반사라고 합니다[ 1 ]. 그러므로 일반적으로 "프레넬" 이라고 이야기하면 "프레넬 공식( Fresnel Equation )" 을 말하는 것입니다.

인간과 빛 ] 에서 빛은 전자기파( electromagnetic wave )라고 이야기한 것이 기억나실 겁니다. 여러 가지 전자기파로 구성된 집합이 빛이죠. 빛이 프리즘을 통과하면서 여러 가지 파장으로 분리되는 것을 기억하고 계실 겁니다( 그림2 ).

그림2. 빛이 프리즘을 통과하면 여러 파장으로 분리됩니다. 출처 : [ 3 ].

이런 일은 왜 벌어지는 것일까요? 지금부터 알아 보도록 하겠습니다.

굴절률( Refractive index )

굴절률은 빛이 진공에서 특정 매질( media )로 이동할 때 속도가 얼마나 느려지는지 그 비율을 측정한 것입니다. 일반적으로 과학 문서에서는 "Index of refraction( IOR )" 이라는 표현이 많이 사용됩니다. "Refractive index" 와 "Index of refraction" 은 같은 의미입니다. "index" 는 사전적 의미로 "지수" 라는 뜻을 가집니다. "물가지수", "임금지수" 이런 표현을 할 때 사용하죠. 즉 우리 말로 억지로 번역하자면 "굴절지수" 정도가 되겠습니다. 그냥 굴절률이라고 생각하시면 됩니다.

빛의 속도는 초당 지구 일곱바퀴 반을 돌 수 있는 매우 빠른 속도( 299,792,458 m/s )입니다. 지구에서 달까지 1 초면 갈 수 있죠. 그런데 이것은 진공( vaccum ) 기준에서의 속도이며, 실제로는 어떤 매질을 만났을 때 속도가 감소합니다. 왜 감소하는지에 대해서는 설명하지 않겠습니다. 왜냐하면 이를 설명하기 위해서는 글이 좀 어려워질 수 있고 이 문서의 주제를 벗어나기 때문입니다. 에너지 보존 법칙과도 관계가 있는데요, 만약 궁금하시면 위상 속도( phase velocity ) 문서를 참고하시기 바랍니다.

어쨌든 매질을 만나면 빛의 속도가 줄기 때문에 빛의 방향도 바뀝니다. 이를 좀 쉽게 설명하려면 바퀴의 예를 들 수 있습니다( 그림3 )( 빛에 대해서 이해하고 싶으신 분들은 뉴턴 하이라이트의 "빛이란 무엇인가" 라는 책을 필독하실 것을 권장합니다. ).

그림3. 속도가 다르면 빛이 꺾이는 이유. 출처 : [ 5 ].

그림3 에서 볼수 있듯이 포장도로에서 모래로 바퀴가 진입하면 마찰력때문에 한쪽바퀴의 속도가 줄어들죠. 그런데 다른쪽 바퀴는 여전히 같은 속도로 움직이므로 몸체의 방향이 틀어집니다. 그런데 만약 바퀴가 작다면 더 많이 회전해야 하므로 그 동안 느린쪽 바퀴의 방향은 더 틀어지겠죠. 반대로 바퀴가 크다면 더 적게 회전하므로 다른쪽 바퀴가 덜 틀어지겠죠.

바퀴의 크기는 주파수와 연관해서 생각할 수 있습니다. 그림2 를 다시 보죠( 스크롤이 귀찮으니 밑에 이미지를 복사해 넣었습니다 ).  

파장이 긴( 바퀴가 큰 ) 붉은 빛은 덜 꺾이고, 파장이 짧은( 바퀴가 짧은 ) 보라색 빛은 많이 꺾이는게 보이시죠? 굴절률이 제대로 계산되기 위해서는 파장별로 처리되어야 합니다. 하지만 대기 산란( atmospheric scattering )처럼 파장별 산란 결과가 중요한 분야가 아니라면 그냥 대충 퉁쳐서 처리하곤 합니다.

스넬의 법칙( Snell's law )

과거에 사람들은 매질이 다르면 빛이 꺾인다는 것 까지는 알게 되었지만 얼마나 꺾이는 지까지는 모르고 있었습니다. 이것을 공식화 한 사람이 바로 스넬( Snell )입니다. "스멜~" 이 아니라 "스넬" 입니다. 이 스넬의 법칙은 고등학교 때도 배워서 들어는 보셨을 겁니다.

그림4. 서로 다른 굴절률을 가진 매질에서의 빛의 굴절. 출처 : [ 6 ].

그림 4에서 붉은색 라인은 빛의 경로를 의미합니다. 매질의 경계( interface ) 에서 빛이 굴절합니다. 표면의 노멀( normal )과 입사광( incident light )의 각은 θ1 입니다. 그리고 노멀과 굴절광( refracted light )의 각은 θ2 입니다. v 는 속도( velocity )를 의미하고, λ 는 파장( wavelength )를 의미합니다. 그리고 n 은 굴절률( IOR )을 의미하죠. 이것들의 관계를 설명한 것이 바로 스넬의 법칙입니다( 식1 ).

식1. 스넬의 법칙. 출처 : [ 6 ].

이런 현상 때문에 기름과 물을 컵에 채워놓고 파동을 만들면 그림5 와 같은 결과가 나오게 됩니다.

그림5. 스넬 법칙에 따른 파동의 변화. 출처 : [ 6 ].

그런데 이 굴절과 관련해서 좀 재밌는 현상이 있습니다. 빛이 굴절률이 다른 매질을 만나게 되면 특정 각도에서는 굴절을 하지 않고 완전히 반사를 하는 경우가 있다는 겁니다( 그림7 ).

그림7. 빛의 굴절 패턴. 특정 각도에서는 전반사가 발생함( 오른쪽 ). 출처 : [ 7 ].

위의 그림에서 Critical angle 이라고 된 각도를 넘어서면 전반사가 발생합니다. 임계각을 구하는 공식은 식2 와 같습니다.

식2. 전반사각 구하는 공식. 출처 : [ 7 ].

아크릴 병 내부에서 바깥쪽으로 빛이 전반사되는 예를 들면, 대기의 굴절률이 1.0 이고 아크릴의 굴절률이 약 1.50 이므로 전반사 각도는 41.8 도입니다.

식3. 아크릴 병의 전반사각 계산. 출처 : [ 7 ]

프레넬 공식( Fresnel equation )

자 이제 우리는 어떤 매질에서 여행하고 있던 빛이 다른 매질을 만나게 되면 반사하거나 굴절하게 된다는 것을 알게되었습니다. 물론 흡수될 수도 있지만 여기에서는 다루지 않겠습니다.

우리가 그래픽스에서 반사와 굴절을 계산하기 위해서는 얼마만큼의 빛이 반사되고 얼마만큼의 빛이 굴절되는지 알아야 합니다. 그것을 알려주는 공식이 바로 프레넬 공식입니다.

프레넬 공식을 그래프로 나타내면 다음과 같은 결과가 나옵니다.

그림8. 프레넬 공식 그래프. 출처 : Real-Time Rendering, 3rd Edition.

그림이 좀 어려워 보이죠? 가로축이 눈과 노멀방향의 각도이고 세로축이 반사율입니다.보시면 알겠지만 물이나 풀과 같은 비금속 물질들은 수직으로 봤을 때( 0 도 ) 매우 낮은 반사율을 보이며, 수평으로 봤을 때( 90 도 ) 매우 높은 반사율을 보입니다. 비금속같은 금속같은 경우에는 어디에서 보든 0.5 이상의 반사율을 보여 주죠.

그런데 우리가 모든 각도에서의 굴절률( IOR )을 알 수는 없습니다. 그러므로 대충 근사계산을 하는 식( 일반적으로 Shilick's approximation, 식4 )을 사용합니다. 그리고 입력값으로 물체를 수직에서 바라본( 0 도 ) 반사율값을 넣는 근사 계산식을 사용합니다. 위의 "프레넬 공식 자세히 보기" 를 펼쳐 본 사람들을 위해서 설명을 드리자면, R0 값이 바로 그림8 에 있는 그래프에서 0 도의 반사율 값이라 생각하시면 됩니다. 대부분의 엔진이나 문서에서는 F0 라고 표현을 합니다( 아마도 "Fresnel at Degree 0" 의 약자겠죠 ).

만약 정확한 값을 넣고자 한다면, 모든 매질의 굴절률 값을 수집한 다음에 계산을 하시면 됩니다. 일반적으로는 관찰자가 대기 속에 있다고 가정하기 때문에 어떤 매질의 굴절률이 n 이라고 할 때 다음과 같이 계산됩니다.

식5. F0 계산.

물의 예를 들어 보죠. 물의 경우에는 1.33 의 굴절률을 가지고 있습니다. 이를 식5 에 넣어 보면 F0 는 식6 과 같이 계산됩니다.

식6. 물의 F0 계산.

각 매질의 굴절률( IOR )은 [ IOR LIST ] 라는 페이지에 정리되어 있습니다. PDF 로 정리된 것도 있습니다[ IOR_Table.pdf ].

일반적으로 이 F0 를 "Metallic" 핀에다가 넣어 주시면 됩니다.

UE4 의 프레넬 노드

프레넬 공식은 림라이트나 실루엣을 만들기 위한 공식이 아닙니다. 하지만 그런 효과를 원하시는 분들을 위해서 Fresnel 노드가 따로 존재하기는 합니다[ 10 ].

그림9. 프레넬 노드. 출처 : [ 10 ].

하지만 이것을 사용하는 것에 대한 책임은 본인이 지셔야 합니다. 왜냐하면 Metalic 으로서 반영되는 Fresnel 은 그 의미가 명확하지만, 이 노드를 사용하는 것은 그냥 효과이기 때문입니다. 이 노드를 사용하게 된다면 비물리적인 결과를 산출할 수 있습니다. 보통 이 노드를 사용할 때는 Metalic 을 제거하고 Emissive 를 사용합니다.

그림10. UE4 프레넬 노드의 사용. 출처 : [ 10 ].

참고자료

[ 1 ] Fresnel equation, Wikipedia.

[ 2 ] Auguistin-Jean Fresnel, Wikipedia.

[ 3 ] Light, Wikipedia.

[ 4 ] Refracitve index, Wikipedia.

[ 5 ] 빛이란 무엇인가? 10 page, Newton Highlight. 일본 뉴턴프레스.

[ 6 ] Snell's law, Wikipedia.

[ 7 ] Refraction, Wikipedia.

[ 8 ] Schlick's approximation, Wikipedia.

[ 9 ] Real Shading in Unreal Engine 4.

[ 10 ] 머티리얼에 프레넬 사용하기, UE4 Document.

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


[ PBR 이란 무엇인가 ] 16. Reflection 에 대한 잘못된 상식들

사전적 의미

우리는 일상적으로 diffuse reflection( 확산방사 ) 과 specular reflection( 정반사 ) 이라는 용어를 사용합니다. 그런데 정말로 그 의미를 알고 사용하고 계시나요?

일단 diffuse 의 사전적인 의미를 살펴 보도록 하겠습니다.

1. ( 빛, 열, 냄새 따위를 ) 발산[ 방산( 放散 ) ]하다.

2. ( 지식을 ) 넓히다, 보급시키다, ( 정서 따위를 ) 충만시키다.

3. [ 理 ] ( 기체나 액체를 ) 확산( 擴散 )시키자; [ 理 ] ( 빛을 ) 산란시키다.

출처 : 네이버 사전.

여러분이 알고 있는 뜻과 일치하나요?

이제 specular 의 사전적 의미도 살펴 보도록 하겠습니다.

1. 거울 같은, 비추는, 반사하는, 반영하는

2. ( 광학 ) 정( 正 )반사성의

출처 : 네이버 사전.

이것도 여러분이 알고 있는 뜻과 일치하나요?

이제 저런 사전적인 의미가 실제적인 의미와 얼마나 일치하는지 살펴 보도록 합시다.

Diffuse 반사는 표면의 거칠기 때문에 발생한다?

많은 분들이 diffuse reflection 이 표면의 거칠기 때문에 발생한다고 생각합니다. 특히 PBR 에 있어 중요한 요소인 미세면( microfacet )을 생각하면 그렇게 생각하는 것도 당연합니다. 하지만 diffuse 는 대부분 표면의 거칠기 때문에 발생하는 것이 아닙니다. 물론 일부는 그렇게 발생합니다.

사전적인 의미로 봤을 때 diffuse 는 "발산", "확산" 이라는 의미를 가지고 있습니다. 즉, 단순하게 표면에 부딪혀서 반사되어 나오는 요소가 아니라는 의미입니다.

[ 1 ] 에 의하면, diffuse 가 발생하는 원리는 다음과 같습니다. 글의 일부를 번역해 보았습니다.

Solids( 인공물 )에서  diffuse reflection 은 일반적으로 표면의 거칠기 때문에 발생하지 않습니다. 물론 평평한 표면에서 specular reflection 이 발생하지만, 그렇다고 해서 그것이 diffuse reflection 이 발생하는 것을 막지는 않습니다. 하얀 대리석을 매우 연마( polishing )해도 하얀색은 남습니다; 아무리 연마를 해도 그것이 거울이 되지는 않습니다. 연마는 약간의 specular reflection 을 산출하지만, 나머지 빛은 여전히 확산되게 반사됩니다.

어떤 표면이 diffuse reflection 을 제공하는 가장 주요한 원리에서, 그것은 정확하게는 표면이라는 개념을 포함하고 있지 않습니다: 대부분의 빛은 아래 그림에서 보이는 것처럼 표면 아래 존재하는 scattering center( 산란 센터, 역주 : 전자기파가 산란되는 곳 )들의 영향을 받습니다.

물론 매우 일부이기는 하지만 산란없이 diffuse reflection 을 보여 주는 경우는 존재합니다. 아래 그림처럼 표면 아래로 빛이 흡수되지는 않았는데 그 표면이 매우 거칠어서 여러 반향으로 반사가 되었다면 그것도 현상적으로 봤을 때는 diffuse 라고 이야기할 수 있겠죠.

비균일 표면으로부터의 diffuse reflection. 출처 : [ 1 ].

굳이 이러한 원리에 대해서 이야기하는 이유는 무엇이라 생각하시나요? "쓰는 사람 입장에서 그러든지 말든지 상관없잖아요?" 라고 하시는 분들 계실 수도 있습니다.

하지만 이 이야기를 하는 이유는 albedo( 반사율 ) 에 대해서 이야기하기 위함입니다. 우리가 흰색 빛을 물체에 쐈을 때 사과는 붉은색으로 보이고( 물론 녹색 사과도 있지만 딴지는 걸지 말아 주세요 ㅠㅠ ), 나뭇잎은 녹색으로 보입니다. 보통 이에 대해서 설명할 때 붉은색을 반사했다고 표현하는데, 사실은 표면 아래도 들어가 산란이 일어나면서 일부 파장을 흡수하고 나머지 파장이 방출된 것입니다.

자! 그럼 그냥 표면에서 반사해 버린 빛과 표면 아래에서 산란시켜서 반사해 버린 빛의 차이가 보이나요? Specular reflection 은 반사되었을 때 자신의 빛의 파장을 그대로 유지하지만, diffuse reflection 은 재질의 구성에 따라서 반사되었을 때 자신의 빛의 파장을 유지하지 못합니다. 그러므로 specular reflection 은 빛의 색을 그대로 유지하고 diffuse reflection 은 빛의 색을 유지하지 못하는 것입니다.

일반적인 경우에 표면 아래에서의 산란을 계산하기에는 너무 복잡하고 계산비용이 비쌉니다. 그래서 이를 albedo 라는 개념으로 퉁치는 거죠. 

그런데 여기에서 한가지 의문이 들 수 있습니다. 왜 specular 도 albedo 의 영향을 받느냐는 겁니다. 실제로 UE4 같은 엔진을 사용해 보면 Yellow( = Red + Green ) 라이트를 Green 재질에다가 쐈을 때 specular 성분에 순수한 Green 색상이 포함됩니다. 그냥 순수한 Yellow 가 나와야할 것 같은데 말이죠... 그 이유는 위의 위키피디아 글에서도 언급되어 있듯이 평평한 면이라고 하더라도 diffuse 를 막을 수 없기 때문입니다. 세상에는 순수하게( 혹은 이상적으로 ) 매끈한 평면이란 존재하지 않습니다. 이 부분은 Fresnel 과 관련이 있는데 나중에 자세히 다루도록 하겠습니다.

모든 물체는 diffuse reflection 을 가진다?

그렇지 않습니다. 금속( metal ), 빛이 들어갈 수 없는 물질, 개스( gas ), 액체( liquid ), 유리( glass ), 투명 플래스틱( transparent plastic ), 일부 보석( gem )들이나 소금 결정( salt crystal )과 같은 단결정( single crystal ) 물질들, 그리고 티슈( tissue )나 수정체( lens of a eye ) 같은 매우 특별한 재질들은 diffuse reflection 을 가지지 않습니다[ 1 ].

사실 이러한 재질들에 대해서 모두 설명하는 것은 어렵기 때문에 우리가 가장 흔하게 접하게 되는 금속에 대해서만 설명을 드리도록 하겠습니다.

금속은 모든 빛을 흡수합니다. 즉 albedo 는 0 이라고 할수 있습니다. 그런데 어떻게 반사가 생기는 걸까요? 금속 내부의 자유전자는 에너지를 받으면 아래 그림처럼 방출됩니다. 그래서 금속은 자신을 구성하고 있는 원자에 따라서 고유의 색상을 가지게 되는 거죠.

금속의 반사의 본질.

그러면 금속은 어느 방향으로 반사를 시킬까요? 여기에서 구체적으로 언급하지는 않겠지만 그것은 Fresnel 과 관련이 있습니다. 나중에 자세하게 다루도록 하겠습니다. 

Specular reflection 은 입사각과 반사각이 같을 때 가장 세다?

우리는 일반적으로 거울면 반사, 즉 정반사는 입사각과 반사각이 같을때 가장 세다고 알고 있습니다. 하지만 그것은 사실이 아닙니다. 이는 표면의 roughness 에 따라 달라집니다. 이 역시 Fresnel 과 관련이 있습니다. 이 역시 나중에 자세하게 다루도록 하겠습니다.

어쨌든 일반적인 재질에서 specular 는 거울면 반사를 하지 않고 약간 벗어나서 반사를 합니다. 이를 off-specular reflection 이라 부릅니다. 가장 센 곳을 off-specular peak 라고 부르죠. 굳이 우리말로 번역하자면 "정반사를 벗어난 반사" 정도 되겠습니다.

Off-specular reflection 의 예: 지표각에 가까워질수록 specular 방향( M )이 정반사의 각과 달라짐. 출처 : [ 2 ].

참고자료

[ 1 ] Diffuse reflection, Wikipedia.

[ 2 ] A Reflectance Model for Computer Graphics, Robert L. Cook and Kenneth E. Torrance.

주의 : 번역이 개판이므로 이상하면 원문을 참조하세요.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

원문 : How To Split Specular And Diffuse In Real Images


How To Split Specular And Diffuse In Real Images

실제보다는 어렵다고 생각하는 것을 정리해 두십시오. 제가 집에서 만든 것은 실제 이미지에서 diffuse 요소와 specular 요소를 분리하는 편광을 사용하는 기능입니다. 아티스트들은 사진 레퍼런스를 찾기 위해 많은 시간을 들입니다. 하지만 specular 와 diffuse 를 분리하는 것은 재질을 이해하는 데 있어 도움을 줍니다. 타이틀 이미지에서( 역주 : 여기에는 타이틀 이미지가 없습니다. 첫 번째 이미지를 의미합니다 ), 왼쪽은 diffuse 만 나온 것이고, 중간은 diffuse 와 specular 가 같이 나온 것이고, 오른쪽은 specular 만 나온 것입니다. 제가 여기에 대한 전문가는 아니지만, 그것을 이해하는 방식에 대해서 이야기하고자 합니다.

 

 1. Original

 Image:

 

 2. Diffuse Only:

 

 3. Specular Only:

 

이걸 스스로 하고 싶다면, 다음과 같은 것들이 필요합니다:

1. 수동 모드로 찍을 수 있는 카메라. 예를 들면 SLR.

2. 카메라를 만지지 않고 여러 장을 찍을 수 있도록 해 주는 원격 셔터 콘트롤러.

3. 광원. 저의 경우에는 Ikea 에서 산 램프를 사용합니다.

4. 편광 필름 몇 개. 저는 polarization.com 에서 구합니다. 여러분은 "Fully Laminated Linear Polarizer Sheets" 를 원할 겁니다. Linar 필터인지 확인하세요. Circular 필터를 구하시면 안 됩니다.

초기 단계에 컴퓨터 그래픽스에서는 오브젝트가 diffuse 반사와 specular 반사를 모두 가지고 있다고 가정했습니다. 어떤 빛이 표면을 비추고 있고 그 근처에 카메라가 있다고 가정해 봅시다. 그런데 이런 모델은 엄청나게 단순화된 모델입니다.

표면을 비추는 일부 빛들은 엣지( edge, 역주 : 그림에서 표면의 테두리를 의미하는 듯 )에서 몰래 빠져 나갑니다. 우리가 백색 빛과 파란색 표면을 가지고 있다면, 그 빛은 백색으로 그냥 남아 있을 겁니다. 우리는 이것을 specular 라고 부르죠.

어떤 빛은 표면에 의해 흡수될 것입니다. 일부 전자들은 소멸될 것이고, 새로운 포톤들이 랜덤한 방향으로 방출될 겁니다. 만약 우리가 백색 빛과 파란색 표면을 가지고 있다면, 방출되는 빛은 파란색이 겁니다. 우리는 이를 diffuse 라고 부릅니다.

여러분은 이미 그 사실을 알고 계실 겁니다. 그렇지 않다면 이제 알게 됐겠죠. 흥미로운 점은 그 빛이 편광될( polarized ) 수 있다는 것입니다. 여러분의 친구인 wikipedia 에 가면 더 많은 것을 배울 수 있을 겁니다. 게다가 specular 빛은 자신의 입사 편광( incoming polarization )을 유지하지만 diffuse 빛은 그렇지 못합니다. 그래서 diffuse 빛은 랜덤한 방향으로 랜덤한 편광을 가진채 다시 전파되는 겁니다. 우리는 그것을 비편광되었다( unpolarized )고 말할 수 있죠( 역주 : [ 편광으로 입체 영화를 본다 ] 에 좀 더 쉬운 설명이 있습니다 ).

여기에 장치가 있습니다.

매우 기본적입니다. 저는 카메라의 모든 설정을 수동 모드로 만들었습니다. 이런 의미죠:

1. 수동 노출( exposure, 역주 : [ 이제 노출을 조정해 보자 ] 를 참조하세요 ).

2. 수동 조리개( aperture, 역주 : [ 이제 노출을 조정해 보자 ] 를 참조하세요 ).

3. 수동 화이트 밸런스( white balance, 역주 : [ 화이트 밸런스 완벽 컨트롤하기! ] 를 참조하세요 ).

4. 수동 감도( ISO, 역주 : [ 카메라 감도( ISO )란? ] 을 참조하세요 ).

5. 자동 초점( Auto Focus ) 끄기.

6. 색상 프로우파일( profile )을 Adobe 98 이 아니라 sRGB 로 설정( 표준 감마 스페이스 색상 공간, 역주 : [ RGB 색공간 ] 을 참조하세요 ).

그리고 원격 셔터에서 손가락을 살짝 뗍니다. 그런 방식으로 저는 카메라에 손대지 않고 여러 장의 사진을 찍습니다. 만약 여러분이 카메라를 손대서 여러 장의 사진을 찍는다면, 정렬이 흔들릴 것이라고 생각합니다. 그리고 우리는 광원을 가지고 있습니다. 이제 광원에 대해 자세히 살펴 보죠.

광원은 그냥 Ikea 에서 사온 할로겐( halogen, 역주 : 발음이 핼러즌 ㅡㅡ ) 램프입니다. 하지만 그 앞에 선형 편광 필터가 달려 있습니다. 그 필터를 전기식 테이프( electrical tape )로 붙였습니다. 왜냐하면 저는 품격이 있는 사람( claasy guy )이니까요. 경고를 하나 하자면, 할로겐 램프는 엄청 뜨거워지므로 제 편광 필터가 녹아버려서 좀 휘었습니다.

이제 우리가 할 일은 두 개의 사진을 찍는 겁니다. 그렇지만 카메라 앞에 편광 필터를 달 것입니다. 카메라와 광원에 모두 편광 필터를 달아야 한다는 것을 기억하세요. 만약 제가 비디오 게임 프로그래머가 되기 위해 LA 에 왔다고 생각한다면, 잘못 생각하고 계신 겁니다. 손 모델의 꿈을 꾸고 있는 동안 돈을 버는 방법일 뿐입니다( 역주 : 안 웃김 ㅠㅠ ).

편광 필터에 오렌지색 테이프가 보이세요? 저는 이제 편광 필터를 90 도 회전시킬 것이고 다른 사진을 찍을 겁니다. 오렌지 테이프가 어떻게 이동했는지 확인해 보세요.

이제 우리가 찍은 첫 번째 사진이 있습니다. 정렬이 완벽하게 되었다면, 그 안에 specular 가 전혀 없을 겁니다. 물론 보통 그런 경우는 있을 수가 없죠. 왜냐하면 손으로 정렬을 완벽하게 하는 것은 불가능하기 때문입니다.

이것이 어떻게 동작할까요? 편광이 표면에 비춥니다. Specular 빛은 표면에서 반사되어서 카메라로 들어 오죠. 그 빛은 편광 필터에 도달하고, 그 편광 필터는 빛의 편광에 대해 수직으로 정렬되어 있습니다. 그러면 편광 필터에 의해 모든 것( specular )이 흡수됩니다. 반면에 diffuse 빛은 표면에 의해 흡수되기는 하지만 비편광된 상태로 다시 방출됩니다. 그래서 편광 필터는 diffuse 빛의 절반만 흡수합니다. 그리고 나머지는 카메라로 들어 오게 되죠. 그래서 그 이미지는 50% 의 diffuse 와 0% 의 specular 를 가집니다. 이제 두 번째 이미지를 보죠.

이 녀석은 카메라로 향하는 specular 빛에 대한 편광이며 편광 필터에 대해 정렬되어 있습니다. 그래서 모든 빛이 통과하게 되죠. 반면에 diffuse 빛은 여전히 비편광 상태이며, 절반이 흡수됩니다. 이 이미지는 50% 의 diffuse 와 100% 의 specular 를 가집니다.

만약 첫 번째 이미지를 A 라고 하고 두 번째 이미지를 B 라고 한다면, diffuse 이미지는 2 * A 이고 specular 이미지는 B - A 입니다. 물론 이 이미지들은 sRGB 프로우파일로 저장되어 있습니다. 그래서 두 개의 이미지를 비교하고 그것들을 분리해서 각 결과를 sRGB 이미지로 만드는 쉐이더 코드를 만들었습니다. 항상 그렇듯이, 이 코드를 실제로 테스트해 본 건 아닙니다.

float LinearToSrgb(float val)
{
   float ret;
   if (val &lt;= 0.0)
      ret = 0.0f;
   else if (val &lt;= 0.0031308f)
      ret = 12.92f*val;
   else if (val &lt;= 1.0f)
      ret = (pow(val, 0.41666)*1.055f)-0.055f;
   else
      ret = 1.0f;
   return ret;
}

float SrgbToLinear(float val)
{
   float ret;
   if (val &lt;= 0.0f)
      ret = 0;
   else if (val &lt;= 0.04045f)
      ret = val / 12.92f;
   else if (val &lt;= 1.0f)
      ret = pow((val + 0.055f)/1.055f,2.4f);
   else
      ret = 1.0f;
   return ret;
}

int g_bSpecOrDiff;

float4 ps_main( float2 texCoord  : TEXCOORD0 ) : COLOR
{
   float3 srcA = tex2D(Texture0, texCoord ).rgb;
   float3 srcB = tex2D(Texture1, texCoord ).rgb;
   float3 linA = SrgbToLinear(srcA);
   float3 linB = SrgbToLinear(srcB);
   float3 linDiff = linA*2;
   float3 linSpec = linB-linA;
   float3 texDiff = LinearToSrgb(linDiff);
   float3 texSpec = LinearToSrgb(linSpec);
   float3 ret = g_bSpecOrDiff ? texDiff : texSpec;
   return ret;
} 

만약 이 코드가 맞다면 여러분의 diffuse 이미지는 다음과 같이 보일 겁니다:

그리고 specular 이미지는 다음과 같이 보일 겁니다:

이 절차는 완벽한 것은 아닙니다.

1. 보통 diffuse-only 이미지에서 약간의 specular 를 포함하게 될 것입니다.

2. 어떤 물체들에 대해서는 별로 좋은 결과를 내지 못합니다. Specular-only 이미지에서, 손잡이가 약간 푸르스름합니다. 저는 손잡이가 유전체( dielectric )여서 ( 역주 : 빛의 ) 주파수를 약간 교란한다고 생각합니다.

3. 만약 specular 나 diffuse 이미지가 1.0 에서 잘린다면( clamping ), 그런 픽셀들은 엉망이 될 겁니다. 그럴 때는 노출값을 낮추는 것이 좋습니다.

4. 원격 셔터를 사용하십시오. 그렇지 않으면 정렬 문제가 생길겁니다.

별로 문제가 많이 발생하지 않고 여러분만의 specular/diffuse 분리를 해낼 수 있었으면 좋겠네요.

휴~ 긴 글이었습니다.

주의 : 이상한 내용이 포함되어 있을 수 있으므로 이상하면 참고자료를 확인하세요.

주의 : 번역이 개판이므로 이상하면 원문을 확인하세요.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

원문 : Everything has Fresnel

이번에는 지난 시간에 이어 specular 현상이 왜 발생하는지에 대해서 다루는 John Hable 의 글을 번역해 보았습니다. 쉐이더 코드가 일부 들어 가 있지만 같이 나오는 다이어그램을 보시면 이해하기가 크게 어렵지는 않을 것입니다.


[ PBR 이란 무엇인가 ] 15. [ 번역 ] 모든 것은 프레넬을 가집니다

이 포스트는 "모든 것은 빛납니다" 라는 포스트의 2탄이라고 생각하셔도 됩니다. 표준 specular 라이팅이 게임에서 매우 자주 사용되지만, 우리가 게임에서 거의 보기 힘든 효과는 바로 프레넬( fresnel )입니다.

여러분은 이제 specular 가 무엇인지 알고 있습니다. 비디오 게임에서 가장 일반적인 specular 모델은 Blinn-Phong 이며 다음과 같이 정의됩니다.

 H = normalize( V + L )
 specVal = pow( saturate( dot( H, N ) ), power );

이 경우 V 는 뷰 벡터이며, L 은 라이트 벡터입니다. 그리고 N 은 노멀 벡터이고, power 는 specular 요소입니다. H 는 유도된 하프 벡터이며, 이 벡터는 뷰 벡터와 라이트 벡터의 절반을 의미합니다.

이것은 어떻게 동작하는 것일까요? 여기 다이어그램이 있습니다.

여러분은 뷰, 라이트, 노멀 벡터를 볼 수 있습니다. 자, 이 함수를 사용하면, specular value peak( specular 값이 가장 큰 부분 ) 는 어디에 생길까요? 직관적으로 볼 때, 여러분은 뷰 벡터가 라이트 벡터에 대한 반사 벡터일 때 specular 함수가 최대이기를 원할 겁니다. 그러면 무슨 일이 벌어질까요? 이 함수는 하프 벡터가 노멀에 정확하게 정렬되었을 때 최대값이 되는데, 이는 뷰 벡터가 라이트 벡터에 대한 정확한 ( 거울같은 ) 반사 벡터일 때 발생합니다.

여기에 다른 경우가 있습니다:

역시 뷰 벡터는 라이트 벡터로 반사됩니다. 이 경우에 specular highlight 가 더 밝을까요, 아니면 더 흐릿할까요, 아니면 첫 번째 경우랑 같을까요? 글쎄요, 그것은 같습니다( 역주 : 계산 결과로만 보자면 같다는 의미 ). 왜냐하면 여러분은 두 경우에 모두 specular highlight 의 최대 값을 보고 있는 것입니다. 이것이 실세계에서는 어떻게 동작을 하는 것일까요? 짧은 답은 NO 입니다.

여기에 벽돌 그림이 있는데, 서로 다른 카메라 각도에서 촬영한 것입니다. 위의 이미지에서 빛과 카메라는 모두 아래쪽을 향하며, 이것은 첫 번째 경우를 표현합니다. 두 번째 줄에서는, 빛이 지표각( grazing angle )에서 비추고 있으며, 이는 두 번째 경우를 표현합니다. 필자는 specular 와 diffuse 를 편광필터로 분리했으며, 그래서 왼쪽에 있는 것은 diffuse 이며, 오른쪽에 있는 것은 specular 입니다. 벽돌을 체크해 보죠.

어쩌라고!!! 빌어먹을 벽돌처럼 단순한 재질에 대해서도 specular 에 대한 Blinn-Phong 모델은 완전 잘못된 결과를 냅니다. 그리고 이러한 일이 발생한 이유는 프레넬이라 불리는 자명한 것 때문입니다.

다른 두 가지 경우의 specular 를 살펴 보도록 합시다. Blinn-Phong 에 의하면, 그것들은 동일한 세기를 가지지만, 현실에서는 지표각에 가까울수록 훨씬 밝아집니다( 역주 : 필자가 무슨 의도로 그림을 더 첨부했는지 모르겠지만 첨에 나왔던 그림과 동일합니다. 차이가 뭔지 알아 보려고 노력하실 필요는 없습니다. 그냥 스크롤 하지 말라고 그랬나 봅니다 ).

 

이 효과를 다루려면, 여러분은 프레넬을 사용할 수 있습니다. 프레넬을 근사계산하는 매우 좋은 실시간 근사계산은 바로 Schlick Fresnel 입니다. From the GPU Gem 3 chapter on skin:

 float base = 1 - dot( V, H );
 float exponential = pow( base, 5.0 );
 float fresnel = exponential + F0 * ( 1.0 - exponential );
 specVal *= fresnel;

왠지는 모르겠지만 대부분의 사람들은 물, 유리, 금속과 같이 정말 밝게 빛나는 표면에서만 프레넬이 나오기를 원하는 경향이 있습니다. 하지만 실제로는 프레넬은 거의 모든 재질에 존재하는 강한 효과입니다. 사실 필자는 프레넬은 덜 빛나는 재질에서 가시적으로 더욱 중요한 역할을 한다고 주장하고 싶습니다. 여기 PVC 파이프의 일부가 있습니다.

분명히, PVC 는 프레넬을 가집니다. 그러나 필자의 관점에서는 프레넬이 PC 보다는 벽돌에서 더 가시적으로 중요한 효과를 가진다고 이야기하고 싶습니다. 아무 것도 없는 데서 강한 specular 가 나오는 것이 높은 specular 를 더 높게 만들어 주는 것 보다는 더 중요합니다. 그게 더 중요한 효과가 아닐까요? 필자에게 있어, 물, 유리, 금속을 위해서만 프레넬을 고려하는 것은 잘못된 것이라고 봅니다. 왜냐하면 그것은 덜 빛나는 표면에서 엄청난 가시적 차이를 만들기 때문입니다. 여기에 몇 가지 예제가 더 있습니다.

싸구려 판지. 매우 오해를 받고 있습니다. 그것은 항상 "순수 diffuse 재질"로서 참조되고 있습니다. 심지어는 그것이 실제로는 그것의 빛나는 친구들과 어울려 다닐 자격이 있음에도 불구하고 말이죠. Specuar 는 직각에서 중요합니다. 왜냐하면 그것이 미묘한 채도 감소( subtle desaturation )를 추가하기 때문입니다. 하지만 판지는 지표각에서 매우 밝은 specular 반사를 보여 줍니다.

여러분이 아침에 일하러 가기 위해 운전을 할 때 왜 더 밝은지 생각해 본 적이 있으신가요? 대부분의 사람들은 태양이 자신들의 눈에 들어 오고 있기 때문이라고 생각합니다. 사실 그 밝음의 주요 원인은 포장도로가 매우 강한 프레넬을 가지고 있기 때문입니다. 다음에 운전할 때 태양이 눈에 들어 오면, 사이드 미러를 확인해 보십시오. 그러면 사이드 미러에 보이는 포장도로가 당신의 전면에 있는 포장도로보다 얼마나 어두운지 알 수 있을 것입니다.

여기에 어떤 천이 있습니다. Ikea 에서 가지고 온 수건입니다. 거친 면재질 천은 여러분이 집 주변에서 볼 수 있는 일반적인 재질보다 더 적은 specular 를 가집니다. 이러한 비교는 썩 좋지는 않은데, 두 번째 이미지가 전체적으로 더 밝기 때문입니다. 그래서 specular 에서 diffuse 로 상대적으로 변환하는 것을 보기가 힘듭니다. 만약 더 좋은 예를 원하신다면, 그것은 독자의 경험으로 남겨 두겠습니다.

그리고 재미로 X-Rite 컬러 체커를 확인해 봅시다. 컬러 체커를 사용할 때, 여러분은 카메라로부터 수직으로 그것을 배치하는 것이 좋습니다. 왜 그렇게 해야 하는지 저에게 물어볼 필요가 없어졌으면 좋겠군요.

프레넬 만세!

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

주의 : 번역이 개판이므로 이상하면 원문을 확인하세요.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

원문 : Everything is Shiny, Jonh Hable.

지난 시간까지는 조도( illuminance )에 대한 내용들을 중점적으로 다뤘지만, 이번 시간부터는 휘도( luminance )에 대한 내용을 중점적으로 다루게 됩니다. 

PBR 라이팅 모델에서 휘도계산을 하는데 있어서 기존의 라이팅 모델들과 다른 점은 여러 가지가 있지만, 아티스트들의 입장에서 가장 크게 느껴지는 부분은 diffuse 와 specular 가 계산되는 방식일 겁니다. 예전에는 diffuse 와 specular 의 세기를 아티스트가 직접 설정해야만 했습니다. 물론 완벽하게 이해하고 사용하는 것은 아니지만 대충 값을 조정하다가 보면 원하는 결과가 나오곤 했습니다.

그런데 PBR 로 오면서 metalicity( 금속성 ) 와 roughness( 거칠기 ) 라는 항을 통해 diffuse 와 specular 성분을 조정하게 됩니다. 물리에 기반한 개념이므로 아티스트가 처음에 이해하기는 어렵습니다. 하지만 그 값들은 매우 명확한 물리적 의미를 가지고 있기 때문에 전체적인 통일성을 높일 수 있으며, 값들을 ( 라이팅 종류에 따라 ) 건별로 조정할 필요가 없기 때문에 작업 생산성이 증가하게 됩니다.

그러므로 PBR 이 휘도를 계산하는 데 있어 배경으로 깔고 있는 개념들에 대해서 적절히 이해하는 것이 올바른 재질을 설정하는 데 있어 도움이 될 것 이라 생각합니다. 보통 PBR 을 처음 접하시는 분들이 diffuse map 에 그린대로 결과가 나오지 않는다는 이야기를 많이 하시는데, 이것은 diffuse 와 specular 가 어떤 식으로 반영되고 있는지를 이해하지 못하고 있기 때문에 나올 수 밖에 없는 질문이며, 매우 당연한 질문입니다.

휘도를 다루는 데 있어 반사의 성질 및 구성요소를 이해하는 것은 매우 중요합니다. 그런데 첨부터 에너지 보존법칙이니 metalicity 니 roughness 니 하는 것들을 이야기하는 것은 잘 와닿지도 않고 어려울 수 있습니다. 그래서 이러한 개념에 대해서 쉽게 설명하고 있는 John Hable 의 글을 두 개 번역하기로 했습니다. 오늘은 첫 번째 주제인 "Everything is Shiny" 입니다.


[ PBR 이란 무엇인가 ] 14. [ 번역 ]모든 것은 빛납니다

가끔씩 필자는 초심자용 온라인 문서나 논문에서 specular 에 대해서 언급하는 것을 보게 됩니다. 그리고 "옷이나 판지( cardboard )와 같은 일부 재질들은 스펙큘러를 가지지 않는 순수한 diffuse surface 들입니다" 라는 줄을 보게 됩니다. 이는 거짓말입니다. 실세계에 존재하는 모든 물체는 specular 를 가지고 있습니다. 심지어는 매우 오래된 옷이나 판지라도 specular 를 가집니다. 이것을 집에서 만든 편광 설정을 통해서 이를 확인했습니다. 편광 필터를 수작업으로 만들고 있었기 때문에, 여기에서 보여주는 구분이 완벽하지는 않습니다만, 충분히 잘 동작하는 것으로 보입니다.

먼저, 면직물 셔츠로 시작할 것입니다. 이는 필자가 구할 수 있는 가장 diffuse 한 것입니다. 거의 대부분의 면직물 셔츠들은 더 많은 specular 를 가지고 있습니다.

 Main:

 

 Diffuse:

 

 Specular:

 

이제 어떤 데님( 역주 : 청바니 만들 때 쓰는 푸른색 질긴 면직면. 출처 : 네이버 사전 )을 살펴 봅시다. 좀 더 빛나는 옷 재질입니다.

 Main:

 

 Diffuse:

 

 Specular:

 

다음으로 어떤 판지를 살펴 봅시다. 엡! 그것은 빛납니다.

 Main:

 

 Diffuse:

 

 Specular:

 

비교를 위해서, 여기에 내 팔레트중 하나를 가지고 왔습니다. Specular highlight 가 매우 밝습니다.

 Main:

 

 Diffuse:

 

 Specular:

 

여기에 몇 가지 다른 것들이 있습니다. 벽돌이 얼마나 많은 specular 를 가지고 있는지를 확인해 보세요.

 Main:

 

 Diffuse:

 

 Specular

 

마지막으로 보도에 깔린 콘크리트 조각이 있습니다. 보도의 콘크리트는 포장도로보다는 덜 빛납니다.

 Main :

 

 Diffuse:

 

 Specular:

 

 

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


[ 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 에러 발생.

Before-After 이미지 비교 플러그인. 

 

 

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


[ PBR 이란 무엇인가 ] 10. Image Based Lighting

Global Illumination & Indirect Lighting ] 에서는 GI 를 위해 indirect light 가 고려되어야 한다고 했습니다. 

광원이 있는 경우라면 그것을 통해 indirect light 를 계산할 수 있죠. 하지만 현실적으로 모든 light 를 배치하는 것은 힘듭니다. 그렇기 때문에 이를 근사계산하기 위해 그래픽스 연구자들은 indirect light 를 image 로부터 얻어 오는 방법에 대해서 고민했고, 요새 자주 사용되는 Image Based Lighting( IBL ) 이라는 개념을 생각해 냈습니다.

이 문서에서는 이 기법이 어떻게 발전해 왔는지, 구체적으로 어떤 개념인지에 대해 다루도록 하겠습니다. UE4 에서 IBL 이 사용되는 방식에 대해서는 시리즈의 다음 문서에서 다루도록 하겠습니다.

아티스트 분들은, 글 내용 중에 이상한 개념들이 나오는데, 너무 어렵게 생각하지 마시고 그냥 그런게 있다고 생각하고 넘어가시기 바랍니다. 언급을 안 하기도 그렇고 하기도 그렇고 그래서 대충 개념만 썼는데, 어려울 수 있습니다. 그냥 기본 설명과 그림을 위주로 보시기 바랍니다. 어려운 개념들은 몰라도 크게 상관은 없는 것들입니다.

Cubemap

그래픽스 카드들은 큐브맵이라는 것을 지원합니다. 큐브맵은 어떤 시점을 중심으로 둘러싼 환경들을 미리 렌더링해서 저장할 수 있는 텍스쳐입니다. 보통 특정 위치에서 여러 방향으로 사진을 찍은 다음에 그것을 보정해서 합치는거죠. 이러한 큐브맵이 실시간 그래픽스에서 가장 먼저 사용된 곳이 SkyBox 와 SkySphere 였습니다.

그림1. 스카이 큐브맵. 출처 : Rendering a skybox using a cube map with opengel and glsl.

그림2. 그림1 의 렌더링 결과. 출처 : 그림1 과 같음.

보통 원경에다가 사용했기 때문에 거리에 의한 왜곡의 영향도 덜 받았으며 가시 품질도 나름 괜찮았죠.

Environment Mapping

그런데 텍스쳐를 이용해 금속의 반사 소스로 활용하려는 움직임이 있었습니다. 1976 년에 Blinn 과 Newell 이 "Texture and reflection in computer generated images" 라는 논문에서 이 개념에 대해 발표한 바가 있고 80 년와 90 년대 초반에 여러 사람에 의해 사용되고 발전해 왔습니다( 자세한 내용에 대해 알고 싶으시다면 [1] 의 문서를 확인하세요 ).

이러한 방식을 Environment Mapping( 환경 매핑 ) 혹은 Reflection Mapping( 반사 매핑 ) 이라 부릅니다. 특히 그래픽스 하드웨어가 지원하는 큐브맵을 이용하면 이를 쉽게 표현할 수 있습니다.

그림3. Environment Mapping 예제. 출처 : Environment Mapping, Han-Wei Shen.

큐브맵의 좌표는 그림4 처럼 큐브( 정육면체 )의 중심을 원점으로 하는 좌표계로 표현됩니다.

그림4. 큐브맵 좌표계. 출처 : CS 354 Texture Mapping.

그렇기 때문에 그림5 에서 볼 수 있듯이, 표면의 normal 과 viewer 를 향한 vector 를 알게 되면 반사벡터를 쉽게 알 수 있습니다.

그림5. 환경 매핑 좌표 구하기. V 는 View 에서 서피스까지의 벡터, N 은 노멀, Vr 은 반사 벡터. 출처 : Mapping Techniques.

그림5 에서 큐브가 아니라 구가 나와서 헷갈리는 분도 계실 수 있지만, 구는 그림6 처럼 큐브에 매핑될 수 있습니다. 벡터는 길이는 달라도 방향이 동일하면 정규화( normalize )했을 때 같은 벡터가 됩니다. 그것을 큐브맵의 좌표로 사용합니다.

그림6. sphere 와 cube 에서 벡터.

이때까지는 EnvironmentMapping 이라는 것이 금속의 반사를 표현하기 위한 소스의 역할만 하고 있었습니다.

Image Based Lighting

그렇지만 하드웨어 성능이 더 발전하면서 Environment Mapping 을 한 단계 더 발전시켜 광원으로 만드려는 시도가 있었으며, 이것이 바로 Image-Based Lighting( IBL, 이미지 기반 조명 ) 개념입니다. 이것에 대해 주도적으로 연구한 사람은 Paul Debevec 이라는 분인 것 같더군요[2]( [2] 에 가면, 2003 년까지의 HDR 및 IBL 관련 자료들에 대한 링크가 많습니다. 관심있는 분들은 확인해 보시기 바랍니다 ).

IBL 과 Environment Mapping 의 가장 큰 차이는 이미지 소스가 광원으로서 사용되느냐 그렇지 않느냐에 있습니다.

IBL Diffuse

표면에 들어 온 빛은 그림7 처럼 반사( reflected ), 투과( transmitted ), 흡수( absorbed )됩니다. 

 

그림7. 빛의 반사, 투과, 흡수. 출처 : Waves, KaiserScience.

그 중에서 반사 성분은 그림8 처럼 크게 diffuse reflection( 난반사 ) 과 specular reflection( 정반사 ) 로 나뉘죠. 다들 이 개념에 대해서는 익숙할 것입니다. 그런 현상이 왜 발생하는지는 시리즈의 뒷부분에서 다루도록 하겠습니다.

그림8. Diffuse reflection & Specuar reflection. 출처 : Diffuse reflection, Wikipedia.

어쨌든 기존 environment mapping 에서는 큐브맵을 specular 성분으로만 사용했습니다. 앞서 말했듯이 금속을 표현하기 위한 용도였죠. 하지만 이를 제대로 된 광원으로서 사용하기 위해서는 diffuse 성분도 고려되어야 하며, GI 까지 생각하면 그림9 처럼 주변광들까지 고려가 되어야 합니다. 주변광까지 고려해서 큐브맵으로부터 서피스에 들어 온 빛들은 서피스 입장에서 보면 조도( illuminance, irradiance )라 할 수 있겠죠. 그래서 이러한 정보를 담고 있는 것을 irradiance map 이라고 부릅니다.

그림9. IBL 에서 irradiance. 출처 : [3].

이 irradiance map 은 해당 지점에서 오는 난반사 성분을 받아야 하기 때문에 원래 이미지보다는 뭉개진 느낌이 나겠죠.  그림10 의 오른쪽에 보이는 큐브맵이 irradiance map 입니다.

그림10. Irradiance map 샘플. 출처 : [3].

그런데 여기에서 한 가지 문제가 발생합니다. 큐브맵 텍스쳐를 샘플링( 텍스쳐에서 텍셀( texel )값을 가지고 오고 필터링( filtering )하는 작업 )하는 것은 그렇게 비용이 싼 작업이 아닙니다. 게다가 그림9 에서처럼 매우 많은 영역을 샘플링해야 합니다. 그림9 에서는 예를 들기 위해서 화살표 몇개만 그려주고 2D 로 보여줬지만 3D 에서 엄청난 텍셀을 샘플링해야 합니다. 물론 하나만 할 수도 있겠지만, 그럼 주변광( indirect lighting )을 받는다는 GI 원칙에서 어긋나겠죠. [3] 에 따르면 한 면이 128 X 128 해상도인 큐브맵에서 diffuse 를 구하기 위해서는 50,000 만큼의 샘플링이 필요하다고 하네요.

그래서 연구자들은 이를 좀 단순하게 계산하는 방법을 찾았습니다. 그것이 바로 Spherical Harmonics( SH, 구면 조화 )입니다. 이름부터 무시무시합니다. 어디선가 한번씩은 들어 보셨을 거라 생각합니다. 이 주제에 대해서는 저도 완전히 파악하고 있지 않기 때문에 나중에 시간이 나면 자세히 다루도록 하겠습니다.

SH 는 구면에서 뭔가 튀어나오거나 들어 간 물체의 형태를 그림11 처럼 수학적으로 표현하는 것입니다. 그런데 계수( l )가 얼마나 크냐에 따라서 좀더 디테일한 모양이 나오는 것이죠. 이 함수에다가 각도같은 걸 넣으면 원하는 형태를 얻을 수 있습니다. 솔직히 이해하기 힘드니 "그냥 그렇구나" 하시면 됩니다.

그림11. Spherical Harmonics 예제. 출처 : Opinions on Spherical harmonics.

그런데 이걸 어디다 쓰느냐구요?

예를 들어 그림10 의 irradiance map 을 구에다가 매핑한 다음에 밝기에 따라 튀어 나오게 하면 그림12 같은 느낌이 되겠죠(그냥 개념적으로 쉬운 설명을 위한 용도로 만들었으므로 정확한 것은 아닙니다 ).

그림12. 그림10 의 irradiance 를 구에 매핑했을 때의 느낌.

그럼 이 모양에 SH 를 적용해서 단순한 함수로 만듭니다. 그림11 에서 봤듯이 어떤 계수를 사용하느냐에 따라 좀 더 많은 모양이 나오고 결과가 정확해지겠죠. 대체적으로 "l = 3" 정도면 그럴싸한 모양이 나온다고 합니다.

어쨌든 이런 식으로 각 텍셀을 SH 함수로 만들면 빛이 나가는 경향과 세기가 결정됩니다. 아티스트 분들은 그냥 "최적화 함수구나" 라고 이해하시면 됩니다. 우리가 사용하는 irradiance map 은 단순히 diffuse 를 저장한 것이 아니라 SH 를 저장한 것이라는 것만 알고 계시면 됩니다.

이러한 IBL diffuse 를 적용하면 그림13 과 같은 결과가 나옵니다.

그림13. IBL Indirect Diffuse. 출처 : [3].

IBL Specular

Specular 성분은 Environment Mapping 에서 다뤘던 것과 큰 차이는 없습니다. 하지만 PBR 에서는 roughness( 거칠기 ) 라는 개념을 도입했고, 같은 금속이라도 얼마나 연마되었느냐에 따라서 그림14 에서 볼 수 있듯이 매끈함의 정도가 틀립니다( roughness 에 대해서는 시리즈의 뒤쪽에서 설명하겠습니다 ).

그림14. Roughness 에 따른 매끈함의 변화.

일반적으로 거칠기에 의해 blurring 이 먹는 것을 표현하기 위해 그림15 처럼 mipmap 을 만들어 처리합니다. LOD 레벨이 높아질수록 Roughness 가 높은 것으로 취급합니다.

그림15. 반사맵 밉맵. 출처 : [3].

Roughness 0.1 일 경우에 specular 만 출력한 예가 그림16 에 나와 있습니다.

그림16. Roughness 01 에서 IBL Indirect specular 의 예. 출처 : [3].

정리

IBL 은 이미지를 광원으로 취급해서 indirect light 성분을 획득하는 방식입니다. 실시간 GI 를 구현하기 힘든 상황에서 대안으로 사용합니다. 사실, 실시간 GI 를 사용한다고 해도 모든 광원을 배치하는 것은 불가능하기 때문에, 같이 사용하는 경향이 있습니다.

실제 엔진에서는 importance sampling 이라든가 하는 복잡한 방식을 사용합니다. 왜냐하면 diffuse 처럼 specular 로 일정 영역을 샘플링해야 제대로 된 결과가 나오기 때문입니다. 이 기법은 반사, Area light, Ray-tracing, 적분( integral ) 등의 주제와 관련이 있으므로 여기에서 설명하기에는 좀 주제가 무겁습니다. 그러므로 나중에 따로 주제를 잡아서 이야기하도록 하겠습니다( 만약 자세한 내용을 알고자 한다면, "IBL Importance Sampling" 이나 "IBL Filtered Importance Sampling" 등의 키워드로 검색해 보십시오.

UE4 의 경우에는 Split Sum Approximation 과 Prefiltered Environment Map 이라는 기법을 사용해 IBL specular 를 구현한다는 것만 언급하고 넘어가겠습니다. 자세한 내용은 [ Real Shading in Unreal Engine 4 ] 의 [ Image based lighting ] 항목을 참조하세요 ).

참고자료

[1] The Story of Reflection Mapping.

[2] HDRI and Image-Based Lighting, SIGGRAPH 2003.

[3] Physcally Based Rendering - Part Three.

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


[ PBR 이란 무엇인가 ] 9. Global Illumination & Indirect Lighting

빛의 경로

지금까지는 빛이 서피스에 반사되어 들어 오는 경우만을 이야기했습니다. 하지만 빛이 눈에 들어 오는 경로는 그림1 처럼 매우 복잡합니다. 산란( scattering ), 투과( Transmission ), 반사( Reflection ), 흡수( Absorbtion )가 모두 고려되어야 매우 자연스러운 결과가 나옵니다.

그림1. 다양한 경로로 눈에 들어 오는 빛.

Local Illumination

컴퓨팅 파워의 한계 때문에 실시간 그래픽스에서 그림1 에 나오는 모든 요소들을 처리할 수는 없습니다. 그래서 PBR 이전에는 빛이 직접적으로 닿는 부분만 고려해 렌더링을 했습니다. 그렇게 되면 빛이 직접적으로 닿지 않는 부분은 어두워지겠죠. 그렇지만 우리는 실세계에서는 완벽하게 어두운 부분은 존재할 수 없음을 알고 있습니다. 왜냐하면 한 서피스에 도달하는 빛은 여러 서피스로부터 반사된 후에 올 수 있다는 것을 알고 있기 때문입니다. 그래서 주변광( ambient light ) 라는 개념을 만들어서 어두운 부분을 대충 밝혀 줍니다.

Ambient 를 얼마만큼 밝혀줘야 하느냐? 그것은 아티스트의 감에 달려 있었습니다. 그래서 global ambient 와 material ambient 가 나뉘어 있었습니다. 오브젝트가 놓여 있는 환경이 다르다보니 어쩔 수 없는 선택이었죠.

이런 방식을 local illumination( 지역 조명 ) 이라고 부르며, 그 결과는 그림2 와 같습니다.

그림2. Local Illumination. 출처 : [1]

Global Illumination

사람들은 Local Illumination 이 부자연스럽다는 것을 알고 있습니다. 서피스에 들어 온 빛은 다시 다른 서피스를 위한 광원으로 사용될 수 있습니다. 물론 투과되는 빛도 다른 서피스를 위한 광원으로 사용될 수 있습니다. 이렇게 서피스에서 반사되거나 굴절되는 모든 빛을 새로운 광원으로 삼는 것을 Global illumination 이라 부릅니다.

그림3. Global Illumination. 출처 : [1]

그림3 을 보시면 그림2 와 비교했을 때 굴절과 반사가 제대로 제대로 적용되고 있음을 확인할 수 있습니다. 우리가 실세계에서 익숙하게 볼 수 있는 복잡한 결과가 나오게 되는겁니다.

이제 여기에서 여러분에게 GI 의 핵심 성분들에 대해서 이야기하도록 하겠습니다.

그림4. 눈에 들어 온 GI 성분들. 출처 : [2]

    • Direct light 는 광원에서 표면으로 직접 들어 온 빛을 의미합니다.

    • Indirect light 는 표면에 들어 온 빛이 반사되어 새로운 광원처럼 사용되는 경우를 의미합니다.

만약 direct light 성분만 계산을 하게 되면 그림5 와 같은 결과가 나옵니다.

그림5. Direct light Only. 출처 : [3].

바닥에 비춰진 빛이 반사되어( bounced ) 광원( indirect light )으로 작용하면, 그림6 과 같은 결과가 나옵니다.

그림6. Direct light + 1-bounced indirect light. 출처 : [3].

그렇다면 광원에서 나와서 한 번 반사된( 1-bounced ) 빛이 한 번 더 반사되면( 2-bounced ) 어떻게 될까요? 다시 다른 오브젝트나 바닥으로 반사되는 빛들도 존재하겠죠? 그러면 그림7 처럼 됩니다.

그림7. Direct light + 2-bounced indirect light. 출처 : [3].

이러한 bounce 가 많으면 많을수록 더 자연스러운 결과가 나오게 됩니다.

하지만 실시간 그래픽스에서 이런 계산을 하는 것은 너무 힘듭니다. 그래서 이를 근사계산하기 위해서 LPV( Light Propagation Volume ), SVOGI( Sparse Voxel Octree Global Illumination ) 등의 실시간 GI 알고리즘들과 Image Based Lighting( IBL ) 과 같은 개념들이 나오게 되었습니다.

Indirect Lighting Bounce 설정

UE4 에서는 LPV 를 적용하지 않는 이상 Static 과 Stationary 에 대해서만 GI 를 적용해 줍니다. UE4 에서는 이를 "Lightmass Global Illumination" 이라고 표현합니다.

툴바에 있는 "Settings" 풀다운 버튼에는 "World Settings" 메뉴가 있습니다.

그림8. WorldSettings 메뉴.

거기에 보면 "Lightmass" 카테고리가 있죠. 그 중에 "Num Indirect Lighting Bounces" 항목이 있습니다. 위에서 언급했듯이, 광원에서 나온 빛이 반사되서 나오는 횟수를 의미합니다. 이것이 0 이라면 direct light 성분만을 고려하겠다는 겁니다.

그림9. Direct light only.

이제 bounce 를 2 와 4 로 주고 결과를 확인해 보도록 하겠습니다.

그림10. Direct Light + Indirect Light. Bounce 2 일 때와 4 일 때의 결과.

Bounce 가 2 일 때와 4 일 때 미약하지만 영향을 주는 범위가 넓어지는 것을 알 수 있습니다. 하지만 2 일 때와 4 일 때의 차이는 범위가 조금 넓어졌다는 것 말고는 큰 차이가 없습니다. 반사를 하면 할수록 빛이 감쇠되기 때문에 너무 높은 값을 잡아 봐야 별로 소용이 없습니다. 그러므로 2 정도의 값이 적절합니다. 이는 Lightmass 빌드 속도에도 영향을 주므로 신중하게 결정할 필요가 있습니다.

Indirect Lighting Intensity 설정

하지만 기대했던 것처럼 Indirect light 의 영향력이 크지 않아 실망하시는 분들이 계실겁니다. 이 때 사용할 수 있는 것이 "Indirect Lighting Intensity" 속성입니다. UE4 의 광원들에는 "Light" 카테고리에 "Indirect lighting Intensity" 라는 항목이 있습니다.

앞의 "Lightmass" 카테고리의  설정은 전역 설정이지만 "Light" 카테고리 설정은 개별 설정입니다. 이 값을 2 로 해 놓고 결과를 확인해 보도록 하겠습니다.

그림11. Direct Light + Indirect Light. Bounce 2 일 때와 Intensity 값을 2 배로 늘려서 결과 비교.

Intensity 를 올리면 굳이 bounce 를 올리지 않더라도 더 멀리까지 영향을 주는 것처럼 보일 수 있습니다. 물론 가까운 곳에서 indirect lighting 의 영향이 세지기는 하겠죠.

Direct Light 와 Indirect Light 의 관계

그런데 실무를 하다가 보면 이상한 의문이 드는 분들이 계실 겁니다. "Light intensity 를 내렸더니 direct lighting 성분이 내려 가고 indirect lighting 성분이 올라 가요!!!", "Lighting build 를 했더니 갑자기 결과가 너무 달라져요!!!"

그림11 까지의 스샷들은 5000 루멘을 기준으로 찍은 것입니다. 그림11 의 두 번째 환경에서 light intensity 만 100 으로 고쳐보겠습니다.

그림12. 5000 루멘에서 100 루멘으로 내렸을 때

이상한 결과가 나왔지만 매우 당연한 것입니다. Indirect lighting 이라는 것은 Lightmass 빌드를 돌려야만 계산되는 것입니다. 그래서 intensity 를 바꾸게 되면 direct lighting 성분에는 즉각 반영이 되지만, indirect lighting 성분은 이전에 계산했던 값 그대로 남아 있게 됩니다. 그러면 direct light 성분이 낮아졌으므로 indirect light 성분은 ( auto exposure 에 의해 ) 상대적으로 밝아지는 것이죠. 그러므로 stationary light 의 Intensity 값을 변경하면 반드시 Lightmass 빌드를 해 주셔야 합니다. 물론 Static 도 그래야 합니다.

이러한 사실을 이용하면 재밌는 비주얼 디버깅을 해 볼 수 있습니다. Intensity 값을 0 으로 해 버리면 direct light 성분이 평가되지 않으므로 indirect light 성분만 볼 수가 있습니다. 실제 영향을 준 범위가 어디까지인지 알 수가 있겠죠.

그림13. Intensity 를 0 으로 만들어 Indirect 성분만 확인.

Indirect light 성분을 조정하는 것은 direct light 성분에 전혀 영향을 주지 않습니다. 단지 상대적인 관계가 달라 보이는 것입니다.

Lighting Quality 와 메쉬

Lightmass 를 preview 로 빌드하면 indirect lighting 결과가 상당히 이상하게 나올 수 있습니다.

그림14. Lighting Quality 설정.

그림15 는 indirect light 가 테이블을 뚫고 나오는 것을 보여 줍니다.

그림15. Preview 에서 잘못 나오는 indirect lighting 결과.

제가 lightmass 구현을 안 봐서 잘 모르겠지만, 아마도 volume 에다가 illuminance 나 luminance 를 저장하는 것이 아닌가 싶습니다. 그리고 preview 에서 product 로 품질이 올라갈 수록 그 volume 의 크기가 조밀해지는 것이 아닌가 싶습니다. Ray 샘플 개수나 photon 개수 및 밀도 등 여러 요소들이 달라지기 때문입니다. 그렇기 때문에 샘플링되지 않은 부분들은 보간이 됩니다.

그래서 테이블의 두께를 3 배 정도로 해 봤습니다. 그랬더니 그림16 에서 볼 수 있듯이 preview 에서도 차폐가 거의 제대로 되는 것을 확인할 수 있었습니다.

그림16. 테이블의 두께를 3 배로 늘렸을 때의 결과.

사실 그림16 도 완벽한 결과는 아닙니다. Production 으로 빌드했을 때는 완벽하게 차폐가 됩니다.

여기에서 이야기하고자 하는 것은 메쉬의 두께가 얇으면 preview 에서 잘못된 결과를 산출할 수 있기 때문에 이를 고려하면서 작업을 하셔야 한다는 것입니다. 사실 너무 얇으면 production 에서도 문제가 발생합니다.

그러므로 가급적이면 너무 얇은 메쉬들은 안 만드는 것이 좋습니다. 만약 어쩔 수 없이 그런 메쉬들을 만들어야 한다면, bounce 를 1 로 설정하고 production 이나 high 품질에서 라이팅 결과를 확인하시는 것이 바람직할 것이라 생각합니다.

PostProcessVolume

전체 indirect lighting 성분을 전반적으로 올리고 싶은 경우가 있습니다. 그런 경우에는 PostProcessVolume 을 설치하시면 됩니다. "Rendering Features" 카테고리에 보면 "Global Illumination" 이라는 하위 카테고리가 있습니다.

"IndirectLightingColor" 는 전체 indirect light 성분의 색상에 곱해지며, "Indirect Lighting Intensity" 는 전체 indirect light 성분의 세기에 곱해집니다. 이런 설정은 전역적이므로 함부로 건드리지는 않는 것이 좋습니다. 라이트 설정을 다 해 놨는데 공식이 바뀌어서 전반적으로 intensity 가 달라졌다든가 하는 경우나 게임 그래픽스 컨셉상 이상한 조명을 만들고 싶다든가 하는 경우에만 사용해야겠죠.

그림17. PostProcessVolume 을 사용한 indirect 성분 조정.

여러분은 그림17 에서 보이듯이 이런 설정은 direct lighting 성분에 전혀 영향을 주지 않는다는 점을 이해해야만 합니다. Direct lighting 성분은 여전히 흰색으로 보이고 있음을 기억하세요.

참고자료

[1] Global illumination, Wikipeida.

[2] PRACTICAL REAL-TIME VOXEL-BASED GLOBAL ILLUMINATION FOR CURRENT GPUS, Alexey Panteleev, NVIDIA.

[3] Real-time Global Illumination Using Voxel Cone Tracing, Fredrik Prantare.

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


[ PBR 이란 무엇인가 ] 8. Attenuation Radius 설정

거리 감쇠

3. 빛의 감쇠 ] 에서는 빛의 감쇠가 1 / dist2 으로 이루어진다고 했습니다. 하지만 실질적으로 컴퓨터 프로그램에서는 그런 식을 사용할 수 없습니다. 왜일까요?

dist = 0 인 상황이라면 결과가 1 / 0 이 됩니다. 그러면 컴퓨터는 이를 inf( 무한대, infinity ) 라고 평가하게 됩니다. 그래서 보통은 기본 감쇠 공식은 거리가 0 이 될 수 없다고 가정하고 계산을 하게 됩니다. 게다가 가장 가까울 때의 빛이 원래 빛의 밝기보다 밝아서는 안 되는 원칙( 에너지 보존 법칙 )도 지켜줘야겠죠.

그래서 UE4 에서는 거리 감쇠를 위해 식1 을 사용합니다.

식1. UE4 에서의 거리 감쇠 공식.

DistanceBias 라는 것이 갑자기 튀어 나와서 놀라셨겠지만, 이것은 쉐이더에서 그냥 1 로 정해져 있습니다.

식만 봐서는 잘 모르겠죠? 이를 그래프로 보면 그림1 과 같습니다.

 

그림1. 1 / dist2 과 1 / ( dist2 + 12 ) 의 비교. 거리( x 값 )가 0 이 되면 파란 그래프는 최대값이 1 이 됩니다.

하지만 여전히 이 감쇠 그래프는 문제점을 가지고 있습니다. x 값이 엄청나게 커져도 0 이 되질 않습니다. 즉 빛은 그 세기가 다를뿐 무한대의 거리까지 퍼져나간다는 의미입니다.

감쇠 반경 마스크

대부분의 엔진들에서 광원들은 "Attenuation Radius" 라는 속성을 가지고 있습니다. 이는 광원이 영향을 미치는 거리를 강제로 줄여버리는 역할을 합니다.

그림2. UE4 의 Attenuation Radius 속성. Source Radius 는 area light 의 속성입니다. 

왜 이런 일을 할까요? 사실 이유는 별게 아닙니다. 컴퓨팅 파워가 부족하기 때문입니다.

그림3 을 보시면 어느 정도 거리가 멀어지면 빛의 거의 영향을 미치지 못하는 것을 볼 수 있습니다.

그림3. 눈으로 보면 일정 거리 이상에 빛이 닿지 않는 것처럼 보입니다. 하지만 사실은 그림1 에서 확인할 수 있듯이 매우 약한 빛이 닿고 있는 것이죠.

씬 내에는 수 백개 많으면 수 천개의 광원이 존재할 수 있는데, 이를 모두 계산하기에는 비용이 너무 비쌉니다. 그래서 특정 광원이 미치는 범위를 조절하게 됩니다. 그것이 바로 감쇠 반경( Attenuation Radius )인 것이죠. 이를 UE4 에서는 내부적으로 "Light Radius Mask" 라고 부릅니다.

이는 식2 와 같이 계산됩니다.

식2. LightRadiusMask 공식.

식이 어렵죠? 이해하시려고 노력하실 필요는 없습니다. 저도 잘 이해가 안 갑니다. 여기에서 saturate() 라는 함수는 0 보다 작은 값은 0 으로, 1 보다 큰 값은 1 로 잘라주는 함수입니다.

어쨌든 그래프로 느낌만 이해하면 됩니다. 예를 들어 Radius 가 1 인 경우와 2 인 경우를 살펴 봅시다. 그럼 그래프는 그림4 와 같은 모양을 가집니다.

그림4. LightRadiusMask 그래프. Radius 가 1 이면 Dist 가 1 이 넘어갈 때 밝기가 0 이 되고, Radius 가 2 이면 Distance 가 2 가 넘어갈 때 밝기가 0 이 됩니다.

거리 감쇠와 감쇠 반경의 결합

이제 위에서 언급한 두 개의 식( 식1식2 )을 곱한 것이 최종 감쇠 공식입니다.

식3. 최종 감쇠 공식.

식을 봐봐야 머리만 아프니 그래프를 살펴 보죠. 이것의 형태는 그림5 와 같습니다. 그래프의 차이를 좀 더 명확하게 보기 위해서 빛의 밝기를 10 이라고 가정했습니다.

그림5. 빛의 밝기가 10 일 때 최종 감쇠 그래프.

여러분은 그림5 를 보면서 Radius 가 크면 클수록 Attenuation Radius 를 적용하지 않은 원래의 감쇠 공식과 비슷해진다는 것을 알 수 있을 겁니다. 그러므로 과도하게 적은 Radius 를 잡으면 감쇠가 이상한 형태로 나오는 결과를 산출하게 됩니다.

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


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

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


[ PBR 이란 무엇인가 ] 6. 휘도( luminance ) 측정

휘도

오늘은 드디어 빛이 여행하는 최종단계를 다룹니다. 광원에서 방출되어 표면에 도달한 빛은 표면의 재질( 속성 )에 따라 여러 방향으로 반사됩니다. 그리고 나서 그 빛 중의 일부는 우리의 눈에 도달하게 됩니다.

그림1. 조도와 휘도의 관계.

여러 분이 인터넷에서 광속( luminance flux ), 조도( illuminance ), 휘도( luminance ) 등에 대해서 검색을 하게 되면, 휘도에 대해서 검색했을 때 가장 많은 자료들이 나오는 것을 확인할 수 있습니다. 인간은 눈에 들어 온 빛만을 인지할 수 있기 때문에 당연한 것이겠죠. 그만큼 중요하며 대부분의 쉐이딩( shading ) 이론들이 이 휘도와 관련이 있습니다.

휘도는 다음과 같이 정의됩니다.

휘도는 특정 방향으로 이동하는 단위 면적당 광도( luminous intensity )를 측정한 것입니다. 그것은 특정 영역에서 방출되거나 반사되어 주어진 입체각 안에 들어 온 빛의 양을 의미합니다. 휘도의 국제 단위는 제곱미터 당 칸델라( candela per square meter( cd/m2 ) )입니다.

뭐 어렵게 써 놨지만 그냥 그림2 처럼 눈에 들어 온 빛의 양을 측정하는 것입니다. 개념 설명을 위주로 할 것이기 때문에 여기에서 공식은 자제하겠습니다. 사실 이 휘도라는 것은 매우 복잡하기 때문에 모델도 다양하고 여기에서 바로 다루기에는 어려운 부분이 있습니다. 앞으로의 재미로 남겨 두기로 하죠.

그림2. 특정 면적을 가진 표면에 대한 조도와 휘도.

거대 표면( macro-surfce )과 핀홀( pin-hole ) 카메라

지금까지 우리는 조도와 휘도를 이야기할 때 매우 큰 면적을 가진 표면과 큰 면적을 가진 눈을 중심으로 이야기했습니다. 하지만 그러한 관점에서 설명하면 이해하기가 어렵고 실제로 계산이 복잡해집니다. 그러므로 이쯤부터는 미세 표면과 핀홀 카메라를 중심으로 이야기를 하도록 하겠습니다.

만약 우리가 관심을 가지는 표면의 크기가 우리가 겨우 인지할 수 있을만한 점에 가깝다고 가정해 봅시다. 점에 가깝다는 것이 중요합니다. 완전히 점은 아닙니다. 이것을 거대 표면( macro surface )이라고 부르는데요, 여기에 이의가 있는 분들이 있을 수 있습니다. "Macro" 는 "거대한" 이라는 뜻인데, 점에 가깝다고 해 놓고서는 거대하다고 이야기하니 짜증나실테지만, 이는 "Micro" 와 대비되는 상대적인 개념입니다. 우리가 점처럼 보고 있는 면적보다도 더 눈에 잘 안 보이는 매우 작은 면적을 가진 표면을 미세 표면( micro surface 혹은 micro facet ) 이라 부르고 있기 때문에, 상대적으로 큰 것을 거대 표면이라 부른 것 뿐입니다.

PBR 이 도입되기 이전에는 거대 표면을 대상으로 라이팅을 계산했죠. 이에 대해서는 나중에 다루도록 하겠습니다. 어쨌든 앞으로 "점에 가까운 표면"을 그냥 "표면" 이라고 부르고 더 작은 표면을 "미세 표면" 이라고 부르도록 하겠습니다.

거대표면과 미세표면. 우리 눈에는 거대 표면마저 점처럼 보입니다. 출처 : [2].

그런 관점에서 보면 특정 표면에 들어 빛들의 각도차이가 거의 없으니 그냥 선처럼 보이겠죠. 그러면 조도와 휘도의 관계는 그림3 처럼 표현될 수 있습니다.

그림3. 점에 가까운 면적을 가진 표면에 대한 조도와 휘도.

여기서 말하고자 하는 것은 무엇일까요? 휘도가 관심을 가지는 것은 오직 눈에 들어 온 빛의 양이라는 점입니다. 그림2 의 관점에서 바라보든 그림3 의 관점에서 바라보든 차이는 없습니다. 그림2 같은 경우에는 그림3 보다 더 많은 점들을 대상으로 하고 있는 것일 뿐이죠. 사실 그래픽스 영역에서는 그림3 의 관점을 가지고 조도와 휘도 관계를 계산하는 것이 좀 더 편하긴 합니다.

하지만 아직도 복잡합니다. 눈이 면적을 가지고 있다고 가정하고 있기 때문에 들어 오는 빛을 계산하기도 힘들죠. 그래서 대부분의 실시간 그래픽스에서는 바늘구멍 카메라 모델을 사용합니다. 그림4 처럼 눈에 면적이 있는 것으로 보는 것이 아니라 그냥 점으로된 구멍이 있는 것처럼 생각하는 것입니다. 

그림4. 핀홀 카메라 모델. 출처 : Pinhole camera(DIY/homemade).

그러므로 모든 빛은 한점에 수렴한다고 가정하죠. 이 경우, 눈으로 들어 온 빛들의 각도 차이가 거의 없으므로 선에 가깝게 표현되겠죠. 그러면 조도와 휘도는 그림5 의 관점에서 설명됩니다.

그림5. 점에 가까운 면적을 가진 표면과 점에 가까운 면적을 가진 눈에 대한 조도와 휘도.

우리가 보통 실시간 그래픽스 이론에서 보고 있는 눈, 표면, 빛의 관계는 그림5 의 표현에 가깝습니다.

왜 조도와 휘도를 구분하나요

어떤 표면에 들어 온 빛은 사람의 눈에 모두 들어 오지 않습니다. 왜냐 모든 표면은 빛을 흡수( Absorbtion ), 반사( Reflection ), 투과( Transmission )시키기 때문이죠. 게다가 이 반사 성분은 확산반사( diffuse reflection ) 성분과 정반사( specular reflection, 거울면 반사 ) 성분으로 나뉩니다.

출처 : Waves, KaiserScience.

이 중에서 사람의 눈에 들어 올 수 있는 성분은 반사와 투과입니다. 그리고 우리가 앞으로 다루게될 모든 렌더링 이론들은 대부분 이 두가지 성분과 관련이 있습니다. Albedo( 표면 반사율 ), BRDF( Bidriectional Reflectance Distribution Function, 양방향 반사도 분산 함수 ), BTDF( Bidirectional Transmittance Distribution Function, 양방향 투과도 분산 함수 ) 등은 그림6 처럼 표면에 들어 온 빛이 얼마나 반사되고 투과되는지를 함수로서 표현한 것들입니다.

그림6. BRDF 와 BTDF. 출처 : [3].

그리에서 여기에서 산란( Scattering )이라는 용어가 나오는 것이 약간 불편한 분들이 있을 것 같아서 부가설명을 하자면, 산란이라는 것은 쉽게 이야기하면 전자기파가 모종의 이유로 자신의 방향으로 가지 못하고 다른 방향으로 가는 현상을 의미합니다. 반사와 투과 등의 모든 개념을 파동 관점에서 설명한 것이죠. 자세한 것은 시리즈의 뒷 부분에서 자세히 설명하도록 하겠습니다.

정리

표면에 들어 온 빛( 조도 )은 전부 눈( 휘도 )으로 들어 오지 않습니다. 그 이유는 표면이 빛을 흡수, 반사, 투과시키기 때문입니다. 사람이 눈으로 볼 수 있는 빛은 반사되거나 투과된 빛들입니다. 얼마만큼의 빛이 반사되고 투과되었는지를 결정하는 함수들이 Albedo, BRDF, BTDF 입니다. 대부분의 렌더링 이론들은 이와 관련이 있습니다.

지금까지는 빛과 그것의 양을 계산하는 것과 관련한 기본 개념 및 용어들을 설명하느라 시간을 보냈습니다. 마치 활용할 데가 없는 수학 공식을 배우듯이 지루하셨을거라 생각을 하지만, 이러한 용어들은 앞으로 자주 반복해서 나올 것이기 때문에 글을 읽느라 보낸 시간이 아깝지는 않을 것이라 기대해 봅니다. 

다음 시간부터는 본격적으로 PBR 에 대한 이야기를 풀어 나가보려고 합니다. 가급적이면 실제 예제들을 중심으로 렌더링 결과를 비교한다든가 하는 식으로 진행하려고 생각하고 있습니다.

참고 자료

[1] Luminance, Wikipedia.

[2] Microfacet Modles for Refraction through Roufh Surfaces, Bruce Walter, Stephen R. Marschner, Hongsong Li, Kenneth E. Terroance.

[3] Bidirectional scattering distribution function, Wikipedia.

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


[ PBR 이란 무엇인가 ] 5. 조도( illuminance ) 측정

조도

오늘은 조도에 대해 이야기하고자 합니다. 여러분은 [ 2. 조도와 휘도 ] 에서 조도와 휘도의 개념에 대해 공부했습니다. 여기에서는 조도와 휘도를 어떻게 측정하는지에 대해서 다루도록 하겠습니다. 하지만 수학적인 계산보다는 단위와 개념을 이해할 수 있도록 하는데 치중했기 때문에, 이를 구현하고자 하는 사람에게는 좀 부족한 글이 될 수 있습니다.

조도( illuminance )라는 것은 조명이 비추는( illuminate ) 빛이 얼마나 표면( surface )에 도달하느냐를 측정한 값입니다.

이를 광속 개념을 통해 정의하면 다음과 같습니다.

조도는 단위 면적( 제곱미터, m2 )당 광속( luminous flux )입니다. 이는 입사광이 표면을 얼마만큼 비추고 있는지를 측정하는 것입니다[1].

( [ 4. 광원의 밝기( 광속 ) ] 에서 광속( luminous flux, luminous power )은 광원이 방출하는 전체 빛의 양이라고 했습니다. )

말이 좀 어렵나요? 이럴땐 그림을 봅시다.

어떤 표면이 있고 거기에 빛을 비춘다고 해 봅시다. 그러면, 그림1 에서 보이듯이, 그 표면의 1m2 내에 들어 온 빛의 양이 조도이며, 그것을 1 럭스( lux, lx )라고 합니다.

림1. 조도. 단위 면적당 광도.

광속의 단위는 루멘( lumen, lm )이고 단위 면적은 제곱미터( square meter, m2 )이므로 럭스는 다음과 같이 계산됩니다.

식1. 럭스 계산.

조도와 거리( 감쇠 )

그런데 눈치가 빠르신 분들은 뭔가 문제가 있다는 것을 깨달으셨을 겁니다. 실세계에서는 빛과 광원간의 관계가 위의 그림에 있는 것처럼 편리하게 빛과 1 미터 떨어져서 존재하는 것이 아닙니다.

우리는 [ 3. 빛의 감쇠 ] 에서 빛의 양은 거리가 멀어질 수록 감쇠하게 되어 있으며, 1 / distance2 의 비율로 감쇠한다고 배웠습니다. 

그렇기 때문에 조도를 계산할 때는 거리를 고려해야 합니다.

구의 중심이 광원이라고 가정해 봅시다. 반지름이 r 일 때 r2 만큼의 겉면적을 커버하는 원뿔형의 입체각을 1 스테라디안이라 한다고 했습니다. 여기에서 r 을 오브젝트와의 거리라고 가정해 봅시다. 그리고 오브젝트의 면의 방향( 노멀, normal )은 구의 중심을 바라보고 있다고 가정해 봅시다.

만약 r 이 1 m이라면 면적은 1 m2 이고 식1 에서 나온 것과 같은 결과를 냅니다. 하지만 r 의 값이 1 보다 커질수록 r2  만큼 넓은 면적을 커버해야 하며 1 / r2 만큼 받는 빛이 약해지겠죠. 반대로 r 이 1보다 작아지면 더 좁은 면적을 커버해야 하며 빛이 강해지겠죠. 이를 좀 더 쉽게 이해할 수 있도록 그림2 에 평면도로 표현해 보았습니다.

그림2. 거리에 따른 조도의 변화. 중간의 면( 붉은색 )은 빛과 1 스테라디안의 입체각을 이룸.

그림2 를 보면서 좀 헷갈리는 사람이 있을 수 있습니다. 가장 좌측의 입체각이 크고, 가장 우측의 입체각이 작습니다. 1스테라디안당 N 개의 광자를 쏘았다고 하면, 가장 좌측은 N 개보다 많은 광자를 받았고, 가장 우측은 N 개보다 적은 광자를 받은 것입니다.

조도와 기울기

눈치가 빠른 분들은 다른 문제도 있다는 것을 깨달았을 것입니다. 실세계에서는 면이 광원을 마주보고 있는 경우가 거의 없습니다.

조도를 계산할 때 고려해야 할 다른 부분은 면의 기울기입니다. 면적이 같은 두 개의 평면이 존재할 때, 하나는 면의 노멀이 빛 방향과 수직이고, 다른 하나는 수직이 아니라고 합시다. 이 경우 수직이 아닌 면은 그림3 처럼 빛을 덜 받게 됩니다.

그림3. 기울기에 따른 조도의 변화.

이러한 기울기에 따른 차이를 고려해서 조도의 양을 계산하는 것이 램버트 코사인 법칙( Lambert's Cosine Law )입니다.

그림4. 램버트 코사인 법칙. 출처 : Lambert's Cosine Law, Ocean Optics.

사실은 이 코사인 법칙을 증명( ? )하려 했는데, 너무 어려운 글이 될 것 같아서 생략했습니다. 가능한한 쉽게 쓰려고 하는데 자꾸 프로그래머 본능이 꿈틀대네요.

정리

조도라는 것은 표면에 들어 온 빛의 양을 의미합니다. 이것을 시리즈의 앞 부분에서 언급한 광도측정 단위들을 사용하면, "단위 면적당 광속"이라고 표현할 수 있습니다. 이 조도의 단위는 럭스( lux )이며 lux 혹은 lx 로 표기합니다.

조도는 면과 광원의 거리 및 기울기에 영향을 받습니다. 거리에 의한 영향은 빛의 감쇠 공식에 의해 계산되며, 기울기에 의한 영향은 램버트 코사인 법칙에 의해 계산됩니다.

참고로 태양빛의 경우에는 거리에 의해 감쇠되지 않는다고 가정합니다. 모든 빛이 평행하게 들어오고 있다고 가정하기 때문이죠. 다시 말씀드리지만 거리가 멀어졌을 때 감쇠되는 것은 커버해야 할 면적이 늘어나기 때문이지 단순히 거리가 늘어났기 때문이 아닙니다. 태양빛은 평행하다는 가정을 깔아 버리면, 거리가 멀어지더라도 커버해야 할 면적에 변화가 없기 때문에 감쇠가 되지 않는 것입니다.

참고자료

[1] Illuminance, Wikipedia.

 

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


[ PBR 이란 무엇인가 ] 4. 광원의 밝기( 광속 )

개요

3. 빛의 감쇠 ] 에서는 좀 지루한 수학과 기하에 대해서 이야기했습니다. 거리, 호도( radian, 단위 : rad ), 입체각( solid angle, steradian, 단위 : sr ) 등에 대해서 이야기한 것은 바로 오늘의 주제인 빛의 측정단위에 대해서 이야기하기 위함이었습니다.

노파심에 다시 이야기하지만 우리는 방사측정( radiometry )가 아니라 광도측정( photometry )의 관점에서 빛을 측정하는 단위에 대해서 이야기할 것입니다. 광도측정은 빛을 구성하는 다양한 파장 중에서 가시 파장에 대해서 다루는 측정방법입니다.

지금부터는 약간의 수식이 나올 수 있습니다. 하지만 원리를 이해한다면 크게 어렵지 않을 것이라 생각합니다. 이해가 안 가면 질문을 주세요. 모르는 것이 부끄러운게 아닙니다. 알려고 하지 않는 것이 부끄러운거죠. 지금은 어렵게 느껴지지만 가면 갈수록 익숙해질 것이고, 지금 고민하고 공부한 것들이 나중에는 큰 차이를 만들어낼 것입니다.

광선속( 광속, Luminous Flux, Luminous Power )

광선속( 光線束 ) 혹은 광속( 光束 )이라는 것은 속도를 의미하는 것이 아닙니다. 빛 광( 光 )과 묶을 속( 束 )입니다. 말 그대로 빛다발이죠. 빛의 속도를 의미하는 광속( 光速 )이 아닙니다. 요새는 사람들이 한자를 잘 모르니 영어로 표현하면, luminous 는 "빛나는" 이란 뜻이고 flux 는 "흐름" 이라는 뜻입니다.

일단 단어의 구성은 그렇구요, 광속이란 단위시간에 광원에서 나오는 빛( 가시광선 )의 총량입니다. Radiometry 관점에서 보면 Radiant Flux( 방사속 ) 라는 용어가 이에 대응됩니다.

그림1. 광속은 광원에서 나오는 빛의 총량입니다. 출처 : Watts, Lumens, Candles and Lux.

여러분이 어떤 조명을 보고 "저 조명의 밝기는 XX 입니다" 라고 말할 때 사용하는 것이 바로 광속입니다. 단위는 루멘( lumen )이죠. 루멘은 라틴어로 빛이라는 뜻이며, 기호로는 lm 을 사용합니다.

요즘 조명기구 회사에서는 밝기를 표기할 때 루멘( lm )으로 표기합니다. 기존에는 와트( 일률, W ) 단위를 많이 표기했는데, 와트는 에너지를 얼마나 소비하느냐를 의미하는 것이지 얼마나 밝냐를 의미하는 것이 아니기 때문입니다.

조명 회사의 LED 제품 소개의 예. 출처 : CREE.

그렇다면 루멘은 어떻게 결정되는 것일까요? ( 이것과 관련한 글이 없어서 저의 추측으로 소설을 써 봤습니다. )

우리는 아주 옛날부터 초( candle )라는 것을 사용해서 빛을 비춰왔습니다. 그래서 과학자들은 촛불을 기준으로 해서 빛의 밝기를 측정하려고 한 것 같습니다. 이러한 관점에서 보면 촛불이 방출하는 전체 빛의 양을 1 루멘이라고 하는 것이 편리하겠죠.

하지만 LED 처럼 빛이 방출되지 않는 영역이 존재하는 경우도 있습니다. 게다가 일반적으로 측정기를 광원을 완벽하게 둘러싼 형태로 만들어내기가 어렵습니다. 보통 한 방향에서 일정 영역만큼을 측정한 값을 가지고 전체 결과를 추정하는 경우가 많죠. 그렇기 때문에 촛불의 빛의 양을 1 루멘이라고 하면, 다른 광원들의 빛의 양을 측정하기가 조금 곤란할 것입니다.

뭔가 기준이 되는 단위가 필요했을 것이고, 1 sr( 스테라디안을 ) 기준으로 삼았을 것입니다.

만약 1 sr 만큼의 입체각으로 방출되는 빛의 양을 알게 된다면, 빛을 비추지 않는 영역은 배제하고 빛을 비추는 영역만을 모아서 빛의 양을 수집하는 것이 쉽겠죠.

[2]에서는 광도( luminous intensity )를 다음과 같이 정의하고 있죠.

광도는 점 광원에서 특정 방향으로 단위 입체각( solid angle, 1 스테라디안 )당 방출되는 광속( luminous power )입니다. 광도의 단위는 칸델라( candela, cd )입니다.

그림3. 광도( luminous 칸델라. 출처 : Watts, Lumens, Candles and Lux.

이를 수식으로 표현하면 식1 과 같습니다. 여기에서 광도를 X, 광속을 Y, 빛을 방출하는 전체 입체각을 Z 라 하겠습니다 :

식1. 칸델라의 정의. 1 스테라디안당 광속.

점광원 같은 경우에는 전방향으로 빛을 방출하기 때문에 단위 입체각은 1/4pi 가 됩니다( 단위구의 최대입체각이 4pi 입니다 ). 그러므로 점광원에서 광도( 칸델라 )를 구하는 식은 식2 와 같습니다.

식2. 점광원에서의 광도 계산.

광속( 루멘 )은 다음과 같이 정의됩니다.

식3. 루멘의 정의. 광도 X 입체각.

만약 점광원이라면 그것의 광속은 다음과 같이 계산되는 거죠.

식4. 점광원에서의 광속 계산.

뭔가 순환논리같다는 생각이 듭니다. 여기에서 엄청나게 헷갈리는거죠. 광속은 칸델라를 사용해서 정의되는데, 칸델라는 광속을 사용해 정의됩니다.

이에 대한 자료는 지금까지 찾지 못했는데요... 그래서 그냥 소설을 써 보겠습니다( 제 친구 햄짱과 같이 쓴 소설입니다 ).

과학자들은 처음에 촛불을 1 루멘이라고 불렀다가, 위에서 언급했던 것처럼 전방향으로 빛을 방출하지 않는 광원이 나오면서, 전방향에서 방출한 빛의 양을 기준으로 삼는 것은 어렵다고 생각했을 것입니다. 그래서 특정방향으로 일정 범위( 단위 입체각 ) 안에서 방출되는 빛의 양을 기준으로 삼아야겠다는 생각을 했을 것입니다. 그리고 그것을 빛의 양을 측정하는 기본 단위인 광도( luminous intensity )라 정의했을 것입니다.

그런데 촛불의 광속이 1 루멘이라고 하면, 그것의 광도는 1 보다는 작은 소수점 아래 단위의 값이 될 것입니다( 구의 전방향을 커버하는 면적보다는 1 스테라디안이 커버하는 면적이 훨씬 적겠죠 ) . 이를 피하기 위해서 최소단위인 광도를 1 칸델라로 맞췄을 것이라고 생각합니다. 

머리가 아파 보이지만 1 칸델라는 국제 표준으로 정의된 빛의 양입니다. 1 리터( l )라든가 1 그램( g )이라든가 하는 정해져 있는 양입니다. 이제 칸텔라가 뭔지 알게 되었으니 초의 광속( 루멘 )을 계산해 보죠. 초가 전방향으로 빛을 방출한다고 하면, 그것의 입체각은 4π 스테라디안입니다( 반지름이 r 인 구의 겉면적이 4πr2 이고 1 스테라디안은 r2 만큼의 겉면적을 커버하는 입체각이므로 구의 전체 입체각이 4π 스테라디안이 되는 것입니다 ).

그러면 이제 촛불의 밝기( 광속 )를 계산해 볼까요?

식3. 촛불의 광속 계산.

실제로 촛불 하나의 광속은 12.57 루멘입니다.

정리

특정 방향으로 1 스테라디안 영역에서 방출된 촛불의 빛의 양을 광도( luminous intensity )라 하면 단위는 칸델라( candela, cd )입니다.  1 스테라디안( steradian, sr ) 범위에서 방출된 칸델라를 루멘( lumen, lm )이라 합니다. 루멘을 사용해 광원의 전체 빛의 양을 측정한 것을 광속( luminous flux, luminous power )이라 합니다.

참고자료

[1] Lumious flux, Wikipedia.

[2] Candela, Wikipedia.

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


[ PBR 이란 무엇인가 ] 3. 빛의 감쇠

개요

시리즈의 3 번째입니다. 지난번에 [ 2. 조도와 휘도 ] 에서 방사측정( Radiometry )과 광도측정( Photometry )의 차이에 대해 언급했습니다. 기억이 잘 안 나시는 분들을 위해 복습하자면 방사측정은 빛의 모든 파장 영역의 에너지( power )를 측정하는 것이고, 광도측정은 빛의 가시 파장 영역이 밝기( brightness )를 측정하는 것입니다.

그런데 모든 물리단위들은 기준이라는 것이 있어야 합니다. 우리가 물리기반( Physically-based )를 이야기하고 있으니, 당연히 물리적으로 납득할 수 있는 기준과 단위들이 필요하겠죠.

UE4 엔진의 경우에는 포인트/스포트 라이트의 세기를 설정하는 기준 단위가 루멘( lumen )이라고 이야기하고 있습니다[1]. 세기( intensity )를 밝기( brightness )라고 표현하기도 합니다.

그림1. UE4 에서의 빛의 세기를 설정하는 속성. 루멘단위.

요새 다른 PBR 엔진들도 루멘을 사용하는 추세이죠. 이렇게 된 이유는 광원의 세기를 아티스트의 기분에 따라 설정하지 말고 어떤 기준을 잡고 설정하라는 의미일 것입니다. 그래서 아티스트들이 루멘이 무엇인지 이해할 필요가 있습니다. 루멘이 무엇인지 이해하는 것은 빛에 대해서 더 깊게 이해할 수 있는 기반을 제공할 것입니다.

이 문서에서는 어떤 조명( 광원 )의 광속( luminous flux ), 광도( luminance intensity ), 조도( illuminance ), 휘도( luminance )가 어떤 의미인지 이해하기 위한 선행학습 과정에 대해 다룰 것입니다. 여기에서부터는 약간의 기하와 산수가 나오기 때문에 조금 짜증나실 수는 있습니다. 하지만 여러분은 이 개념들에 대해서 무작정 외우려고 하실 필요는 없습니다, 어떤 원리인지를 이해하고 단위만 잘 기억하시면 됩니다.

그렇게 어려운 내용은 아니니 차분히 읽어 보시면 이해하실 수 있을 거라 생각합니다.

빛은 왜 감쇠할까요?

일반적으로 우리는 빛이 감쇠( attenuation )한다고 알고 있습니다. 강한 빛도 멀리 갈 수록 약해집니다. 여기에서 주의하실 점은 감쇄( reduction )가 아니라 감쇠( attenuation )라는 것입니다. 감쇠는 힘이나 세력이 약해짐을 의미하고, 감쇄는 힘이나 세력이 약해져서 최종적으로는 없어짐을 의미합니다. 지금은 왜 뜬금없이 차이를 이야기할까 싶으시겠지만 뒤에 좀더 설명을 할 것입니다.

빛이라는 것은 전자기파들의 조합이기는 하지만( 파동성 ), 어떤 경우에는 입자들처럼 보일 때가 있습니다( 입자성 ). 입자처럼 보일 때의 빛을 광자( photon, 포톤 )라고 합니다. 그냥 그러려니 하시면 됩니다. 여기에서 더 들어 가면 저도 잘 모르는 양자론에 대해 주절거려야 하기 때문에 더 나아가지는 않겠습니다. 대신에 빛이라는 것을 대충 이런 식으로 이해하시면 됩니다( 정확하지는 않지만 그러려니 하십시오 ).

그림2. 광자의 느낌? 여러개의 전자기파의 묶음을 입자의 관점에서 볼 때 광자라 부릅니다.

어쨌든 광자의 개념에서 빛이 발산하는 모습을 생각해 봅시다.

진공상태라고 가정하면 다른 입자( 먼지, 대기중의 분자들 )들에 의한 산란( 투과, 흡수, 반사 )이 발생하지 않습니다. 그러므로 눈에 들어 오기 전까지는 영원히 직진하겠죠.

그림3. 진공상태에서의 광자의 이동.

눈치가 빠르신 분들은 이 시점에서 한 가지 의문을 가지게 될 겁니다; "그럼 왜 거리가 멀어지면 빛이 감쇠하나요?".

우리는 태양광에 대해서는 평행하게 빛이 도달한다고 생각합니다.

그림4. 태양에서 오는 빛은 평행하다고 가정합니다.

이것이 사실일까요? 그렇지 않습니다. 태양과의 거리는 엄청나게 멀기 때문에 지구에서 사는 사람의 입장에서는 상대적으로 빛이 평행하게 오는 것처럼 느껴질 뿐입니다( 그림5 참조 ). 단지 그래픽스 영역에서는 그 차이가 무시할 수 있을 정도로 의미가 없을 뿐이죠.

그림5. 같은 크기를 가진 물체가 광원에서 멀어졌을 때의 들어 오는 빛들의 각도차이의 변화.

자, 그럼 여러분은 이제 포인트/스포트 라이트와 태양광과의 차이를 아실 수 있습니다. 그냥 거리의 차이입니다. 빛을 받는 오브젝트들의 거리가 너무 가깝기 때문에 각도의 차이가 커집니다. 태양광의 경우에는 엄청나게 많은 광자를 내 뿜고 있으며 태양과 지구와의 거리가 너무 멀기 때문에 거의 평행하게 광자들이 도달합니다. 그래서 우리는 태양광에 대해서 따로 감쇠 거리를 설정하지 않는 것입니다. 하지만 포인트/스포트 라이트들은 내뿜는 광자의 개수가 상대적으로 적으며 가까운 오브젝트가 가까운 거리에서 빛을 받기 때문에 감쇠 거리라는 것이 중요해진 것입니다.

그런데 이 각도가 감쇠라는 것과는 무슨 상관일까요?

세상이 2차원의 평면이고 광원이 점광원이라고 가정합시다. 이 광원은 16 개의 광자를 내뿜을 수 있는 능력을 가졌는데, 광자의 속도가 초당 1 m 라고 가정합시다( 그냥 가정입니다. 광자의 속도는 빛의 속도입니다 ). 1 초 단위로 깜박인다고 가정해 봅시다. 그러면 모든 방향으로 빛을 내 뿜겠죠? 2초 뒤에 광자의 위치는 어떻게 될까요? 시간의 추이에 따라 어떻게 변하는지 그림6 에서 설명하고 있습니다.

그림6. 시간의 변화에 따른 광자의 위치. 2초후에 보면 처음 발사한 광자들은 2 m 를 이동했고, 두번째로 발사한 광자들은 1m 를 이동했습니다.

여러분은 그림6 을 보고 어떤 생각을 하셨나요?

광원으로부터의 거리가 멀어질 수록, 광자끼리의 거리도 멀어집니다. 결국 같은 각도 내에 존재하는 광자의 개수는 동일하지만, 그것이 커버해야 하는 면적이 넓어졌습니다. 이것을 3D 관점에서 보면 그림7 과 같은 느낌이 됩니다.

그림7. 3D 에서 빛의 감쇠. 출처 : [2].

r 의 거리에서 한칸에 해당하는 면적에 광자가 3( = 3/1 ) 개가 있었다면, 2r 의 거리에서는 에서는 한칸에 평균적으로 3/4 의 광자가 존재합니다. 물론 광자의 위치에 따라 3 개나 2 개가 있을 수 있겠죠. 그림7 에서는 2 개가 있는 것으로 나왔군요.  3r 의 거리에서는 한칸에 평균적으로 1/3( = 3/9 )의 광자가 존합니다. 그림7 에서는 1 개로 나왔구요. 그물을 잡아 당겨서 늘리면 구멍이 커지는 느낌으로 생각하시면 됩니다.

이렇게 면적당 광자의 개수관계를 생각하면, 거리에 따라 빛의 밝기가 감쇠하는 것과 관련한 공식을 다음과 같이 쓸 수 있습니다. 일단 여기에서는 광자 1 개당 세기를 1 이라고 가정했습니다.

식1. 거리에 따른 빛의 세기의 감쇠. 거리의 제곱에 반비례합니다.

수학을 좀( ? ) 배우신 아티스트들은 이 공식을 보면서 의문을 가지게 될 것입니다; "언제 빛의 세기가 0 이 되나요?".

Distance 가 무한대가 되더라도 0 에 수렴할 뿐 0 이 되지는 않습니다. 즉 거리가 멀어질 수록 세기가 기하급수적으로 낮아지기는 하지만 아예 영향력이 없는 것은 아니라는 것이죠. 그러므로 대부분의 엔진들의 광원에는 UE4 에서와 같은 "Attenuation Radius( 감쇄 반경 )" 라는 속성이 있습니다[1]. 광원의 영향력을 모든 오브젝트에 반영한다는 것은 현실적으로 힘들기 때문입니다( 라이팅 계산이 무겁기 때문에 현재 하드웨어에서는 힘듭니다 ). 게다가 인간의 눈은 성능이 별로( ? ) 안 좋기 때문에 0 에 가까운 밝기를 인지하지 못합니다. 계산해 봐야 크게 의미가 없는 것입니다. 그래서 아티스트가 보기에 이 정도까지만 라이팅을 처리해도 괜찮겠다 싶은 거리를 지정하게 하는 것입니다. 쓸데없이 거리를 길게 잡게 된다면, 눈에는 거의 안 보이는데 처리를 해야 해서 계산비용이 늘어나겠죠.

예전에는 감쇠를 위해서 "Light Radius" 혹은 "Source Radius" 라는 용어를 사용했지만, area light 라는 개념이 나오면서 "source radius" 는 광원 자체의 반지름을 의미하는 것으로 바뀌었습니다. 그러므로 "Attenuation Radius" 와 "Source Radius" 를 혼동하지 마시기 바랍니다.

감쇠는 어떻게 계산된 것인가요?

앞에서 감쇠된 빛의 세기는 거리의 제곱에 반비례한다는 결론을 냈습니다. 물론 그림7 에서 볼 수 있듯이 거리가 1r 일 때 1칸, 2r 일때 4 칸, 3r 일때 9 칸, 이런 식으로 하면 거리의 제곱에 반비례한다는 사실이 유추되기는 합니다. 하지만 그림7 은 단순화해서 표현한 것일 뿐입니다.

이것이 어떻게 유도되었는지를 명확하게 이해해야 앞으로 나올 개념들을 이해하는데 도움이 됩니다. 이를 위해서는 입체각( solid angle )이라는 개념에 대해 먼저 알아야 합니다.

호도법

입체각에 대해 알려면 호도법에 대해 먼저 이해하는 것이 편합니다. 입체각이라는 것은 3차원에서의 각을 표현하는 것이기 때문에 2차원에서의 각인 호도법을 살펴 봄으로써 좀 더 쉽게 이해할 수 있기 때문입니다.

사람들은 평면에서 각을 표현할 때 도( degree )라는 단위를 선호합니다. 그래서 아티스트가 접하게 되는 대부분의 단위는 도 단위로 표현이 됩니다. 하지만 수학적인 영역으로 들어 가면 도 단위보다는 호도( radian ) 단위를 선호합니다. 매우 직관적이고 편하기 때문입니다.

여러분은 학교에서 반지름( radius )이 1 인 원의 둘레는 2π( pi, 파이 ) 라고 배우셨을 겁니다. π 는 순환하지 않는 무한소수로 원주율이라고 불립니다. 그 값인 3.1415926... 을 열심히 외우셨을테구요. 이때 질문이 들어갑니다. 각도가 a° 일 때 만들어지는 부채꼴의 호의 길이( arc length )는 얼마일까요?

그림8. 반지름 1인 원에서 각도가 a 도일 때의 부채꼴의 호의 길이는?

360 도일 때 원의 둘레가 2π 였기 때문에 비례식을 쓰면 a 도일때의 부채꼴의 호의 길이를 알 수 있죠.

식2. 반지름 1인 원에서 a 도인 부채꼴의 호의 길이 x 구하기.

좀 더 응용해 봅시다. 반지름이 r 일 때는 원의 둘레가 2πr 입니다. a 도일때의 부채꼴의 호의 길이는 무엇일까요? 식2 에서처럼 비례식을 사용해 값을 구할 수 있습니다.

식3. 반지름이 r 인 원에서 a 도인 부채꼴의 호의 길이 x 구하기. 

반대로 해 봅시다. 반지름 r 인 원에서 부채꼴의 호의 길이가 x 일 때 부채꼴의 각은 몇 도인가요? 식2, 3 에서 처럼 비례식을 써서 구할 수 있습니다.

식4. 반지름이 r 인 원에서 호의 길이가 x 인 부채꼴의 각 a 구하기. 

뭔가 숫자를 외워야 하고 나눗셈이 들어갑니다. 이건 매우 귀찮은 작업이며 실수의 여지가 있습니다. 그래서 수학자들은 이를 쉽게 할 수 있는 방법을 찾게 되었고, 호도법이라는 것이 나오게 되었습니다( 1714 년에 Roger Cotes 라는 사람이 호의 길이를 각처럼 사용하자는 제안했다고 합니다[3] ). 호도법이라는 것은 반지름이 r 일 때 부채꼴의 호의 길이가 r 인 경우를 1 라디안( radian )이라고 표현하는 것입니다. 즉 각의 크기를 도에서 길이의 단위로 바꿔버리는 겁니다( 그림9 참조 ).

그림9. 반지름이 r 인 부채꼴의 호의 길이가 r 일 경우 각도를 1 라디안이라 합니다.

여기가 바로 질문이 나올 타이밍입니다; "대체 뭐가 편한데요?".

우리는 반지름이 r 인 원의 둘레가 2πr 이라는 것을 알고 있습니다. 반지름이 r 인 부채꼴( 반원 )의 호의 길이는 πr 이죠. 이 때의 각은 π 입니다( 그림10 참조 ).

그림10. 반지름 r 인 ( 그리고 각이 π 인 ) 반원의 호의 길이는 πr 입니다.

관계가 명확하게 보이시나요? 만약 반지름이 r 인 부채꼴의 각도가 a 라디안이라고 하면, 그것의 호의 길이는 ar 인 것입니다( 그림11 참조 ).

식5. 반지름이 r 이고 각이 a 인 부채꼴의 호의 길이 x 구하기.

그림11. 반지름이 r 이고 부채꼴의 각이 a 라디안이면, 호의 길이는 ar 입니다.

뭔가 복잡하게 변환할 필요가 없습니다. 각과 반지름 알면 호의 길이를 알 수 있고, 각과 호의 길이를 알면 반지름을 구할 수 있고, 반지름과 호의 길이를 알면 각을 구할 수 있습니다. 매우 직관적이고 계산이 단순해집니다.

3차원에서의 각

평면에서의 각은 라디안이라는 단위를 사용해서 쉽게 표현할 수 있다는 것을 알게 되었습니다. 하지만 3 차원에서의 각도는 어떻게 표현해야 할까요?

애초에 우리는 각이라는 것을 평면에서만 사용해 왔습니다. 삼차원에서 10 도라는 개념이 상상이 되시나요?

그림12. 3차원에서 10도를 상상해 봅시다.

3D 에 익숙하신 분들이라면 이를 경도와 위도처럼 나누어서 생각하실 수 있을겁니다. 그림13 처럼 중심이 되는 방향( 벡터 )를 기준으로 수평으로 몇도 수직으로 몇도 이렇게 나눌 수 있겠죠( 물론 다른 표현들도 존재합니다만, 머리만 복잡해지므로 여기에서 굳이 다루지는 않겠습니다 ).

그림13. 3차원에서의 각을 수직각과 수평각으로 나누어서 생각할 수 있습니다.

하지만 수학자들은 호도법을 발명했듯이 입체각( solid angle )이라는 것을 발명하게 되었습니다[4]. 반지름이 r 인 구에서, 면적이 r2 인 면적을 포함하는 원뿔형의 각을 1 스테라디안( steradian, sr )이라고 부르고, 이러한 각도를 입체각이라 정의한 것입니다( 그림14 참조 ).

그림14. 반지름이 r 인 구에서 r2 인 면적을 포함하는 원뿔형의 입체각을 1 스테라디안이라 부릅니다. 출처 : [4]. 

그럼 여기에서 또 뭔가 룰이 보이시나요? 호도법이 호의 길이를 각으로 치환했듯이, 입체각은 겉넓이( 면적 )를 각으로 치환한 것입니다. 호도법에서는 1차원적인 곱인 "호의길이 X 반지름" 으로 표현되듯이, 입체각은 2차원적인 곱인 "겉면적 X 반지름의 제곱" 으로 표현되고 있습니다.

우리는 구의 겉면적을 4πr2 로 알고 있습니다. r2 이 1 스테라디안을 의미하므로 구 전체의 입체각은 4π 인 것입니다.

만약 반지름이 r 인 구에서 입체각이 a sr( 스테라디안 )인 원뿔형의 겉면적을 구해야 한다면 다음과 같은 관계로 표현될 수 있습니다.

식6. 반지름이 r 인 구에서 입체각이 a sr 일 때 원뿔형의 겉면적 x 구하기.

호도법처럼 매우 단순하게 표현되는 것을 알 수 있습니다.

빛이 거리의 제곱에 반비례해 감쇠하는 이유

반지름이 r 인 구에서 입체각 a 인 원뿔형의 겉면적이 ar2 이라는 사실을 알게 되었습니다. 만약 r 값이 늘어난다고 해도 a 는 각이기 때문에 변하지 않습니다. 어떤 광원이  a 라는 입체각 내에 광자를 1 개를 내 보낸다고 가정해 봅시다. 그리고 그것의 밝기를 1 이라고 하죠. 1초가 지났을 때의 반지름은 1 이고 r 초가 지났을 때 반지름은 r 이겠죠. 그렇다면 각각의 겉면적은 a ( = a X 12 ) 과 ar2 입니다. 겉면적의 비율은 1 : r이 됩니다.

면적이 r2 만큼 늘었으니, 광자가 커버해야 하는 영역은 더 넓어지며, 밝기는 반대로 1 / r2 에 비례하는 것입니다.

정리

빛이 감쇠하는 것은 엄밀히 말해 거리가 멀기 때문이 아닙니다. 거리가 멀어질 수록 면적에 비해 광자의 개수가 줄어들기 때문이죠. 거리에 따라 면적이 얼마나 커지느냐는 입체각의 정의를 통해 알 수 있습니다.

참고자료

[1] 라이팅 기초, Unreal Engine Documents.

[2] Light Attenuation, Technical Grahic Director.

[3] Radian, Wikipedia.

[4] Solid Angle, Wikipedia.

주의 : 정확하지 않은 내용이 포함되어 있을 수 있으므로 이상하면 참고자료를 확인하세요.


[ PBR 이란 무엇인가 ] 2. 조도와 휘도

개요

[ PBR 이란 무엇인가 ] 에 대한 두 번째 주제인 [ 조도와 휘도 ] 에 대해서 설명합니다. 그래픽스에 관심을 가지고 계신 분들은 "radiance", "irradiance", "luminance", "illuminance" 라는 용어들에 대해 한 번쯤은 들어 보셨을 겁니다.

그런데 어떤 문서에서는 비슷한 현상을 설명하면서 radiance 라 하고 어떤 문서에서는 luminance 라고 합니다. 이 두 개의 차이를 인지하지 못하는 분들은 혼란에 빠지게되죠. 사전을 찾아 보면 다음과 같이 나옵니다.

    • Radiance : 복사 휘도.
    • Irradiance : 복사 조도.
    • Luminance : 휘도
    • Illuminance : 조도.

이것만 보고 의미의 차이를 이해할 수 있는 분은 많지 않을거라 생각합니다. 이 문서에서는 이러한 용어들의 차이에 대해서 명확하게 정리해 보고자 합니다.

방사측정( Radiometry ) 과 광도측정( Photometry )

우리는  [ 인간과 빛 ] 문서에서 빛이 다양한 파장의 전자기파로 구성된다는 것을 알게되었습니다. 그런데 에너지가 입자나 파동의 형태로 전달되거나 방출되는 것을 방사선( radiation )이라고 합니다[1]. 즉 가시광선을 포함한 전체 파장 영역의 전자기파들로 구성된 빛을 방사선이라 부를 수 있는 것입니다.

넓은 의미에서( 전체영역 ) 빛을 다루기 위해서 에너지를 측정하는 것을 방사측정( radiometry )이라 합니다. 그리고 좁은 의미에서( 가시영역 ) 빛을 다루기 위해서 에너지를 측정하는 것을 광도측정( photometry )이라고 합니다. Visible-Radiometry 같은 용어가 아니라 Photometry 라는 용어를 사용한 것은 아마도 카메라를 통해 사진이 찍히는 과정이 인간의 눈을 통해 들어 온 빛을 인지하는 과정과 유사하기 때문이지 않을까 생각합니다.

어쨌든 방사측정에서는 제곱미터당 일률( watt per squre meter )의 단위로 에너지를 측정하고, 광도측정에서는 제곱미터당 루멘( lumens per squre meter, lux ) 단위로 에너지를 측정합니다. 쉽게 이해하자면 전자는 파워( power )를 의미하고 후자는 밝기( brightness )를 의미한다고 생각하시면 됩니다.

좀 지루하죠? 어쨌든 radiance, irradiance 는 방사측정에서 사용하는 용어이고, luminance, illuminance 는 광도측정에서 사용하는 용어입니다. 단지 어떤 영역의 파장들을 다루고 있느냐, 에너지를 어떤 관점에서 바라보느냐의 차이가 있을 뿐이죠. 그러므로 일반적으로 그래픽스에서는 radiance 를 luminance 로 irradiance 를 illuminance 로 대체해서 이해하셔도 무방합니다. 그래픽스에서는 빛을 photometry 의 관점에서 다루고 있기 때문에 큰 차이는 없습니다( 어차피 보이지도 않는 에너지에 대해 고민해 봐야 무슨 소용이 있겠습니까... ).

조도와 휘도

조도의 "조( 照 )"는 "비출 조" 입니다. 그리고 휘도의 "휘( 輝 )"는 "빛날 휘"입니다. 조는 능동적인 표현이고 휘는 수동적인 표현입니다. 빛을 생각할 때 광원에서 빛이 나오는 것을 "비춘다"라고 표현하고, 어딘가 부딪혀서 눈에 들어 오는 것을 "빛난다" 라고 표현하는 것입니다.

이것을 관점을 좀 다르게 해서 생각해 보면, 다음과 같이 표현할 수 있습니다.

물 표면에 들어온 빛의 양 X 를 조도( illuminance )라 하고 눈으로 들어 온 빛의 양 Y 를 휘도( luminance )라고 합니다. 여기에서 일루미넌스와 루미넌스가 헷갈릴 수 있습니다. 원래 Illuminate 라는 단어는 "비추다" 라는 뜻입니다. 그래서 어떤 면적에 빛을 비춘 양을 illuminance 라고 부르는 것입니다. 비슷한 느낌의 예를 들자면 GI( Global Illumination ) 가 있습니다. 이것을 우리말로 번역하면 "전역 조명" 정도로 정의할 수 있겠죠.

그런데 조도와 휘도를 구분하는 이유는 무엇일까요?

표면( surface )에 도달한 빛이 모두 관찰자의 눈으로 들어 오는 것은 아닙니다. 어떤 매질( 재질 )의 표면에 입사한( incident ) 빛은 흡수( absorbed )되거나 투과( tramsmitted )거나 반사( reflected )됩니다. 방출( emitted )되는 빛도 있지만 여기에서는 다루지 않겠습니다. 우리는 어떤 물체에서 반사된 빛만을 인지할 수 있는 것입니다. 그래서 조도와 휘도를 구분하는 것입니다.

출처 : Waves, KaiserScience.

참고자료

[1] Radiation, Wikipedia.

[2] Radiometry, Wikipedia.

[3] Photometry, Wikipedia.

 

 

주의 : 정확하지 않은 내용이 포함되어 있을 수 있습니다. 이상하면 참고자료를 확인해 보세요.


[ PBR 이란 무엇인가 ] 1. 인간과 빛

 

들어가며

술자리에서 직장 동료에게서 PBR 에 대해 PT 를 했으면 좋겠다는 이야기를 들었습니다. 그래서 프로그래머가 아닌 사람들이 PBR 에 대해 쉽게 이해할 수 있게 하는 방법이 무엇일까에 대한 고민을 해 보았습니다. 짧은 시간에 많은 내용을 전달하는 것은 힘들 것이라는 생각이 들었고, 너무 깊게는 아니지만 꼭 필요한 내용들에 대해서 설명하는 것으로부터 시작해야겠다는 생각이 들었습니다. 그래서 [ PBR 이란 무엇인가 ] 라는 글을 정리하기로 했습니다.

Physically Based Rendering( PBR ) 이라는 것은 "물리에 기반한 렌더링"이라는 의미입니다. 엄밀하게 이야기해서 현세대의 그래픽 하드웨어와 이론으로는 완벽하게 물리적으로 정확한 렌더링을 하는 것은 불가능합니다. 이진정보를 통해 자연현상을 완벽하게 표현한다는 것은 거의 불가능에 가깝습니다. 단지 기존보다는 "더욱" 물리적으로 정확하다는 의미라고 생각하시면 됩니다.

[1] 과 [2] 에 의하면 물리적으로 정확한 렌더링 모델이라는 것은 다음과 같은 요소들에 의해서 결정됩니다:

    • 전역 조명( Global Illumination ) : 이미지 기반 라이팅( Image Based Lighting, IBL ).
    • 에너지 보존 법칙( Energy Conservation ).
    • 반사도( Reflectivity ) : 디퓨즈 및 스펙큘러( Diffuse & Specular ).
    • 미세면( Microsurface ) : 러프니스( Roughness ).
    • 프레넬 법칙( Fresnel's Law ).
    • 금속성( Metalicity ).

아마 아티스트들은 위에서 언급한 요소들을 보는 순간 멘붕에 빠지게 될 것입니다. 사실 그래픽스에 익숙하지 않은 프로그래머들도 처음에는 이해하기 힘들겁니다. 사실 PBR 에 대해서 설명하려면 책 한권은 필요합니다. 그렇지만 이 문서의 목적은 아티스트에게 PBR 을 이해하기 위한 기본적인 정보들을 전달하는데 있기 때문에 너무 깊게 파고 들지는 않을 계획입니다. 그럼에도 불구하고 분량이 매우 많기 때문에 시리즈를 구성하기로 했습니다.

앞에서 언급한 요소들에 대해서 설명하기 이전에 알아야할 정보들에 대해서 설명한 다음에, 본격적으로 PBR 에 대해서 다룰 계획입니다.

빛이란 무엇인가

우리는 일상생활에서 빛이라는 개념을 자연스럽게 받아들이고 있습니다. 하지만 막상 "빛이란 무엇인가요?" 라는 질문을 누군가 하게 된다면 명확하게 대답할 수 있는 사람은 드물 것입니다. 하물며 아인슈타인만 해도 다음과 같이 말했다고 하죠[3].

나의 여생동안 빛이 무엇인지 되집어 보며 살 것이다 - 1917 년경
지난 50 여년 동안 곱씹어 고민해 왔음에도 빛이란 무엇인가에 대한 답에 더 가까이 다가서지 못했다. 물론 장난꾸러기같은 사람들은 답을 안다고 생각하겠지만 그들은 자신을 속이고 있는 것이다 - 1951 년.

아인슈타인이 양자론과 관련한 연구성과를 인정하고 싶지 않아서 한 말일 것이라 생각하기는 하지만, 어쨌든 빛이란 무엇인가에 대한 질문에 대답하는 것이 쉽지 않은 것만은 사실입니다. 여기에서 빛의 입자성과 파동성에 대해서 이야기하기에는 너무 주제가 무거워지기 때문에 단순하게 파동성과 관련한 부분에 대해서만 언급하도록 하겠습니다.

빛이라는 것은 기본적으로 전자기파( Electromagnetic wave )라고 할 수 있습니다. 전자기파라는 것은 전자기적 과정에 의해 발생하는 에너지입니다. 그냥 파동의 형태로 발산되는 에너지라고 이해하시면 됩니다. 빛이라는 것은, 수소원자같이 개별적으로 셈을 할 수 있는 입자가 아니라, 파장이 다른 여러 가지 전자기파로 구성되어 있습니다[4].

그림1. 전자기파의 스펙트럼[4].

좁은 의미로 봤을 때, 빛은 그림1 의 가시 영역을 의미합니다. 넓은 의미로 봤을 때, 빛은 그림1 의 전체 영역을 의미합니다. 우리가 볼 수 없는 많은 부분이 존재하고, 다들 적외선( IR, Infrared Rays )이나 자외선( UV, Ultra Violet )같은 이름에는 익숙할 것입니다. 시리즈의 다음 글에 언급하겠지만, 이러한 관점의 차이가 광도측정( Photometry )과 방사측정( Radiometry )의 차이를 만듭니다.

빛이라는 것은 위에서도 말했듯이 파장이 다른 여러 개의 전자기파로 구성되어 있기 때문에 프리즘같은 것을 써서 각각의 파장을 분리해낼 수도 있습니다. 시리즈의 뒷편에서 언급하겠지만 이는 파장에 따라서 굴절률이 달라지는 것을 이용한 것이죠. 무지개도 이런 원리로 생성됩니다.

그림2. 빛이 프리즘을 통과하면서 파장에 따라서 분리되는 현상을 보여줍니다[4].

눈치가 빠른 분들이라면 그림2 에서 각 색깔별로 파장의 길이가 다르다는 것을 눈치채실 수 있을 겁니다. 빨간색의 파장의 길이가 가장 길고, 보라색의 파장의 길이가 가장 짧습니다. 일단 여기에서는 빛이라는 것이 하나의 색상을 가지는 것이 아니라 여러 전자기파의 조합이라는 것만 이해하시면 됩니다.

그런데 인간은 어떻게 이러한 빛을 인지할 수 있는 것일까요?

우리는 빛을 어떻게 인지할까

그림3. 인간의 눈 구조. 출처: Encyclopedia Britannica, 1994.

인간의 눈에 있는 망막( Retina )을 확대해 보면 원추세포( cone cell : blue cone, red cone, green cone )와 간상세포( rod )라는 것이 있습니다. 원추세포는 색상을 감지하고 간상세포는 명암을 감지합니다. 원추세포는 그것을 구성하고 있는 감광세포의 종류에 따라 받아들일 수 있는 색상이 정해져 있죠. 그림3 을 보시면 세 종류의 원추세포가 존재하며, 그것의 색상이 RGB 라는 것을 알 수 있습니다. 이를 빛의 3원색이라 하고 실제로 텍스쳐( texture )를 만들 때도 RGB 를 사용하게 됩니다. RGB 채널을 사용하는 것은 어느날 갑자기 그렇게 하기로 결정된 것이 아닙니다. 인간의 눈과 관련이 있는 것입니다.

간상세포는 약 9000 만개이고 원추세포는 약 600 만개인데, 간상세포는 약한 빛에도 반응하고 원추세포는 일정 세기의 빛에 반응합니다[7]. 결과적으로 인간의 눈은 색상대비보다 명도대비에 더욱 민감하다고 할 수 있습니다.

대부분의 포유류들은 인간보다 적은 개수의 감광색소를 가지고 있다고 합니다. 반면에 새들의 경우에는 감광색소를 더 가지고 있어서 자외선까지 볼 수 있다고 합니다. 앞에서 언급한 가시광선이라는 것은 인간의 관점에서의 가시광선이라는 것이죠. 그러므로 새들은 인간보다 좀 더 다채로운 세상을 볼 수 있고 벌레를 쉽게 발견할 수 있다고 합니다.

그림4. 인간과 새가 인지할 수 있는 파장 비교[6].

좀 더 직관적으로 이해할 수 있도록 예를 들면 인간과 새가 보는 차이는 다음과 같습니다.

그림5. 인간과 새가 보는 색상의 차이[6].

그림5 에서 알 수 있듯이 인간의 눈에는 비슷해 보이는 색상이 새의 눈에는 확연하게 차이가 날 수 있습니다. 노란색 꽃에 노란 벌레가 앉아 있을 때. 인간은 둘을 구별하지 못하지만, 새의 입장에서는 그 차이가 명확하게 보일 수도 있는 것이죠.

인간의 눈에 있는 원추세포가 가장 민감하게 반응하는 구간이 녹색과 인접해 있어 인간은 녹색빛을 가장 멀리서도 인지한다고 합니다. 그리고 원추세포에 이상이 생기면 색맹이 된다고 합니다[7].

정리

빛은 다양한 파장을 가진 전자기파의 집합이며, 인간의 그중 가시영역의 파장을 가진 전자기파( 가시광선 )들만 인지할 수 있습니다. 그런데 인간의 눈은 RGB 의 조합으로 색상을 인지하게 되며, 이것은 우리가 빛의 3 원색을 사용하게 되는 근간이 되었습니다.

참고자료

[1] Physically based rendering, Wikipedia.

[2] Basic Theory Of Physically-Based Rendering, Marmoset.

[3] 빛이란 무엇인가? Barray R. Marsters.

[4] Light, Wikipedia.

[5] 파동의 표시, 더불어 생각하는 과학 교실.

[6] Color Vision in Birds.

[7] 원추세포, 위키백과.

원문 : https://developer.nvidia.com/content/depth-precision-visualized

주의 : 번역이 개판이므로 이상하면 원문을 참조하세요.

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.


 

Depth Precision Visualized

 

By Nathan Reed, posted Jul 15 2015 at 03:54PM

Tags : Gameworks Expoert Developer, DX12, DX11

 

 

깊이 해상도( Depth Precision, 깊이 정밀도 ) 는 모든 그래픽 프로그래머가 이미 겪거나 나중에 겪게 되는 똥꼬의 고통( 골칫거리, pain in the ass )입니다. 많은 기사들과 논문들이 이 주제에 대해 다뤘으며, 다양한 깊이 버퍼 포맷과 설정들을 여러 게임들, 엔진들, 그리고 장치들에서 찾아 볼 수 있습니다.

 

그것이 원근 투영( perspective projection )과 상호작용하는 방식 때문에 GPU 하드웨어 깊이 매핑( mapping )은 다소 많이 알려져 있지 않으며, 그 공식에 대해 공부해도 즉각적으로 명확해지지 않을 것입니다. 그것이 동작하는 방식에 대한 직관을 얻기 위해서는 약간의 그림을 그려 보는 것이 도움이 됩니다.

 

그림 1.

 

이 기사는 세 개의 주요 파트로 나뉩니다. 첫 번째 파트에서는 비선형 깊이 매핑에 대한 약간의 동기( motivation )을 제공하려고 합니다. 두 번째 파트에서는 비선형 매핑이 서로 다른 상황에서 동작하는 방식을 이해할 수 있도록 돕는 몇 개의 다이어그래을 제공할 것입니다. 세 번째 파트에서는 Paul Upchurch 와 Mathieu Desbrun [2012 ] 에 의해 제시된 Tightening the Precision of Perspective Rendering 의 주요 결과에 대한 논의 및 재현( reproduction )을 제공합니다. 앞의 방법( 링크 )은 깊이 정밀도 상에서 실수( floating point ) 반올림( roundoff ) 에러의 효과와 관련이 있습니다.

 

 

 왜 1/z 인가

 

 

GPU 하드웨어 깊이 버퍼는 일반적으로 카메라 앞의 놓인 오브젝트에 대한 거리를 선형적으로 표현하지 않습니다. 이는 사람들이 깊이 버퍼를 처음 접했을 때 기대하는 것과는 반대입니다. 그 대신, 깊이 버퍼는 월드 공간 깊이에 대한 역( reciprocal of world-space depth )에 비례하는 값을 저장합니다. 이러한 관례가 생긴 동기에 대해서 간단하게 언급하고자 합니다.

 

이 기사에서, 필자는 깊이 버퍼에 ( [0, 1] 로 저장된 ) 값을 표현하기 위해 d 를 사용할 것입니다. 그리고 뷰 축을 따라 측정된 거리인 월드 공간 깊이를 표현하기 위해서 z 를 사용할 것입니다. 월드 공간의 단위( unit )는 미터같은 단위입니다. 일반적으로 그것들의 관계는 다음 같은 형태입니다.

 

그림 2.

 

여기에서 a, b 는 ( 뷰 프러스텀의 ) near plane 과 far plane 의 설정입니다. 다시 말해, d 는 항상 1/z 의 선형 리매핑( remapping )입니다.

 

그것을 살펴 보면, 여러분은 를 취한다는 것은 여러분이 원하는 z 에 대한 어떤 함수가 될 것이라 생각할 수 있습니다. 그러면 왜 이런 선택을 하는 것일까요? 두 가지 주요 이유가 있습니다.

 

먼저, 1/z 은 원근 투영의 구조와 자연스럽게 어울립니다. 이는 거의 대부분의 범용 변환( transformation ) 클래스들이 직선( straight line )을 보존할 수 있도록 해 줍니다 -- 이는 하드웨어 래스터화( rasterization )를 유용하게 만들어 줍니다. 왜냐하면 삼각형의 곧은 엣지( straight edge )가 스크린 공간에서도 여전히 곧게 유지되기 때문입니다. 우리는 원근 나눗셈의 이점을 취함으로써 1/z 에 대한 선형 리매핑을 생성할 수 있습니다. 그 원근 나눗셈은 하드웨어가 미리 수행해 줍니다:

 

그림 3.

 

물론 이 접근법의 진정한 힘은 그 투영 행렬이 다른 행렬들과 곱해질 수 있다는 점이며, 그것은 여러분으로 하여금 여러 변환 단계들을 하나로 결합할 수 있도록 해 줍니다.

 

두 번째 이유는 Emil Persson 이 언급했듯이 1/z 가 스크린 공간에서 선형이라는 점입니다. 그러므로 래스터화를 수행하는 동안 삼각형에 대해 d 를 보간하는 것이 쉽습니다. 그리고 계층적 Z 버퍼, early Z-culling, 깊이 버퍼 압축과 같은 것들을 수행하는 것이 더욱 쉬워집니다.

 

 

 깊이 맵 그려보기

 

 

공식은 어렵습니다; 그림을 몇 개 확인해 보죠.

 

그림 4.

 

이 그래프들은 읽는 방법은 오른쪽에서 왼쪽으로, 그리고 나서 위에서 아래로입니다. 왼쪽 축에 표시된 d 에서 시작합시다. d1/z 에 대한 임의의 선형 리매핑일 수 있기 때문에, 우리는 이 축에서 원하는 곳에 0 과 1 을 배치할 수 있습니다. 눈금( tick mark )들은 개별적인 깊이 버퍼 값들을 표시합니다. 설명을 위한 목적으로, 필자는 4 비트로 정규화된 정수 깊이 버퍼를 시뮬레이션하고 있습니다. 그래서 16 개의 고르게 매치된 눈금들이 존재합니다( 역주 : 24 = 16 ).

 

각 눈금에서 수평으로 갔을 때 1/z 커브와 만나는 곳에서 아래쪽 축으로 수선을 내려 봅시다. 이 축은 개별 값들이 월드 공간 깊이 범위로 나타나는 곳입니다.

 

위의 그래프는 "표준" 을 보여 줍니다. 이는 D3D 및 유사 API 들에서 사용되는 바닐라( vanilla ) 깊이 매핑입니다. 여러분은 1/z 커브가 near 평면과 가까운 곳의 값들을 모으고 far 평면과 가까운 값들을 흩트러 놓는다는 것을 즉각적으로 이해할 수 있을 겁니다.

 

또한 near 평면이 깊이 해상도에 있어서 엄청난 영향력을 미치는 이유도 쉽게 알 수 있습니다. Near 평면을 당기는 것은 d 범위를 1/z 커브의 점근선( asymptote )을 급등시키며( skyrocket ), 이는 값들이 더욱 한쪽으로 쏠리도록 분산( lopsided distribution of values )시키는 결과를 산출합니다:

 

그림 5.

 

이와 유사하게, 이러한 문맥에서 우리는 far 평면을 무한하게 밀어 내도 큰 영향을 주지 못하는 이유를 쉽게 알 수 있습니다. 그것은 단지 1/z = 0 으로 d 범위를 확장한다는 것을 의미할 뿐입니다:

 

그림 6.

 

실수 깊이 버퍼는 어떨까요? 다음 그래프는 3 개의 지수( exponent ) 비트와 3 개의 가수( mantissa ) 비트를 사용하는 실수 포맷을 시뮬레이션하는 것과 관련한 눈금들을 추가한 것입니다:

 

그림 7.

 

이제 [0, 1] 범위에 40 개의 눈금이 존재합니다 -- 앞의 16 개보다는 많지만, 대부분이 near 평면 근처에 불필요하게 몰려 있으며, 거기는 우리가 더 많은 정밀도를 필요로 하지 않는 곳입니다.

 

깊이 범위를 역전시키기 위해 요새 광범위하게 알려진 트릭은 d = 1 에 near 평면을 매핑하고 d = 0 에 far 평면을 매핑하는 것입니다 :

 

그림 8.

 

훨씬 낫군요! 이제 실수에 대한 quasi-logarithmic distribution 은 1/z 의 비선형성을 조금 없앱니다. 이는 우리에게 정수 깊이 버퍼와 비교했을 때 near 평면에서 유사한 정밀도를 제공하며, 원하는 곳에서 광범위하게 정밀도를 증가시킬 수 있게 해 줍니다. 이 정밀도는 여러분이 멀리 이동할 때마다 매우 천천히 악화됩니다.

 

역전된 Z 트릭은 아마도 여러 번 독립적으로 재창조되어 왔을 것입니다. 그러나 최소한 Eugene Lapidous 와 Guofang Jiao 에 의해 작성된 ( 안타깝게도 지금은 접근 가능한 무료 링크가 없는 ) GIGGRAPH '99 paper 까지 돌아갈 수 있습니다. 그것은 더욱 최근에 Matt PettineoBrano Kemen 의 블로그 포스트, 그리고 SIGGRAPH 2012 talk 에서 Emil Persson 의 Creating Vast Game Worlds 에서 다시 대중화되었습니다.

 

이전의 모든 다이어그램은 투영 후의 깊이 범위를 D3D 관례처럼 [0, 1] 이라 가정합니다. OpenGL 에서는 어떨까요?

 

그림 9.

 

 OpenGL 은 기본적으로 투영 후의 깊이 범위를 [-1, 1] 이라 가정합니다. 이는 정수 포맷에서는 별 차이를 만들지 않지만, 실수 포맷에서는 불필요하게 중간에 대부분의 정밀도가 몰립니다( 나중에 저장을 위해 깊이 버퍼에서 [0, 1] 로 매핑되기는 하지만, 그것은 도움이 되지 않습니다. 왜냐하면 [-1, 1] 로의 초기 매핑이 이미 그 범위의 뒤쪽 절반 정도를 날려먹었기 때문입니다 ). 그리고 대칭적인 관점에서 볼 때, 역전된 Z 트릭은 여기에서 아무것도 할 수 없을 것입니다.

 

운좋게도 데스크탑 OpenGL 에서는 광범위하게 지원되는 ARB_clip_control 을 사용해서 이 문제를 해결할 수 있습니다( 이제는 glClipControl 이라는 것을 통해서 OpenGL 4.5 의 코어에서 지원됩니다 ). 불운하게도, GL ES 에서는 못합니다.

 

 

 

 반올림( roundoff ) 에러의 효과

 

 

1/z 매핑과 실수 깊이 버퍼를 사용하는 방식과 정수 깊이 버퍼를 사용하는 방식을 비교하는 것은 정밀도 스토리의 큰 부분이지 전부는 아닙니다. 심지어 여러분이 렌더링하려고 시도하는 씬을 표현하는데 충분한 깊이 정밀도를 확보하고 있다고 하더라도, 정점 변환 과정에서 정밀도 에러가 발생하기 쉽습니다.

 

이전에 언급했듯이, Upchurch 와 Desbrun 은 이에 대해 연구했고, 반올림 에러를 최소화하기 위한 두 개의 주요한 권고사항을 제시했습니다.

 

1. 무한 far 평면을 사용하십시오.

2. 투영 행렬을 다른 행렬과 분리하십시오. 그리고 정점 쉐이더에서 그것들을 뷰 행렬과 합치기보다는 개별적인 연산을 적용하십시오.

 

Upchurch and Desbrun came up with these recommendations through an analytical techunique, based on treating roundoff errors as small random perturbations introduced at each arithmetic operation, and keeping track of them to first order through the transform process. 필자는 그 결과를 직접적인 시뮬레이션을 통해 확인해 보기로 했습니다.

 

필자의 소스 코드는 여기에 있습니다 -- Python 3.4 with numpy. 이는 일련의 랜덤 점을 생성하고, 점들을 깊이에 의해 정렬하고, near 및 far 평면 사이에 점들을 선형적으로 혹은 대수적으로( logarithmically ) 배치합니다. 그리고 나서 그 점들을 뷰 및 투영 행렬들과 원근 나눗셈을 통해서 넘기는데, 32 비트 실수 정밀도 처리량( throughput )을 사용합니다. 그리고 선택적으로 최종 결과를 24 비트 정수로 양자화( quantize )합니다. 마지막으로 그것을 여러 번 실행해 인접한 두 개의 점들이, 같은 깊이 값으로 매핑되거나 실질적으로 순서가 바뀜으로 인해, 몇 번이나 구분불가능해지는지를 셈합니다. 다시 말해 그것은 서로 다른 시나리오 하에서 깊이 비교 에러가 발생하는 비율을 측정합니다 -- 우리는 이를 Z 파이팅과 같은 이슈라고 봅니다.

 

여기에서 near = 0.1 로 far = 10K 인 선형적으로 배치된 깊이들에 대해서 획득된 결과를 보여 줍니다( 필자는 logarithmic 깊이 배치를 시도해 봤고 다른 near/far 비율들도 배치해 봤습니다. 세부적인 수치들이 다양해져도 일반적인 경향은 같았습니다 ).

 

이 표에서 "indist" 는 indistinguishable 을 의미합니다( 두 개의 인접한 깊이가 최종적으로 같은 깊이 값으로 매핑된 것입니다 ). 그리고 "swap" 은 두 개의 인접한 깊이의 순서가 바뀌는 것을 의미합니다.

 

그림 10.

 

이를 그래프화하지 않은 점은 죄송합니다. 그러나 너무 많은 차원들이 존재해서 그래프로 만들기 힘들었습니다! 어떤 경우라도, 수치들을 보면, 몇 가지 일반화가 가능합니다.

 

    • 대부분의 설정에서 실수냐 정수냐 하는 것은 의미가 없습니다. 수치 에러는 양자화 에러에서 발생합니다. 부분적으로 이는 ( float32 는 23 비트의 가수를 가지기 때문에 ) float32 와 int24 가 [0.5, 1] 에서 거의 유사한 크기여서 실제적으로는 대부분의 깊이 영역에서 추가적인 양자화 에러는 거의 존재하지 않기 때문에 발생합니다.
    • 대부분의 경우에, ( Upchurch 와 Desbrun 의 제안대로 ) 뷰 및 투영 행렬들을 분리하는 것이 좀 더 나은 결과를 산출합니다. 그것은 전체적인 에러율을 낮춰주지는 않지만, it does seem to turn swaps into indistinguishables, which is a step in the right direction.
    • 무한한 far 평면은 에러율에서 극소적인 차이를 보여줄 뿐입니다. upchurch 와 Desbrun 은 절대적인 수치 에러가 25% 감소한다고 했지만, 비교 에러의 비율을 감소시킨다고 해석하기는 어려워 보입니다.

 

그런데 위에서 지적한 것들은 실용적인 관점에서는 별로 없습니다. 왜냐하면 여기에서 중요한 실제 결과는 다음과 같기 때문입니다: 역전된 Z 매핑은 기본적으로 환상적입니다. 확인해 봅시다:

 

    • 실수 깊이 버퍼를 사용하는 역전된 Z 는 이 테스트에서 0 의 에러율을 제공합니다. 물론 여러분은 입력 깊이 값들의 배치를 좀 더 타이트하게 만들어서 에러를 생성할 수는 있겠죠. 그래도 여전히 역전된 Z 는 다른 옵션들보다는 더욱 정확한 결과들을 산출할 것입니다.
    • 정수 깊이 버퍼를 사용하는 역전된 Z 는 다른 정수 옵션들과 비교하면 좋은 결과를 산출합니다.
    • 역전된 Z 는 미리 결합된 뷰/투영 행렬을 사용하는 것과 분리된 뷰/투영행렬을 사용하는 것에서의 차이를 제거해 버립니다. 그리고 무한한 far 평면을 사용하는 것과 유한한 far 평면을 사용하는 것의 차이도 제거합니다. 다시 말해 역전된 Z 값을 사용하면, 정밀도에 영향을 주지 않으면서도, 투영 행렬을 다른 행렬들과 결합시킬 수 있으며, 원하는 far 평면을 지정할 수 있다는 이야기입니다.

 

필자는 여기에서의 결론은 명확하다고 생각합니다. 어떤 원근 투영 상황이라고 할지라도, 실수 깊이 버퍼를 역전된 Z 와 함께 사용하십시오! 그리고 여러분이 실수 깊이 버퍼를 사용하지 못하는 상황이라고 해도 여전히 역전된 Z 버퍼를 사용해야 합니다. 이는 모든 정밀도 문제에 대한 만병통치약은 아닙니다. 여러분이 극단적인 깊이 범위를 포함하는 오픈 월드 환경을 만들고 있다면 더욱 그러합니다. 그렇지만 그것은 시작일 뿐입니다.

 

Nathan 은 Graphics Programmer 이며, 현재 NVIDIA 의 DevTech 소프트웨어 팀에서 일하고 있습니다. 여러분은 그의 블로그를 여기에서 구독하실 수 있습니다.

Visual Studio 에서 텍스트 편집기 설정을 하는 것은 좀 스트레스 받는 일입니다. 특히 재설치를 했거나 팀 내에 코딩룰이 있다면 좀 더 스트레스를 받죠.

 

그래서 텍스트 편집기의 설정을 공유하는 방법에 대해서 설명하고자 합니다.

 

사실 이건 Visual Stdio 의 환경 설정을 공유하는 방법의 일부라고 봐도 무방합니다. 거의 모든 설정이 포함됩니다.

 

[ 도구 >> 설정 가져오기 및 내보내기 ] 메뉴 아이템을 클릭합니다.

 

 

 

다들 똑똑하신 분들이니 더 이상의 설명은 필요없겠죠? 다이얼로그만 봐도 설명이 다 되어 있네요.

 

[ 다음 ] 버튼을 누르시면 트리뷰가 나오는데 필요한 것만 선택해서 저장/로드하시면 됩니다.

'ETC' 카테고리의 다른 글

before-after plugin test  (0) 2017.07.23
Behavior Tree 개념 및 동작  (6) 2016.06.02
[ 번역 ] 와우 전투 시스템  (0) 2015.11.27
The activity diagram  (0) 2014.10.15
응용 수학에 대해서 참고할 만한 사이트.  (0) 2012.02.22
발음 기호를 제대로 익힐 수 있는 사이트.  (0) 2012.02.22
SyntaxHighlighter test.  (0) 2011.03.01

알림 : 이 문서는 주위 분들에게 질문을 하기 위해서 작성되었습니다. 구현이 완전히 끝나면 따로 문서를 공유할 계획입니다.



제가 이번에 [ Terrain Synthesis from Digital Elevation Models ] 라는 논문의 내용을 구현해 보려고 하고 있습니다.

 

이것은 세 가지 단계로 이루어져 있습니다; profile recognition, polygon breaking, branch reduction. 이것은 [ Profile-recognition & Polygon-breaking Algorithm( PPA ) ] 를 사용합니다.


현재 저는 profile recognition 까지 구현을 한 상태입니다. 아래 그림은 valley 에 대한 profile 덩어리를 보여주고 있습니다: target-connection, cross-line removal, parallel-line removal 까지 수행한 상태입니다.

 

 

이제 polygon breaking 을 적용할 차례인데요, 닫힌 폴리곤에서 중요하지 않은 라인 세그먼트를 배제하면서 중심 라인은 보존하는 단계입니다. 즉 일종의 thinning 알고리즘입니다. 저자의 경우에는 [ k x k thinning ] 의 4 가지 원칙을 지켰다고 이야기하더군요. 5 번째는 branch reduction 을 통해서 해결한다고 합니다.


어쨌든 닫힌 폴리곤의 중요하지 않은 라인 세그먼트라는 것은, valley 를 찾는 경우에는 가장 높은 elevation 을 가진 세그먼트이고, ridge 를 찾는 경우에는 가장 낮은 elevation 을 가진 세그먼트입니다.


그냥 삼각형이나 사각형인 경우에는 문제가 쉽게 해결이 됩니다. loop 를 돌기도 편하죠. 하지만 아래 그림에서 붉은 색으로 보이는 복잡한 모양의 닫힌 폴리곤같은 경우를 만나면 머리가 좀 아파집니다.



전에 처음 시도했을 때는 Brute-force 기법을 사용해서 재귀함수를 만들었습니다. 그랬더니 라인이 너무 많아 stack-overflow 가 발생하더군요. 


그리고 그 다음에 시도했을 때는 stack algorithm 을 사용해 라인 세그먼트를 쌓아 가다가 시작 세그먼트와 만나는 닫힌 폴리곤이 형성되면 그 중에서 가장 적절하지 않은 세그먼트를 배제하는 방법을 취했습니다. 이 방식은 매우 시간이 오래 걸렸습니다. 게다가 뭘 잘못했는지 중간에 맘에 안드는 결과가 나오는 곳이 있더군요.


[ A Visual Basic program for ridge axis picking on DEM data using the Profile-Recognition and Polygon-Breaking Algorithm ] 에서는 [ 4. Polygon-breaking ] 섹션에서 "dead-end detection" 이라는 것에 대해서 언급합니다. 


A technique of "dead-end detection" has been designed in this paper to preempt many unnecessary tracing steps: when the segment tracing process encounters a dead-end of a branch, all the segments in this branch are marked as "route disabled". The tracing process then regresses to the root of the dead-end branch and the system flags the branch to be avoided for future tracings.


여기에서 branch 가 모양을 의미하는 건지 tree 의 branch 를 의미하는 건지 모호하고 branch 의 dead-end 라는 의미도 모호하네요. 이 문서에서 tree 자료구조에 대한 언급도 없었구요. 전체적으로 "dead-end detection" 알고리즘을 이해하기 어렵습니다. 자료구조를 어떻게 잡아야 할지도 모르겠구요.

 

현재 제 구현에서는 각 샘플포인트가 다음과 같이 정의되어 있습니다.


 

기존에 stack 방식의 구현을 했을 때는 Point 자료구조에서 Line 이라는 자료구조를 뽑아 내서 그것들을 기준으로 polygon breaking 을 처리했었습니다.

 

어쨌든 여기에서 좀 막막한 감이 있는데 polygon breaking 알고리즘 및 자료구조에 대해서 이해가 가는 분이 계시면 저에게 가르침을 주시면 감사드리겠습니다.

 

추가 : 제 구현 코드는 다음과 같습니다 : WpfApplication1.zip

 


해결!

 

그래프의 관점에서 이를 설명할 수 있습니다. 물론 너비우선탐색을 적용하면 트리구조라 해도 상관이 없습니다. 그냥 보이는 대로 폴리곤의 라인 세그먼트를 생각도 되구요.

 

하지만 어떠한 관점에서 구현하든지 앞에서 언급했듯이 재귀 함수를 구현할 수는 없습니다. 저같은 경우에는 재귀없이 깊이우선탐색을 하기 위해 stack 을 사용했습니다.

 

세그먼트를 높이 순으로 sorting 하기 때문에 한 번이라도 closed 상태가 되면 가장 불필요한 세그먼트는 시작 세그먼트라 할 수 있습니다. 저는 이 시작 세그먼트가 가장 불필요하다는 전제를 까먹고 구현하다가 고생을 했던 것이죠. 패스의 끝점( End point )에 도달하면 이전 분기로 롤백하면서 기존에 지나왔던 세그먼트를 정리하고 그 중에서 가장 불필요한 세그먼트의 높이를 기록해야 한다고 생각했습니다. 그것이 머리와 구현을 복잡하게 만들었던 것입니다. 이미 시작 세그먼트가 가장 불필요한 세그먼트라는 전제가 있는데 말이죠. 물론 이런 전제가 아니라도 알고리즘은 충분히 복잡합니다. 실수의 여지가 엄청나게 많죠.

 

어쨌든 시작 세그먼트가 포함되었던 closed polygon 은 닫힌 상태가 깨집니다. 만약 closed polygon 이 아니었고 endpoint 에 도달했다면 그 역시 닫힌 상태가 깨진 것이죠. 즉 closed polygon 이었든 그렇지 않았든 시작 세그먼트를 경유하려고 시도하는 모든 테스트는 더 이상 필요하지 않은 것이죠. 즉 이 시작 세그먼트를 경유하면 막다른 길( dead-end )로 가게 되어버리는 것입니다. 이런 시작 세그먼트에는 IsDeadEnded 라는 플래그를 설정해 둡니다. 그러면 다음 탐색시에 이 세그먼트를 테스트할 필요가 없습니다. 이런 식으로 세그먼트를 하나씩 제거하다가 보면 점점 탐색 속도가 빨라지게 됩니다.

 

제가 dead-end detection 없이 brute-force 로 탐색을 해 봤는데 100 x 100 이미지를 처리하는데 20 분 가까이 기다려도 끝이 나지 않았습니다. Dead-end detection 을 도입하니 3 초 안에 끝나더군요. 알고리즘 최적화의 무서움을 새삼 느꼈습니다.

 

나중에 전체 PPA 기법에 대해 따로 정리해 보도록 하겠습니다.

 

결과는 다음과 같습니다. Cyan 색상이 최종 결과입니다. 아직 branch reduction 부분이 완벽하지 않네요. 연구를 좀 더 해 봐야 할 것 같습니다.

 

 

비교대상을 만들어 주신( VB 코드를 구해서 C# 코드로 컨버팅해서 공유해 주신 ) 오석주 TD 님께 감사드립니다. 제가 제 구현에 확신을 가지고 계속 작업하는데 많은 도움을 주었습니다. 코드 흐름 자체는 잘못된 것이 아니라는 확신을 가지고 작업할 수 있었습니다.

 

원래 구현자는 배열을 여러 개 만들어서 플래그를 설정하면서 작업을 하더군요. VB 6.0 시절이라 그런지 개체지향프로그래밍에는 익숙하지 않은 것 같았습니다. 아니면 성능때문에 일부러 그렇게 한 것일지도 모르죠. 배열이 하도 복잡해서 제대로 이해를 하지는 못했지만 주석의 흐름으로 봤을 때 비슷한 방향으로 구현하고 있다는 것을 알 수 있었습니다.

 

앞으로 합성 구현이 남았는데 관련 논문의 양을 보니 가슴이 답답해 지네요( OTL ).

원문 : http://www.howardzzh.com/research/terrain/

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

주의 : 번역이 개판이므로 이상하면 원문을 참조하십시오.

주의 : "feature" 를 "특징"이라고 번역했는데 뭔가 어색할 때가 있습니다. 그냥 마음속으로 "특징"을 "feature" 라고 읽으시기 바랍니다.

주의 : Appendix 는 번역하지 않았습니다. 원문을 참조하세요.

 

Terrain Synthesis from Digital Elevation Models

 

Howard Zhou, Jie Sun, Greg Turk, Member, IEEE, and James M. Rehg, Member, IEEE

 

Abstract -- 이 논문에서 우리는 지형 합성을 위한 예제 기반( example-based, 역주 : 이 "예제" 라는 표현은 "본보기", "기준" 등의 의미로 사용되는 것 같습니다. 그런 느낌으로 이해해 주시기 바랍니다. ) 시스템을 제공합니다. 우리의 접근법에서 샘플 지형으로부터의 ( 하이트 필드( height field )로 표현되는 ) 패치( patch )들은 새로운 지형을 생성하는데 사용됩니다. 이러한 합성은 사용자가 스케치한 특징( feature ) 맵으로부터 생성되는데, 그 맵은 결과로 나오는 합성된 지형에서 어느 부분이 특징적인지를 지정하게 됩니다. 우리의 시스템은 큰 규모의 곡선적인 특징( 산등성이와 골짜기 )들을 강조하게 됩니다. 왜냐하면 그러한 특징들이 대부분의 지형에서 주요한 가시적 요소들이기 때문입니다. 예제 하이트 필드와 사용자의 스케치 맵은 모두 기하영역의 기법을 사용해 분석됩니다. 시스템은 예제 데이터로부터 사용자의 스케치에서 찾을 수 있는 특징과 일치하는 패치를 찾습니다. 그 패치들은 그래프 컷( graph cut )과 푸아송 편집( Poisson editing, 역주 : [ poisson image editing ] 참조 )을 사용해 서로 합쳐집니다. 패치들이 합성된 지형에 배치되는 순서는 특징 트리에 대한 넓이 우선 탐색( breadth-first traversal, 역주 : [ Data Structure - Breadth First Traversal ] 참조 )에 의해서 결정됩니다. 그리고 이는 표준 래스터-스캔 배치( raster-scan placement ) 순서에 있어서 개선된 결과를 산출하게 됩니다.

 

Index Terms - Terrain synthesis, Digital Elevation Models, terrain analysis, texture synthesis.

 

1. INTRODUCTION

 

 

지형 함성을 가능하게 해 주는 수많은 애플리케이션들이 존재합니다. 그리고 보통은 그 ㅈ지형은 씬에서 주요 가시 요소가 됩니다. 그런 애플리케이션은 풍경( landscape ) 디자인,  비행 시뮬레이션, 응급 대응 훈련, 전장 시뮬레이션, 장편 극영화 특수 효과, 컴퓨터 게임들을 포함하고 있습니다. 수년 동안 그래픽스 연구자들은 합성 지형을 생성하기 위한 효율적인 기법들을 개발하는 데 있어 주목할 만한 발전을 해 왔습니다. 이전의 지형 합성은 프랙탈( fractal ) 모델과 물리적인 침식( erosion ) 모델을 사용해 사실적으로 보이는 지형을 만드는데 초점을 맞춰 왔습니다.

 

컴퓨팅 파워와 지형 가시화 기법에 대한 개발 수준이 급격하게 성장하면서, 좀 더 사실적인 지형에 대한 요구가 꽤 증가되어 왔습니다. 게다가 지형 모델링 애플리케이션의  사용자들은 새로운 지형을 창조하는 데 있어 더 많은 제어권을 원하게 되었습니다. 그러나 현재 지형 합성 기법들은 몇 가지 제약을 가지고 있습니다. 먼저 이 기법들은 사용자에게 원하는 지형의 특징을 배치하는 데 있어서 적은 제어권을 제공하거나 아예 제공하고 있지 않습니다. 다음으로 이러한 기법들에서 제어 파라미터를 사용하는 것은 그랜드 캐년( Grand Canyon )과 같은 기하학적 특징을 가진 지형과 같은 원하는 스타일의 지형을 생성하기 어렵게 만듭니다.

 

그림 2a2b 는 좀 유명한 상업 소프트웨어에 의해 제공되는 프랙탈 및 침식 지형의 예를 보여주고 있습니다. 그것들은 흑백 고도 맵( intensity-coded elevation map )처럼 출력되고 있습니다. 이러한 스타일의 지형들은 그림 2c2d 에 나와 있는 자연적인 지형들과는 매우 다릅니다.

 

우리는 새로운 예제 기반 지형 합성 기법을 제시하는데, 이는 지형 특징 배치와 지형 스타일에 대해 사용자가 직관적으로 제어할 수 있어야 하는 필요성에 대해 다룹니다. 우리의 기법은 예제 기반 텍스쳐 합성으로부터 패치를 매칭하고 배치하는 하는 기술을 이용합니다. 우리 접근법에서 사용자가 스케치한 지형 특징 맵( 스케치 맵이라 불림 )과 실제 지형 데이터( 예제 하이트 필드라 불림 )는 원하는 지형의 스타일을 포함하고 있습니다. 예제 하이트 필드는 디지털 고도 모델( Digital Elevation Map, DEM ) 형식입니다. 이는 US Geological Survey 에서 온라인으로 이용할 수 있습니다. 다음으로 우리 시스템은 자동적으로 실제 지형 데이터의 가시적 스타일을 보존하면서 스케치 맵의 특징 제약들을 만족시켜주는 새로운 하이트 필드를 생성하게 됩니다. 우리 접근법을 통한 합성 결과는 그림 1 에 나와 있습니다.

 

그림 1. Flathead National Forest Mountain Range( 위쪽 이미지 ) 와 Grand Canyon( 아래쪽 이미지 )의 DEM 을 사용해 합성된 지형들.

 

그림 2. 현재의 기법들로 생성된 지형의 예제들. ( a ) Terragen 에 의해 1024 x 1025 해상도로 생성된 프랙탈 지형( ridged multi-Perlin ). ( b ) Bryce 에 의해 1024 x 1024 로 생성된 침식 지형. 각각은 두 개의 실제 고도 맵들과 비교됨: ( c ) 몬타나의 Flathead National Forest 산맥의 1/3 각초( arcsecond )( 10 m ) DEM. ( d ) 켄터키의 Vernon 산의 1/3 각초( 10 m ) DEM. ( DEMs courtesy of the US Geological Survey ).

 

스케치 맵은 사용자에게 합성 과정을 제어하는 데 있어 쉽고 직관적인 방법을 제공합니다. 각 맵은 그림 10a 의 중앙에 있는 이미지에서의 분기점( bifurcation point )과 같은 중요한 지형 특징의 위치를 지정하게 됩니다. 사실 브러쉬의 너비와 픽셀 강도는 별로 중요치 않습니다. 왜냐하면 그것들은 더 어두운 픽셀은 더 낮은 고도를 가리키고 더 밝은 픽셀은 더 높은 고도를 가리킨다는 단순한 원칙을 따르기 때문입니다. 이 연구에서 우리의 목표는 가시적으로 주목할만한 지형을 생성하는 것입니다. 우리는 합성된 지형이 기하학적으로 정확한가에 대한 개별적인 이슈에 대해서는 다루지 않을 것입니다.

 

우리 알고리즘의 시작점은 스케치 맵과 예제 하이트 필드에서 중요한 지형 특징을 식별하는 것입니다. 우리 시스템은 큰 규모의 곡선적인 특징들에 집중하는데, 그런 것들로는 강, 계곡, 산등성이같은 것들이 있습니다. 왜냐하면 그러한 것들이 보통 대규모 지형에서는 ( 그림 2c2d 에서 보이는 것처럼 ) 중요한 가시적 요소이기 때문입니다. 스케치 맵의 지형구조와 예제 하이트 필드에의 지형구조는 보통 서로 다르며 공간적으로 큰 큐모에서 매칭시키는 것은 거의 정확하지 않습니다. 그러나 우리는 적은 규모에서 볼 때는 그렇지 않다는 것을 발견했습니다. 계곡의 분기 영역이나 직선 영역과 같은  공통적인 지형 특징들은 양쪽 맵에서 모두 찾아볼 수 있습니다. 그러므로, 우리 알고리즘은 스케치 맵을 작은 패치 영역으로 분할하고 예제 하이트 필드에서 구조적인 특징이 일치하는 부분을 검색합니다.

 

스케치 맵에서 구조 정보를 추출하는 것은 그것의 단순함 때문에 직관적입니다. 반대로 실제 지형 하이트 필드에서 산등성이나 계곡과 같은 구조 정보를 추출하는 것은 어려울 수 있습니다. 여기에서 우리는 기하 영역의 연구를 이요합니다. 특히 우리는 하이트 필드로부터 산등성이 및 계곡의 축과 같은 구조 정보를 추출하기 위해 Chan et al. [1] 에 의해 개발된 윤곽 인식( Profile recognition ) 및 폴리곤 분열 알고리즘( Polygon breaking Algorithm, PPA )을 사용합니다.

 

일단 중요한 구조적 지형 특징이 추출되고 나면, 우리 알고리즘은 다음과 같은 단계들을 밝게 됩니다: 첫 번째로 스케치 맵과 예제 하이트 필드에서 패치 영역을 매칭하고 정렬하기 위해서 곡선적 지형 특징들이 제약으로서 사용됩니다. 두 번째로 이러한 특징들은 이웃 패치 영역 사이의 경계가 겹치는 곳을 따라 매칭을 하기 위한 제약으로 사용됩니다. 패치는 가중치화된 점수 결합( weighted combination of scores )을 기반으로 선택되는데, 그 점수는 패치와 그것의 이웃들 사이의 적합도( goodness-of-fit ) 및 요청된 패치의 변형량( amount of deformation )을 가늠하게 됩니다. 패치 배치의 순서는 사용자 스케치 맵으로부터 생성된 특징 트리에 대한 넓이 우선 탐색에 의해 결정됩니다. 각 패치는 가시적 분절을 최소화하기 위해서 graph-cut seam finding 과 Poisson seam removal 의 조합을 사용해 출력 맵에 배치됩니다.

 

이 논문은 다음과 같은 기법들에 공헌을 했습니다:

 

    • 합성된 지형 특징들을 스케치 기반으로 지정해서 사용자가 함성 결과 전반을 직관적으로 제어할 수 있도록 해 주는 기법.
    • 큰 규모의 곡선적 지형 특징들을 매칭하고 배치하기 위한 특징 기반 접근법. 이는 큰 지형 데이터베이스들을 효율적으로 만들 수 있도록 해 줬으며, 합성 과정에서 중요한 가시적 요소들을 보존해 줍니다.
    • 표준 래스터-스캔 오더링의 대안으로서 사용되는 트리기반 오더링( tree-ordered ) 패치 배치 알고리즘. 이는 사용자 입력과 매칭되는 지형 구조를 더욱 신뢰성있게 재구축하는 결과를 산출합니다.
    • 서로 다른 유형의 지형들 사이에서의 전이를 합성하고 여러 개의 DEM 들을 사용해서 단일한 합성된 출력물을 만드는 기능.

 

우리는 예전의 텍스쳐 합성 기법들로부터 이 연구에 대한 영감을 얻었습니다. 그 연구들에 대해 다음 섹션에서 리뷰하도록 하겠습니다.

 

2. RELATED WORK

 

합성된 지형을 생성하는 두 개의 주요 접근법이 존재합니다: 프랙탈 풍경 모델링과 물리적 침식 시뮬레이션. 프랙탈 풍경 모델링은 Mandelbrot 의 개척적인 연구[2] 로 거슬러 올라 갑니다. 그 이후로 다양한 확률적( stochastic ) 하위분할 기술들이 소개되었습니다. Fournier et al. [3] 은 프랙탈 서피스를 생성하기 위해 random midpoint displacement 기법을 소개했습니다. Voss [4] 는 fractional Brownian surfce 에 successive random displacement 를 추가했습니다. Miller [5] 는 프랙탈 지형을 생성하기 위한 square-square 하위분할 스키마와 하이트 필드를 렌더링하기 위한 병렬 처리 알고리즘을 제안했습니다. Lewis [6] 는 일반화된 확률적 하위분할을 제안했습니다. Szeliski 와 Terzopoulos [7] 는 결정론적 스플라인( deterministic spline )과 확률적 프랙탈들을 제약있는 프랙탈들로 결합함으로써 사용자의 제어권 문제를 다뤘습니다. 최근의 프랙탈 기반 접근법은 [8] 과 [9] 에 소개되어 있습니다.

 

물리적 침식 시뮬레이션은 풍경 공식에 대한 모델과 지형학( geomorphologic ) 커뮤니티로부터의 하천 침식( stream erosion )에 기반해 지형 디테일( detail )을 합성하는 것에 대한 대안적 접근법입니다. 그것은 보통 거친 하이트 필드가 생성된 후의 개선( refinement ) 단계로서 사용되었습니다. Kelly et al. [10] 은 처음으로 하도망( stream netowrk )에 의한 침식을 시뮬레이션 함으로써 자연스런 지형을 근사계산하는 기법을 소개했습니다. 나중에 Musgrave et al. [11] 은 프랙탈 모델링 접근법과 침식 시뮬레이션 접근법을 하나의 프레임웤으로 결합했습니다. [12], [13], [14], [15], [16] 에 예시된 최근의 물리적 침식 기술들은 물리적 모델링 관점과 계산적 효율성을 모두 증진시키는데 초첨을 맞춰왔습니다. 적절히 튜닝된 파라미터들을 사용함으로써 이 기법들은 사실적으로 보이는 지형들을 생성할 수 있습니다.

 

프랙탈 기술과 물리적 침식 기술들은 모두 절차적인( procedural ) 개선을 통해 지형 디테일을 추가합니다. 이는 보통 비직관적인 파라미터 튜닝을 포함하게 됩니다. 최근에 Brosz et al [17] 은 현존하는 DEM 데이터로부터 고해상도 지형 디테일을 추출하고 mutiresolution analysis 를 통해 그것을 저해상도 지형에 적용하려고 시도했습니다. 경험적으로 볼 때 그들의 기법은 꽤 세부화된 소스 및 타깃 지형을 모두 요구하며, 사용자가 임의의 지형을 생성할 수 있는 자유도를 제공하지 않습니다.

 

사용자에게 합성된 지형 전반에 대한 더욱 직관적인 제어를 제공하기 위해서, 이미지 기반 대안들이 Lewis [18] 및 Perlin 과 Velho [19] 에 의해 제안되었습니다. 이 작업들에서, 지형은 텍스쳐 유형으로서 사용되며( viewed ) 사용자 제어는 텍스쳐에 대한 직접적인 조작을 통해서 제공되었습니다. 그러나 사용자가 사실적이고 자연스러운 하이트 필드를 손으로 그리는 것은 너무 어렵기 때문에, 이 기법들은 보통 사실적인 디테일이 부족해진다는 문제점을 겪게 되었습니다.

 

2.1. Commercial Software

 

우리는 우리의 기법과 비슷한 접근법을 사용하는 지형 합성을 위한 상업 소프트웨어를 발견하지 못했습니다. 간단히 네 개의 주요 상업 시스템들을 리뷰하겠습니다; Mojoworld, Terragen, World Machine, Bryce. 이것들은 최신 기법을 대표합니다. Mojoworld 와 Terragen 은 둘 다 프랙탈 합성 엔진을 가지고 있는데, 그 엔진은 지형을 절차적으로 생성하니다. 그 결과는 매우 조밀( compact )합니다. 왜냐하면 그것들은 그때그때( on the fly ) 생성되기 때문입니다. 반면에 WorldMachine 은 사실적으로 느껴지는 지형을 합성하기 위해서 그것의 절차적 모양( shape )과 노이즈 생성기의 상단에 기하적 침식을 제공합니다.이런 모든 시스템들에서 합성 결과에 대한 제어는 생성 과정에서 전역 파라미터들을 변경함으로써 가능합니다. 그러나 독특하고 사실적인 지형 타입을 생성하기 위해서 이러한 파라미터들을 설정하는 것은 어렵습니다. 더우기 이 시스템들은 주요 지형 특징을 사용자가 지정해서 배치하는 것을 지원하지 않습니다. Bryce 는 프랙탈 합성 엔진과 침식 합성 엔진을 모두 지원하면서 페인팅 접근 방식을 사용해 사용자가 하이트 필드를 직접적으로 수정할 수 있도록 합니다( 기본적으로 [18] 의 기법에 프랙탈 노이즈를 추가한 것 ). 그림 2c 와 2d 는 그런 상업 시스템들에 의해 생성된 예제 지형들입니다. 그러나 이러한 제품들 중의 어떤 것도 Grand Canyon 스타일과 같은 특정 스타일로 지형을 합성하는 것이 불가능합니다.

 

2.2. Texture Synthesis

 

이미지 기반 텍스쳐 합성은 주어진 예제 이미지로부터 픽셀을 그림으로써 임의의 큰 텍스쳐 패치를 생성하는 절차를 의미합니다. 지난 몇 십년 동안, 합성된 텍스쳐의 품질은 2D 와 3D 영역에서 지속적으로 발전해 왔습니다. 이는 nonparametric neighborhood-based 기법인 [21], [22], [23] 에 대한 픽셀 기반 기법들을 발전시킴으로써 이루어졌습니다 [20]. [24], [25], [26], [27], [28], [29], [30], [31] 에서 예시된 가장 최근의 패치 기반 기술들은 두 개의 공통적인 단계를 가지고 있습니다: 1) 칸텍스트( context ) 영역과 가장 유사한 이웃들을 단순한 텍스쳐에서 검색합니다. 그리고 2) ( 부분적으로 ) 합성된 출력 텍스쳐에 패치나 픽셀을 결합( merge )합니다. Dynamic programming [24] 와 graph cuts [26] 가 패치 결합 단계를 최적화하기 위해 사용되었습니다. 우리는 지형 데이터의 유일한 특징을 다루는 검색-결합 전략을 사용합니다.

 

최근에 Zhang et al. [32] 는 임의의 서피스에서 다양한 텍스쳐를 점진적으로( progressively ) 합성하기 위해 특징 기반 왜곡( feature-based warping ) 기법과 블렌딩( blending ) 기법을 소개했습니다. 그들의 연구에서 특징 텍스톤 마스크( feature texton mask )는 수작업을 통해 추출됩니다. Wu 와 Yu [27] 는 패치 기반 텍스쳐 합성을 이끌어 내기 위해서 고수준 특징으로서의 입력 텍스쳐에서 추출된 엣지( edge )를 사용합니다. 우리는 이 앞선 연구들을 두 가지 방식으로 발전시킵니다. 첫 번째로 우리는 원하는 지형 특징을 사용자가 스케칭하는 것을 지원하기 위해서 곡선적 특징들을 사용하며 큰 지형 데이터 셋에서 패치를 매칭하기 위한 효율적인 검색 방식을 사용합니다. 두 번째로 다양한 범위의 스타일로 된 큰 DEM 으로부터 산등성이와 계곡을 신뢰성있게 추출할 수 있는 특징 분석 기술을 소개합니다. 우리는 표준 엣지 검색 기법들이 이 작업을 위해서는 부적절하다는 것에 대해서 설명할 것입니다.

 

 

Hertzmann et al [25] 는 이미지 유추( analogies ) 프레임웤을 소개했습니다. 이는 texture-by-numbers 접근법을 통해 지형 이미지를 합성하는데 사용될 수 있습니다. 이 연구는 새로운 지형 하이트 필드를 직접적으로 합성하지 않습니다. 하이트 필드 합성을 위한 그 애플리케이션은 지형 스타일을 특화하는 산등성이나 계곡과 같은 확장된 구조를 보존하는 지역적 이웃 매칭( local-neighborhood matching ) 접근법을 보장하기 어렵습니다. [30] 은 drag-and-drop features 와 synthesis magnification 을 포함하는 GPU 기반 텍스쳐 합성 기법을 제시했습니다. 이 기법들은 지형 하이트 맵 상에서 산을 재배치하는데 사용되었습니다. 이 연구와는 대조적으로 우리의 관심은 원하는 사용자 스케치를 사용해서 확장된 지형 특징을 매칭함으로써 특정 스타일로 온전한 지형 이미지를 자동적으로 생성하는 데 있습니다. 그러므로 우리는 자동 특징 추출 및 매칭을 완전하게 지원합니다.

 

우리의 지형 합성을 위한 예제 기반 접근법이 최근의 패치 기반 기법들의 성공에 영감을 받았기는 하지만, 지형 합성은 하이트 필드 상에서 단순히 텍스쳐 합성을 하는 것이 아닙니다. 지형 생성 문제는 세 가지 주요 방식에서 전통적인 텍스쳐 합성과는 구분될 수 있습니다. 첫째로 광범위한 지형 유형들이 ( 산등성이 및 계곡과 같은 ) 전역 특징들의 조합에 의해서 특화될 수 있는데, 이 특징들은 입력 지형 맵으로부터 신뢰성있게 추출될 수 있습니다. 반면에 범용 이미지 텍스쳐들을 위해서는 그러한 수비게 식별가능한 전역 특징이 존재하지 않습니다. 둘째로 다양한 범위의 애플리케이션에 있어 유용하기 위해서는 지형 합성은 반드시 전역적으로 제어되어야 합니다. 그리고 ( 보통 기가픽셀 이상인 ) 큰 지형 데이터 셋은 반드시 사용자의 제약을 만족시킬 수 있도록 검색되어야 합니다. 이는 작은 입력 텍스쳐 패치를 같은 지역적 강도( local intensity ) 구조를 가지고 있는 더 큰 출력 이미지로 "키우는" 기본적인 텍스쳐 합성 문제와는 대조되는 부분입니다. 셋째로 텍스쳐 합성을 위한 많은 최근의 cut-and-merge 기술들은 이미지 텍스쳐들이 많은 자연스러운 분계선들( boundaries )을 포함한다는 사실을 이용한다는 것입니다. 그 분계선들은 텍스쳐 엘리먼트들을 결합하는 곳을 따라서 좋은 경계( good seam )를 제공하게 됩니다. 반면에 지형 데이터는 자연스러운 경계가 존재하지 않습니다. 더우기 두 개의 인접한 패치들에서 하이트 필드 사이에 존재하는 불일치는 즉각적으로 눈에 띄게 될 것입니다. 높이 차를 제거하기 위해서 블러링( blurring )을 사용하는 것은 매우 잘 보이는 아티팩트를 산출하게 됩니다. 우리는, 최근의 graph-cut 기법과 Poisson seam removal 기법을 결합해서, 지형 패치를 결합( merge )하는 문제를 다루는 방법을 보여 줄 것입니다.

 

3. FEATURE EXTRACTION

 

이섹션에서 우리는 예제 하이트 필드와 사용자의 스케치 맵으로부터 지형 특징을 추출하기 위한 기법에 대해서 기술할 것입니다. 우리는 지형 특징 이라는 개념을 강, 계곡, 산등성이와 같은 대규모의 곡선적 특징들을 가리키기 위해 사용합니다. 그러한 지형 특징들은 지형의 전체적인 외형( layout )을 특화합니다. 그림 3a 는 예제 하이트 필드를 보여 주는데, 이는 Grand Canyon 의 일부를 포함하며 쉐이딩된 입체 지도( shaded relief map )으로 표시됩니다. 이 예제는 큰 규모의 곡선적 구조( 본류 강바닥 )과 풍부한 이차 구조( 협곡의 가파른 면 및 다른 특징들 )을 모두 보여 줍니다. 우리의 목적은 주요 구조를 원하는 스케치 모양과 매칭시키는 것인데, 이 때 이 데이터 내에 표현된 풍부한 디테일들이 보존되어야 합니다.

 

그림 3. 특징 추출 메서드의 비교. ( a )  쉐이딩된 입체 지도로 출력된 Grand Canyon 하이트 필드. ( b ) Canny 엣지 검출기에 의해 추출된 특징. ( c ) PPA 에 의해 검출된 특징.

 

지형 특징이 엣지 검출을 통해 식별될 수도 있을 것 같습니다. 그러나 더 정밀하게 검토해 보면 복잡한 지형 데이터에 엣지 검출을 적용하는 것에는 몇 가지 문제가 있습니다. 먼저 지형 특징들은 하이트 필드에 대한 지역적 극값들( local extrema )에 의해 특화되는 반면, 엣지 검출 기법은 이미지의 극대값( maximal derivatives )에 기반하고 있습니다( 역주 : [ Application of Derivatives ] 참조 ). 결과적으로 지형 데이터에 대한 엣지 검출 애플리케이션을 사용하면 그것은 지역 높이값의 다양함 때문에 그림 3b 에서 볼 수 있는 것처럼 겉으로 보기에는 그럴싸한 특징들을 산출합니다[각주:1]. 또한 지형 데이터는 특화된 분기 구조를 가지는데 이는 표준 엣지 검출 알고리즘에 의해 정확하게 다뤄질 수가 없습니다( 예를 들어, 그림 3b 의 오른쪽 아래 코너를 확인해 보십시오 ).

 

우리는 대규모 지형 특징들을 식별하기 위해서 지형학 문헌에 의존합니다. 특히 우리 작업을 위해 Chang et al. [1] 에 의해 개발된 PPA 를 받아 들였습니다. 우리는 이 섹션에서 그 기법에 대해 간단히 요약할 것이며 세부 사항에 대해서 알고자 하는 독자들은 그들의 논문을 읽어 보시기 바랍니다.

 

그림 4. Chang 의 PPA. ( a ) 외곽선 인식( profile recognition ). ( b ) 타깃 연결( target connection ). ( c ) 폴리곤 분열( polygon breaking ). ( d ) 분기 제거( branch reduction ).

 

 

PPA 는 산등성이나 계곡을 추출할 수 있습니다. 여기에서 우리는 산등성이 검색에 대해서만 다룰 것입니다. 이를 적절히 수정하면 계곡도 검색할 수 있습니다. 먼저 각 그리드 포인트( 하이트 필드의 픽셀 )를 방문해서 그것이 산등성이가 될 수 있는 것인지를 판단합니다. 여덟개의 바깥쪽으로 향하는 패스에서 그리드 포인트들이 검사됩니다( 그림 4a 에서 노란 점들 ). 그리고 이 패스들을 따라가다가, 현재 점의 높이에서 중앙 포인트의 높이를 뺐을 때 문턱값[각주:2]보다 더 아래로 떨어지면( 역주 : 더 큰 차이가 나면 ), 중앙 포인트를 후보로 마킹합니다. 이러한 모든 후보들은 다른 인접 후보들과 세그먼트로 연결됩니다( 그림 4b 의 붉은 원 ). 한 세그먼트가 다른 세그먼트를 가로지르면, 더 낮은 고도를 가진 세그먼트가 취소됩니다( 그림 4b 에서 점선으로 된 세그먼트들 ). 그리고 나서 닫힌 폴리곤에 대해서 가장 중요하지 않은 세그먼트들( 그림 4c 에 보이는 것처럼 가장 낮은 높이를 가지는 남은 세그먼트 )을 반복적으로 제거함으로써 폴리곤들이 모수석 라인( 역주 : 잔가지가 많은 나무 모양 라인 ) 패턴으로 분열됩니다. 이 처리는 더 이상 닫힌 폴리곤이 없을 때까지 계속됩니다. 폴리곤 분열 처리가 끝나면, 더 짧은 가지들이 제거됩니다( 그림 4d 에서 점선으로 된 세그먼트 ).

 

예제 지형에 대한 PPA 의 출력이 그림 3c 에 나와 있습니다( 역주 : 왜 ridge 를 검색한다고 하면서 valley 를 검색했는지... ). 계곡의 축들은 하이트 필드에서 중요한 곡선적 특징들을 인간이 인지하는 것과 일치한다는 데 주목하십시오. 우리 시스템은 PPA 를 사용해서 예제 하이트 필드와 사용자 스케치 맵의 특징들을 식별하게 됩니다. 스케치 맵의 단색값들은 고도 값으로 취급됩니다. PPA 의 출력은 산등성이나 계곡을 따라가는 긴 체인( chain )을 형성하는 연결된 라인 세그먼트의 집합입니다. 이 세그먼트 체인들은 사용자의 스케치와 주어진 하이트 필드의 영역 사이에서 특징 매칭을 위한 기저입니다. 세그먼트 체인이 식별되고 나면, 그것들은 두 개의 특징 유형을 형성하기 위해서 분석될 것입니다: 고립된( isolated ) 특징과 곡선적인 ( 패스 ) 특징. 고립된 특징 은 분기점이거나 끝점입니다. 곡선적 특징 은 고립된 특징들을 잇는 긴 세그먼트 체인입니다.

 

PPA 의 핵심 속성은 ( 일반적으로 숲인, 역주 : 숲은 연결이 끝어진 비순환 그래프를 의미 ) 트리를 형성하는 특징을 추출하는 것입니다. 왜냐하면 모든 닫힌 폴리곤들은 분석 절차에서 분열되기 때문입니다. 이 속성은 합성을 하는 동안 패치의 배치 순서를 결정하기 위해서 트리 순회를 사용할 수 있도록 해 줍니다. 이에 대해서 다음 섹션에 다룰 것입니다.

 

4. FEATURE-BASED PATCH MATCHING AND PLACEMENT

 

우리의 합성 프로세스는 예제 하이트 필드로부터 패치를 추출함으로써 새로운 하이트 필드를 만들고, 그것을 사용자 스케치에 의해 표현된 하이트 필드 출력에 배치합니다. 보통 패치 크기는 80 x 80 픽셀입니다( 이는 예제 지형 데이터의 스케일과 사용자가 원하는 결과의 디테일에 의해 결정됩니다 ). 패치 선택과 배치는 두 단계로 이루어집니다: feature patch matching 및 placment 와 nonfeature patch placement 입니다.

 

그림 5. 각 feature 유형에 대한 지형 패치들의 예. ( a ), ( b ), ( c ) 는 각각 분기점 패치, 끝점 패치, 경로 패치입니다. ( d ), ( e ), ( f ) 는 feature 추출 이후의 관련 패치들입니다. 경계 색상은 그림 6b 에서의 feature 유형을 의미합니다.

 

그림 6. 패치 배치 순서를 설명. ( a ) 예제 스케치 맵. ( b ) PPA 분석에 의해 반환된 트리 구조. 분기점 feature 와 끝점 feature 는 경로 feature 와 연결됩니다. ( c ) 루트 패치가 먼저 배치됩니다. ( d ) 너비 우선 탐색은 부가적인 패치들에 대한 배치를 돕습니다. ( e ) 트리 탐색이 완료되면, nonfeature 패치들을 배치하기 시작합니다. ( f ) 최종 결과입니다.

 

 

첫 번째 단계인 feature matching 및 placement 는 스케치 맵의 분기점( branch point )과 끝점( endpoint )인 isolated feature 와 curvilinear feature( path ) 인 nonisolated feature 를 모두 배치합니다. 그림 5 에서는 이러한 유형의 feature 들의 예를 보여 줍니다. 이러한 feature 들과 일치하는 패치를 찾고 이 패치들을 출력 하이트 필드에 배치합니다.

 

만약 우리가 PPA 로부터 추출한 isolated feature 들을 노드로 취급하고 curvilinear feature( path ) 들은 그래프의 edge 로 취급한다면, 이 그래프는 비순환( acyclic )임을 보장받을 수 있습니다. 우리 알고리즘은 너비 우선 검색 순서를 사용해 패치 영역들을 일치시키고 정렬시킵니다. 먼저 isolated feature( 만약 제공된다면 일반적으로 브랜치 )들을 루트로 선택합니다. 그리고 나서 그래프에서 한 번에 하나의 edge 를 탐색해 내려가는 데 모든 edge 들이 루트로부터 이어질 때까지 계속합니다. 이 프로세스는 그림 6c, 6d, 6e 에 설명되어 있습니다. 이는 전통적인 텍스쳐 합성 기법과는 좀 다르다는 것에 주의해야 합니다. 전통적인 기법들은 rigid patch placement order( 예를 들어 왼쪽에서 오른쪽, 위에서 아래 )를 따릅니다.

 

마지막으로, 하이트 필드에서 아직 채워지지 않은 모든 영역들은 어떤 명확한 feature 도 포함하고 있지 않은 입력으로부터의 영역을 사용해서 채워집니다. 최종 결과는 그림 6f 에 나와 있습니다. 두 가지 단계들은 모두 배치를 위해 몇 가지 후보 패치들을 고려해야 합니다. 그리고 사용하기 위해 선택된 패치는 적어도 사용자의 스케치와 어울리는 것이어야 하며 이미 배치된 패치들과 어울려야 합니다( 패치 배치는 출력 하이트 필드에 침착되어야 할( deposited ) 픽셀을 선택하기 위해서 graph-cut 알고리즘을 실행하며, 이에 대해서는 섹션 5.1 에서 설명할 것입니다 ). 섹션 4.1 과 4.2 에서는 매칭 및 배치 연산에 대해서 좀 더 세부적으로 설명할 것입니다.

 

4.1. Feature Patch Matching and Placment

 

위에서 설명했듯이 사용자 스케치의 isolated feature 들은 세그먼트 그래프에서 branch point 이거나 endpoint 입니다. 그림 6b 는 세 개의 분기점와 5 개의 끝점을 가집니다. 시스템은 사용자의 스케치 맵을 분석하고 그러한 모든 isolated feature 들을 식별합니다. 한 번에 하나씩 그러한 각각의 isolated feature 가 검사되며, 후보 매칭들의 리스트는 예제 하이트 필드의 isolated feature 들로부터 형성됩니다. 분기점과 끝점에 대해 순서대로 살펴 보도록 합시다.

 

4.1.1. Branch Points

 

분기점 매칭의 경우, 분기점들의 차수( degree, 혹은 밸런스( valence ) )가 일치해야만 하며 진출( outgoing ) 세그먼트 체인의 각도( angle )는 반드시 비슷해야 합니다. 각도 매칭의 품질을 결정하기 위해서는, d 개의 이용가능한 alignment( 노선? ) 만이 d 차수를 가진 분기점에 대해서 고려되어야만 합니다. 예를 들어 그림 5d 에서 보이는 것처럼 3 차수의 브랜치 패치는 3 개의 alignment 들인 ( 1, 2, 3 ) -> ( 1, 2, 3 ), ( 2, 3, 1 ), ( 3, 1, 2 ) 을 가집니다( 이 패치들의 미러 이미지는 새로운 패치로 취급되며 그것의 alignment 는 독립된 프로세스입니다 ). 일반적으로 주어진 예제 하이트 필드에는 그러한 분기점들이 많이 존재하지 않으므로, 모든 후보 매칭들에 대한 검사는 극단적으로 빠릅니다.

 

사용자 스케치와의 피팅( fit )을 위해 후보 패치들에 대한 왜곡( warping )을 수행하려면, 첫 번째 제어점 집합 {Pi} 가 그 패치들을 위해 식별되어야만 합니다. 이 제어점들은 분기점 자체의 위치로 구성되며, 부가적으로 각 진출 경로가 패치에 새겨진 원과 접촉( intersect )하는 위치( 그림 5d 참조 )가 추가됩니다. 예를 들어 세 개의 분기를 가지는 패치는 총 4 개의 제어점을 가지게 됩니다. 스케치 맵으로부터의 패치를 위해 그것과 관련한 제어점 집합 {P'i} 도 정의됩니다. 이제 우리는 제어점 {Pi} 를 {P'i}  에 매핑하는 연속적인 좌표 변환을 필요로 하며, 그것은 찌그러짐( distortion )을 최소화해 줄 것입니다. 우리는 이 목적을 달성하기 위해 thin-plate spline( TPS ) interpolation [33] 을 사용합니다. 왜냐하면 그것은 적은 제약을 사용하고 integral bending norm 에 의해 측정된 것처럼 왜곡을 최소화합니다. 우리는 두 개의 개별적인 TPS 함수를 사용해 좌표 변환을 형성하는데, 그 변환은 원본 하이트 필드의 모든 위치를 왜곡된( warped ) 하이트 필드의 보간된 위치로 매핑합니다. 가장 낮은 변형( deformation ) 에너지를 가진 최상의 k 분기점 패치들은 앞으로의 매칭을 위한 후보가 됩니다.

 

 

4.1.2. Endpoints

 

끝점 매칭은 직관적입니다. 왜냐하면 모든 끝점들은 비슷한 curvilinear feature 들을 가지기 때문입니다: 상대적으로 짧은 세그먼트 체인이 끝점에서 나옵니다. 이는 쉽게 정렬될 수 있습니다. 그러므로 이 경우에는 모든 끝점 패치들을 왜곡없이 앞으로의 매칭을 위한 후보로 선택합니다( 그림 5e ).

 

 

4.1.3. Path Features

 

그림 7. ( a ) 경로를 따라 고르게 샘플링된 점에서 저장된 경로에 대해 수직인 Height profile 들. 두 경로가 만나는 점 P 에서의 Height profile 은 P2 와 P3 에서 저장된 profile 과 선형적으로 보간됩니다. ( b ) P3 에서 저장된 5-point height profile 의 예.

 

우리의 모든 curvilinear feature 들은 ( PPA 로부터 나온 ) 라인 세그먼트 체인인데, 그것은 두 개의 isolated feature 들 사이를 연결합니다. 우리 시스템은 사용자 스케치의 라인 세그먼트 체임을 순회하는 동안에 curvilinear feature 들을 따라 일치하는 패치들을 검색합니다. 시스템은 이 체인들을 따라 패치의 1/2 크기만큼을 이동합니다. 그리고 각 이동 단계마다 패치를 내려 놓는 것이죠. 분기 패치와 마찬가지로 curvilinear feature 를 따라 가는데 있어서의 후보 패치들은 사용자 스케치와 일치하도록 변형됩니다. 후보 패치에는 항상 p0, p1, p2 라는 세 개의 제어점이 존재합니다( 그림 5f ). 그리고 왜곡을 결정하는 사용자 스케치에는 세 개의 관련 점들인 P'0, P'1, P'2 가 존재합니다. 외부 제어점인 p1 과 p2 는 패스가 패치에 새겨진 서클을 가로지는 곳에 위치합니다. 중간의 p0 은 패치 내의 체인의 중심점입니다. 이는 분기 패치의 중앙 제어점과 유사합니다.

 

모든 후보 패치들은 매칭 기준의 조합을 고려해서 순위가 매겨집니다. 가장 좋은 후보 패치가 출력 하이트 필드에 합성됩니다. 우리는 다음과 같은 비용 개념을 사용합니다:

 

    • Cd. 이는 TPS 왜곡( [33] 을 확인하세요 )으로부터의 변형 에너지를 의미합니다. 변형 에너지는 후보 패치 지형 구조가 스케치 맵의 제약과 얼마만큼이나 일치하는지를 측정합니다. TPS 왜곡은 ( 제어점이 한 라인상에 존재해서 폐기되는 경우를 제외하고는 ) 대부분의 경우에 패치를 원하는 구성으로 왜곡할 수 있습니다. 하지만 대규모 변형은 거슬리는 찌그러짐을 만들어낼 수 있으며, 여기에서는 패널티를 부여 받습니다.
    • Cg. 이는 graph-cut 점수( Appendix A 참조 )를 의미합니다. Graph-cut seam 비용은 후보 패치가 이미 머지된 패치들과 겹치는 영역에서 얼마나 어울리는지를 지시합니다( graph-cut 은 자동적으로 하나의 패치가 다중의 패치와 겹치는 경우를 제어하며 그 겹침이 해제될 수 있습니다 ). 높은 graph-cut 점수는 그 seam 이 더 심해서 Possion editing 에 의해서 제거되기 힘들다는 것을 의미합니다.
    • Cf. 이는 feature 차이점( dissimilarity ) 비용을 의미합니다. 차이점 비용은 만나는 경로들을 따라서 존재하는 feature 들이 얼마나 일치하는지를 측정합니다[각주:3]. 우리는 curvilinear 경로를 고르게 샘플링하고, 각 샘플 포인트에서 그 경로의 방향과 직각인 coarse height profile 을 저장합니다( 현재 우리 구현에서는 7-point profile 을 사용합니다 )( 그림 7 ). 그 경로가 P 에서 다른 경로와 만날 때, P 에서의 height profile 은 가장 가까운 이웃인 P2 와 P3 에서 저장된 profile 들과 선형적으로 보간됩니다. 경로가 만나는 곳에서의 height profile 은 sum of squared differences( SSD ) 를 사용해 비교됩니다.
    • Cis. 이는 사용자가 지정한 제약( 들 )을 의미합니다. height 제약이나 path smoothness 제약 등이 있습니다.

 

전체 비용은 전체 매칭 비용들의 선형 결합입니다.

 

.

 

α 는 가중치일 뿐만 아니라 그 비용을 위한 normalization 계수( coefficient )이기도 하다는 데 주의하시기 바랍니다. 우리의 현재 시스템에서 우리는 대부분의 테스트 케이스에 다음과 같은 값을 사용합니다.

 

 

 

이 계수를 변경하면 특정 매칭 기준이 강조되며, 다른 합성 결과를 산출하게 됩니다. Curvilinear feature 매칭과 너비우선 탐색 순으로의 배치를 마치고 나면, 출력 하이트 필드는 그림 6e 에서 보이는 것 중 하나와 비슷하게 보일 것입니다.

 

그림 8그림 10a 의 스케치 맵과 그림 10b 의 예제 하이트 맵을 사용하여 만들어진 래스터-스캔 순서 패치 배치와 트리-순서 패치 배치를 비교한 것을 보여 줍니다. λ 심볼의 내부 구조는 래스터 스캔 결과에서는 잘못 해석됩니다( 그림 8a ). 이는 행 기반 스캔이 서클의 경계를 즉각적으로 lock on 해서 앞으로의 매칭에 대한 제약을 생성하기 때문입니다. 그 제약은 내부의 좋은 매칭들을 방해하게 됩니다. 그림 8b 는 가장 어려운 요소( center branch point )가 가장 먼저 매칭될 때 λ 와 서클이 모두 복제( reproduce )될 수 있음을 보여 줍니다.

 

그림 8. ( a ) 래스터-스캔 패치 배치 순서를 사용해 만들어진 합성. ( b ) 트리 순회를 사용해서 만들어진 합성.

 

4.2. Nonfeature Patch Placment

 

Isolated feature 와 curvilinear feature 들이 배치되고 나면, 출력 하이트 필드의 빈 영역들은 특별한 feature 를 가지지 않는 공간들입니다. 이 영역을 채우는 실행가능한 방법은 이미 배치되어 있는 픽셀들과 어울리는 패치를 복사하는 것입니다. 이를 위해 우리 시스템은 이미 채워진 영역들로부터 "성장시켜" 나갑니다. 특히, coarsely spaced increments 내의 정방형 패치 위치들은( 예를 들어 수평적이나 수직으로 매 100 pixel ) 이미 합성된 하이트 필드와 겹쳐진 지역에 대한 영역과 관련해 내림차순으로 채워집니다. 시스템은 겹쳐진 지역의 SSD 에 기반해, 패치와 이미 합성된 하이트 필드 사이의 고품질 매칭을 검색하며, k 개의 후보들을 선택합니다. 그리고 나서 머징( merging )중인 곳의 SSD 점수와 graph-cut 비용의 결합( combination )에 따라 최적의 매칭을 찾습니다. 최적의 패치가 배치되고, 시스템은 겹친 지역을 찾기 위해 하이트필드를 계속해서 순회합니다. 출력 맵의 모든 피셀이 채워지면 이 과정이 끝나게 됩니다.

 

이미 합성된 출력 하이트 필드와의 최적의 매칭을 찾기 위한 SSD 기반 검색은 Fast Fourier Transformations( FFTs ) [ 34 ], [ 35 ] 에 의해 가속화될 수 있습니다. FFT 기반 접근을 사용하면, 매칭 비용은 O( n log( n ) ) 시간으로 계산될 수 있으며, 여기에서 n 은 소스 하이트 필드의 픽셀 개수입니다. 이는 SSD 구현이 O( n2 ) 의 시간이 걸린다는 점과 대조됩니다.

 

5. PATCH MERGING

 

그림 9. 패치 배치 및 Seam 제거에 대한 설명. ( a ) 패치 매칭은 graph-cut 을 통해 식별됩니다. ( b ) 배치 배치는 seam 을 산출합니다. ( c ) Possion seam remover 는 최종 결과를 산출합니다.

 

우리 시스템은 두 개의 기법을 사용해 출력 하이트 필드에 배치된 패치들 사이의 부드러운 전이( transition )를 보장합니다. 첫 번째 기법은 graph-cut 기술입니다 [ 26 ], [ 36 ]. 이것은 이미 배치된 픽셀들과 배치되고 있는 지역으로부터의 픽셀들 사이에서 good seam 들을 검색합니다. 두 번째 기법은 현존하는 pixel 과 새롭게 배치되고 있는 패치의 픽셀 사이에서 더욱 완만한( gentle ) 전이를 생성하기 위해서 Poisson equation [ 37 ] 을 사용합니다.

 

5.1. Graph-Cut Optimal Seam Finder

 

Graph-cut 알고리즘은 패치들 간의 겹친 지역에서 seam 을 찾아내는데, 그것은 어떤 픽셀이 최종 이미지에서 유지되어야 하는지를 결정하게 됩니다. 이는 max-flow/min-cut 그래프 문제를 해결함으로써 수행됩니다. 이는 cut 을 가로지르는 mismatched elevation 에 대한 비용을 최소화합니다. 그래프에서 edge 는 인접 픽셀들 간의 연결을 표현하며, 그것들은 높이차에 기반한 가중치를 부여받습니다. 일부 패치의 경우에, 우리는 주어진 패치로부터 branch feature 패치의 중앙 픽셀들과 같은 특정 그룹의 픽셀들이 포함되기를 원할 것입니다. 이는 특정 edge 의 가중치를 무한대로 높게 설정함으로써 수행될 수 있습니다.

 

앞에서 언급했듯이 graph-cut 알고리즘으로부터 최종 seam 의 비용이 시스템에 의해 사용됩니다. 시스템은 주어진 feature 를 표현하기 위해 여러 후보 패치 중 하나를 선택하기 위해 이를 사용합니다. Seam 을 검색하기 위해서 graph-cut 알고리즘을 사용하는 방법에 대한 세부사항은 [ 26 ] 에서 찾아 볼 수 있습니다.

 

5.2. Poisson Seam Remover

 

시스템이 graph-cut 을 사용해 출력 맵에 새로운 패치들을 배치하고 나면, seam 을 따라 불연속성이 가시화될 것입니다( 그림 9b ). 우리는 이를 개선하기 위해서 seam 들에서 gradient value 들을 인위적으로( artificially ) 설정합니다. 우리는 Poisson equation 을 사용함으로써 이를 수행하는데, 그것은 Perez et al. 이 색상 이미지에서 seam 을 가로질러 pixel 을 매칭시키는 방식 [ 37 ] 과 유사합니다.

 

우리의 고도( elevation ) 조정 단계는 겹친 지역( 일반적으로 패치 크기의 1/3 )에서의 먼저 elevation value 를 gradient value 로 변환합니다. 그리고 나서 seam 을 가로지르는 gradient value 들은 인위적으로 0 으로 설정됩니다. 마지막으로 Poisson equation 을 사용해 이 인접 gradient value 들에 대한 최적의 elevation 집합을 찾습니다. 이 작업의 결과는 새로운 elevation 집합인데, 이는 seam 을 더욱 부드럽게 만들어 줍니다( 그림 9c ). 우리 시스템은 그러한 elevation 조정을 지역적으로 수행합니다. 이는 배치된 패치 당 한 번만 그 메서드가 호출된다는 의미입니다. Elevation 조정을 지역적으로 수행하면, 엄청난 양의 행렬 연산을 수행해야 하는 상황을 피할 수 있습니다. 더 세부적인 내용은 Appdendix B 에서 확인하시기 바랍니다.

 

6. TERRAIN SYNTHESIS RESULTS

 

우리는 그림 1, 10, 11, 12, 13, 15 에서 보이는 결과를 산출하기 위해 US Geological Survey terrain data 로부터 만든 DEM 을 사용했습니다. 데이터의 원점, 해상도, 크기들이 표 1 에 기술되어 있습니다.

 

우리의 예제 하이트 필드는 1,200 x 1,200 에서 4,097 x 4,097 의 범위를 가지고 있으며, 16 비트 하이트 해상도를 가집니다. 이렇게 큰 지형 맵들은 전통적인 텍스쳐 합성 접근법에서는 심각한 검색 문제를 발생시킵니다. 80 x 80 크기를 가지는 패치를 사용하면, 4,000 x 4,000 크기의 맵의 경우 약 16 million( 1600 만 ) 개의 가능한 매칭들이 존재하게 됩니다. 그러나 우리의 feature 기반 분석의 결과로서( 역주 : 앞에서 이야기한 PPA 를 의미하는 듯 ), 우리는 채워져야 할 후보 위치에 대한 매칭을 600 개 수준으로 필터링할 수 있었습니다. 이는 계산 비용을 엄청나게 줄여 줬습니다. 우리의 기법은 대략 5 분에서 6 분 정도의 시간을 소비해 대표적인( ? ) 지형을 합성할 수 있었습니다. Intel Pentinum 4 2.0 GHz 프로세서와 2 Gbyte 메모리를 사용했습니다. 모든 결과들은 Planetside 사의 Terragen 지형 렌더링 시스템을 사용해서 렌더링되었습니다. 지형 지오메트리에 매핑될 텍스쳐로는 ( 높이와 기울기에 의해 결정된 ) 절차적 텍스쳐를 사용했습니다.

 

그림 10 그림 11 은 우리의 지형 합성 접근법을 사용해서 같은 스케치 맵으로부터 다양한 출력을 뽑아 내는 것을 보여 줍니다. 그림 10d 는 Mount Jackson 의 지형과 그림 10a 에서 보여준 스케치 맵을 사용해서 만든 ( 1,000 x 1,000 ) 합성 결과를 보여 줍니다. 이 결과가 많은 패치들로부터 생성되기는 했지만, 내부의 λ 와 외부의 서클이 끊기지 않는 산등성이를 형성하고 있습니다. 또한 커튼같이 접히는 특징들이 산의 측면을 따라 잘 보이고 있습니다. 그림 10h 는 Grand Canyon 지형을 사용한 ( 2,000 x 2,000 ) 합성 결과를 보여 줍니다. 이 예제를 위해 그림 10e 의 스케치 맵을 사용했는데, 그것은 그림 10a 의 역( inverse )입니다.  그래서 산등성이 대신에 계곡이 선택됩니다. 여기에서 λ 와 서클은 Colorado River 와 만나는 조각( piece )으로부터 형성됩니다. 그런데 아직 seam 위치는 검출되지 않았습니다. 사용자 스케치가 영향을 주기는 하지만, 이 지형은 원본 데이터의 풍부한 침식 형태( water-carved feature )를 보존합니다. 그림 11d 는 Mount Vernon 지형에 대한 ( 1,000 x 1,000 ) 합성 결과를 보여 줍니다. 그림 11h 는 Flathead National Forest mountain 지역을 사용한 ( 2,000 x 2,0000 ) 합성 결과를 보여 줍니다. 그럼 12d 는 Mount Vernon 지형과 중국어로 "물" 이라고 그려진 사용자 스케치를 사용한 결과를 보여 줍니다. 이 결과들은 우리 시스템이 매우 다양한 유형의 지형과 매우 다양한 특성들을 처리하면서도 입력 스케치 맵의 핵심 feature 를 유지하는 능력을 가졌음을 보여 줍니다. 그림 15 는 Grand Canyon 과 Flathead range 의 합성 결과에 대한 클로즈업( close-up ) 뷰를 제공합니다. 그림 13a 는 Crand Canyon 높이 맵을 렌더링한 것을 보여 줍니다. 우리는 스케치 맵을 획득하기 위해 이 맵으로부터 PPA 를 사용해 협곡 feature 를 추출했습니다. Puget Sound DEM 스타일로 스케치를 합성함으로써, 우리는 그림 13b 에서 보여 주듯이 Grand Canyon 에 대한 ( 4,000 x 2,000 ) "산맥" 버전을 획득했습니다.

 

표 1.

 

 

그림 10. 하프라이프( half-life ) 심볼을 사용한 다중 지형 합성 결과. ( a ) 사용자 스케치. ( b ) Mount Jackson. ( c ) 합성 결과. ( d ) Colorado 의 Mound Jackson 의 elevation map 으로부터 합성된 지형을 렌더링함. ( e ) 사용자 스케치. ( f ) Grand Canyon. ( g ) 합성 결과. ( h ) Grand Canyon 의 elevation map 으로부터 합성된 지형을 렌더링함.

 

그림 11. 하프라이프( half-life ) 심볼을 사용한 다중 지형 합성 결과. ( a ) 사용자 스케치. ( b ) Mount Vernon. ( c ) 합성 결과. ( d ) Kentucky 의 Mound Vernon 의 elevation map 으로부터 합성된 지형을 렌더링함. ( e ) 사용자 스케치. ( f ) Flathead 산맥. ( g ) 합성 결과. ( h ) Montana 의 Flathead National Forest 산맥( mountain range )의 elevation map 으로부터 합성된 지형을 렌더링함

 

그림 12. 중국어로 "물" 이라 쓰여진 스케치를 사용한 지형 합성. ( a ) 사용자 스케치. ( b ) Mount Vernon. ( c ) 합성 결과. ( d ) Kentucky 의 Mound Vernon 의 elevation map 으로부터 합성된 지형을 렌더링함.

 

그림 13. Grand Canyon 을 산맥으로 바꿈. Grand Canyon DEM 으로부터 추출된 feature 를 사용자 스케치로서 사용하는데, 이를 Puget-Sound 스타일 산맥의 elevation map 으로부터 Grand Canyon 의 구조를 따르는 산맥을 합성하기 위해서 사용함.

 

6.1. Synthesis with Multiple Terrain Style

 

 

그림 14. 다중 elevation map 들로부터 합성된 중간계의 3D 맵. ( a ) 중간계 맵. ( b ) 수계( water system )가 마스킹된 사용자 스케치. ( c ) 합성 결과. ( d ) 다중 elevation map 들로부터 합성된 지형을 렌더링.

 

표 2.

 

 

 

그림 14 에서는 더 큰 예제를 보여 줍니다. 그것은 여러 종류의 지형 스타일을 사용하는 중간계( Middle Earth )에 대한 3D 맵을 생성하는 것을 보여 줍니다. 단순한 스케치맵( 그림 14b )은 사용자에 의해서 생성되었는데, 중간계( 그림 14a )에 대해 아티스트가 렌더링한 맵으로부터 추출된 것입니다. 사용자는 다양한 지형 스타일을 스케치 맵의 각 부분에 설정할 수 있습니다( 표 2 ). 이 예제에서 사용자는 먼저 Mordor 의 Mount Doom 의 위치에 Mout St. Helen 을 배치했습니다( 지역적 특징 ). 다음으로 우리 시스템은 나머지 지형( 3,470 x 2,996 )을 자동으로 합성했고 그것을 Mount Doom 과 연속적으로 머지했습니다. 사용자 스케치는 다양한 산맥들과 하곡( river valley )들을 포함한다는데 주목하십시오( Mordor 의 북서쪽 코너 근처의 죽음의 늪( Dead Marshes ). 해안( coast ), 호수( lake ), 강( river ) 등은 단순한 마스크를 사용해 생성됩니다. 그것은 원본 맵으로부터 추출되었습니다. 합성된 하이트 맵( 그림 14c )은 Planetside 의 Terragen 지형 렌더링 시스템을 사용해 렌더링되었으며, 아트적인 느낌을 주기 위해 절차적 텍스쳐와 마스킹된 텍스쳐들이 사용되었습니다. 이 예제는 우리 시스템이 다중의 지형 스타일들을 하나의 합성 지형으로 결합하고 그것들을 seam 없이 블렌딩할 수 있는 능력을 가지고 있음을 보여 줍니다.

 

http://www.computer.org/tvcg/archves.html 에서 ( DivX 로 인코딩된 ) 동영상을 보시기 바랍니다. http://www.cc.gatech.edu/howardz/terrain 에 가면 추가적인 결과들을 확인하실 수 있습니다.

 

7. CONCLUSION

 

우리는 예제 기반 텍스쳐 합성 기법이 지형 합성의 영역에서 성공적으로 수용될 수 있음을 입증했습니다. 그 결과는 사용자가 제어 가능한 합성된 지형에서의 새로운 수준의 가시적 현실성입니다. 우리의 접근법은 geomorphology community 로부터 가지고 온 분석 기법을 사용해 하이트필드로부터 유용한 지형 특성을 추출할 수 있다는 사실에 영향을 받았습니다.

 

우리는 트리 순서에 기반한( tree-ordered ) 패치 배치 알고리즘을 소개했는데, 그것은 feature 트리에 대한 너비 우선 탐색에 기반합니다. 우리의 결과는 이 배치 기법이 표준 래스터-스캔 배치 순서보다 우월함을 입증합니다.

 

우리는 다양한 스타일의 지형을 합성하면서도 주요 지형 특징의 위치들에 대한 제어를 유지할 수 있다는 것을 증명했습니다. 우리 시스템은 원하는 지형 특성을 지정하기 위해 직관적인 스케치 기반 인터페이스에 기반합니다. 우리는 우리 기법이 지형 합성에 있어서 예제 기반 접근법을 온전하게 사용하는 첫 번째 시스템이라고 확신합니다.

 

우리 기법은 예제 기반 기법들과 동일한 제약들을 가지고 있습니다. 특히 최종 합성의 품질은 이용가능한 지형 데이터의 풍성함( richness )에 의존합니다. 만약 사용자가 원하는 지형 특성을 예제 하이트필드에서 찾지 못했다면, 그것은 원하는 결과를 산출하지 못할 것입니다. 예를 들어, 중요한 curvilinear feature 들이 거의 없는 사막으로부터 지형 데이터를 가지고 왔다면, 우리 기법은 형편없게 동작할 것입니다. 또한 curvilinear feature pattern 이 극단적으로 복잡해도 형편없게 동작할 것입니다. 추가적인 이슈는 패치 크기를 지정할 필요가 있다는 것입니다. 그것은 예제 지형의 해상도 및 스케일에 의존합니다.

 

우리는 우리의 현재 기법을 몇 가지 방향으로 확장할 계획을 가지고 있습니다. 먼저 스케치 맵에 부가적인 제약들을 도입함으로써 합성된 지형 전반에 대해 사용자가 더 많은 제어를 할 수 있도록 하는 데 관심을 가지고 있습니다. 예를 들어 우리는 특정 위치에서 원하는 elevation 을 지정할 수 있는 기능을 제공할 계획을 가지고 있습니다. 이는 matcher 와 함께 Poisson solver 를 제약함으로써 수행될 수 있습니다. 또한 우리는 현재 C0 연속성을 적용( enforcement )하고 있는데 이를 C1 연속성을 적용하는 연구를 하고 있습니다. 우리는 렌더링을 위해 elevation 과 texture map 의 joint synthesis 를 연구하고 있으며, 지형 합성에 대한 상호적인 제어( interactive control )에 대해서 연구하고 있습니다.

 

그림 15. Grand Canyon( 위쪽 )과 Flathead National Foreset 산맥( 아래쪽 )으로부터 합성된 지형에 대한 클로즈업 뷰.

 

APPENDIX A

GRAPH_CUT SEAM FINDING

 

생략. 원문 참조.

 

APPENDIX B

POSSION SEAM REMOVAL

 

생략. 원문 참조.

 

ACKNOWLEDGEMENTS

 

생략. 원문 참조.

 

REFERENCES

 

[1] Y.-C. Chang, G.-S. Song, and S.-K. Hsu, “Automatic Extraction of Ridge and Valley Axes Using the Profile Recognition and Polygon-Breaking Algorithm,” Computer and Geosciences, vol. 24, no. 1, pp. 83-93, 1998.
[2] B.B. Mandelbrot, The Fractal Geometry of Nature. W.H. Freeman,1982.
[3] A. Fournier, D. Fussel, and L. Carpenter, “Computer Rendering of Stochastic Models,” Comm. ACM, vol. 25, no. 6, pp. 371-384, 1982.
[4] R.F. Voss, “Random Fractal Forgeries,” Fundamental Algorithms for Computer Graphics, R.A. Earnshaw, ed., 1985.
[5] G.S.P. Miller, “The Definition and Rendering of Terrain Maps,”Proc. 13th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’86), vol. 20, no. 4, pp. 39-48, 1986.
[6] J.P. Lewis, “Generalized Stochastic Subdivision,” ACM Trans.Graphics, vol. 6, no. 3, pp. 167-190, 1987.
[7] R. Szeliski and D. Terzopoulos, “From Splines to Fractals,” Proc. 16th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’89), pp. 51-60, 1989.
[8] D.S. Ebert, F.K. Musgrave, D. Peachy, K. Perlin, and S. Worley, Texturing and Modeling: A Procedural Approach. Morgan Kaufmann, 2002.
[9] C. Dachsbacher, Interactive Terrain Rendering: Towards Realism with Procedural Models and Graphics Hardware, http://www.opus.ub.uni-erlangen.de/opus/volltexte/2006/354/, 2006.
[10] A.D. Kelley, M.C. Malin, and G.M. Nielson, “Terrain Simulation Using a Model of Stream Erosion,” Proc. 15th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’88), pp. 263-268,1988.
[11] F.K. Musgrave, C.E. Kolb, and R.S. Mace, “The Synthesis and Rendering of Eroded Fractal Terrains,” Proc. 16th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’89), vol. 23, no. 3, pp. 41-50, 1989.
[12] P. Roudier and B.P.M. Perrin, “Landscapes Synthesis Achieved through Erosion and Deposition Process Simulation,” Computer Graphics Forum, vol. 12, no. 3, p. 375, Aug. 1993.
[13] N. Chiba, K. Muraoka, and K. Fujita, “An Erosion Model Based on Velocity Fields for the Visual Simulation of Mountain Scenery,”J. Visualization and Computer Animation, vol. 9, no. 4, pp. 185-194, 1998.
[14] K. Nagashima, “Computer Generation of Eroded Valley and Mountain Terrains,” The Visual Computer, vol. 13, nos. 9-10, pp. 456-464, 1997.
[15] B. Benes and R. Forsbach, “Layered Data Representation for Visual Simulation of Terrain Erosion,” Proc. 17th Spring Conf. Computer Graphics (SCCG ’01), p. 80, 2001.
[16] B. Neidhold, M. Wacker, and O. Deussen, “Interactive Physically Based Fluid and Erosion Simulation,” Proc. Eurographics Workshop Natural Phenomena, 2005.
[17] J. Brosz, F.F. Samavati, and M.C. Sousa, “Terrain Synthesis By-Example,” Proc. First Int’l Conf. Computer Graphics Theory and Applications (GRAPP ’06), 2006.
[18] J.-P. Lewis, “Texture Synthesis for Digital Painting,” Proc. 11th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’84), pp. 245-252, 1984.
[19] K. Perlin and L. Velho, “Live Paint: Painting with Procedural Multiscale Textures,” Proc. 22nd Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’95), pp. 153-160, 1995.
[20] D.J. Heeger and J.R. Bergen, “Pyramid-Based Texture Analysis/Synthesis,” Proc. 22nd Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’95), pp. 229-238, 1995.
[21] A.A. Efros and T.K. Leung, “Texture Synthesis by Non-Parametric Sampling,” Proc. Int’l Conf. Computer Vision, pp. 1033-1038, 1999.
[22] L.-Y. Wei and M. Levoy, “Fast Texture Synthesis Using Tree-Structured Vector Quantization,” Proc. 27th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’00), pp. 479-488, 2000.
[23] M. Ashikhmin, “Synthesizing Natural Textures,” Proc. Symp. Interactive 3D Graphics, pp. 217-226, 2001.
[24] A.A. Efros and W.T. Freeman, “Image Quilting for Texture Synthesis and Transfer,” Proc. 28th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’01), pp. 341-346, 2001.
[25] A. Hertzmann, C.E. Jacobs, N. Oliver, B. Curless, and D.H. Salesin, “Image Analogies,” Proc. 28th Ann. Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’01), pp. 327-340, 2001.
[26] V. Kwatra, A. Scho¨ dl, I. Essa, G. Turk, and A. Bobick, “Graphcut Textures: Image and Video Synthesis Using Graph Cuts,” ACM Trans. Graphics, Proc. 30th Int’l Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’03), vol. 22, no. 3, pp. 277-286, 2003.
[27] Q. Wu and Y. Yu, “Feature Matching and Deformation for Texture Synthesis,” ACM Trans. Graphics, Proc. 31st Int’l Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’03), vol. 23, no. 3, pp. 364-367, 2004.
[28] P. Bhat, S. Ingram, and G. Turk, “Geometric Texture Synthesis,”Proc. Eurographics Symp. Geometry Processing, 2004.
[29] A. Lagae, O. Dumont, and P. Dutre´, “Geometry Synthesis by Example,” Shape Modeling Int’l, 2005.
[30] S. Lefebvre and H. Hoppe, “Parallel Controllable Texture Synthesis,” ACM Trans. Graphics, Proc. 32nd Int’l Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’05), pp. 777-786, Aug. 2005.

[31] S. Lefebvre and H. Hoppe, “Appearance-Space Texture Synthesis,”ACM Trans. Graphics, Proc. 33rd Int’l Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’06), vol. 25, no. 3, pp. 541-548, 2006.
[32] J. Zhang, K. Zhou, L. Velho, B. Guo, and H.-Y. Shum, “Synthesis of Progressively-Variant Textures on Arbitrary Surfaces,” ACM Trans. Graphics, vol. 22, no. 3, pp. 295-302, 2003.
[33] F.L. Bookstein, “Principal Warps: Thin-Plate Splines and the Decomposition of Deformations,” IEEE Trans. Pattern Analysis and Machine Intelligence, vol. 11, no. 6, pp. 567-585, 1989.
[34] C. Soler, M.-P. Cani, and A. Angelidis, “Hierarchical Pattern Mapping,” ACM Trans. Graphics, vol. 21, no. 3, pp. 673-680, July 2002.
[35] S.L. Kilthau, M.S. Drew, and T. Mo¨ ller, “Full Search Content Independent Block Matching Based on the Fast Fourier Transform,”Proc. IEEE Int’l Conf. Image Processing (ICIP ’02), pp. 669-672, 2002.
[36] Y. Boykov, O. Veksler, and R. Zabih, “Fast Approximate Energy Minimization via Graph Cuts,” IEEE Trans. Pattern Analysis and Machine Intelligence, vol. 23, no. 11, pp. 1222-1239, Nov. 2001.
[37] P. Pe´rez, M. Gangnet, and A. Blake, “Poisson Image Editing,”ACM Trans. Graphics, Proc. 30th Int’l Conf. Computer Graphics and Interactive Techniques (SIGGRAPH ’03), vol. 22, no. 3, pp. 313-318, 2003.

 

저자 소개 생략. 원문 참조.

  1. 다양한 스케일을 사용해서 엣지를 검출해도 이 문제를 쉽게 해결할 수 없습니다. 왜냐하면 인지적으로 볼 때 중요한 지형 구조들이 스케일을 변경했을 때 유지된다는 보장을 할 수 없기 때문입니다. [본문으로]
  2. 우리는 일반적으로 이 문턱값을 입력 하이트 필드의 높이 범위의 절반 정도로 잡습니다. [본문으로]
  3. Graph-cut 비용은 겹침( overlaping )에 대한 전체적 품질을 측정하지만, 그것은 겹치는 영역에 존재하는 모든 픽셀의 가중치를 동일하게 설정합니다. Feature 차이점 측정을 graph-cut 알고리즘에 통합하기는 힘듭니다; 그러므로 feature 를 강조하기 위해 개별적인 feature 차이점 비용 개념이 우리 시스템에 구현되었습니다. [본문으로]

주의 : 공부하면서 정리한 것이라 잘못된 내용이 있을 수 있습니다.

주의 : BSP 알고리즘에 대해 구체적으로 설명을 하지 않습니다. 어느 정도 개념을 알고 있다고 가정합니다.



UE4 Brush, BSP, PhysX Convex 에 대해


BSP( Binary Space Partitioning ) 는 둠라는 게임에서 사용된 이후로 매우 유명해졌습니다. 말 그대로 씬을 두 개의 노드로 분할한다는 의미로 이와 관련된 자료는 인터넷에 많이 있으니 검색해 보시기 바랍니다. 이 문서에서는 UE4 에서 어떤 방식으로 BSP 를 이용하는지에 대해서 중점적으로 다룹니다.


기존의 1인칭 슈팅 게임에서는 한정된 자원으로 게임을 만들어야 했기 때문에 메쉬의 사용을 최소화하고 빠르게 위치 및 충돌을 검출하기 위해서 BSP 를 많이 사용했습니다. 특히 얼리얼에서는 CSG( Constructive Solid Geometry ) 라는 개념을 통해 씬을 쉽게 구성할 수 있는 도구를 제공했습니다.


하지만 이제는 예전과는 다르게 1인칭 슈팅 게임들도 복잡한 지오메트리를 사용하게 되고 그에 걸맞게 하드웨어 성능과 공간분할 알고리즘들이 발전하게 되면서 BSP 를 사용하지 않는 추세입니다. 그러나 언리얼에서 여전히 BSP 를 중요하게 사용하는 부분이 있습니다. 바로 Geometry 라 표현되는 개체입니다.


게임을 만들어 보신 분이라면 한 번쯤은 "Trigger Volume" 이라는 액터를 배치해 보신적이 있을 것입니다. 이 트리거 볼륨 액터는 사용자가 어떤 영역에 들어 오고 나가는 것을 알려줍니다.


그림1


하지만 저 계단 위쪽에 들어 왔는지 나갔는지를 판단하려면 매우 복잡한 모양의 트리거 볼륨이 필요합니다. 왼쪽에 있는 "Modes" 툴바의 "Geometry Editing" 항목을 선택하면 이것을 복잡하게 편집할 수 있습니다. "Extrude" 를 기능을 사용해서 멋지게 편집해 봤습니다. 절대 여러 개의 볼륨을 사용한 것이 아닙니다. 하나만으로 만들었습니다.



그림2


이 경우에도 저 볼륨의 영역에 캐릭터가 들어갔다 나갔다 하면 앞에서와 같은 이벤트가 발생하게 됩니다. 


예전에는 BSP 노드에 대한 충돌검사를 수행했습니다. 하지만 UE4 에서는 PhysicsX 를 도입하면서 BSP 노드를 convex 집합으로 바꾸게 됩니다. 이것을 언리얼에서는 "Aggregate Geometry" 라 부르게 됩니다. 



언리얼에서 Volume 은 AVolume 이라는 개체로 표현되며, 이는 ABrush 를 상속합니다.



그림 3


    • ABrush 는 기존의 UE3 의 CSG 브러쉬를 대체합니다. "Placement Mode" 에서 "Geometry" 카테고리에 포함됩니다. 여러 개의 액터가 존재하는데 이것은 브러쉬 빌더의 타입을 보여 줍니다. FBspModeModules::StartupModule() 메서드에서는 "Geometry" 카테고리에 이 빌더들을 등록하고 있습니다. 관심이 있으신 분들은 찾아 보시기 바랍니다.
    • AVolume 은 ABrush 중에서 볼륨 개체를 추상화하기 위해서 사용됩니다. 기본적으로는 ABrush 의 기능과 동일합니다.
    • ABrushShape 은 geometry editing mode 에서 "Lathe" 를 위해 사용된다고 하는데 뭔지 잘 모르겠습니다.


그림 4


어쨌든 ABrush 가 여기에서 다루고자 하는 핵심입니다. ABrush 에서는 폴리곤 데이터를 UModel 개체에 물리 및 렌더링 데이터를 UBrushComponent 를 개체에 저장합니다.


이 문서에서 이야기하고자 하는 핵심 클래스들은 다음과 같습니다. 물론 훨씬 더 많은 자료구조를 포함하고 있지만 주제와 밀접한 클래스들만 정리했습니다.


그림 5


사설이 길었는데요 이제 조금 구체적으로 파 보도록 하겠습니다.


Brush : Polygons & BSP Nodes



언리얼에서는 브러쉬는 어떤 메쉬를 생성하기 위한 틀을 의미합니다. 액터로서는 ABrush 로 표현되지만, 실제 폴리곤( 혹은 지오메트리 ) 데이터는 UModel 개체에 포함되어 있습니다. UModel* ABrush::Brush 라고 정의되어 있습니다.


우리가 에디터에서 geometry editing mode 를 켜 놓고 편집을 하게 되면 실제로는 UModel 개체를 수정하게 되는 것입니다. 이것은 브러쉬의 지오메트리를 표현하기 위한 많은 정보들을 포함합니다. 그 중의 핵심이라고 할 수 있는 것은 폴리곤 데이터( FPoly )와 BSP 노드 데이터( FBspNode )입니다.


폴리곤 데이터는 하나의 면( FPlane )을 표현합니다. 물론 범위가 정해져 있으니 점 데이터도 포함하게 됩니다. 예를 들어 큐브는 6 개의 폴리곤과 24 개( 각 노드당 4 개 )의 점으로 구성됩니다. 점을 공유하고 있지는 않더군요. 그런데 점을 하나 수직으로 움직이게 되면 면 하나가 두 개의 삼각형으로 전환됩니다. 이 경우에는 7 개의 폴리곤과 26 개의 점으로 구성됩니다; 5 * 4 + 3 * 2 = 26.


그림 6


여기서 퀴즈입니다. 그림 2 의 폴리곤은 몇 개일까요? 볼륨을 둘러 싸는 가장 바깥쪽 면 개수만 세야 합니다. 답은 댓글에...


편집을 통해 폴리곤 정보가 변경되면 FBSPOps::bspBuild() 라는 메서드가 호출됩니다. 


UE4 에서 BSP 를 구성하는 데 있어 핵심이 되는 파라미터는 "Balance" 입니다. 이는 균형잡힌 이진트리를 만드느냐 아니면 가장 분할( 다른 폴리곤을 자르는 것 )이 적은 이진트리를 만드냐를 결정하게 됩니다. 그것의 pseudo code 는 다음과 같습니다.



여기에서 관계라는 것은 front, coplanar, back, split 입니다. 각각의 카운트를 세는거죠. 그리고 점수는 다음과 같이 계산됩니다.



이 Balance 값은 현재 15 로 하드코딩되어 있습니다( 수정 : 코드 검색하다가 보니 0, 15, 70 인 경우가 있었습니다 ). 예전에는 수치를 받았던거 같은데 UE4 로 오면서 경험적으로 좋은 수치를 설정한 것 같습니다. 


내용을 곱씹어 보면 다른 폴리곤을 많이 쪼갤수록 그리고 Front 와 Back 의 개수 차이가 클수록 점수가 높아집니다. 그런데 Lerp( linear interpolation ) 처럼 Balance 값을 통해 가중치를 조절합니다.


만약 Balance 가 크다면 어떻게 될까요? 100.0 - float(Balance) 의 값이 작아지므로 Front 와 Back 의 차이가 클수록 좋은 점수를 받겠죠. 반대로 Balance 가 작다면 어떻게 될까요? 100 - float(Balance) 의 값이 커지므로 쪼개는 개수가 많아질수록 좋은 점수를 받겠죠. 헷갈리니 예를 들어 보도록 하겠습니다.


그림 7 은 3 이 splitter 로 선택되었을 때의 상황을 가정하고 그린 것입니다. 2 를 2f 와 2b 로 나누지 않은 이유에 대해서는 나중에 알게 될 것입니다. 그림 7 에서 각 폴리곤들의 노멀은 바깥쪽이라고 가정하시고 보시면 됩니다.


그림 7


 Balance

 1 ( f : 0, b : 4, s : 0 )

 2 ( f : 0, b : 4, s : 0 )

 3 ( f : 1, b : 2, s : 1 )

 4 ( f : 1, b : 2, s : 1 )

 5 ( f : 0, b : 4, s : 0 )

 Best Splitter Order

 15

 60

 60

 90

 90

 60

 1, 2, 5, 3, 4

 75

 300

 300

 90

 90

 300

 3, 4, 1, 2, 5


Balance 가 75 인 경우에는 3 이 splitter 로 사용됩니다. 3 이 가장 먼저 splitter 로 사용되며 5f, 5b 가 생성됩니다. 여기에서 의문이 발생할 것입니다. 왜 2 는 쪼개지지 않을까요. 4 가 splitter 로 사용되는 시점에는 3 을 기준으로 front-end 로 분할된 상황이기 때문에 2 는 4 가 쪼갤 수 있는 대상이 아닙니다. 4 는 front 에 있고 2 는 back 에 있습니다. 그러므로 분할은 3 을 splitter 로 사용했을 때 단 한 번만 발생합니다. 최종적으로 리스트에는 1, 2, 3, 4, 5f, 5b 가 존재하게 됩니다.


결국 Balance 가 15 인 경우와 75 인 경우에는 다음과 같은 이진 트리가 나오게 됩니다.




그림 8


그림 8 에서 보면 leaf 노드에 색이 칠해져 있는 것을 볼 수 있습니다. leaf 노드에 도달했다는 것은 면으로 이루어진 도형이 닫힌거라 생각하시면 됩니다. 


예를 들어 오른쪽 그림의 경우를 봅시다. 평면 3, 1, 2, 5b 에 대해서 어떤 점과의 위치 관계를 구했을 때 모두 backside 라고 하면 녹색 도형 안에 점이 존재한다고 할 수 있습니다. 이것을 BSP Tree Traverse 의 관점에서 보자면 back-leaf 노드에 도달한 것입니다. 


왼쪽 그림의 경우를 봅시다. 만약 front node 가 존재한다면 그쪽으로 concave 가 형성되는 구멍이 뚫려 있다고 보시면 됩니다. 만약 front-leaf 노드에 도달했을 때 폴리곤의 normal 과 점의 관계가 backside 라고 한다면 닫히게 되는 것입니다. 만약 frontside 라고 하면 열린 폴리곤이라는 의미가 됩니다.


PhysX Convex



이제 이것을 실제로 사용하기 위해서 PhysX 에 넘겨 줄 필요가 있습니다. 그런데 많은 분들이 알고 계시다시피 물리 엔진들은 concave 를 사용하지 않습니다. 사용한다고 하더라도 내부적으로 이것을 convex 집합으로 바꾸게 됩니다. 이 convex 집합이 바로 Aggregate Geometry 입니다.


BSP 노드들을 Convex 로 바꾸는 작업은 ModelToHullsWorker() 에 의해서 수행됩니다. 여기에서 하는 작업은 별거 없습니다. 그림 8 처럼 concave 를 convex 로 쪼개는 작업을 합니다.


노드를 순회하면서 폴리곤을 수집하다가 leaf 를 만나면 그때까지 수집했던 폴리곤들을 사용해 convex 를 하나 생성합니다. 그런데 여기서 알아야 할 점은 front node 를 만나면 새로운 convex 를 만들기 위해 폴리곤들을 수집하기 시작한다는 것입니다. 아까도 언급했듯이 front node 가 있다는 것은 그쪽으로 뚫려 있다는 이야기이므로 leaf 를 만날 때까지 새로운 폴리곤을 수집해야 합니다.


닫힌 convex 가 완성되면 AddConvexPrim() 이라는 메서드를 호출하게 되는데요, 여기에 위에서 언급했던 폴리곤의 리스트( 즉 plane list )를 넘겨 줍니다. 그 메서드에서는 정확성을 높이기 위해서 vertex snapping 과 같은 작업을 수행하고 FKAggregateGeom 에다가 FKConvexElem 를 추가해 줍니다. 여기까지 하면 PhysX Convex 를 생성할 준비를 마친 것입니다.


실제 PhysX Convex 는 UBodySetup::CreatePhysicsMeshes() 호출을 통해 생성됩니다.


생성된 PhysX Convex 와 Brush Helper 의 모양을 비교해서 보여줬으면 좋겠지만 제가 PhysX Mesh 를 출력할 수 있는 방법을 찾지 못해서 안타깝네요. 


PhysX Capture



PhysicsX Visual Debugger ( PVD ) 를 사용해서 씬을 캡쳐할 수 있다는 제보를 받고 시도를 해 봤습니다. 이미 API integration 은 되어 있는 상황이더군요.


UE4 에서는 PvdConnect() 라는 메서드를 통해서 이를 지원합니다. 


에디터 콘솔 명령창에서 "pvd connect""pvd disconnect" 를 사용해서 활성화/비활성화할 수 있습니다. 실제 적용되는건 play 를 눌렀을 때입니다. 그러므로 play 버튼을 누른 후에 connect 했다가 disconnect 하면 됩니다. 계속 캡쳐되고 있으므로 프레임 디버깅할 것이 아니라면 빨리 disconnect 하세요.


그런데 저는 pvd 연결하면 프로그램이 멈춰버리더군요.... 나중에 방법을 찾으면 캡쳐해서 올리도록 하겠습니다.


추가 : 결국 해결( ? )했습니다. pvd3.exe 가 너무 많이 떠 있어서 그랬던 것 같습니다. 다 지우고 하나만 띄우니 되네요.


좌표계를 UE4 와 일치하는 화면을 보시려면 "Left-Handed", "Z+" 설정하시고 Bounding box 를 "All" 로 하시면 쉽게 확인할 수 있습니다. Convex 별로 physx collision 이 할당된 것을 확인하실 수 있습니다.



주의 : 공부하면서 정리한 것이라 잘못된 내용이 포함될 수 있습니다.


 

1. 들어가며

 

UE4 와 관련한 프로그래밍을 하면서 처음에 가장 헷갈리는 부분이 패키지와 애셋이라는 개념입니다. 그리고 이것은 나중에 C++ 코드를 통해 패키지나 애셋을 로드하다가 보면 머리가 아파집니다. 경로를 집어 넣으라고 하는데 뭐가 뭔지 알수가 없습니다.

 

사실 언리얼 공식 문서인 [ 애셋과 패키지 ] 에 그 개념들에 대해서 설명을 하고 있기는 하지만 좀 부족합니다. 아니 부족하다기 보다는 우리가 생각하는 애셋과 패키지가 아닙니다. 뭐랄까 아티스트나 디자이너 위주의 추상화된 개념이라 할 수 있습니다. 실제 API 들에서 명명하는 개념과는 다릅니다.

 

특히나 커맨드렛 같은 도구를 통해 애셋관리를 자동화할 때 이 문제는 심각해집니다. 애셋을 복사하고 싶은데 애셋을 복사하는 방법을 모릅니다. 그래서 어찌어찌 내가 애셋을 복사한다고 하는게 패키지를 복사하는 것이라는 것을 깨닫게 됩니다. 그런데 패키지를 복사한 후에 경로 문제 때문에 애셋을 로드하지 못하는 사태가 발생합니다.

 

많은 분들이 이런 문제들 때문에 골치가 아팠을 것이라 생각합니다. 그래서 이 문서에서는 그런 개념들에 대해 정리해 볼 계획입니다. 모쪼록 도움이 됐으면 좋겠네요.

 

2. 패키지와 애셋

 

여러분은 패키지라고 하면 무엇이 생각나십니까? 여러 개의 애셋을 하나로 묶어 놓은 ( Unity3D 에서와 같은 ) 번들을 생각하시는 분도 있을테고 배포하기 위해서 묶어 놓은 패키지를 생각하실 수 있을 것입니다. UE4 에디터에서 공식적으로 패키지라는 이름이 등장하는 것은 후자의 경우입니다. 쿠킹 패키징할 때의 패키지입니다.

 

 

하지만 코딩 문맥에서는 패키지라는 것은 그냥 "*.uasset" 파일입니다. UE3 에서는 어땠는지 모르겠지만 UE4 에서는 패키지와 애셋은 일반적으로 1:1 로 대응됩니다. 그럼 ".uasset" 파일을 칸텐츠 브라우저에서는 애셋이라 부르고 코드에서는 패키지라 부르는 것일까요? 여러분은 어떻게 생각하시나요?

 

아래의 칸텐츠 브라우저에 있는 "FirstPersonCharacter" 는 코딩문맥에서 보면 패키지인가요 애셋인가요?

 

 

답은 애셋입니다. 좀 더 명확하게 하기 위해서 익스플로러에서 이 파일을 복제해 보겠습니다.

 

 

보이시나요? 동일한 "*.uasset" 파일을 복사하게 되면 파일의 이름은 다른데 칸텐츠 브라우저에서의 이름은 동일합니다. 그러므로 제가 위에서 "*.uasset" 은 패키지이고 칸텐츠 브라우저에서는 애셋으로 표현된다고 말씀드린 것입니다. 절대 "FirstPersonCharacter2" 라는 이름으로 애셋이름이 변경되지 않습니다.

 

"에이~ 내가 해봤는데 칸텐츠 브라우저에서 애셋 복사하면 이름 바뀌던데?" 라고 반문하시는 분 있을 겁니다. 맞습니다. 칸텐츠 브라우저에서 애셋을 복사하면 패키지 복사와 함께 이름 변경까지 수행해 줍니다. 그러나 이는 칸텐츠 브라우저의 동작이지 패키지의 동작은 아니라는 겁니다. 그러므로 나중에 패키지를 코드를 통해 복사하게 되면 문제가 발생하는 겁니다.

 

정리하자면 패키지라는 것은 "*.uasset" 을 이야기하는 것이고 애셋이라는 것은 "*.uasset" 에 포함되어 있는 대표 오브젝트를 의미합니다. 편의상 이를 애셋이라는 이름으로 개념화한거죠. 여러분이 코드를 보실 때 주의하실 점은 "class UAsset" 과 같은 검색어를 날릴 필요는 없다는 것입니다. 존재하지 않습니다. UObject 를 상속한 어떠한 오브젝트라도 패키지의 대표 오브젝트가 될 수 있습니다.

 

3. Paths

 

이제 패키지와 애셋이 다르다는 것을 인지했으니 코드 상에서는 이에 어떻게 접근을 하는지 알아 보도록 하겠습니다. 위에서 한 것과 똑같은 동작을 코드에서 수행해 보도록 하겠습니다.

 

3.1. LongPackageName

 

일단 패키지를 로드하기 위해서는 어떤 경로를 사용해야 할까요? 여러 분이 칸텐츠 브라우저에서 애셋 위에 마우스를 올리시면 다음과 같은 툴팁을 보실 수 있습니다.

 

 

저기에서 "/Game" 이라는 이름으로 "Path" 가 시작되는 것이 보이실 겁니다. UE4 에서 사용하는 모든 칸텐츠의 경로는 저 "/Game" 이라는 접두어로 시작됩니다. 그것은 "/Content" 라는 이름에 대한 키처럼 사용됩니다. 아래 그림을 보시면 붉은색 박스가 게임 디렉토리이고 녹색 박스가 위에서 표현하고 있는 Path 입니다. 단지 "Content" 라는 이름이 "Game" 이라는 이름으로 변경되었을 뿐이죠. 

 

 

 

어쨌든 이 경로와 확장자를 제거한 패지키 이름을 합치면 그것을 LongPackageName 이라 부릅니다. 이 LongPackageName 에서는 "\" 가 아니라 "/" 를 디렉토리 분리자로 사용합니다.

 

 

우리의 경우에는 "/Game/FirstPersonCPP/Blueprints/FirstPersonCharacter" 가 되겠죠. 

 

 

3.2. Filename

 

앞에서 로드한 패키지를 "/Game/FirstPersonCPP/Blueprints/FirstPersonCharacter2" 이라는 이름으로 저장해 보도록 하겠습니다. 그런데 패키지를 저장하는 메서드는 Filename 이라는 인자를 요구하는군요.

 

 

Filename 이라는 인자를 요구하는 경우에는 절대 경로를 요구하는 것이라 보시면 됩니다. 확장자까지 확실히 붙어 있어야 합니다. 우리는 LongPackaeName 을 알고 있기 때문에 이를 절대 경로로 바꾸는 과정을 거치게 됩니다. 아래 코드를 살펴 보십시오. 어렵지 않을 것입니다.

 

 

FPackagePath::LongPackageNameToFilename() 은 LongPackageName 을 상대 경로인 파일경로로 바꿔줍니다. DestRelFilename 은 "../../../../Users/lifeisforu/Documents/Unreal Projects/PackageTest/Content/FirstPersonCPP/Blueprints/FirstPersonCharacter2.uasset" 이 나왔습니다. 이 메서드의 두 번째 인자로 공급해 주면 DestLongPackageName 에 확장자를 포함시켜주지 않아도 되고 임시 변수를 만들지 않아도 됩니다.

 

FPaths::ConvertRelativePathToFull() 은 상대경로를 절대경로로 바꿔줍니다. 이 시점에서 DestAbsFilename 은 "C:/Users/lifeisforu/Documents/Unreal Projects/PackageTest/Content/FirstPersonCPP/Blueprints/FirstPersonCharacter2.uasset" 이 나옵니다.

 

하지만 이것은 Windows 플랫폼에 맞는 이름이 아니죠. 그래서 플랫폼에 맞게 분리자 문자( '/' )를 교정해 줍니다. FPaths::MakePlaformFilename() 이 그 역할을 합니다. 그러면 DestAbsFilename 은 "C:\Users\lifeisforu\Documents\Unreal Projects\PackageTest\Content\FirstPersonCPP\Blueprints\FirstPersonCharacter2.uasset" 이 됩니다.

 

이제 패키지를 저장하고 나서 보면... 짠!

 

 

위에서 나온 것과 같은 상황이 발생합니다. 애셋 이름과 패키지 이름이 다릅니다. 그냥 패키지만 복사하면 이게 정상입니다.

 

3.3. ObjectPath

 

이제 애셋의 이름을 정상적으로 고쳐줄 필요가 있겠군요. 그러나 package 관련 메서드들에는 절대 RenameAsset 과 관련한 이름이 존재하지 않습니다. 정말 우울한 일이죠. 애셋을 로드해야만 합니다.

 

여기에서 ObjectPath 라는 개념이 나옵니다. StaticLoadObject 는 "Name" 이라는 인자를 받습니다. "LongPackageName" 이나 "Filename" 이 아닌 그냥 "Name" 이라는 인자는, 그것이 오브젝트와 관련되어 있다면, 십중팔구 ObjectPath 라 생각하시면 됩니다.

 

ObjectPath 라는 것은 "{LongPackageName}.{AssetName}" 입니다. 애셋의 이름이 마치 확장자인 것처럼 사용됩니다. 그러나 프로그래머 관점에서는 클래스의 필드 이름 정도로 생각하시는 것이 좋을 것입니다. A 오브젝트의 B 필드에는 A.B 라는 식으로 접근하니 이해하기 편할 것입니다.

 

패키지 파일은 복사했지만 애셋 이름은 고친적이 없으니 이 애셋을 로드하기 위해서는 그 경로는 "/Game/FirstPersonCPP/Blueprints/FirstPersonCharacter2.FirstPersonCharacter" 입니다. 이를 "/Game/FirstPersonCPP/Blueprints/FirstPersonCharacter2.FirstPersonCharacter2" 로 고쳐야겠죠.

 

 

이제 애셋이름이 패키지 이름과 동일하게 생성되는 것을 확인할 수 있습니다.

 

 

4. 결론

 

정리하자면 다음과 같습니다.

 

    • 패키지 : uasset 파일.
    • 애셋 : 패키지를 대표하는 오브젝트.
    • LongPackageName : "/Game" 으로 시작하는 확장자를 배제한 패키지 이름.
    • Filename : 시스템에서의 절대 경로. FPackageName 과 FPaths 라는 유틸리티를 사용해서 LongPackageName 으로부터 변환할 수 있음.
    • ObjectPath : 패키지 이름과 애셋이름을 합친 경로. 애셋이름이 확장자처럼 사용됨.
    • 코드에서 패키지를 복사하려면 애셋 이름도 반드시 따로 변경해야 함.

 

원문 : https://software.intel.com/en-us/top-down-microarchitecture-analysis-method-win

주의 : 허락받고 번역한 것이 아니므로 언제든 내려갈 수 있습니다.

주의 : 번역이 개판이므로 이상하면 원문을 참조하십시오.

 

Tuning Applications Using a Top-down Microarchitecture Analysis Method

 

애플리케이션이 CPU 마이크로아키텍쳐의 이점을 취할 수 있도록 하기 위해서는, 애플리케이션이 이용가능한 하드웨어 리소스들을 활용하는 방법에 대해서 알 필요가 있습니다. 이러한 지식은 on-chip Performance Monitoring Units( PMUs )를 통해서 획득할 수 있습니다. PMUs 는 CPU 코어에 내장된 전용로직인데요, 이것은 특정 하드웨어 이벤트가 시스템에서 발생할 때마다 그것을 카운트합니다. 이러한 이벤트들의 예로는 캐시 미스( Cache Miss )나 분기 예측 실패( Branch Misprediction ) 등이 있습니다. 이러한 이벤트들은 Cycles per Instruction( CPI )와 같은 유용한 고수준 지표( metrics )들을 생성하기 위해서 관찰되고 병합될 수 있습니다.

 

특정 마이크로아키텍쳐는 그것의 PMU 를 통해서 수천개의 이용가능한 이벤트들을 만들 것입니다. 그러나 그것은 특정 성능 이슈들을 검출하고 수정하는 데 있어서 유용한 이벤트들이 어떤 것인지를 결정하는데 있어서 보통 불분명합니다. 보통 원본( raw ) 이벤트 데이터로부터 유용한 정보를 획득하기 위해서는 마이크로아키텍쳐 설계와 PMU 명세에 대한 깊은 지식을 요구합니다. 그러나 여러분은 그 데이터들을 행동 정보( actionable information )들로 변환하기 위해 미리 정의된 이벤트들과 지표들, 그리고 top-down 특성화( characterization ) 기법들을 사용하는 것으로부터 이점을 취할 수 있습니다.

 

Top-down Microarchitecture Analysis Method Overview

 

현대 CPU 들은 리소스들을 가능한한 효율적으로 활용하기 위해서 하드웨어 스레딩( hardware threading, 역주 : hyper-threading 인듯 ), 비순차실행( out-of-order execution ), 명령어 수준 병렬화( instruction-level parallelism )뿐만 아니라 pipelining 을 도입합니다. 이러한 관점에서 볼 때, 어떤 유형의 소프트웨어 패턴들과 알고리즘들은 여전히 비효율적입니다. 예를 들어, linked data 구조는 소프트웨어에서 일반적으로 사용되지만, 그것은 하드웨어 프리패쳐( prefetcher )를 무시( defeat )하는 indirect addressing 을 발생시킵니다. 많은 경우에, 이러한 동작은 데이터를 획득하는 동안 파이프라인 내에 bubbles of idleness 를 생성할 수 있으며, 실행할 다른 명령들이 존재하지 않습니다. Linked data 구조는 소프트웨어 문제에 대한 적절한 해결책이 될 수 있지만, 비효율적입니다. 소프트웨어 수준에서 기저 CPU 파이프라인에 영향을 줄 수 있는 다른 많은 예들이 존재합니다. Top-down Characterization 기법에 기반하는 Top-down Microarchitecture Analysis Method 는 여러분이 알고리즘과 데이터 구조를 현명하게 선택할 수 있도록 하기 위한 관점을 제공한다는 목적을 가지고 있습니다. Top-down Microarchitecture Analysis Method 에 대한 세부사항에 대해 알고자 한다면 Intel(R)  64 and IA-32 Architectures Optimization Reference Manual, Appendix B.1 을 참조하십시오.

 

Top-down 특성화는 이벤트 기반 지표들의 계층적 조직화를 의미하는데, 이는 응용프로그램에서의 주요 성능 병목을 표현합니다. 그것의 목적은 응용프로그램이 실행되는 동안 CPU 파이프라인이 얼마나 잘 활용되고 있었는지를 평균적으로 보여주는 것입니다. 이전 프레임워크에서는 이벤트를 해석하기 위해서 CPU 클락틱( clocktick )을 카운팅하는데 의존했습니다 - CPU 클락틱들의 얼마만큼이 어떤 유형의 연산( 예를 들어 L2 캐시 미스때문에 데이터를 회수 )을 위해서 사용되었는지를 확인. 대신에 이 프레임워크는 파이프라인의 리소스들을 카운팅하는데 기반합니다. Top-Down 특성화를 이해하기 위해서, 몇 가지 구조적( architectural ) 개념을 고수준에서 살펴봅시다. 마이크로아키텍쳐의 많은 세부사항들은 이 프레임워크에서 추상화되었으며, 이는 여러분이 하드웨어 전문가가 되지 않고도 그것을 사용하고 이해할 수 있도록 해 줄 것입니다.

 

현대 고수준 CPU 의 파이프라인은 매우 복잡합니다. 아래의 간략화된 뷰에서, 파이프라인은 개념적으로 두 부분으로 나뉘는데, 프론트엔드( Font-end )와 백엔드( Back-end )입니다. 프론트엔드는 architectural instruction 으로 표현된 프로그램 코드를 패칭( fetching )하고 그것들을 micro-op( uOps )라 불리는 하나 이상의 저수준 하드웨어 연산으로 디코딩( decoding )합니다. 그리고 나서 uOp 들은 할당( allocation )이라 불리는 과정을 통해 Back-end 에 넘겨집니다. 일단 할당이 되고 나면 백엔드는 uOp 의 데이터 명령들이 이용가능한 시점이 언제인지를 모니터링하고 이용 가능한 실행 유닛( execution unit )에서 uOp 를 실행할 책임이 있습니다. uOp 실행의 완료는 retirement( 은퇴 ) 라 불립니다. 그리고 retirement 가 uOp 의 결과가 architectural state( CPU 레지스터나 메모리 )에 반영되는 곳입니다. 보통 대부분의 uOp 들은 완전히 파이프라인을 통해 통과하고 리타이어됩니다. 그러나 종종 예측적으로 패치된 uOp 들은 리타이어 전에 취소될 수도 있습니다 - 예를 들면 mispredicted branche 의 경우입니다.

 

 

최신 인텔 마이크로아키텍쳐의 파이프라인의 프론트엔드는 사이클당 4 개의 uOp 들을 할당할 수 있으며, 백엔드는 사이클당 네 개의 uOp 를 리타이어시킬 수 있습니다( 역주 : 나중에 나올라나 모르겠지만, 그래서 이상적인 CPI 는 0.25 입니다 ). 파이프라인 슬롯은 하나의 uOp 를 처리하기 위해서 필요한 하드웨어 리소스를 표현합니다. Top-Down 특성화는 각 CPU 코어와 각 클락 사이클에 대해 4 개의 파이프라인 슬롯이 활성화된다고 간주합니다. 그리고 나서 그것은 특별히 설계된 PMU 를 사용해서 그러한 파이프라인 슬롯들이 얼마나 잘 활용되고 있는지를 측정합니다. 파이프라인 슬롯의 상태들은 할당 위치( 위 그림에서 별표로 표시 )에서 수집되는데, 여기에서 uOp 들은 프론트엔드를 떠나 백엔드로 가게 됩니다. 애플리케이션의 런타임 동안에 이용가능한 각 파이프라인 슬롯은 위에서 기술된 간략화된 파이프라인 뷰에 기반에 4 개 중 하나의 카테고리들로 구분될 것입니다.

 

사이클 동안에, 파이프라인 슬롯은 비어있을 수도 있고 uOp 로 채워져 있을 수도 있습니다. 만약 슬롯이 한 클락 사이클 동안 비어 있다면, 이것은 스톨( stall, 멈춤 ) 때문입니다. 이 파이프라인 슬롯을 분류하는데 필요한 다음 단계는 스톨을 발생시킨 것이 파이프라인의 프론트엔드인지 백엔드인지 여부를 결정하는 것입니다. 이는 설계된 PMU 이벤트와 공식들을 사용해서 수행됩니다. Top-Down 특성화의 목적은 주요 병목을 식별하는 것이므로, 스톨이 프론트엔드 때문인지 백엔드 때문인지를 아는 것은 매우 중요합니다. 일반적으로 프론트엔드가 uOp 를 슬롯에 채울 수 없는 상황때문에 발생했다면, 그것은 이 사이클에 대해 프론트엔드 바운드( bound ) 슬롯으로 분류됩니다. 이것이 의미하는 것은, 프론트엔드 바운드 카테고리에 있는 어떤 병목에 의해 성능이 제한된다는 것입니다. 프론트엔드가 uOp 를 이미 가지고 있지만 백엔드가 그것을 다룰 수 있는 상황이 안 되기 때문에 uOp 를 건네줄 수 없다면, 비어있는 파이프라인 슬롯은 백엔드 바운드로 분류될 것입니다. 백엔드 스톨은 일반적으로 백엔드가 일부 리소스를 사용할 수 없는 경우에 의해 발생합니다. 예를 들면 버퍼를 로드하고 있다든가 하는 경우입니다. 그러나 프론트엔드와 백엔드가 모두 스톨되었다면, 그 슬롯은 백엔드 바운드로 분류될 것입니다. 왜냐하면 이 경우에 프론트엔드에서의 스톨을 해결하는 것은 애플리케이션의 성능을 개선하는데 거의 도움이 되지 않기 때문입니다. 백엔드는 블락킹 병목( blocking bottleneck )입니다. 그리고 그것은 어떠한 효과를 발생시킬 수 있는 프론트엔드에서의 이슈를 해결하기 전에 먼저 제거될 필요가 있습니다.

 

만약 프로세서가 스톨되지 않았다면, 파이프라인 슬롯은 할당 지점에서 uOp 로 채워질 것입니다. 이 경우에는 슬롯을 분류하는 방법을 위한 결정 요인( determination factor )은 uOp 가 결과적으로 리타이어되어야 하는 슬롯이 무엇인가에 달려 있습니다. 만약 그것이 리타이어를 했다면, 그 슬롯은 Retiring 으로 분류됩니다. 만약 프론트엔드에 의한 부정확한 분기 예측이나 Self-Modifying-Code 때문에 파이프라인 플러쉬같은 클리어 이벤트 때문에 리타이어를 안 했다면, 그 슬롯은 Bad Speculation 으로 분류됩니다. 이 네 개의 카테고리는 Top-Down 특성화의 상위 수준을 구성합니다. 응용프로그램을 특성화하기 위해서, 각 파이프라인 슬롯을 정확히 네 개 중의 하나로 분류합니다.

 

 

네 개의 카테고리로 파이프라인 슬롯을 분산시키는 것은 매우 유용합니다. 이벤트에 기반한 지표들을 여러 해동안 이용해 왔음에도 불구하고, 이 특성화 이전에는 가장 영향력이 있는 이용가능한 성능 이슈들이 무엇인지 식별하는 접근법이 없었습니다. 성능 지표들이 이 프레임워크로 배치될 때, 여러분은 어떤 이슈가 가장 먼저 문제가 되는지 알 수 있습니다. 파이프라인 슬롯들을 네 개의 카테고리로 분류하기 위해 필요한 이벤트들은 Sandy Bridge 라는 마이크로아키텍쳐 코드 네임을 가진 코어부터 이용할 수 있습니다 - 그것은 2 세대 인텍 코어 프로세서 패밀리와 인텔 제온 프로세서 E5 패밀리에서 사용되었습니다. 그 다음에 나온 마이크로아키텍쳐들은 이 고수준 카테고리들을 더 세부적인 성능 지표들로 분리하는 것을 허용합니다.

 

Top-Down Analysis Method with VTune Amplifier

 

Intel(R) VTune TM Amplifier 는 General Exploration analysis type 을 제공하는데, 이것은 Top-Down 특성화에서 정의된 이벤트를 수집하기 위해서 미리 구성되어 있으며, Ivy Bridge 라는 코드 네임을 가진 마이크로아키텍쳐부터 이용할 수 있습니다. General Exploration 은 다른 유용한 성능 지표들을 계산하기 위해서 요구되는 이벤트들을 수집하기도 합니다. General Exploration analysis 의 결과는 기본적으로 General Exploration viewpoint 에서 기보여집니다.

 

General Exploration 결과는 특성화의 top-down 성질을 강화하기 위해서 계층적 칼럼( column )에서 보여집니다. Summary window 는 전체 응용프로그램을 위해 각 카테고리에 있는 파이프라인 슬롯에 대한 백분율을 제공합니다. 여러분은 여러 가지 방식으로 결과를 탐색할 수 있습니다. 가장 일반적인 방식은 함수 수준에서 지표를 보기 위해 결과를 탐색하는 것입니다.

 

 

각 함수에 대해, 각 카테고리에 있는 파이프라인 슬롯들이 보여집니다. 예를 들어 위에서 선택된 price_out_impl 함수는 그것의 파이프라인 슬롯의 2.2 % 가 Front-End Bound 카테고리에, 7.4% 가 Bad Speculation 카테고리에, 64.2% 가 Memory Bound 카테고리에, 8.4% 가 Core Bound 카테고리에, 17.8% 가 Retiring 카테코리에 속해 있습니다. 각 카테고리는 그 카테고리 아래의 지표를 보기 위해서 펼쳐질 수 있습니다. 잠재적으로 문제가 되는 영역에 대해서 주의를 주기 위해 자동으로 하이라이트가 사용되는데, 이 경우에는 price_out_impl 를 위한 Memory Bound 파이프라인 슬롯이 너무 높은 비율을 가지고 있습니다.

 

Microarchitectural Tuning Methodology

 

성능 개선을 수행할 때, 중요한 것은 응용프로그램의 최고 핫스팟( hotspot )을 찾는 것입니다. 핫스팟들은 거의 대부분의 CPU 시간을 많이 소비한 함수들입니다. 이 스팟들에 초첨을 맞추는 것은 전체 응용프로그램 성능에 영향을 주는 최적화를 보장할 것입니다. VTune Amplifier 는 이를 위해 두 개의 특정 분석 유형을 가지고 있는데, Basic Hotspots 와 Adavanced Hotspots 입니다. General Exploration viewpoint 에서 핫스팟들은 가장 높은 클락틱( clockticks ) 이벤트 카운트를 가진 함수나 모듈을 결정함으로써 식별될 수 있습니다. 그 클락틱은 CPU 클락틱에 대한 수치를 측정합니다. 마이크로아키텍쳐 수준에서의 성능 개선의 최대 이점을 취하기 위해서는, 병렬화를 추가하는 것과 같은 알고리즘적 최적화가 이미 적용되어 있어야 합니다. 일반적으로 시스템 성능 개선이 먼저 수행되고, 응용프로그램 수준 알고리즘 개선이, 그리고 나서 구조적 및 마이크로아키텍쳐 수준 성능 개선이 수행됩니다. 이 과정 역시 Top-Down software tuning methodology 에서 "Top-Down" 이라 불립니다. 그것은 workload selection 과 같은 성능 개선의 다른 중요한 관점들과 함께 De-Mystifying Software Performance Optimization 기사에 설명되어 있습니다.

 

    1. 핫스팟 함수를 선택( 응용프로그램의 전체 클락틱에서 큰 비율을 차지하는 것 ).
    2. Top-Down 기법과 아래 설명된 가이드라인을 사용해 핫스팟의 효율성을 평가.
    3. 비효율적이라면 주요 병목을 표현하는 카테고리로 파고 듬. 그리고 원인을 식별하기 위해 다음 수준의 하위 병목을 확인.
    4. 이슈를 최적화. VTune Amplifire tuning guides 는 각 카테고리 내의 많은 기저 성능 이슈들에 대한 특정 개선 제안을 포함함.
    5. 중요한 핫스팟들이 평가될 때까지 이를 반복함.

 

VTune Amplifier 는 미리 정의된 문턱값 외의 값이 있거나 핫스팟을 발생시키면 자동으로 GUI 내에서 지표값들을 하이라이팅합니다. VTune Amplifier 는 응용프로그램을 위한 전체 클락틱의 5% 보다 큰 값을 가진 함수를 핫스팟으로 분류합니다. 파이프라인 슬롯이 병목인 것으로 여겨지는 특정 카테고리에 존재하는지 판단하는 것은 workload( 역주 : workload : 주어진 시간에 처리한 작업의 양 )에 의존적입니다. 그러나 일부 일반적인 가이드라인들은 아래 표에 제공되어 있습니다.

 

 

이 문턱값들은 인텔의 연구소에서 일부 workload 들을 분석한 것에 기반합니다. 핫스팟에 대해 ( Retiring 이 아닌 ) 카테고리 내에서 소비한 시간이 가장 높거나 지시된 범위보다 크다면, 이 연구는 유용할 것입니다. 만약 이것이 하나 이상의 카테고리에 대해서 참이라면, 가장 톺은 시간을 소비한 카테고리는 가장 먼저 연구되어야 할 것입니다. 핫스팟은 각 카테고리에서 소비된 시간을 가질 것이며 일반적인 범위 내에 있는 값들을 가지고 있다면 문제가 되지 않을 것입니다.

 

Top-Down 기법을 깨닫는 데 있어서 중요한 것은 병목으로 식별되지 않은 카테고리에 있는 이슈들을 최적화하기 위해서 시간을 소비할 필요가 없다는 것입니다 - 그렇게 하는 것이 중대한 성능 증진으로 이어지지는 않습니다.

 

Tuning for the Back-End Bound Category

 

개선되지 않은 응용프로그램의 대부분은 Back-End Bound 를 가지고 있을 것입니다. 백엔드 이슈를 해결하는 것은 보통 필요 이상으로 리타이어까지의 시간이 길어지는 지연( latency )의 원인을 해결하는 것입니다. Sandy Bridge 라는 코드 네임을 가진 인텔 마이크로아키텍쳐에서, VTune Amplifier 는 높은 지연을 가지는 원인들을 찾기 위한 백엔드 바운드 지표들을 가지고 있습니다. 예를 들어 LLC Miss( Last-Level Cache Miss ) 지표는 데이터를 위해 DRAM 에 접근할 필요가 없는 코드의 영역을 식별하며, Split Loads 와 Split Store 지표는 좋지 않은 성능을 유발할 수 있는 메모리 접근 패턴들을 지적합니다. Sandy Bridge 의 지표들에 대해 더 많은 정보를 얻고 싶으면 Tuning Guide 를 참조하십시오. ( 3 세대 인텔 코어 프로세서 패밀리에서 사용된 ) Ivy Bridge 부터는 Back-End Bound 분류의 이벤트들이 Memory Bound 와 Core Bound 라는 하위 지표로 분리됩니다. 네 개의 카테고리 하의 지표는 파이프라인 슬롯 영역( domain )이 아닌 영역에서 사용될 것입니다. 각 지표는 기저 PMU 이벤트에 기반해 가장 적절한 영역을 사용할 것입니다. 더 많은 세부사항을 원한다면 documentation for each metric or category 를 참조하십시오.

 

Memory Bound 와 Core Bound 하위 지표는 실행 유닛( execution unit )에 대한 활용과 관련한 이벤트를 사용해 결정됩니다 - 최상위 분류에서는 할당 단계가 사용되는 것과는 대조적입니다. 그러므로 이 지표들의 합은 상위 수준에서 결정되는 Back-End Bound 비율과 일치하는 것은 아닙니다( 뭐 대충 맞기는 합니다 ).

 

Memory Bound 카테고리에서의 스톨은 메모리 서브시스템과 관련해 발생합니다. 예를 들어 캐시 미스와 메모리 접근은 Memory Bound 스톨을 발생시킬 수 있습니다. Core Bound 스톨은 각 사이클 동안에 CPU 에서 이용가능한 실행 유닛들을 최적이 아닌 형태로 사용함( less-than-optimal use )으로써 발생합니다. For example, several multi-cycle divide instructions in a row competing for the divide units could cause Core Bound stalls. For this breakdown, slots are only classified as Core Bound if they are stalled AND there are no uncompleted memory accesses. 예를 들어 pending loads 가 존재하면 그 사이클은 Memory Bound 로 분류됩니다. 왜냐하면 실행 유닛들이 loads 가 데이터를 반환하지 않고 있는 동안 굶주리고( starved ) 있기 때문입니다. 이러한 유형의 분리( breakdown )을 특별히 허용하기 위해 PMU 이벤트들이 하드웨어야 설계되었는데, 그것은 응용프로그램 내의 진짜 병목을 식별하는데 도움을 줍니다. Back-End Bound 이슈의 대부분은 Memory Bound 카테고리에 존재할 것입니다.

 

Memory Bound 카테고리 하의 대부분의 지표들은 L1 캐쉬부터 메모리까지의 메모리 계층에서 어떤 레벨이 병목인지를 식별합니다. 다시 말해, 이 판단을 위해서 사용된 이벤트들은 주의깊게 설계되었습니다. Back-End 가 스톨되고 나면 그 지표들은 특정 레벨의 캐시나 in-flight store 에 대한 pending loads 의 스톨의 영향인지 확인하려 합니다. 만약 핫스팟이 주어진 레벨에서 발생했다면, 그것은 캐시나 메모리 레벨에서 그것의 데이터의 대부분을 받고 있음을 의미합니다. 최적화는 데이터를 코어에 가깝게 이동하는 것에 초점을 맞춰야 합니다. Store Bound 가 하위 카테고리로서 호출될 수도 있는데, 그것은 의존성을 표시합니다 - 파이프라인에서의 loads 가 이전 store 에 의존하는 것과 같은 상황. 이러한 각 카테고리 내에는 Memory Bound 실행을 발생시키는 특정 응용프로그램 동작을 식별할 수 있는 지표들이 존재합니다. For example, Loads Blocked by Store Forwarding and 4K Aliasing are metrics that flag behaviours that can cause an application to be L1 Bound.

 

Core Bound 스톨은 보통 Back-End Bound 보다는 적습니다. 이는 이용가능한 컴퓨팅 리소스들이 많은 메모리를 요청하지 않은 상태에서 충분히 활용되거나 사용되지 않을 때 발생할 수 있습니다. 예를 들어 캐시에 들어 맞는 데이터 상에서 부동소수점( FP ) 수학 계산을 수행하는 tight loop. VTune Amplifer 는 이 카테고리 내의 동작들을 검출하기 위한 일부 지표들을 제공합니다. 예를 들어 Divider 지표는 divider 하드웨어가 매우 많이 사용되고 있을 때의 사이클을 식별합니다. 그리고 Port Utilization 지표는 분리된( discrete ) 실행 유닛을 위한 경쟁을 식별합니다.

 

 

주의 : 회색 지표값들은 이 지표를 위해 수집된 데이터가 신뢰할 수 없음을 의미합니다. 예를 들어 PMU 이벤트들을 위해 수집된 샘플들의 개수가 너무 적거나 할 때 발생할 수 있습니다. 여러분은 이 데이터를 무시하거나 collection time, sampling interval, workload 를 수정한 다음에 다시 데이터를 수집할 수 있습니다.

 

Tuning for the Front-End Bound Category

 

 

Front-End Bound 카테고리는 몇 가지 유형의 파이프라인 스톨을 다룹니다. 파이프라인의 프론트엔드 부분이 응용프로그램 병목이 되는 경우는 일반적이지 않습니다; 하지만 프론트엔드가 머신 스톨의 중대한 이유가 되는 경우가 몇 개 있습니다. 예를 들어 JIT 된 코드와 인터프리트된 코드는 프론트엔드 스톨을 발생시킬 수 있습니다. 왜냐하면 명령어 스트림이 진보된 컴파일러 코드 레이아웃의 이점을 취하지 않고 동적으로 생성되기 때문입니다. Front-End Bound 카테고리에서 성능을 개선하는 것은 일반적으로 코드 레이아웃( co-locating hot code )와 컴파일러 기술과 관계가 있습니다. 예를 들어 branchy 코드나 큰 footprint 를 가진 코드는 Front-End Bound 카테고리에서 하이라이팅될 수 있습니다. Code size optimization 과 compiler profile-guided optimization( PGO )와 같은 기술들은 많은 경우에 스톨을 제거할 것입니다.

 

Ivy Bridge 와 그 이후의 마이크로아키텍쳐에서 Top-Down 기법은 Front-End Bound 스톨을 2 개의 카테고리로 나누게 됩니다. 그것은 Front-End Latency 와 Front-End BandWidth 입니다. Front-End Latency 지표는 프론트엔드에 의해 제출된 uop 들이 하나도 없는데 백엔드는 그것들을 이미 소비하고 있는 사이클을 기록합니다. 프론트엔드 클러스터는 사이클당 4 개의 uop 들을 제출할 수 있다는 점을 기억하십시오. Front-End Bandwidth 지표는 4 개 미만의 uop 들이 제출된 사이클을 기록하는데, 이는 프론트엔드의 능력을 효율적으로 사용하고 있지 못하다는 것을 표현합니다. 더 많은 지표들을 각 카테고리 아래에서 확인할 수 있습니다.

 

대부분 Bad Speculation 카테고리로 계산되는 분기 예측 실패는 프론트엔드에서의 비효율성으로 이어질 수도 있는데, Ivy Bridge 부터는 Front-End Latency 아래의 Branch Resteers 병목 지표에 의해 표기됩니다.

 

 

VTune Amplifier 는 Front-End Bound 코드의 원인을 식별할 수 있는 지표들을 열거합니다. 만약 이러한 카테고리들이 결과에서 매우 많이 보인다면, 그것들의 원인과 해결방법을 결정하기 위해서 지표들을 더 깊이 살펴 봐야 합니다. 예를 들어, ITLB Overhead ( Instruction Transition Lookaside Buffer Overhead ) 와 ICache Miss( Instruction Cache miss ) 지표들은 Front-End Bound 실행을 겪고 있는 영역을 가리킬 것입니다. 개선 제안을 확인하고 싶으면 VTune Amplifier tuning guides 를 참조하십시오.

 

Tuning for the Bad Speculation Category

 

세 번째 최상위 수준 카테고리인 Bad Speculation 은 파이프라인이 도움이 되지 않는 연산들을 패치하거나 실행하느라 바쁘다는 것을 표현합니다. Bad Speculation 파이프라인 슬롯은 절대 리타이어되지 않는 uop 들이 제출되거나 부정확한 예측으로부터 머신이 복구되느라 스톨되기 때문에 낭비되는 슬롯입니다. Bad Speculation 은 분기 예측 실패와 머신 클리어에 의해서 발생하며, 많이 발생하지는 않지만 Self-Modifying-Code 같은 경우에도 발생합니다. Bad Speculation 은 Profile-Guided Optimization( PGO )와 같은 컴파일러 기술들에 의해 줄어들 수 있는데, 이는 간접 브랜치들을 피하고 머신 클리어를 발생시키는 에러 조건들을 제거합니다. Bad Speculation 을 교정하는 것은 Front-End Bound 스톨을 줄이는데 도움이 되기도 합니다. 여러분의 마이크로아키텍쳐를 위한 특정 개선 기법들은 VTune Amplifier tuning guide 에서 찾아볼 수 있습니다.

 

 

Tuning for the Retiring Category

 

 

최상위 수준의 마지막 카테고리는 Retiring 입니다. 이것은 일반적으로 유용한 연산들로 파이프라인이 바쁘다는 것을 의미합니다. 이상적으로 응용프로그램은 가능하면 이 카테고리로 분류된 슬롯을 많이 가지고 있어야 합니다. 그러나 파이프라인 슬롯의 많은 부분들이 Retiring 인 코드라고 할지라도 더 개선할 여지가 있습니다. Retiring 카테고리 하에서 성능 이슈가 발생하는 한 가지 경우는 micro-sequencer 를 너무 많이 사용하는 것입니다. Micro-sequencer 는 이는 특정 조건을 발생시키기 위해 uop 들의 긴 스트림을 생성함으로써 Front-end 를 돕습니다. 이 경우 Retiring uop 들이 많이 존재함에도 불구하고, 그것들 중 일부는 회피될 수 있습니다. 예를 들어 Denormals 이벤트에서 적용하는 FP Assists 는 주로 컴파일러 옵션( DAZ 나 FTZ )을 통해 줄어들 수 있습니다. 이러한 이슈들을 마이그레이션하는 것을 돕기 위해서 Code generation choice 들이 사용될 수 있습니다 - 더 많은 세부사항은 VTune Amplifier tuning guides 에서 확인하십시오. Sandy Bridge 에서 Assists 는 Retiring 카테고리 하의 지표로서 식별됩니다. Ivy Bridge 이상에서는 이상적인 retirement 카테고리 내의 파이프라인 슬롯은 두 개의 하위 카테고리로 나뉘는데, 그것들은 각각 General Retirement 와 Microcode Sequencer uops 입니다.

 

 

만약 아직 병렬화( parallelization )나 벡터화( vectorization )과 같은 알고리즘적 개선 기법을 사용하지 않았다면, 그것을 적용하는 것은 코드 영역의 성능이 Retiring 카테고리로 가도록 만드는데 도움을 줄 것입니다.

 

Conclusion

 

Top-Down 기법과 VTune Amplifier 에서의 그것의 기능은 PMU 들을 사용하는 성능 개선을 위한 새로운 방향성을 제시합니다. 이러한 특성화에 익숙해지기 위해 연구하는 개발자들의 시간은 노력을 들일 가치가 있을 것입니다. 왜냐하면 그것에 대한 지원이 최신 PMU 들에 설계되어 있으며 인텔 마이크로 아키텍쳐에서 가능하면 앞으로 그 계층이 더 확장될 것이기 때문입니다. 예를 들어 Sandy Bridge 와 Ivy Bridge 에서의 특성화는 매우 차이가 납니다.

 

Top-Down 기법의 목적은 응용프로그램 성능에 있어 주요 병목을 식별하는 것입니다. VTune Amplifier 의 General Exploration 분석과 visualization 기능은 여러분이 응용프로그램을 개선할 수 있도록 하기 위한 행동 정보들을 제공합니다. 또한 이러한 기능들은 응용프로그램의 성능 뿐만 아니라 여러분의 최적화 생산성을 매우 신장시켜 줍니다.

 

Parent topic : Tuning Methodology

 

See Also

 

Interpreting General Exploration Data

 

Tuning Guides and Performance Analysis Papers

 

Vectorization Advisor

 

Clockticks Vs. Pipeline Slots-Based Metrics

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

+ Recent posts