한양과학기술고등학교

에듀테크 프로젝트 대시보드

레이 트레이싱 (Ray Tracing)

레이 트레이싱 (Ray Tracing)

현실에서 빛은 전구에서 나와 물체에 부딪히고 튕기다가 운 좋게 우리 눈에 들어온다. 눈에 안 들어오는 광선까지 전부 따라가면 계산이 무한히 낭비된다. 그래서 레이 트레이싱은 순서를 거꾸로 뒤집는다 — 눈(카메라)에서 화면의 픽셀마다 광선을 쏘고, 그 광선이 어떤 물체에 처음 닿는지 찾고, 그 지점에서 빛이 얼마나 오는지 (그림자·반사·굴절) 되짚어 색을 계산한다. 아래 렌더러는 브라우저에서 진짜로 이 과정을 픽셀 하나하나 돌린다. 조명을 움직이고 반사 깊이를 바꿔 보라.

1. 브라우저 안의 실시간 레이 트레이서

장면에는 구 3개와 체크무늬 바닥이 있다. 카메라는 정면 고정, 조명 각도 슬라이더로 점광원을 궤도 위에서 돌린다. 반사 깊이를 0으로 두면 순수한 색만, 1~4로 올리면 구 표면에 옆 물체가 거울처럼 비친다. 해상도를 올릴수록 선명하지만 픽셀 수만큼 광선이 늘어 느려진다.

렌더링 준비 중…
조명을 옆으로 밀면 각 구가 바닥에 부드러운 그림자를 드리우고, 빛을 향한 쪽에 밝은 점(스페큘러 하이라이트)이 생긴다. 두 효과 모두 화면 픽셀에서 새 광선을 하나 더 쏘아 얻는다.

2. 픽셀마다 광선 하나 — 카메라에서 쏜다

카메라 위치 O 를 원점으로 두고, 눈앞에 가상 스크린을 하나 세운다. 그 스크린의 각 픽셀 중심을 향하는 단위 방향 벡터 d 를 구하면, 광선은 반직선 P(t) = O + t·d (t > 0) 하나로 표현된다. 가로 480 × 세로 360 화면이면 17만 개의 광선을 쏜다 — 데모가 조금 버벅이는 이유다.

P(t) = O + t · d
d = normalize(  forward + (2·px/W − 1)·tan(fov/2)·right + (1 − 2·py/H)·tan(fov/2)·up  )

3. "무엇에 맞았나?" — 광선-구 교차 방정식

중심 C, 반지름 r 인 구 위의 점은 |X − C|² = r² 를 만족한다. 여기에 광선 X = O + t·d 를 대입하면 t 에 대한 이차방정식이 나온다. 판별식의 부호가 곧 답이다 — 음수면 안 맞음, 양수면 두 교점(들어갈 때·나올 때) 중 작은 양의 t 가 눈에 보이는 표면이다. 모든 물체에 대해 이 t 를 구하고 가장 가까운(t가 최소인) 것을 고르면, 그게 이 픽셀에 보이는 물체다.

(d·d) t² + 2 d·(O−C) t + (|O−C|² − r²) = 0
판별식 Δ = b² − 4ac    Δ < 0 → 안 맞음  |  Δ ≥ 0 → t = (−b − √Δ) / 2a
표면 법선 N = normalize( P(t) − C )
법선 N은 표면이 "어느 쪽을 보고 있는지"를 나타내는 화살표다. 조명 계산이 전부 이 N 과 빛 방향의 각도에서 나온다. 바닥 같은 평면은 더 간단해서 t = −(O·n + h) / (d·n) 한 줄로 교점이 구해진다.

4. 색 정하기 — 퐁(Phong) 조명 모델

교점에서의 색은 세 성분을 더한 것이다. ① 앰비언트: 사방에서 오는 최소한의 빛(완전한 암흑 방지). ② 디퓨즈: 표면이 빛을 향한 정도 max(0, N·L) — 정면으로 받을수록 밝다(램버트 법칙). ③ 스페큘러: 반사 방향 R 이 시선 V 와 가까울 때 생기는 날카로운 흰 점, max(0, R·V)shininess.

color = ka·ambient  +  kd·(N·L)·light  +  ks·(R·V)α·light
L = 교점→광원 단위벡터  |  R = reflect(−L, N)  |  V = 교점→카메라 단위벡터

