[경고] 아래 글을 읽지 않고 "광선 추적"을 보면 바보로 느껴질 수 있습니다.
[그림 1] 광선 추적의 기본 개념(출처: wikipedia.org)
컴퓨터 기술에서 사용하는 광선 추적(ray tracing)은 [그림 1]처럼 3차원 대상체를 2차원 화면에 그리기 위한 수학적 방법론이다[1]. 우리가 화면(screen)으로 설정한 곳에 형성되는 2차원 영상(image)이 중요하므로, 광선 추적에서 광선이 시작되는 위치는 광원(light source)이 아니고 관찰자의 눈(eye)이나 사진기(camera)이다. [그림 1]을 보면, 사진기에서 광선이 발사(launch)되어서 주변 물체(object)를 찾고, 물체와 교차(intersection)한 위치에서 광원이 보이는지 확인한다. 광원이 보이면 광원으로 광선을 생성하고, 볼 수 있는 광원이 없으면 빛의 반사 법칙에 따라 반사된 광선을 다시 쏜다. 원래 광선 추적은 1968년박정희 정부 시절에 컴퓨터 분야에서 제안되어 급격히 발전했기 때문에, 전자파에 대한 광선 추적인 전자기 광선 추적(electromagnetic ray tracing)을 이해하기 위해서는 먼저 컴퓨터 그래픽스(computer graphics, CG) 분야의 용어와 개념을 학습해야 한다.
- 그래픽스 혹은 영상화(映像化, graphics): 원래 정보를 전달하기 위한 시각 영상을 의미했으나, 지금은 자료를 그림 형태로 표현하는 기법; 그래픽스를 컴퓨터로 실행하면 컴퓨터 그래픽스 혹은 CG라고 명함
- 렌더링 혹은 표현하기(rendering): 3차원 형상을 담은 CAD(computer-aided design) 모형을 컴퓨터로 처리하여 2차원 화면상에 표현하는 과정; 렌더링을 가능하게 해주는 기본 코드(code) 골격을 렌더링 엔진(rendering engine)이라 함
- 쉐이더 혹은 음영기(陰影機, shader): 렌더링을 할 때 물체의 그늘이나 색상을 멋있게 만드는 연산 프로그램; 지금은 그늘 뿐만 아니라 특수 효과를 추가하는 영역까지 쉐이더가 담당
- 렌더링 파이프라인 혹은 표현화 관로(表現化管路, rendering pipeline): 렌더링을 수행하는 구체적인 처리 절차; 요즘의 렌더링 파이프라인은 모두 병렬 처리를 염두에 두고 설계됨
- 광선당 자료 혹은 PRD(per-ray data): 광선 하나가 가진 자료 구조; PRD가 커지면 광선 추적에 필요한 기억 장치 혹은 메모리(memory)가 많이 필요하므로 PRD는 최소한으로 구성; PRD는 광선 탑재량(ray payload)과 같은 개념임
- 음영기 결속표 혹은 SBT(shader binding table): 기하 정보를 광선 연산과 매개변수에 연결하는 색인표(lookup table); 렌더링 과정 동안 사용자 코드를 동작시키기 위해 활용
통상적으로 사용하는 렌더링 파이프라인은 다음과 같이 응용(application), 기하(geometry), 화상화(畵像化, rasterization) 단계로 구성된다.
(a) 기본 구성
(b) 기하 파이프라인의 예시
[그림 2] 렌더링 파이프라인의 간단한 소개(출처: wikipedia.org)
[그림 2(a)]에 있는 응용 단계는 GPU(영상화 처리 장치, graphics processing unit) 여부와 관계없이 항상 CPU(중앙 처리 장치, central processing unit)에서 진행된다. 응용 단계는 영상화를 위한 입력 조건을 다 설정하여 기하 단계가 바로 렌더링에 들어갈 수 있게 한다. 기하 단계는 [그림 2(b)]와 같은 기하 파이프라인(geometry pipeline)을 따라 렌더링을 실제로 실행하는 부분이다. 기하 파이프라인은 기하 도형을 그리는 프로그램상의 절차와 연산을 뜻한다. 컴퓨터에 GPU가 있다면, 응용 단계는 CPU에서 GPU 기억 장치(memory)로 매개변수를 넘기고 기하 단계는 GPU에서 병렬 처리를 하면서 2차원 화면 정보를 빠르게 생성한다. 기하 단계에서 생성한 2차원 영상을 실제로 컴퓨터 화면에 그려주는 단계는 CPU에서 이루어지는 화상화이다. GPU에서 기하 단계가 계산된다면, 그후 GPU에서 CPU 기억 장치로 화면 정보를 넘기고 CPU가 직접 화상 처리를 한다. 병렬화 개념에서 CPU와 그 기억 장치는 호스트 혹은 주관기(主管機, host)라 부른다. 비슷하게 GPU와 그 기억 장치는 디바이스 혹은 장치기(裝置機, device)라 명한다.
[그림 3] 삼각화를 이용한 그물 생성(출처: wikipedia.org)
기하 단계에 사용되는 렌더링은 여러 가지 방식으로 구현될 수 있으며, [그림 2(b)]은 기하 파이프라인의 전형적인 예시이다. 기하 파이프라인에서 가장 먼저 필요한 시작점은 3차원 모형을 기억 장치에 저장하는 자료 구조(data structure) 설정이다. 보통 [그림 3]처럼 3차원 모형은 3점을 가진 삼각화(三角化, triangulation)를 해서 삼각 그물(triangular mesh)을 생성(mesh generation)한다. 삼각 그물 정보는 컴퓨터의 기억 장치에 형식(format)을 지정해서 모두 저장된다. 삼각 그물을 표현하는 기본 방식은 웨이브프런트(Wavefront)의 OBJ(object) 형식[10]이다. 그 다음에 [그림 4]와 같이 관찰자 시점에 사진기(camera)를 만들고 사진기 좌표계(camera coordinate system)도 설정한다. 물론 사진기 좌표계는 전역 좌표계(global coordinate system)에 해당하는 세계 좌표계(world coordinate system)를 기준으로 한다. 또한 광원도 필요하기 때문에 색깔과 위치 등으로 이루어진 광원 매개변수를 정의한다. 이제부터 중요한 단계는 본격적인 광선 추적이다. 사진기에서 나온 광선이 물체에 부딪히는 교차(intersection)를 빠르게 연산해야 한다. 광선에 교차가 된 삼각형 정보를 이용해서 [그림 5]처럼 임의 명중(any hit)과 최근접 명중(closest hit)으로 구분해서 빛의 반사(reflection)와 확산(擴散, diffuse or diffusion)을 모사한다. 빛의 확산은 대상체의 재질(material)로 인해 빛이 퍼지는 특성을 의미한다.
[그림 4] 광선 발사를 위한 사진기 좌표계
광선 발사를 위해서 3차원 좌표계 기반 벡터(vector)를 적극적으로 활용한다[2]. [그림 4]의 세계 좌표계를 기준으로 사진기 혹은 관찰자의 눈(eye) 및 화면(screen)의 중심 위치는 각각 $\bar r_e$와 $\bar r_c$라 둔다. 화면에는 화소(畵素, pixel: picture element) 단위로 영상이 그려지며, 화소 위치는 $\bar r_s(q)$로 정한다. 여기서 지표 $q$[= $0, 1, 2, \cdots, Q-1$]는 화소의 순서, $Q$는 발사하는 광선의 개수 혹은 전체 화소의 수이다. 예를 들어, SD(standard definition)의 경우는 $Q$ = $720 \times 480$ = $345,600$개, HD(high definition)는 $Q$ = $1280 \times 720$ = $921,600$개의 광선이 필요하다. 그러면 사진기에서 화면의 중심으로 가는 법선 벡터는 $\bar w_n$ = $\bar r_c - \bar r_e$로 정의된다. 여기서 $\bar r_c$는 $\bar r_s(q)$가 나타내는 화면의 중심을 표현하는 위치 벡터, $\bar w_n$의 단위 벡터는 $\hat w$, $\hat w$은 화면의 법선 단위 벡터이다. 추가적으로 사진기를 정의할 때 상향 벡터(upward vector) $\bar v_\text{up}$을 하나 설정한다. 상향 벡터는 사진기의 위쪽 방향을 대략적으로 나타낸다. 법선 단위 벡터 $\hat w$와 상향 벡터를 이용해서 사진기 좌표계 $(u, v, w)$를 간편하게 생성한다.
(1)
여기서 $\bar w_n$ = $\bar r_c - \bar r_e$ = $|\bar w_n| \hat w$, $\bar v$와 $\bar v_\text{up}$은 같은 방향일 필요가 없고 비슷한 방향이면 된다. 사진기 좌표계의 원점 $(0, 0, 0)$은 세계 좌표계에서 사진기 혹은 관찰자의 눈(eye)인 $\bar r_e$이다. 비슷하게 화면의 중심은 사진기 및 세계 좌표계에서 각각 $(0, 0, |\bar w_n|)$ 및 $\bar r_c$로 표기되며, 화면은 $\bar u, \bar v$가 만드는 평면과 평행하다. 또한 화면을 구성하는 각 화소 위치는 $\bar r_s$이며, 화면의 가로 길이 $W_s$와 세로 길이 $H_s$는 각도[단위: ˚]로 재는 수평 방향 시야(視野, field of view) $\text{FoV}$와 화면비 혹은 종횡비(畵面比, picture ratio or aspect ratio) $\text{AR}$로 미리 설정한다.
(2)
여기서 화면비는 화면의 가로와 세로 비율인 $W_s : H_s$이다. 사진기 좌표계 $(u, v, w)$는 오른손이 아닌 왼손 규칙으로 좌표축을 구성한 왼손 좌표계(left-handed coordinate system)이다. 사진기 좌표계가 왼손 규칙을 따르는 이유는 사진기와 물체가 서로 마주 보고 있기 때문이다. 광선 추적에서는 세계와 물체 좌표계는 통상적인 오른손 좌표계(right-handed coordinate system)로 선택한다. 그래서 오른손 좌표계로 정의한 물체를 바라보면서 화면에 그리는 좌표계는 당연히 왼손 좌표계가 편한다.
[그림 4]에 그린 광선 위치의 수학적 표현식 $\bar r(\tau)$는 매개변수 $\tau$로 공식화한다.
(3)
여기서 $\bar r_s$는 화면 위의 화소 위치, $\bar d$ = $\bar r(\tau) - \bar r_e$ = $|\bar d| \hat d$, 광선 방향의 단위 벡터는 $\hat d$, $\tau$ = $0$이면 $\bar r(\tau)$는 광선의 시작점인 사진기 위치 $\bar r_e$가 된다. 사진기 점 $\bar r_e$에서 현재 광선 위치 $\bar r(\tau)$로 가는 광선의 길이[= $|\bar r(\tau) - \bar r_e|$]를 나타내는 $\tau$는 광선 추적에서 매우 중요한 변수이다. 일반적으로 $\tau$는 양수이며 $\tau_\min \le \tau \le \tau_\max$와 같은 범위를 가진다. 장면 엡실론(scene epsilon)으로 불리는 최소값 $\tau_\min$은 광선과 물체의 교차를 찾을 때 자기 자신과의 교차는 배제하기 위해 사용된다. 통상적으로 장면 엡실론 $\tau_\min$은 거의 $0$에 가까운 양수로 선택한다. 예를 들어, [그림 4]에서 사진기가 쏜 광선은 물체가 없기 때문에 $\tau_\min$은 필요가 없지만, 노란색 삼각형에서 반사된 광선(reflection ray)이 다른 영역과 교차하는지를 판정할 때는 필수적이다. 즉, $\tau_\min$ = $0$으로 두면 자기 자신과 바로 교차해버리지만, 최소 광선 길이를 양수 $\tau_\min$만큼 설정하면 자기 자신을 벗어난 후에 다른 물체와의 교차를 검색한다. 최대값 $\tau_\max$는 광선이 교차를 찾을 최대 범위를 설정한다. 넓은 범위를 찾을 때는 $\tau_\max$를 키우면 좋지만, 너무 많이 키우면 탐색 범위가 커져서 광선 추적의 계산 속도가 심각하게 떨어진다.
[그림 5] 발사된 광선이 교차되는 특성
[그림 4]와 같이 발사된 광선은 [그림 5]처럼 물체를 구성하는 그물과 다양하게 교차한다. 광선 추적에서는 임의 명중(any hit), 최근접 명중(closest hit), 빗맞음(miss)으로 교차 유무를 구분한다. 임의 명중은 가깝거나 멀거나 관계없이 광선이 아무 물체나 맞은 상태를 뜻한다. 최근접 명중은 임의 명중 중에서 광선과 가장 가까이에서 교차한 경우를 의미한다. 빗맞음은 말뜻 그대로 교차가 없다. 교차 계산에서 최근접 명중이 가장 먼저 나오고 나머지 임의 명중은 나중에 계산될 것 같지만 전혀 아니다. 광선 추적은 병렬화를 쓰기 때문에 교차된 결과는 거리에 관계없이 중구난방으로 나온다. 그래서 임의 명중을 비교하면서 가장 가까운 최근접 명중을 골라내야 한다.
[그림 6] 구 모양으로 표현한 포함 체적
광선이 물체와 교차하는 특성을 계산하기 위해서는 물체의 기하학적 구조를 수학 표현식으로 먼저 정의해야 한다. [그림 6]은 물체를 둘러싸는 포함 체적(bounding volume, BV)을 구 모양으로 표현하고 있다. 벡터 관점에서 구의 방정식을 정의하고, 현재 위치 $\bar r$에 식 (3)을 대입해서 광선과 구가 만나는 교점 $\tau_1, \tau_2$를 구한다.
(4)
(5)
(6)
여기서 $\bar c$는 구의 중심, $r$은 구의 반지름, $\tau_1$ = $\tau_-$, $\tau_2$ = $\tau_+$, $D$는 2차 방정식의 판별식(discriminant)이다. 2차 방정식의 성질에 따라, $D/4 \ge 0$이면 이 광선은 포함 체적에 교차하거나 한 점에서 만난다. 만약 $D/4 < 0$인 경우는 광선이 포함 체적과 만나지 않는다.
[그림 7] 물체를 둘러싼 포함 상자[아테나 석고상을 둘러싼 점선으로 된 직육면체](출처: wikipedia.org)
[그림 8] 축 정렬 포함 상자 혹은 AABB로 표현한 포함 체적
포함 체적은 어떤 형태이든 가능하지만, 계산 가속화에는 간단한 모양이 좋다. 그래서 [그림 6]의 구보다는 [그림 7]에 제시한 직육면체인 포함 상자(bounding box, BBB)가 포함 체적의 대표로 쓰인다. 포함 상자 중에서도 [그림 8]과 같이 세계 좌표축에 각 면이 평행인 축 정렬 포함 상자(axis-aligned bounding box, AABB)가 대부분 사용된다. 여기서 AABB는 에이에이비비라고 읽는다. AABB는 직육면체의 평면이 세계 좌표축 $\hat x, \hat y, \hat z$와 평행하므로, 교차를 매우 쉽게 판단할 수 있다. 광선 위치 $\bar r(\tau)$가 [그림 8]에 그려진 AABB에 교차하는 조건은 다음처럼 부등식으로 나타낸다.
(7a)
(7b)
(7c)
(8)
여기서 $\bar r_e$ = $(x_e, y_e, z_e)$, $\hat d$ = $(d_x, d_y, d_z)$, $\tau$가 변하는 범위는 $\tau_\min \le \tau \le \tau_\max$이다.
[그림 9] 포함 체적 계층 혹은 BVH의 기본 개념(출처: wikipedia.org)
구 혹은 AABB로 정의한 포함 체적을 이용해서 식 (3)의 광선과 [그림 3]처럼 삼각화된 물체의 교차를 판단할 수 있지만, 물체를 구성하는 삼각형의 개수가 매우 많아질 때는 문제가 된다. 그래서 물체 삼각형을 저장할 때에는 탐색에 유리한 자료 구조를 미리 정의해서 사용한다. 광선과 포함 체적의 교차를 판별하기 위해 가장 많이 사용하는 자료 구조는 트리 혹은 나무(tree) 개념을 쓰는 포함 체적 계층(bounding volume hierarchy, BVH)이다[2]. 포함 체적 계층 혹은 BVH가 기본적으로 자료 처리하는 방식은 [그림 9]에 있다. 3차원 공간에 원, 삼각형, 육면체, 별 등의 도형이 있다면, 이 모든 도형을 항상 탐색하면서 광선 교차를 판정하기는 어렵다. 그래서 모든 도형이 포함된 아주 큰 포함 체적을 만들고, 영역별로 포함 체적을 쪼개면서 도형이 소속된 포함 체적을 트리 형태로 분류한다. 구로 3차원 공간을 채우면 비는 부분이 생겨서 BVH를 쓰기 불편한다. 따라서 AABB를 포함 체적으로 써서 모든 공간을 편하게 쪼개고, 식 (8)을 이용해 매우 간단하게 도형이 특정 AABB에 속하는지를 판단한다. BVH가 동작하는 방식을 자세히 이해하기 위해 [그림 9]를 본다. 먼저 아주 큰 AABB로 A 영역을 정의한다. A 영역 속에는 또 다른 AABB인 B, C 영역이 있고, B 영역 내부에 2개의 작은 AABB도 존재한다. 만약 C 영역으로 광선 $\bar r(\tau)$가 지나가면, B 영역의 교차를 계산할 필요없이 C 영역에 있는 삼각형이나 햇살 모양과의 교차만 고려하면 된다. 이러한 방식으로 BVH는 광선과 물체의 교차를 매우 빠르게 찾을 수 있다.
[그림 10] 삼각형의 무게 중심 좌표계
BVH를 이용해 광선이 특정한 AABB를 지나는 경우를 찾으면, 그 다음 단계로 광선과 삼각형이 교차하는지 판정한다. 여러 방식 중에서 삼각형의 교차 계산에는 묄러–트럼보어 알고리즘(Möller–Trumbore algorithm)이 많이 쓰인다[3]. 묄러–트럼보어 알고리즘의 핵심에는 [그림 10]에 제시한 삼각형에 쓰이는 무게 중심 좌표계(barycentric coordinate system)가 있다. 무게 중심 좌표계는 면적 좌표계(area coordinate system)라고도 하며, 볼록 집합(convex set)을 만드는 볼록 결합(convex combination)의 특별한 예이다. [그림 10]에 보인 무게 중심 좌표계를 쓰면, 삼각형 위의 임의 점 $\bar T(\alpha, \beta)$는 매개변수 $\alpha, \beta$로 기술된다.
(9)
여기서 $\alpha + \beta + \gamma$ = $1$, $\bar e_1$ = $\bar v_1 - \bar v_0$, $\bar e_2$ = $\bar v_2 - \bar v_0$이다. 매개변수 $\alpha, \beta$는 항상 $\alpha \ge 0$, $\beta \ge 0$이며, 삼각형 위에 있기 위해 $\alpha + \beta \le 1$도 만족해야 한다. 왜냐하면 조건 $\alpha + \beta$ = $1$은 선분 $\overline{BC}$를 표현하기 때문이다.
(10)
여기서 $\bar e_3$ = $\bar v_2 - \bar v_1$이다. 식 (9)의 표현식이 무게 중심 혹은 면적 좌표계라 불리는 이유는 삼각형 내부의 점 $T$와 각 꼭지점이 만드는 삼각형의 면적이 $\gamma : \alpha : \beta$ = $\triangle TBC : \triangle TCA : \triangle TAB$ 비율을 만족하기 때문이다. 이 면적 비율은 좌표계 기반 벡터와 행렬식(determinant)을 활용해서 증명할 수 있다.
(11)
묄러–트럼보어 알고리즘은 식 (9)에 정의한 무게 중심 좌표계를 쓰기 때문에 광선과 삼각형의 교차를 매우 편리하게 검증한다. 광선 위치인 식 (3)을 무게 중심 좌표계인 식 (9)와 연립한다.
(12)
여기서 $\bar t$ = $\bar r_e - \bar v_0$, $(\cdot)^T$는 전치 행렬(transpose), 모든 벡터는 행 벡터(row vector)로 생각한다. 식 (12)를 크라메르의 규칙(Cramer's rule)으로 풀고 스칼라 삼중적(scalar triple product)으로 정리한다.
(13)
벡터 연산의 계산량을 줄이기 위해 식 (13)에 나오는 공통된 외적(outer product)을 벡터 $\bar p, \bar q$로 정의해서 다시 공식화한다[3].
(14)
여기서 $\bar p$ = $\hat d \times \bar e_2$, $\bar q$ = $\bar t \times \bar e_1$이다. 최종적으로 식 (14)로 구한 $\alpha, \beta$를 살펴서 $\alpha \ge 0$, $\beta \ge 0$, $\alpha + \beta \le 1$을 모두 만족하면, 광선 $\bar r(\tau)$는 이 삼각형과 교차한다. 이때 추가적으로 구한 $\tau$를 식 (3)에 넣어서 광선과 삼각형이 만난 점도 구할 수 있다. 물론 $\tau$는 광선이 생성되는 범위인 $\tau_\min \le \tau \le \tau_\max$ 안에 있어야 한다.
(a) 광선으로 표현한 다양한 반사 현상
(b) 퐁 반사 모형의 예시
[그림 11] 물체의 반사와 확산에 대한 컴퓨터 표현법(출처: wikipedia.org)
사진기에서 쏜 광선이 물체 삼각형과 교차한다면, 이 삼각형에 색깔과 재질(material) 특성을 부여해서 물체 형태를 현실감 있게 화면상에 나타낸다. 이를 위해 가우스 광학(Gaussian optics)이나 물리 광학(physical optics)과 같은 물리 이론으로 광선을 계산할 수도 있지만, CG는 자연 현상을 완벽하게 재현하는 방향에는 관심이 없고 간단한 연산으로 사람 눈을 속일 정도면 충분하다. 이 관점을 먼저 이해한 공학자는 컴퓨터 과학의 명가인 유타 대학교(University of Utah)의 대학원생 퐁Bùi Tường Phong(1942–1975)이었다. 유타 대학교는 픽사(Pixar)의 창립자 캣멀Edwin Catmull(1945–)의 모교이기도 하다. 1973년퐁 31세, 박정희 정부 시절에 완성한 박사 학위 논문에서 퐁은 [그림 11(b)]와 같은 퐁 반사 모형(Phong reflection model)을 제안했다[4]. 안타깝게도 퐁은 박사 학위를 받은 지 2년만 혹은 논문 [4]가 나온 해에 백혈병으로 요절했다. 토막 상식으로 베트남 성명 중 하나인 퐁은 성이 아니고 이름이며, 퐁의 성은 부이(裴), 우리 식으로 배씨이다. 우리는 공식적인 자리에서 성과 직위를 붙이지만, 베트남은 성이 아닌 이름에 직위를 붙인다. 예를 들어, 우리식으로 퐁을 부르면 부이 박사가 되지만, 베트남 방식으로는 퐁 박사가 되어야 한다.
[그림 12] 퐁 반사 모형을 위한 여러 단위 벡터의 정의(출처: wikipedia.org)
[그림 11(b)]의 퐁 반사 모형에서 물체의 렌더링은 주변광(ambient light), 확산 반사(diffuse reflection), 거울 반사(specular reflection)로 표현된다. 주변광은 광원의 방향에 관계없이 물체 주변에 은은하게 존재하는 균일한 빛이다. 확산과 거울 반사는 [그림 11(a)]에 잘 나타나있다. 즉, 거울 반사는 빛의 반사 법칙에 의해 거울처럼 빛이 반사되는 특성이며, 확산 반사는 물체의 울퉁불퉁함과 반투명성으로 인해 빛이 퍼지면서 반사되는 현상을 나타낸다. 주변광과 헷갈릴 수 있는 개념은 알베도(albedo)이다. 알베도는 물체 표면의 고유한 색깔을 보여주는 평균 반사 특성이다.[정확히는 주변광 대비 반사되는 빛의 비율이 알베도] 주변광과 알베도를 서로 비교하면, 주변광은 빛의 특성이나 알베도는 물체의 재질을 나타낸다. 보통 주변광과 알베도의 곱을 물체의 주변 색깔(ambient color)로 계산한다. 주변 색깔을 이해하기 위한 좋은 재료는 밤에 보이는 달이다. 태양빛이 거친 달 표면에서 반사되므로, 어두운 지구에서 달을 또렷하게 관찰할 수 있다. 여기서 태양빛은 주변광, 달 표면의 고유한 색깔은 알베도, 우리가 보는 달은 주변 색깔이다. 다만 달 표면은 거칠지만 매우 먼 지구에서 관찰하기 때문에, 확산은 무시되고 알베도인 은은한 달 자체의 색깔만 보게 된다. 물체 표면 $\bar r_o$에 비춰지는 빛의 전력 밀도인 조도(照度, illuminance)를 가진 벡터 $\bar I (\bar r_o)$로 기술한 퐁 반사 모형은 다음과 같다.
(15)
여기서 3차원 벡터 $(r, g, b)$의 성분은 RGB(red, green, blue) 색깔, $M$은 광원의 개수, $\bar a \odot \bar b$는 각 원소를 곱하는 아다마르 곱(Hadamard product), RGB 색깔을 표현하기 위해 반사 상수(reflection constant) $\bar k$와 광원 $\bar s$를 벡터로 표현, 첨자 $a, d, s$는 각각 주변광, 확산 반사, 거울 반사를 의미, $\bar k_a, \bar k_d, \bar k_s$는 물체 표면의 고유한 특성, 지수 $\alpha$는 재질의 반짝임 상수(shininess constant)이며 $n_s$로 표기되기도 한다. 덧붙여 [그림 12]처럼 $\hat L_m$, $\hat V$은 각각 제$m$번 광원(light source)과 관람자(viewer)으로 가는 단위 벡터이다. 단위 벡터 $\hat R_m$은 $\hat L_m$이 가르키는 광원에 의한 반사파의 방향이며, $\hat N$은 물체 표면의 법선 벡터이다. 빛의 반사 법칙에 따라 $\hat R_m$은 $\hat L_m, \hat N$으로 결정된다.
(16)
물체의 재질은 람베르트 표면(Lambertian surface)으로 가정해서, 확산 반사에는 람베르트의 코사인 법칙(Lambert's cosine law)을 적용한다. 그래서 조도에 기여하는 확산 반사는 $\hat L_m \cdot \hat N$으로 정의한다. 거울 반사는 관람자가 $\hat R$ 방향에 있을 때 가장 크고, $\hat R$에서 벗어나면 작아지므로, 연산이 편한 내적을 써서 $\hat R_m \cdot \hat V$로 나타낸다. 다만 재질에 따라 거울 반사의 양이 달라지는 성질은 $\alpha$로 보정한다. 퐁 반사 모형에는 연산량이 큰 $\hat R_m$이 존재해서 문제가 된다. 블린James Blinn(1949–)은 이 문제점을 해결하기 위해 블린–퐁 반사 모형(Blinn–Phong reflection model)을 제안했다. 블린–퐁 반사 모형은 $\hat R_m$을 쓰지 않고 $\hat V, \hat L_m$의 각 이등분선(angle bisector)인 $\hat H_m$을 사용한다. 단위 벡터 $\hat H_m$은 중간 벡터(halfway vector)를 의미한다. 단위 벡터 $\hat V, \hat L_m$은 마름모를 만들기 때문에, 중간 벡터 $\hat H_m$은 $\hat V, \hat L_m$의 벡터 합이다.
(17)
(18)
여기서 $\alpha'$은 $\alpha$와는 다른 반짝임 상수이다. 블린–퐁 반사 모형은 퐁 반사 모형과 완벽히 같지는 않다. 관람자가 $\hat R_m$에 있으면 $\hat H_m$ = $\hat N$이 되므로 두 반사 모형은 동일한 결과를 만들지만, $\hat V$가 $\hat R_m$을 벗어나면 약간의 오차가 생긴다. 오차가 조금 있더라도 블린–퐁 반사 모형은 계산 속도가 빠르므로 현존하는 광선 추적 엔진(ray tracing engine)의 기본 알고리즘이 된다[5]–[8].
[그림 12] NVIDIA OptiX 7의 프로그램 관계도: 회색은 사용자가 설정 가능, 초록색은 고정되어 사용자가 수정 불능(출처: [6])
현재 사용 가능한 광선 추적 엔진은 OpenRT(Open-source Ray Tracing), OptiX, DXR(DirectX Raytracing) 등으로 다양하다[5]–[8]. 여러 엔진 중에서 NVIDIA가 자신의 GPU(graphics processing unit)를 위해 만든 OptiX를 중심으로 광선 추적이 계산되는 과정을 자세히 소개한다[6]. OptiX는 NVIDIA가 밀고 있는 GPU 병렬 계산 플랫폼(parallel computing platform)인 CUDA(Compute Unified Device Architecture) 기반[9]으로 설계되므로, OptiX를 작성하는 코드 방식은 CUDA가 기본이다. 추가적으로 OptiX 엔진에서는 광선 연산(ray operation)을 위한 명령어 집합체를 프로그램(program)으로 칭한다. 즉, 우리가 흔히 쓰는 컴퓨터 프로그램이 아닌 광선 추적을 위한 명령어 집합을 프로그램이라 명명한다. OptiX 프로그램은 실행 전에 NVCC(NVIDIA CUDA Compiler)를 사용해서 CUDA 코드를 PTX(Parallel Thread Execution)로 바꾸어야 한다. PTX는 CUDA 플랫폼의 한 부분인 가상 기계용 어셈블리어(virtual machine assembly language)이다.
- 광선 발생 프로그램(RG: Ray Generation Program)
광선 추적에 사용하는 광선을 식 (3)으로 발생시킨다. 광선 추적에서 사용하는 광선 종류(ray type)는 2개가 있다. 하나는 광선 지표(ray index) $0$을 쓰는 휘도(radiance)이고, 나머지는 광선 지표가 $1$인 그림자(shadow)이다. 광선 발생 프로그램은 물체의 색깔과 밝기를 만들기 위해 작동되므로, 휘도형(radiance type) 광선만 사용하고 그림자형(shadow type) 광선은 사용하지 않는다. 그림자와 음영을 만드는 그림자형 광선은 최근접 명중 프로그램(Closest-hit Program)에서 생성되어 발사된다. 또한 휘도형 광선은 최근접 명중 프로그램에만 반응하는 반면, 그림자형 광선은 임의 명중 프로그램에서 계산된다.
- 빗맞음 프로그램(MS: Miss Program)
광선이 어떤 물체에도 맞지 않은 경우에 호출되는 프로그램이다. 보통 이미 정해진 배경색을 반환한다. 광선 종류 관점에서, 빗맞음 프로그램은 휘도형(radiance type)을 담당하고 그림자형(shadow type)은 배제한다.
- 예외 프로그램(EX: Exception Program)
광선 추적에서 오류가 생긴 경우에는 예외 프로그램이 실행된다. 사용자가 원하는 동작을 CUDA로 적절하게 설계할 수 있다.
- 교차 프로그램(IS: Intersection Program)
교차 프로그램은 광선이 물체와 교차하는지를 확인한다. 물체와 교차하지 않는 광선도 교차 프로그램을 거쳐간다.
- 임의 명중 프로그램(AH: Any-hit Program)
광선 종류가 그림자인 광선이 물체 삼각형과 교차하는 경우에만 사용한다. 대신 그림자형(shadow type) 광선은 최근접 명중이나 빗맞음 프로그램에서 처리되지 않는다. 삼각형과 교차한 위치는 식 (9)에 정의한 무게 중심 좌표계(barycentric coordinate system)를 쓴다. 임의 명중 프로그램은 최근접 명중 프로그램이 만든 그림자 광선(shadow ray)의 밝기(luma)를 $0$으로 만들어서 그림자를 생성한다.
OptiX 엔진은 임의 명중이나 최근접 명중 프로그램의 호출 우선 순위를 고려하지 않는다. 즉, OptiX 엔진은 임의 명중이나 최근접 명중 프로그램을 무작위로 호출한다. 따라서 임의 명중 프로그램이 먼저 실행되고 그 다음에 최근접 명중 프로그램이 나온다는 선입견으로 코드를 작성해서는 안된다.
- 최근접 명중 프로그램(CH: Closest-hit Program)
임의 명중 중에서 관람자와 가장 가까운 명중을 다루기 위한 프로그램이다. 광선 종류가 휘도인 광선은 임의 명중 프로그램이 아닌 최근접 명중 프로그램에서만 연산된다. 최근접 명중 프로그램에서는 식 (9)의 무게 중심 좌표계로 교차 위치를 설정하고, 식 (18)을 써서 물체의 조도를 RGB 형태로 표현한다. 또한 반사 광선(reflection ray)과 그림자 광선(shadow ray)을 생성해서 더 현실감 있는 시각화를 추구한다. 반사 광선을 추적할지 여부는 명중된 표면의 회색조(灰色調, grayscale)로 판단한다. RGB 색깔로부터 회색조를 만드는 함수 $g_N(\bar s)$는 NTSC(National Television System Committee) 방식을 주로 채택한다.
(19)
여기서 $\bar s$ = $(r, g, b)$, RGB별 가중치는 인간이 색을 인지하는 능력을 뜻한다. 회색조 함수 $g_N(\bar s)$가 $0$과 $1$이면, 각각 검정색과 흰색을 뜻한다. 물체의 재질 자체가 가진 표면 반사 상수(surface reflection constant) $\bar k_r$이 주어질 때, 표면의 회색조는 $g_N(\bar k_r)$이다. 이 회색조가 [표 1]에 있는 밝기(luma)에 곱해져서 반사 광선의 생성 여부를 판정한다. 보통 밝기가 $0.01$ 이상이면, 반사 광선이 만들어져서 새로운 광선 추적이 재귀적으로 시작된다. 관람자로부터 온 광선[= $-\hat V$]이 표면에서 반사되듯이, $\hat V$와 $\hat N$을 식 (16)에 대입해서 반사 광선의 방향 $\hat R_V$를 만든다.
(20)
따라서 제$q$번 화소 위치 $\bar r_s(q)$로 발사한 광선이 만드는 조도 벡터 $\bar I_{q} (\bar r_o)$와 제$q$번 광선에 의한 반사 광선이 가져온 $\bar I_{rq} (\bar r_o)$에 표면 반사 상수 $\bar k_r$을 곱한 값을 더해서 화소의 색깔을 결정한다. 결국 화소 위치 $\bar r_s(q)$에 생성된 최종적인 조도 벡터 $\bar I_\text{tot} [\bar r_s(q)]$가 다음처럼 완성된다.
(21)
여기서 $\bar I_q (\bar r_o)$은 광선 발생 프로그램이 제$q$번째 만든 광선이 $\bar r_0$에 있는 물체에 의해 다양하게 반사되어 생성된 조도 벡터이다.
[표 1] OptiX에 쓰는 광선의 자료 구조, PRD(per-ray data)
이름 (Name) | 자료형 (Data type) | 의미 (Meaning) |
---|---|---|
색깔(color) | float3 혹은 double3 | 광선이 운반하는 RGB 색깔을 계속 합산해서 담음; 물체에 반사가 되면 식 (18)을 이용해 색깔을 갱신함 |
밝기(luma) | float 혹은 double | 반사 광선을 추적할지 판단할 때 사용; 밝기의 기본값은 1이며 보통 0.01보다 작으면 반사가 없다고 판단 |
깊이(depth) | unsigned int | 광선이 물체에서 반사되는 회수를 저장함; 깊이가 어느 회수 이상이 되면 광선을 종료함 |
OptiX에서 광선이 사용하는 자료 구조인 PRD(per-ray data)는 [표 1]과 같다. 색깔은 3차원 벡터인 $(r, g, b)$로 표현하며, $r, g, b$는 0과 1 사이의 실수로 정의한다. OptiX는 가속화를 위해 float의 3차원 벡터 형태인 float3가 기본적으로 쓰인다. 정밀도를 더 향상하려 할 때는 double의 3차원 벡터인 double3를 쓸 수도 있다.
[참고문헌]
[1] A. Appel, "Some techniques for shading machine renderings of solids," AFIPS (American Federation of Information Processing Societies) Joint Computer Conferences, pp. 37–45, Apr. 1968.
[2] C. Ericson, Real-Time Collision Detection, San Francisco, USA: Morgan Kaufmann, 2005.
[3] T. Möller and B. Trumbore, "Fast, minimum storage ray-triangle intersection," J. Graphics Tools, vol. 2, no. 1, pp. 21–28, 1997.
[4] B. T. Phong, "Illumination for computer generated pictures," Commun. ACM, vol. 18, no. 6, pp. 311–317, Jun. 1975.
[5] OpenRT (Open-source Ray Tracing), Saarland University, Germany.
[6] NVIDIA OptiX Ray Tracing Engine, NVIDIA Corp., California, USA.
[7] S. G. Parker, J. Bigler, A. Dietrich, H. Friedrich, J. Hoberock, D. Luebke, D. McAllister, M. McGuire, K. Morley, A. Robison, and M. Stich, "OptiX: a general purpose ray tracing engine," ACM Trans. Graphics, vol. 29, no. 4, pp. 1–13, Jul. 2010.
[8] DXR (DirectX Raytracing), Microsoft Corp., Washington, USA.
[9] CUDA 11.8.0 Toolkit Documentation, NVIDIA Corp., California, USA, 2022.
[10] "Wavefront OBJ File Format," Sustainability of Digital Formats: Planning for Library of Congress Collections, Library of Congress, USA, 2020. (방문일 2022-09-11)
[다음 읽을거리]