光芒の実装

光芒

グレア表現として,ブルームを実装しましたが これだけでは寂しいので光芒(スター)を実装してみます。

光芒は物理的には,カメラの絞り羽根による光の回折現象によって特定の方向に光が伸びていくような現象のことを呼びます。
https://www.bhphotovideo.com/explora/photography/tips-and-solutions/create-compelling-star-effects-sun-stars-starbursts-photos

ピンポンブラー

今回の実装はピンポンブラーを利用して表現します。

Masaki Kawase, Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L (Wreckless), GDC 2003
http://www.daionet.gr.jp/~masa/archives/GDC2003_DSTEAL.ppt (スライド)

まずは光芒のうち,一本の線を作ることを考えます。
簡単の為に,今回はX-方向に延ばすことにします。 このときに,4点({i=0,1,2,3})をサンプリングするのですが,この時のサンプリングする位置{r_p(i)}と重み{w_p(i)}を以下のように決めます。

{ \displaystyle
\begin{eqnarray}
p &=& 0,1,2,\cdots \\\
i &=& 0,1,2,4 \\\
\\\
r_p(i) &=& 4^p i\\\
w_p(i) &=& a^{r_p(i)} = a^{4^p i}\\\
\end{eqnarray}
}

{p}は繰り返しパス数で,サンプリングを繰り返す度に0から1,2,3とインクリメントされていきます。 サンプリングを行った結果のテクスチャを,またサンプリングに利用することで重みを延伸していきます。

f:id:hikita12312:20170910182042p:plain

このように,直前のブラーの結果を利用しながら繰り返してブラーを適用することを,ピンポンブラーとかいうらしいです。 サンプリング数に対して,指数的にブラーサイズが伸びていくので,かなり頭の良い方法だと思いました。

このサンプリングはX-方向に延ばす場合ですが,距離{r_p(i)}を二次元にして,適宜回転させれば,任意の方向にブラーを延ばすことが出来ます。

f:id:hikita12312:20170910183306p:plain

上が適当に{a}と繰り返し数を決めて4本の光芒を適用させた結果です。前回作成したブルームも少しだけ入っています。 長く光が伸びているのがわかります。

スペクトルテーブル

これだけだとつまらないので,分光的な表現をしてみたいと思います。
前準備として,波長からRGBカラーに変換するテーブルを作成します。

http://www.cvrl.org/ciexyzpr.htm

このサイトから,波長とXYZ色空間での色のテーブルを作ります。

static const float s_aTable[] =
{
    0.16638f,0.0183f,0.81532f,  // 390nm
    0.16595f,0.01874f,0.81531f, // 395nm
    0.16499f,0.01827f,0.81673f, // 400nm
    0.16393f,0.01718f,0.81888f, // 405nm
    .....
}

このXYZ空間から,R=830nm, G=527nm, B=390nmを原色と選んだ色空間へのマッピングを行います。 ちなみに白色点はsRGBと同じく,(x,y)=(0.312,0.329)を利用しました。

https://www.t-kougei.ac.jp/activity/research/pdf/vol36-1-09.pdf

XYZ空間から任意の色空間への変換方法は以下のゲームのための色彩工学のスライドを参考にしました。

https://www.slideshare.net/nikuque/color-science-for-gamesjp

最終的に計算されたXYZ空間からスペクトルRGB空間への変換行列を記載します。

{ \displaystyle
\begin{eqnarray}

\left(
\begin{array}{c}
r \\\
g \\\
b
\end{array}
\right)
=
\left(  
\begin{array}{ccc}
1.78611469 & -0.585037410 & 0.0115048392 \\
-0.308209479 & 1.46176445 & -0.0287458934 \\
-0.357569426 & 0.0865773335 & 0.934579492
\end{array}
\right)
\left(
\begin{array}{c}
x\\\
y\\\
z
\end{array}
\right)
\end{eqnarray}
}

で,このテーブルを256x1サイズのテクスチャにマップした結果がこの画像です。 f:id:hikita12312:20170910185904p:plain 右端と左端は本来は完全に赤色と青色になるべきなのですが,今回は周期的に滑らかに繋がって欲しかったので, 適当に端が390nmと830nmの色の平均(=紫)となるように,滑らかに補間しています。

ちなみに256x1だけではなく,128x1,64x1などのテーブルもミップマップとして作成しておくと,この後の光芒の分光表現を作るときに滑らかに補間されて便利です。

スクリーンスペース光源距離マップの作成

スペクトルテーブルをマッピングするために,各光源からの距離を求めなければなりません。 まずは,光源と判断される明るさを持ったピクセルを1,それ以外を0とするマスクを作成します。

f:id:hikita12312:20170910191550p:plain

このマスクは,前回のブルームを実装したときに作成した,高輝度テクスチャのA成分に格納しておきます。 あとは,そのまま高輝度テクスチャをソースに,光芒を作るためのピンポンブラーをRGBA各チャンネルで同じ重みで行います。

すると,光源マスクされたアルファチャンネルも光芒の方向にボケるわけですが,ボカシのウェイトの関係から, 光源からの距離{r}離れた場所のAチャンネルの強度{\alpha}は"大体"以下のような関係にあるような気がします。
(ちょっと怪しい)

{ \displaystyle
\begin{eqnarray}
\alpha(r) &=& a^{r}
\end{eqnarray}
}

これの逆関数を求めると

{ \displaystyle
\begin{eqnarray}
r &=& \frac{log(\alpha(r))}{log(a)}
\end{eqnarray}
}

{\alpha = a^{r}}の関係を認めてしまえば,ブラー結果の副産物から距離っぽい値を計算することができます。

f:id:hikita12312:20170910192329p:plain

上の図は光芒と,アルファチャンネルから計算された距離{r}を利用して{\mathrm{abs}\big(\sin{(2\pi r/100)}\big)}として,縞模様を作った結果です。 なんとなく等間隔に縞模様が並んでいて,光源からの距離っぽい値になっている気がします。

この距離っぽい値と,スペクトルテーブルを利用して,光芒を着色します。

f:id:hikita12312:20170910193244p:plain

それっぽい感じの着色はできましたが…
うーん,なんか微妙です。修正の余地あり。