5. 광선이 광선을 부른다 — 그림자·반사·재귀

레이 트레이싱이 CGI처럼 보이는 진짜 이유는 교점에서 광선을 또 쏘기 때문이다.

그림자 광선

교점에서 광원 쪽으로 광선을 하나 쏜다. 도중에 다른 물체에 막히면 이 지점은 그늘 — 디퓨즈·스페큘러를 빼고 앰비언트만 남긴다.

반사 광선

표면이 매끄러우면 reflect(d, N) 방향으로 광선을 튕겨 다시 trace() 를 호출한다. 그 결과 색을 일정 비율 섞는다 — 재귀.

깊이 제한

거울 두 개가 마주 보면 반사가 무한히 이어진다. 그래서 depth 를 한도(데모는 최대 4)로 두고 넘으면 검정을 반환해 재귀를 끊는다.

굴절 광선

유리·물은 여기에 스넬의 법칙으로 꺾인 투과 광선을 하나 더 쏜다. 데모에는 없지만 원리는 반사와 똑같다.

광선 1개가 그림자 광선 1개 + 반사 광선 1개를 낳고, 그 반사 광선이 또… 이렇게 광선 트리가 자란다. 픽셀 17만 개 × 물체 4개 × 반사 깊이 → 수백만 번의 교차 계산. GPU의 RT 코어가 바로 이 교차 판정을 전용 회로로 가속한다.

6. 핵심 20줄 (JavaScript)

// 광선 O + t·d 와 구(center C, 반지름 r)의 가장 가까운 교차 t function hitSphere(O, d, C, r) { const oc = sub(O, C); const a = dot(d, d); const b = 2 * dot(oc, d); const c = dot(oc, oc) - r * r; const disc = b * b - 4 * a * c; // 판별식 if (disc < 0) return Infinity; // 안 맞음 const t = (-b - Math.sqrt(disc)) / (2 * a); return t > 0.001 ? t : Infinity; // 자기 자신은 제외 } function trace(O, d, depth) { const hit = closest(O, d); // 모든 물체 중 t 최소 if (!hit) return SKY; const P = add(O, scale(d, hit.t)); // 교점 const N = hit.normal(P); let col = shade(P, N, d, hit.mat); // 앰비언트+디퓨즈+스페큘러(+그림자) if (depth > 0 && hit.mat.mirror > 0) { const r = reflect(d, N); col = mix(col, trace(P, r, depth - 1), hit.mat.mirror); // 재귀! } return col; }

7. 래스터화와 무엇이 다른가

기준래스터화 (게임 기본)레이 트레이싱 (영화·RTX)
기본 질문이 삼각형이 어느 픽셀을 덮나?이 픽셀에 무엇이 보이나?
순회 방향물체 → 화면 (투영)화면 → 물체 (광선)
그림자·반사그림자맵·큐브맵 등 따로 흉내광선 하나 더 쏘면 공짜로 정확
속도매우 빠름, 실시간 60fps+무겁다 — 전용 하드웨어/노이즈 제거 필요
쓰는 곳대부분의 게임, 모바일픽사·블렌더 Cycles, 최신 게임의 반사·전역조명
요즘 게임은 하이브리드다: 화면 대부분은 빠른 래스터화로 그리고, 반사·그림자·전역 조명만 레이 트레이싱으로 얹은 뒤 AI 디노이저로 잡음을 지운다.

8. 한 걸음 더

Whitted, 1980

Turner Whitted가 그림자·반사·굴절을 재귀 광선으로 묶은 논문 한 편으로 오늘날의 레이 트레이싱이 시작됐다. 74분에 512×512 한 장.

패스 트레이싱

표면에서 무작위 방향으로 광선을 튕겨 빛의 모든 경로를 몬테카를로로 적분한다. 부드러운 그림자·색번짐까지 물리적으로 정확하지만 잡음이 대가.

가속 구조 (BVH)

물체가 수백만 개면 광선마다 전부 검사할 수 없다. 경계 볼륨 계층(BVH) 트리로 O(log n) 에 후보를 좁힌다. k-d 트리의 사촌.

RT 코어 / 디노이징

GPU가 광선-삼각형 교차와 BVH 순회를 전용 회로로 처리하고, 픽셀당 광선 1~2개만 쏜 잡음투성이 결과를 신경망이 깨끗한 그림으로 복원한다.