空の色(大気散乱シミュレーション)

空の色(大気散乱シミュレーション)

 太陽光が大気へ突入すると散乱が起こります。この散乱をシェーダーでシミュレーションすることで、空の色を描画することができます。この記事では、GPU Gems 2:Chapter 16. Accurate Atmospheric Scatteringを参考にシェーダーを作成しました。

シェーダーの作成

shader

 作成したシェーダーは以下の通りです。GPU Gem2のコードから大きな変更はありません。

実行結果

 上記シェーダーの実行結果は以下の通りです。InnerRadius=10000、OuterRadius=10250、Kr=0.0025、Km=0.001としています。また、シェーダーを使用するsphereの大きさは20500、カメラ位置は(0, 10000, 0)です。

シェーダーの変更

 このシェーダーでは球の大きさがOuterRadiusと一致していなければなりません。そこで、球の大きさに関わらず実行できるようにシェーダーを変更しました。計算に使用する頂点のワールド座標を半径がOuterRadiusの球の座標となるように変更しています。また、_OuterRadiusと実際の球の大きさ(_SphereRadius)の比をとり、その値をカメラ座標に掛けることで、 OuterRadiusに応じた座標へ変更しています。

実行結果

 変更したシェーダーの実行結果は以下の通りです。InnerRadius=10000、OuterRadius=10250、 SphereRadius=5125, Kr=0.0025、Km=0.001としています。また、シェーダーを使用するsphereの大きさは10250、カメラ位置は(0, 5000, 0)です。

球の大きさを半分にしましたが、先ほどと同様の結果が得られました。

Skyboxでの使用

 図1に示すように、今までは大気中にカメラがあることを想定して計算を行っていました。しかし、Skyboxで使用するにあたり、大気中とされる位置までカメラを移動させるのはあまり現実的ではありません。そこで、図2に示すようにカメラの高さが0のとき、図1の場合と同じ結果が得られるようにコードを変更しました。

図1.大気散乱を計算する際のモデル

図2.スカイボックスにおけるベクトルと座標

 図2におけるカメラから任意の点\(P\)へ向かうベクトル\(\vec d\)に平行でかつ図1のカメラの位置を通る直線\(l\)と大気の外側の交点を \(P'(x,y,z) \) とします。点\(P\)と点\(P’\)においてカメラからの方向は一致します。よって、点 \(P\)において点\(P’\)の計算結果が得られるように処理すればよいということになります。よって、 点 \(P\)の座標を点\(P’\)の座標へ置き換える処理を加えます。

 点 \(P’\)の座標を計算するための式を求めます。カメラの位置を表すベクトル\(\vec{a}\)を通り、 カメラから大気の外側に向かうベクトル\(\vec{d}\)に平行な直線のベクトル方程式は

$$ \vec{p’}=\vec{a}+t\vec{d} \tag{1} $$

となります。ここで、\(t\)は媒介変数です。この式は以下のように表せます。

$$ \begin{align} \left\{ \begin{array}{l} x&=a_{x}+td_{x}\\ y&=a_{y}+td_{y}\\ z&=a_{z}+td_{z} \end{array} \right. \tag{2} \end{align} $$

また、球の式は以下の通りです。

$$ x^{2}+y^{2}+z^{2}=r^{2} \tag{3} $$

式\((2)\)を式 \((3)\)に代入すると、大気の外側と直線\(l\)の交点における\(t\)を求めることができます。

$$ (a_{x}+td_{x})^{2}+(a_{x}+td_{x})^{2}+(a_{x}+td_{x})^{2}-r^2=0\\ (d_{x}^{2}+d_{y}^{2}+d_{z}^{2})t^{2}+2(a_{x}d_{x}+a_{y}d_{y}+a_{z}d_{z})t+a_{x}^{2}+a_{y}^{2}+a_{z}^{2}-r^{2}=0 $$

上式は内積を用いることで

$$ \vec{d} \cdot \vec{d}t^{2}+2\vec{a} \cdot \vec{d}t+\vec{a} \cdot \vec{a}-r^{2}=0 \tag{4} $$

となります。ここで、\(\vec d\)を正規化します。すると、\(\vec{d} \cdot \vec{d}\)は\(1\)となります。よって、上式は以下のようになります。

$$ t^{2}+2\vec{a} \cdot \vec{d}t+\vec{a} \cdot \vec{a}-r^{2}=0 \tag{5} $$

解の公式より\(t\)は

$$ \begin{align} t&=\frac{2\vec a \cdot \vec d\pm \sqrt{(2\vec a \cdot \vec d)^2-4(\vec{a} \cdot \vec{a}-r^{2})}}{2}\\ &=\vec a \cdot \vec d\pm \sqrt{(\vec a \cdot \vec d)^2-\vec{a} \cdot \vec{a}+r^{2}} \end{align} $$

と求まります。球と直線 \(l\)の交点はカメラが球の内部にある場合は二か所存在します。このシェーダーにおいては\(\vec d\)方向の座標のみ必要となります。媒介変数\(t\)が正の時\(\vec d\)方向の座標が得られるので、求めるべき\(t\)は以下の式となります。

$$ t=\vec a \cdot \vec d + \sqrt{(\vec a \cdot \vec d)^2-\vec{a} \cdot \vec{a}+r^{2}} \tag{6} $$

式\((6)\)より得られる\(t\)を式\((2)\)へ代入することで点\(P’\)の座標を求めることができます。

shader

 作成したシェーダーは以下の通りです。IntersectionPosで座標を計算しています。また、計算に使用するカメラ位置は固定しています。

実行結果

上記シェーダーの実行結果は以下の通りです。Skyboxでも問題なく空の色が描画されていることがわかります。

参考サイト

GPU Gems 2:Chapter 16. Accurate Atmospheric Scattering

agehama’s diary:ピクセルシェーダで空を描く

FTEXT:直線のベクトル方程式