Proce55ingでフォトンマッピングを試してみたけど、20万フォトンくらいの放射でメモリの限界になってしまい画質があまりあげられなかったので、C++に置き換えてみる。
Beeさんのsmallppmを元に作る。これなら何か間違ったときに確認できるのでいいね。
…なんとかsmallppmと同じ79文字×128行に詰め込んだ。
Here is the list of features (majority of them are from smallppm and smallpt):
- Global illumination via Photon Mapping
- 128 lines of 79-column (or less) open source C++ code
- Point light source
- Specular, Diffuse, and Glass BRDFs
- Ray-sphere intersection
- Modified Cornell box scene description contains LSDSE path
- Cosine importance sampling of the hemisphere for diffuse reflection
- Russian roulette for path termination
- Russian roulette and splitting for selecting reflection and/or refraction for glass BRDF
- Quasi Monte Carlo sampling using Halton sequence
- Antialiasing via 2x2 super-sampling
- Using kd-tree for radiance estimation
以下、感想:
- 500万フォトンくらいが限界か?
- 画像は1024x768で500万フォトン放射、放射輝度推定に500フォトンしたものを512x384に縮小(はてなでさらに縮小されてる…)
- 500万フォトンでも全然きれいにならない。メモリの制限に引っかかってしまうので、時間かければかけただけきれいにできないのが辛い。
- フォトンが増えるとkd-treeの構築にすげー時間がかかるようになるのがヤバイ
- 推定用フォトンの数を増やすと如実に時間がかかるようになるのがヤバイ。断じてO(k + log n)ではない。kd-treeがうまく動いてるのかどうか疑問
- メモリ節約のため浮動小数型をfloatに変えると、精度不足のためか画像が乱れてしまったので断念
- 一応ちゃんとsmallppmと同じような絵が出たので満足
- 自分の勘違いかわからないんですが、smallppmでは最終的にピクセルの放射輝度を求めるところ(c[i]=c[i]+hp->flux*(1.0/(PIhp->r2num_photon*1000.0)))で拡散面のBRDF 1/π がかかってない?ように思うんですがどうでしょう?そのためこちらではシーン全体が暗くなってしまったので光源のパワーをπ倍して帳尻を合わせてみました。
- OpenMPの#pragmaはsmallppmにあわせて入れてみたけど、動いてるのかどうか未確認
- smallptからの現象で、ベクトルの破壊的な正規化を行うVec::norm()とそのベクトルを使う箇所があって
r = r + radiance(Ray(cam.o+d*140,d.norm()),0,Xi)*(1./samps); |
- dに対してnorm()呼び出しとスカラー乗算が同時に使われているけど、たぶんC++は関数への引数の評価順序は未定義なので(ググッても確証は得られなかった)、処理系依存になってるんではないかと思う(VC, gccとも正規化が先に実行されてるぽい)。なのでVec::norm()は破壊的操作を行わずに正規化したベクトルを返すように変えてみた。
- std::vector
だと連続するメモリを要求してしまい厳しいため、ポインタにしている - LSDSEが難しい理由というのがよくわかってない。フォトンマッピングの場合光源から放射したフォトンはスペキュラ面以外にぶつかったら格納されてしまうので、L(SD){2,}Eは難しいのかなと思うんだけど。まあ拡散面に2回以上あたったものは明るさ的に重要度は低いだろうから必要はないだろうけど。
以下ソース:smallpm.cpp
|