하늘 렌더링 1




수놓인 밤하늘은 보는 이를 취하게 만들기도, 다가오는 먹구름은 거악(巨惡)을 형용하기도, 휘황한 노을은 여운을 남기기도 한다. 이렇듯, 날씨는 감정과 분위기를 좌지우지한다. 그리고 날씨를 표현하는 데 있어 하늘은 필수 불가결하다.
본 시리즈에서는 물리 기반 하늘 렌더링의 원리와 구현을 다룬다.
Ⅰ. 수식#
태양의 빛이 카메라에 도달하기까지의 과정을 어떻게 나타낼 수 있을까?
이를 빛이 매질을 통과하는 식인 VRE(Volume Rendering Equation)로 나타내면 다음과 같다.
$$
L(\mathbf{x}, \boldsymbol{\omega}) = \int_0^D T(t)\,\Big[\sigma_s(\mathbf{x}_t)\,L_{\text{scat}}(\mathbf{x}_t, \boldsymbol{\omega}) + \sigma_a(\mathbf{x}_t)\,L_e(\mathbf{x}_t, \boldsymbol{\omega})\Big]\,dt + T(D)\,L_0
$$
광원을 오직 태양으로 단순화함으로써, 발광(emission)에 관한 항을 제거하면,
$$
L(\mathbf{x}, \boldsymbol{\omega}) = \int_0^D \textcolor{#419BF9}{T(t)}\,\Big[\textcolor{#00CC66}{\sigma_s(\mathbf{x}_t)\,L_{\text{scat}}(\mathbf{x}_t, \boldsymbol{\omega})}\Big]\,dt + \color{orange}T(D)\,L_0
$$
위 식을 천천히 살펴보도록 하겠다.
$T(d)$는 들어온 빛에 대한 남은 빛의 비율 ($\frac{L_{\text{out}}}{L_{\text{in}}}$)이다. 거리 $d$에 대해 다음과 같이 표현한다 (Beer-Lambert 법칙).
$$T(d) = e^{-\sigma_t d}$$
하지만 위 식은 균일(homogeneous) 매질의 경우에만 성립한다. 대기(atmosphere)는 고도(altitude)에 따라 입자의 크기가 달라지는 불균일(heterogeneous) 매질이다. 이 경우,
거리 $d$ 동안 매 지점의 상쇄 함수 $\sigma_t(\mathbf{x})$를 적분해야 한다. 즉,
$$
T(d) = e^{-\int_0^d{\sigma_t(\mathbf{x_s})}ds}
$$
이를 통해 기존 VRE에서 $\color{orange}T(D)\,L_0$는 어떻게 해석할 수 있을까?
앞의 $p(\boldsymbol{\omega}^{\prime}, \boldsymbol{\omega})$는 무엇일까? $\mathbf{x}_t$로 들어온 빛은 산란되어 사방으로 흩어진다. 하지만 우리가 원하는 것은, 카메라 방향으로 산란된 빛의 비율이다. 다시 말해, $\omega^{\prime}$에서 온 빛이 $\omega$로 얼마나 향했냐는 것이다. 이를 나타낸 것이 $p(\boldsymbol{\omega}^{\prime}, \boldsymbol{\omega})$이고, 보다시피 들어오고 나가는 방향을 인자로 갖는다. 보통 코드에서는 두 방향의 $\cos$값을 인자로 갖는다.
$p$는 위상 함수(phase function)라고 불린다. 이는 확률 밀도 함수(probability density function)이며, 전 구간에 걸친 적분 값이 1이 된다는 의미이다. 예를 들어, $\omega$와 $\omega^{\prime}$에 관계 없이 모든 방향으로 균일하게 산란하는 위상 함수는 어떻게 될까? 이를 상수 $c$라고 하면,
$$ p(\omega, \omega^{\prime}) = c $$ $$ \int_0^{2\pi}\int_0^{\pi}c\sin(\theta){d\theta}{d\phi} = 1 $$ $$ c4\pi = 1 $$ $$ c = \frac{1}{4\pi} $$
다시 돌아와서, 배운 것을 토대로 $\textcolor{#00CC66}{\sigma_s(\mathbf{x})L_{\text{scat}}(\mathbf{x}, \boldsymbol{\omega})} = \sigma_s(\mathbf{x}) p(\boldsymbol{\omega}^{\prime}, \boldsymbol{\omega}) L_\text{sun}T(D)$를 한 문장으로 정리하면? 대기로 들어와 상쇄되고 카메라 쪽으로 산란된 햇빛의 양이다.
지금까지 아래 식을 차근차근 뜯어보았다. 매질을 거쳐 카메라에 도달하는 빛의 양을 어느 정도 이해했기를 바란다. $$ L(\mathbf{x}, \boldsymbol{\omega}) = \int_0^D \textcolor{#419BF9}{T(t)}\,\Big[\textcolor{#00CC66}{\sigma_s(\mathbf{x}_t)\,L_{\text{scat}}(\mathbf{x}_t, \boldsymbol{\omega})}\Big]\,dt + \color{orange}T(D)\,L_0 $$ 다음 장에서는 하늘 렌더링 모델에서 이 수치들이 어떻게 결정되는지 알아보겠다.
Ⅱ. 레일리와 미 (Rayleigh and Mie)#
고도에 따라 대기 중 입자의 평균 크기가 달라진다. 이에 따라 산란의 특성이 달라진다. 입자가 비교적 작은 위쪽에는 레일리 산란(Rayleigh scattering)이 주로 일어나고, 입자가 비교적 큰 아래쪽에는 미 산란(Mie scattering)이 주를 이룬다.
두 산란에 따라 위상 함수, 산란 계수, 밀도 분포가 달라지므로 이를 중심으로 살펴보도록 하겠다. 밀도 분포는 해당 고도에서 해당 산란을 일으키는 입자가 얼마나 존재하는지로 이해하면 되겠다.
레일리 산란#
레일리 산란의 위상 함수는 다음을 사용하겠다. $$ p(\mu) = \frac{3(1 + \mu^2)}{16\pi} $$ $\mu$는 들어오는 빛의 방향과 산란되어 나가는 빛의 방향 사잇각의 $\cos$값이다.
레일리 산란의 산란 계수는 벡터로 표현된다. $\sigma_s^r = \begin{bmatrix} 5.802 \\ 13.558 \\ 33.1 \end{bmatrix}\times10^{-6}$을 사용하겠다.
레일리 산란의 고도 $h$에 따른 밀도 분포는 $d^r(h) = e^{\frac{-h}{8.0km}}$ 를 사용하겠다.
위 내용을 hlsl로 옮기면 다음과 같다.
float phase_rayleigh(float mu) {
return 0.05968310365 * (1 + mu*mu);
}
float3 Sr = float3(5.802e-6, 13.558e-6, 33.1e-6); // 레일리 산란 계수
float Dr = exp(-altitude / 8.0km); // 8.0km
미 산란#
미 산란의 위상 함수는 다음처럼 Cornette-Shanks 위상함수로 표현할 수 있다.
$$ p(\mu, g) = {\frac{3}{8\pi}}{\frac{(1 - g^2)(1 + \mu^2)}{(2 + g^2){(1 + g^2 - 2g\mu)}^{3/2}}} $$
$g$는 비대칭 계수(asymmetry parameter)로서, 산란된 빛이 원래 진행 방향으로 얼마나 유지하려는지를 나타낸다.
$g>0$의 경우, 빛이 원래 진행 방향으로 더 많이 산란되고,
$g<0$의 경우, 빛이 반대 방향으로 더 많이 산란된다. 우리는 $g=0.8$로 설정하겠다.
미 산란의 산란 계수 $\sigma_s^m$는 $2.1\times10^{-5}$를 사용하도록 하겠다. 이는 날씨, 대기의 오염도 등에 따라 달라질 수 있다.
미 산란의 고도 $h$에 따른 밀도 분포는 $d^m(h) = e^{\frac{-h}{1.2km}}$ 를 사용하겠다.
위 내용을 hlsl로 옮기면 다음과 같다.
float phase_mie(float mu, float g) {
// Cornette-Shanks
float g2 = g*g;
return 0.11936620731 * (((1 - g2)*(1 + mu*mu)) / ((2 + g2) * pow(1 + g2 - 2*g*mu, 1.5)));
}
float3 Sm = 2.1e-5; // 미 산란 계수
float Dm = exp(-altitude / 1.2e3); // 1.2km
정리#
표로 정리하면 다음과 같다.
| 레일리 | 미 | |
|---|---|---|
| 위상 함수 | \(p(\mu)=\frac{3(1+\mu^2)}{16\pi}\) | \(p(\mu,g)=\frac{3}{8\pi}\frac{(1-g^2)(1+\mu^2)}{(2+g^2)(1+g^2-2g\mu)^{3/2}}\) |
| 산란 계수 | \(\sigma_s^r=[5.802,13.558,33.1]\times10^{-6}\) | \(\sigma_s^m=2.1\times10^{-5}\) |
| 밀도 분포 | \(d^r(h)=e^{-h/(8.0\text{ km})}\) | \(d^m(h)=e^{-h/(1.2\text{ km})}\) |
아래는 레일리나 미 산란만 있을 때와 합쳐졌을 때의 렌더링 결과이다.



아래는 각 산란을 일으키는 입자의 밀도 분포 그래프이다. $x$축은 고도(km)이다. 파란색 레일리는 초록색 미보다 넓게, 보다 높게까지 분포되어 있다. 붉은색 오존은 제일 높은 고도에 텐트 형태로 존재한다.
III. 결과#
아래는 C++20과 Direct3D 12로 구현한 실시간 하늘 렌더링의 결과이다.

레퍼런스#
Sébastien Hillaire — A Scalable and Production Ready Sky and Atmosphere Rendering Technique
Sébastien Hillaire — Physically Based Sky, Atmosphere and Cloud Rendering in Frostbite
Tomoyuki Nishita et al. — Display of The Earth Taking in Account Atmospheric Scattering
Epic — Sky Atmosphere Component in Unreal Engine