2014年9月6日-9月7日、レイトレ合宿2!! が開催されました (https://sites.google.com/site/raytracingcamp2/)
会場は富士山のふもとの河口湖のコテージ
実に素晴らしい富士山が見える景色の元での合宿です
と思いきやあいにくの天気…。これではレイもきちんと飛ばず、ノイズが増えるのではと心配されながらの開催。
さて、いよいよレイトレ合宿本番です。初日夜には3つの講演がなされ、その夜レンダリングが行われ、次の日結果発表&プレゼン発表です。
残念ながら都合により2日目の結果発表前に会場を離れなければならない人もいましたが、笑いあり涙ありで最高の合宿でした。
ちなみに、私は以下のような画像をレンダリングして、同率2位を頂きました。
技術的にはとりわけ深い内容に取り組んでいないので、私が2位をもらっていいものなのか…と思いましたが、投票の結果なのでありがたく受け取りたいと思います。
レンダラの簡単な解説は、発表会で行った3分間プレゼンに記しています。
スライドの参照は SlideShare でどうぞ: レイトレ合宿2!! 3分間アピールプレゼン - お餅
以下、実装した技術についてちょっとだけ詳しく書きたいと思います。
Uni-Directional なパストレーサ
これはあえて言うまでも無いですが、視点からレイを飛ばし、光源にヒットしたら打ち切る普通のパストレーサです。
詳しくは edupt の解説を読みましょう (http://kagamin.net/hole/edupt/)
Image Based Lighting
IBLは、シーン全体を囲う背景そのものを光源とみなすライティングの手法です。
今回私が使った背景画像は以下のようなものです (元はHDRの画像です)
※ http://www.hdrlabs.com/sibl/archive.html のBarcelona Rooftops を利用しています
この画像は、横方向が経度、縦方向が緯度となる座標系になっているので、この画像を球状に丸めてシーンの背景とします。
この背景を使ってどのようにIBLを行うかですが、その方法は非常にシンプルです。
背景がすごい遠い位置にあると仮定できる場合、レンダリングするオブジェクトの位置が多少ずれていても受ける光はほとんど変わりません。
そのため、最もシンプルには、以下のように「レイが飛んでいく方向」だけを見て背景からサンプリングを行い、それを光源のradianceとみなすことでIBLが行えます。
Color radiance;
if (!intersectedToObject) {
// IBL
radiance = scene.ibl->Sample(ray.dir);
} else {
// レイが衝突したオブジェクトに基づいてradianceを計算
}
...
たったこれだけです。IBLを行わない場合、「レイがどこにも当たらなかったら背景色(黒など)にする」という部分が、IBLからray.dirを使ってサンプリングする、と変わっただけです。
サンプリングは、ray.dirが球状の背景の1点を差しているので、これを経度緯度に変換するだけで終わりです。
非常にシンプルですが、これを行うだけで劇的にレンダリング結果の見栄えが変わります。
もちろん、このような適用の仕方だけではなく、Importance Sampling と組み合わせたりすることでさらに収束が速くなります。
これからレイを飛ばしていく人は是非とも入れましょう。
Depth of Field
日本語では被写界深度と呼ばれています。
カメラのピントをシミュレートしたものです。
これを掛けるとどのようになるか、というのはシリコンスタジオさんのYebis3の比較を見ると分かりやすいと思います。
被写界深度表現 | YEBIS3
※Yebis3自体はレイトレーサーではなく、リアルタイムレンダリングのためのポストエフェクトのミドルウェアです
ここでその原理を説明していくほどの元気が無いので、詳しい説明は全部省きます。
私が実装したのはPBRTに載っていた非常にシンプルなモデルです。
絞りの形状も簡単な円にしたので、色々込みで実装が1時間もかからず終わりました。
絞り形状を任意にしたりするとちょっと面倒ですが、それでもそんなに難しくはないと思います。
これも、お手軽にレンダリング結果の見た目を向上させる手法の1つです。
ただし、本当にちゃんとレンズのシミュレートをしようとするのはそんなに簡単じゃないようです(私はやったことが無いので分からないですが。。。)
今回のレイトレ合宿では、nikq さんがそれも行ったレンダラを作っていたようです。
QBVH
以前のエントリで、BVHの構築,交差判定について書きました。
QBVHはBVHのすごい版です(超適当な説明)
BVHは、とあるノードが持つ子ノードは2つでしたが、QBVHは名前の通り4つの子を持ちます (Quad-tree BVH)
子を8つにしたOBVHというものもあるらしいです。
これの何が嬉しいかというと、レイ1つと4つのAABBの交差判定を行う時、SIMDによって一気に4つ分の判定を同時に行うことが出来る点です。
もちろんBVHで一気に2つ分判定することもできますが、一気に2つ判定するのと、4つ判定するのではどちらが効率がいいのかは明らかです。
どういう風にして一気に4つのAABBとの交差判定を行うかは、ototoiさんの記事を読んでみてください。
http://d.hatena.ne.jp/ototoi/20090925/p1
QBVHの構築方法は至って単純で、BVHを作る→木を掘っていき、2つの子ノードの子ノード(孫ノード)4つを1つのノードにまとめる、という方法で可能です(分かりにくくてすいません…)
BVHが実装できているなら、あまり深いことは考えずに実装してみるのがいいと思います。
BVH→QBVHは、BVH無し→BVH有り に比べたら劇的な改善にはなりませんが、それでも体感できるぐらいのスピードアップになります。
obj ローダ
3DデータはOBJを用いていました。
ローダも自分で書きました。
「OBJってフォーマットシンプルだし簡単だな!ローダ作ろう!」
これが全ての元凶でした。
必要そうな一部の実装のみしかしていなかったため、色々と見つけてきたobjのモデルがことごとくちゃんと読み込めないという事態になり、実際にレンダリングするシーンを作るときとてもとても困りました…
tinyobjloader 等のローダを使うほうが低コストで確実だったと今は後悔しています。。。
テクスチャマッピング
もはや説明することは何もないでしょう。
obj には uv 座標も格納されているので、テクスチャを読み込んでモデルにはりつけていました。
テクスチャの読み込みには、stb image というライブラリを用いていました (https://github.com/nothings/stb)。
ちょっと癖があってコンパイル通るようになるまで時間かかりましたが、libpng とかを使うよりは圧倒的に使いやすいです。
使い方に関しては uimac さんのページを見るといいと思います (http://memo.render.jp/library)
レンダリング途中のビューワ
今回、こいつが大活躍でした。
レンダリング結果を確認するとき、いちいち出力された.hdrファイルや.pngファイルを開かないといけないというのは非常に面倒だったので、レンダリング途中もその経過が常に分かるようにしていました。
こんな感じです(静止画だから分かりづらいけど)
このウィンドウ中の絵が、1秒に1回更新され、現在のレンダリング結果を映し出してくれます。
最初こいつを実装したとき、SetPixel で各ピクセルの色を設定していましたが、そしたらビューワの方がレンダラより重いという事態になってしまったため、急きょ OpenGL で書き直しました(とは言えOpenGL2系のすごい簡単なコードのみですが)
時間に余裕が無いときはビューワなんか実装している場合ではないですが、時間に余裕があるなら最初に実装してしまうとすごいいいと思います。
最後に
初参加でしたが、レイトレ合宿2!!はすごい充実した合宿になりました。
合宿当日もでしたが、レンダラを作ることにより色々と勉強が出来ました。
企画・運営をしてくれたqさんとhole君、本当にありがとうございます。
そしてみなさん、仕事に関係あるとかないとか関係なく、是非ともレイトレ書いてみましょう。