지난 포스트에서 레이 트레이싱은 빛을 추적해 이미지 한 픽셀의 색을 구한다는 것을 알아봤습니다. 이번 포스트에서는 추적할 대상인 빛이 어떤 속성들을 가지고 있고 어떻게 생성할 수 있는지 알아보겠습니다.
Ray
빛 추적은 광선이 지나온 자취를 되돌아 가는 것입니다. 레이 트레이싱은 무수히 많은 광선 중 이미지의 한 픽셀을 거쳐 카메라 원점에 도착한 광선만을 추적합니다. 따라서 광선들의 도착점인 카메라 원점은 되돌아 가는 과정에서 출발점이 됩니다. 이 출발점을 라 하겠습니다.
이미지의 크기가 800 x 600 이라면 추적할 광선은 이미지 픽셀의 개수만큼 존재하므로 광선의 개수 역시 800 x 600 입니다. 그리고 800 x 600 개의 광선들은 원점 에서 이미지의 한 픽셀을 향하는 각기 다른 방향 벡터를 가집니다. 이 벡터를 단위 벡터 라 하겠습니다.
광선은 원점 와 벡터 를 이용해 표현할 수 있습니다.
class Ray:
def __init__(self, e: List[float], d: List[float]):
self.e = e # 3차원 벡터
self.d = d # 3차원 벡터 (단위 벡터)
Ray grid
모든 광선은 에서 출발하지만 각각의 픽셀에 따라 방향 벡터 를 달리합니다.
def render_ray_tracing_image():
for y in range(0, height):
for x in range(0, width):
ray = # x, y 를 이용한 광선 객체
grid[y][x] = ray_trace(ray)
픽셀마다 달라지는 광선 객체는 위와 같이 반복문 안에서 생성됩니다. 먼저 최상단 왼쪽 픽셀을 향하는 벡터를 초기값 이라 하겠습니다. 이터레이션마다 초기값이 이동할 한 픽셀의 가로 세로 벡터 , 를 알고 있으면 각 광선의 방향 벡터 는 아래 식과 같습니다.
이를 그림으로 표현하면 다음과 같습니다.
카메라 원점을 라 할 때 월드 좌표계에 대해 카메라의 세 좌표축을 설정합니다. 이 때 카메라 원점과 이미지 평면 사이의 거리는 1입니다.
그림의 카메라 원점 에서 최상단 왼쪽 픽셀을 향하는 벡터를 이라 하고 카메라의 세 좌표축을 , , 라 하면 은 다음과 같이 쓸 수 있습니다.
이 때 , 값은 이미지 평면 가로 세로 길이 각각의 절반입니다. 가로 길이의 경우 카메라의 광각(angle of view) 을 이용해 다음과 같이 쓸 수 있습니다.
는 비례식 을 이용해 아래와 같이 쓸 수 있습니다.
이제 , 를 이용해 을 구할 수 있습니다. 픽셀마다 광선 객체의 방향벡터 를 찾으려면 에 한 픽셀의 가로 세로 벡터 , 를 더하면 됩니다. 예를 들어 픽셀 의 좌표가 (2, 3) 이라면 에 대응하는 광선 객체는 입니다.
, 는 이미지의 가로 세로 길이의 절반인 , 를 이용해 아래와 같이 쓸 수 있습니다.
위 식의 분모에서 1을 빼는 이유는 광선의 방향벡터가 픽셀의 가운데를 향하면서 픽셀 하나만큼의 길이를 제외시키기 때문입니다.
이제 를 알아냈으므로 광선객체의 벡터 속성을 구할 수 있습니다.
식에서 는 광선 객체를 생성하는 이중 반복문의 인덱스입니다. 이로써 각 픽셀에 대한 광선 객체를 구할 수 있게 되었습니다. 광선 객체는 레이 트레이싱의 시작이자 이후 물체와의 부딪힘 판단, 반사, 굴절을 구하는데 사용됩니다. 다음 포스트에서는 광선 객체를 이용해 물체와의 부딪힘을 판단하는 방법을 알아보겠습니다.