렌더링 방정식
고친 사람 github-actions[bot]
렌더링 방정식은 한 점이 눈에 얼마나 밝게 보일지 정해 주는 식입니다. 그 점이 스스로 내는 빛에 사방에서 들어와 반사되는 빛을 더합니다. 들어오는 빛 역시 같은 식으로 구해야 해서 한 번에 풀리지 않습니다. 실물 같은 그림을 만드는 계산법은 대부분 이 식을 근사해서 풉니다.
쉽고 빠른 이해
렌더링 방정식은 화면의 점 하나를 어떤 밝기로 칠할지 정하는 계산 규칙입니다. 빨간 벽 옆의 흰 바닥이 살짝 붉게 물드는 것까지 이 식이 설명합니다.
이 식이 없으면 「맞는 밝기」가 무엇인지 기준이 없습니다. 그림을 빨리 그리는 꼼수는 많습니다. 그 꼼수가 무엇을 빼먹었는지 견줄 잣대가 이 식입니다.
어떻게 도는가:
- 그 점이 전등처럼 스스로 빛나면 그 빛을 먼저 셉니다
- 그 점 위쪽의 모든 방향에서 들어오는 빛을 모읍니다. 재질에 따라 눈 쪽으로 반사되는 몫만 남겨 더합니다
- 들어오는 빛을 알려면 그 방향에 있는 다른 점에서 같은 계산을 합니다. 몇 번 거듭한 뒤 멈춥니다
대가는 계산량입니다. 방향이 끝없이 많아서 몇 개만 골라 근사합니다. 덜 고르면 그림에 자글자글한 알갱이가 남습니다.
그래서 쓰임새가 갈립니다. 한 장에 몇 분을 써도 되는 영화는 식을 거의 다 근사해서 풉니다. 1초에 수십 장을 그려야 하는 게임은 식의 일부를 덜어 낸 계산법을 섞어 씁니다.
상세
동네 소식에 밝은 사람이 아는 것은 자기가 본 것에 이웃에게 들은 것을 더한 것입니다. 그 이웃이 아는 것도 다시 다른 이웃에게 들은 것입니다. 한 사람이 무엇을 아는지 알려면 온 동네를 돌아야 합니다.
한 점의 밝기도 이렇습니다. 그 점이 스스로 내는 빛에 이웃한 표면에서 들어온 빛을 더한 값입니다. 그 이웃의 밝기도 다시 다른 표면에서 들어온 빛에 기댑니다.
렌더링은 3차원 장면을 그림 한 장으로 바꾸는 일입니다. 그림의 점마다 무슨 색을 얼마나 밝게 칠할지 정해야 합니다.
렌더링 방정식은 그 밝기를 정하는 식입니다. 표면의 한 점에서 어느 한 방향으로 나가는 빛이 얼마인지 적습니다. 그 방향을 눈 쪽으로 잡으면 그 점이 눈에 보이는 밝기가 나옵니다.
맞는 밝기의 기준
그림을 빨리 그리는 방법은 여럿입니다. 전등에서 곧장 온 빛만 계산하기도 합니다. 그늘에 고른 밝기를 조금 얹어 어둠을 감추기도 합니다. 이런 방법은 저마다 무언가를 빼먹습니다.
무엇을 빼먹었는지 알려면 빼먹지 않은 답이 있어야 합니다. 렌더링 방정식이 그 답을 적은 식입니다. 빛이 표면에 닿아 튕겨 나가는 물리를 한 줄로 묶었습니다. 덕분에 서로 다른 계산법을 같은 잣대에 놓고 견줄 수 있습니다.
식을 말로 적으면
식 전체는 두 항의 합입니다.
나가는 빛 = 스스로 내는 빛
+ 사방에서 들어온 빛 가운데 이 방향으로 반사되는 몫의 합
첫째 항은 그 점이 직접 내는 빛입니다. 전등이나 모니터 화면 같은 광원이면 이 값이 있습니다. 벽이나 바닥처럼 스스로 빛나지 않는 표면이면 0 입니다.
둘째 항이 이 식의 본체입니다. 다른 곳에서 날아와 이 점에 닿은 빛 가운데 우리가 보는 방향으로 다시 튕겨 나가는 몫을 모두 더합니다. 불 꺼진 벽이 보이는 까닭은 이 항 덕분입니다.
한 방향으로 오는 빛의 세기
식이 다루는 「빛」은 한 점에서 한 방향으로 오가는 빛의 세기입니다. 물리에서는 이 양을 복사휘도(radiance)라 합니다. 점 하나와 방향 하나를 정하면 값이 하나 나옵니다.
같은 점이라도 보는 방향이 바뀌면 값이 달라질 수 있습니다. 거울 속 같은 점이 보는 각도에 따라 다른 것을 비추는 것이 그 예입니다. 식에 점과 방향이 늘 짝으로 붙어 다니는 까닭입니다.
기호로 적은 식
흔히 쓰는 기호로 적으면 이렇습니다. 앞의 말풀이와 항 하나하나가 짝을 이룹니다.
L_o(x, ω_o) = L_e(x, ω_o) + ∫_Ω f_r(x, ω_i, ω_o) · L_i(x, ω_i) · cosθ_i dω_i
x 는 표면의 한 점입니다. ω(오메가)는 방향입니다. 아래 첨자 o 는 나가는 쪽을 뜻합니다. i 는 들어오는 쪽을 뜻합니다. 기호마다 무엇인지는 이렇습니다.
| 기호 | 뜻 | 말풀이에서 |
|---|---|---|
L_o(x, ω_o) |
점 x 에서 방향 ω_o 로 나가는 빛 | 나가는 빛 |
L_e(x, ω_o) |
점 x 가 방향 ω_o 로 스스로 내는 빛 | 스스로 내는 빛 |
∫_Ω … dω_i |
위쪽의 모든 방향 ω_i 에 대해 더한다 | 사방에서 들어온 빛의 합 |
L_i(x, ω_i) |
방향 ω_i 에서 점 x 로 들어오는 빛 | 들어온 빛 |
f_r(x, ω_i, ω_o) |
들어온 빛 가운데 ω_o 쪽으로 반사되는 비율 | 반사되는 몫 |
cosθ_i |
빛이 비스듬히 들어올수록 작아지는 값 | 말풀이에는 없다 |
적분 기호 · cosθ_i · f_r 은 아래 소절 셋에서 하나씩 풉니다.
사방에서 들어오는 빛의 합
표면 한 점에 빛이 들어올 수 있는 방향은 표면 위쪽으로 열린 반쪽 공간 전부입니다. 밥그릇을 엎어 놓은 모양을 떠올리면 됩니다. 이 방향들의 모음이 반구입니다. 식에서는 큰 오메가 Ω 로 적습니다.
방향은 끝없이 많습니다. 끝없이 많은 몫을 빠짐없이 더하는 계산이 적분입니다. 식의 ∫ 기호가 「반구의 모든 방향에 대해 다 더한다」는 뜻입니다.
표면 밑에서 오는 빛은 합에 넣지 않습니다. 불투명한 표면은 밑에서 빛이 들어올 수 없기 때문입니다. 유리처럼 빛이 뚫고 지나가는 재질은 아래쪽 반구까지 넓혀 온 구에 걸쳐 더합니다.
비스듬한 빛이 약해지는 까닭
손전등을 벽에 똑바로 비추면 동그랗고 밝은 원이 생깁니다. 비스듬히 비추면 빛이 길쭉하게 퍼집니다. 그만큼 흐려집니다. 같은 양의 빛이 더 넓은 면에 나뉘어 닿기 때문입니다.
식의 cosθ_i 가 이 효과를 담습니다. 표면에 수직으로 세운 선을 법선이라 합니다. θ_i(세타)는 들어오는 방향이 법선과 이루는 각입니다.
똑바로 들어오면 각이 0 입니다. 이때 코사인은 1 입니다. 표면을 스치듯 들어오면 각이 90도에 가까워집니다. 코사인은 0 에 가까워집니다. 그만큼 그 방향의 빛이 덜 보태집니다.
재질이 정하는 반사
반사되는 몫은 재질마다 다릅니다. 거울은 들어온 빛을 거의 한 방향으로만 반사합니다. 흰 종이는 어느 쪽에서 보든 비슷하게 밝습니다. 빛을 사방으로 고르게 흩뿌리기 때문입니다.
이 차이를 담는 것이 식의 f_r 입니다. 이름은 BRDF(Bidirectional Reflectance Distribution Function, 양방향 반사 분포 함수)입니다. 들어오는 방향과 나가는 방향을 받아 그 짝에서 얼마나 반사되는지 돌려주는 함수입니다. 이름의 「양방향」이 이 두 방향을 가리킵니다.
BRDF 에는 지켜야 할 조건이 있습니다. 모든 방향으로 반사되는 빛을 다 더해도 들어온 빛보다 많을 수 없습니다. 표면이 빛을 새로 만들어 내지는 않기 때문입니다. 이 조건이 에너지 보존입니다.
이 조건이 깨진 재질을 쓰면 튕길 때마다 빛이 불어납니다. 계산을 거듭할수록 장면이 끝없이 밝아집니다.
자기 자신을 부르는 식
식을 다시 보면 들어오는 빛 L_i 가 걸립니다. 점 x 에 어느 방향에서 들어온 빛은 그 방향에 있는 다른 점 y 가 x 쪽으로 내보낸 빛입니다. 곧 L_i 는 y 에서 구한 L_o 입니다.
L_i(x, ω_i) = L_o(y, −ω_i) y 는 x 에서 ω_i 쪽으로 처음 닿는 점
−ω_i 는 방향을 뒤집은 것입니다. y 에서 x 를 바라보는 방향입니다.
x 의 답을 구하려면 y 의 답이 있어야 합니다. y 의 답에는 또 다른 점 z 의 답이 들어갑니다. 구하려는 값이 오른쪽 적분 안에도 들어 있는 꼴입니다. 이런 식을 적분 방정식이라 합니다.
flowchart TD
E["눈"] -->|"x 에서 나가는 빛이 필요"| X["점 x · 바닥"]
X -->|"x 에 들어오는 빛 = y 에서 나가는 빛"| Y["점 y · 벽"]
Y -->|"y 에 들어오는 빛 = z 에서 나가는 빛"| Z["점 z · 천장"]
Z -->|"z 에 들어오는 빛"| L["광원 · 스스로 내는 빛"]
화살표는 「무엇을 알아야 하나」를 따라갑니다. 바닥의 점을 알려면 벽의 점을, 벽의 점을 알려면 천장의 점을 알아야 합니다. 빛은 반대로 광원에서 눈 쪽으로 흐릅니다.
백엔드 개발자에게 익숙한 말로 하면 재귀 함수입니다. 자기 자신을 부릅니다. 끝나는 조건을 두지 않으면 멈추지 않습니다.
튕김 횟수로 펼친 식
식에 자기 자신을 거듭 넣어 펼치면 뜻이 보입니다. 나가는 빛은 튕긴 횟수별 빛을 끝없이 더한 것이 됩니다.
나가는 빛 = 스스로 내는 빛
+ 한 번 튕긴 빛 ← 직접 조명
+ 두 번 튕긴 빛 ← 여기부터 간접 조명
+ 세 번 튕긴 빛
+ …
광원에서 곧장 와서 이 점에서 한 번 튕긴 빛이 직접 조명입니다. 다른 표면을 한 번 이상 거쳐 온 빛은 간접 조명입니다.
둘을 함께 계산하는 일이 전역 조명입니다. 전역 조명이 푸는 식이 이 식입니다. 직접 조명만으로는 그늘 속 물체가 보이지 않아서 둘을 함께 다룹니다.
간접 조명은 눈에 띄는 흔적을 남깁니다. 빨간 벽에 튕긴 빛이 옆의 흰 바닥에 닿으면 바닥이 살짝 붉게 물듭니다. 두 번 튕긴 빛이 만든 색 번짐입니다.
표면은 튕길 때마다 빛의 일부를 삼킵니다. 그 탓에 뒤로 갈수록 항이 작아집니다. 몇 번째 튕김에서 끊어도 그림이 크게 달라지지 않는 까닭입니다.
끝없는 방향을 몇 개로 줄이는 근사
남은 문제는 적분입니다. 방향이 끝없이 많으니 다 더할 수는 없습니다. 방향을 무작위로 몇 개 골라 그 값으로 전체 합을 근사합니다. 무작위 표본으로 답을 근사하는 이 방법이 몬테카를로 방법입니다.
재귀와 근사를 합치면 식은 아래 같은 함수가 됩니다. 파이썬 꼴로 적은 뼈대입니다.
def outgoing(x, wo, bounces):
light = emitted(x, wo)
if bounces == 0:
return light
for wi in pick_directions(x):
y = first_hit(x, wi)
incoming = outgoing(y, -wi, bounces - 1)
light += weight(x, wi, wo) * incoming
return light
emitted 가 스스로 내는 빛입니다. for 문은 적분을 몇 방향의 합으로 바꾼 것입니다. bounces 가 0 이 되면 멈춥니다. 앞에서 말한 「몇 번째 튕김에서 끊기」입니다.
weight 는 세 값을 곱한 것입니다. 앞의 둘은 반사 비율 f_r 과 기울기 cosθ_i 입니다.
셋째 값은 고른 방향 하나가 반구의 얼마만큼을 대신 맡는지입니다. 방향을 고르게 넷 고르면 하나가 반구의 4분의 1을 대신 맡습니다. 그 방향에서 잰 빛이 반구의 4분의 1에 고루 들어온다고 보고 그만큼 곱합니다. 그래서 넷만 더해도 반구 전체를 다 더한 값에 가까워집니다.
튕길 때마다 불어나는 길
이 뼈대에는 함정이 있습니다. 길은 빛이 튕기며 거쳐 가는 점들을 이은 줄입니다. 한 점에서 방향을 둘 고르면 닿은 점마다 또 둘을 고릅니다. 튕길 때마다 따라갈 길이 두 배씩 불어납니다.
flowchart TD
X["점 x"] --> Y1["y1"]
X --> Y2["y2"]
Y1 --> Z1["z1"]
Y1 --> Z2["z2"]
Y2 --> Z3["z3"]
Y2 --> Z4["z4"]
맨 윗줄은 처음 점 하나입니다. 둘째 줄은 한 번 튕긴 뒤의 점 둘입니다. 셋째 줄은 두 번 튕긴 뒤의 점 넷입니다. 방향을 넷씩 고르면 1, 4, 16, 64 로 불어납니다.
패스 트레이싱은 이 불어남을 피합니다. 튕길 때마다 방향을 하나만 골라 길 하나를 끝까지 따라갑니다. 대신 화면의 점마다 이런 길을 여러 개 쏘아 평균을 냅니다.
길이 적으면 점마다 근사한 값이 들쭉날쭉합니다. 화면에 자글자글한 알갱이가 남습니다. 이 알갱이를 노이즈라 합니다.
노이즈는 길을 더 쏘면 줄지만 그만큼 시간이 듭니다. 그래서 다 그린 뒤 알갱이만 따로 지우는 단계를 두기도 합니다. 이 단계가 디노이징입니다.
식을 덜어 내는 계산법
모든 렌더링 방법이 이 식을 다 풀지는 않습니다. 대부분은 식의 일부를 덜어 내고 빨리 그립니다. 어디를 덜어 냈는지 보면 그 방법이 무엇을 못 그리는지 보입니다.
| 방법 | 식에서 덜어 낸 것 | 그 결과 |
|---|---|---|
| 패스 트레이싱 | 거의 없다. 적분을 무작위 표본으로 근사한다 | 표본이 적으면 노이즈가 남는다 |
| 라디오시티 | 재질을 사방으로 고르게 흩뿌리는 것 하나로 고정한다 | 거울 같은 반사를 못 그린다 |
| 지역 조명 | 두 번 이상 튕긴 항을 버린다 | 광원이 안 보이는 그늘이 새까맣다 |
| 주변광 | 간접 조명 항을 상수 하나로 갈음한다 | 구석과 방 한가운데가 똑같이 밝다 |
| 라이트맵 베이킹 | 식을 미리 풀어 결과를 그림 파일에 저장한다 | 광원이나 물체가 움직이면 다시 풀어야 한다 |
라이트맵은 표면마다 밝기를 담아 둔 그림 파일입니다. 베이킹은 그 밝기를 미리 계산해 라이트맵에 담는 일입니다.
영화처럼 그림 한 장에 몇 분을 써도 되는 렌더링이 오프라인 렌더링입니다. 표의 첫 줄처럼 식을 거의 다 근사합니다.
게임처럼 1초에 그림 수십 장을 그려야 하는 렌더링이 실시간 렌더링입니다. 한 장에 쓸 시간이 짧아서 아래쪽 줄들을 섞어 씁니다.
이 식이 다루지 않는 빛
렌더링 방정식은 빛이 표면에서만 튕긴다고 봅니다. 표면과 표면 사이의 공기는 빛을 건드리지 않는다고 칩니다. 이 가정이 안 맞는 장면은 식을 넓혀야 합니다.
| 현상 | 왜 이 식 밖인가 | 넓힌 식이나 방법 |
|---|---|---|
| 안개 · 연기 · 구름 | 빛이 공중의 작은 입자에 부딪혀 흩어진다 | 복사 전달 방정식 · 참여 매질 |
| 피부 · 우유 · 대리석 | 빛이 표면 속으로 들어가 다른 점에서 나온다 | 표면하 산란 |
| 비눗방울의 무지갯빛 | 빛의 파동 성질인 간섭이 만든다. 이 식은 빛을 곧은 선으로만 다룬다 | 파동 광학 |
첫 줄의 참여 매질은 빛이 지나가는 동안 빛을 흩거나 삼키는 물질입니다. 안개 낀 날 헤드라이트 불빛이 기둥처럼 보이는 것이 이 물질 탓입니다.
관련 항목
렌더링 방정식을 이루는 항과 물리량
복사휘도 · BRDF · BSDF · BTDF · 법선 · 반구 · 입체각 · 램버트 코사인 법칙 · 에너지 보존 · 알베도
렌더링 방정식을 푸는 알고리즘
패스 트레이싱 · 양방향 패스 트레이싱 · 메트로폴리스 광 수송 · 포톤 매핑 · 라디오시티 · 광선 추적 · 몬테카를로 방법 · 중요도 샘플링 · 러시안 룰렛 · 디노이징
렌더링 방정식을 덜어 내 근사하는 기법
지역 조명 · 주변광 · 라이트맵 · 베이킹 · 라이트 프로브 · 앰비언트 오클루전 · 화면 공간 전역 조명
렌더링 방정식이 계산해 내는 조명 효과
직접 조명 · 간접 조명 · 전역 조명 · 색 번짐 · 코스틱 · 그림자 · 반사
렌더링 방정식을 넓힌 식과 광학 모형
복사 전달 방정식 · 참여 매질 · BSSRDF · 표면하 산란 · 파동 광학 · 기하 광학
렌더링 방정식을 풀 때 쓰는 수학 개념
적분 · 적분 방정식 · 재귀 · 노이만 급수 · 확률 밀도 함수
렌더링 방정식이 속하는 상위 분야
다른 이름: rendering equation · 렌더 방정식