smallppmを眺めてみる

2009-08-27

内容はわかってないけどsmallppmのソースをちくちくいじって遊んでみる。 速度が速いから気軽に試せてイイ。

影がくっきりしてるので、フォトンは1点から放出してるのかな。フォトンの放出は関数genpで行っている。

pr->o=Vec(50,60,85);

Ray::oはoriginだからこれを乱数で散らせば半影が作れるんじゃ…と思ったらsmallppmでは乱数を使ってない! 乱数を使わないで、どうやったらフォトンを増やすと精度が上がるんだろう…。 genp関数の中でフォトンの方向を決定するのにhal()関数を呼び出している。

int primes[61]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,
83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,
191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283};
double hal(const int b, int j) { // Halton sequence
double h = 0.0, f = 1./(double)primes[b], fct = f;
while (j>0){h += (j%primes[b])*fct; j /= primes[b]; fct *= f;} return h;}

halは「Halton sequence」とのコメント。ref:

Low-discrepancy、低食い違いてなんだ? これがQuasi Monte Carloというやつだろうか… 準モンテカルロって、乱数じゃないけどばらばらに散らばった点でサンプリングするとなんか収束が速くていいよ、ってことかな?

genpではhalの第1引数に0と1を渡してるので、基数として2と3のペアが使われてる。 他には拡散面の跳ね返り方向やロシアンルーレットにもhalを使ってる。

カラーの求め方

syoyoさんのパストレーサーでは求めたラディアンスの0~1を直接カラー値にしている。smallppmではなんかの値(hp->flux*(1.0/(PI*hp->r2*num_photon*1000.0))て書いてあるからラディアンス?)をtoInt()関数でカラーにしてる:

int toInt(double x){return int(pow(1-exp(-x),1/2.2)*255+.5);} //tone mapping

コメントには「トーンマッピング」と書いてあって、なにやら難しい計算をしている。 exp(-x)が1から指数的に小さくなってく値でそれを1から引いて1/2.2乗するので[0, 1)の値。

smallppmの元になったというsmallptを見てみると、ラディアンスを1/2.2乗してる:

inline int toInt(double x){ return int(pow(clamp(x),1/2.2)*255+.5); }

この1/2.2乗はなんなのか。 Windowsでのディスプレイのガンマ値は2.2とからしいけどそれを打ち消すもの?人間の目の特性?

それぞれの計算式の値を比べてみる:

見た目全然違う。 どれがそれっぽいのか。 線形だと値がクランプされて色がおかしくなるけど、1/2.2乗する場合はマテリアルカラーと見た目の色が違うのであわせにくいんじゃないか、という懸念。