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


[ 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 소프트웨어 팀에서 일하고 있습니다. 여러분은 그의 블로그를 여기에서 구독하실 수 있습니다.

+ Recent posts