Continue(s)

Twitter:@dn0t_ GitHub:@ogrew

『Ray Tracing in One Weekend』をやってみたら楽しかった

この間の土曜日、少し時間があったので、界隈では非常に有名とのウワサの『Ray Tracing in One Weekend』を実際に読みながら写経してみました。 シンプルなレイトレーサーを1からC++で実装するという内容で、タイトルの通り週末にビールとナッツを準備してゆる~くやるのにぴったりです。 そして、読み始めてすぐに気がついたんですが今回やった第1部が日本語翻訳された『週末レイトレーシング1という書籍が出ています。

この記事では書籍のトピックごとのレンダリング結果とともに時系列にポイントとなった点をまとめておきます。

ppm description
f:id:taiga006:20200426011315p:plain:w400 まずはppm形式の画像を生成してみます. ピクセルの相対的な位置に応じて色を決めるとよく見るグラデーションになります.(ppmフォーマットについてはこちらから)
f:id:taiga006:20200426011323p:plain:w400 画角を決めてレイとカメラを設置します.
f:id:taiga006:20200426011339p:plain:w400 球面のベクトル方程式から解の方程式を使って実数解の存在条件を求めて球との衝突処理を書きます.
f:id:taiga006:20200426011352p:plain:w400 法線ベクトルを定義して球とレイの衝突する座標とそこの法線から陰影をつけます.
f:id:taiga006:20200426011404p:plain:w400 複数の球を表現するためにhitableという抽象クラス、そしてそれを継承するsphereクラスを定義します. 複数オブジェクトのうち前後などを管理するためのhitable_listというクラスもこのとき定義します.
f:id:taiga006:20200426011416p:plain:w400 cameraクラスを作り、1つのピクセル内で複数のレイを飛ばしてその平均的な色を描画するようにすることで所謂「アンチエイリアス」の処理を施します.(ブログの画像だと小さくて上の画像との差がわかりにくいかも知れません.)
f:id:taiga006:20200426011509p:plain:w400 棄却法(初めて知りました)で単位球内のランダムな点を選択し衝突点からそこに反射させる方法で拡散反射(diffuse reflection)するマテリアルを表現します.
f:id:taiga006:20200426011519p:plain:w400 ガンマ補正を考慮してRGB各色を1/2乗させます.(だいぶ明るくなりました.)
f:id:taiga006:20200426011531p:plain:w400 金属っぽいものを表現するためにmaterialクラスを作成. 今回ここではレイの散乱と減衰をそれぞれ定義する必要があります.このとき鏡面反射するレイの終点にばらつき(fuzzines)をもたせることで金属っぽさが生まれます.(逆にランダム性を持たせないと完全鏡面反射となります.)
f:id:taiga006:20200426011544p:plain:w400 今回定義するマテリアルとしては最後になる誘電体(水とかガラスとか)を定義します.肝となるのは反射と屈折です.屈折はスネルの法則を使います. 「全反射(=屈折せずにレイがすべて反射する)」はスネルの法則に実数解がないケースと言えます.ガラスであれば減衰ベクトル(attenuation)は常に1となります.
f:id:taiga006:20200426011608p:plain:w400 上の状態だと屈折を考慮した完全なガラス体を表現できますが、実際のガラスは角度によって反射の結果が変わります.それを考慮するためにChristophe Schlickの多項式近似を用いてあげます.(ここでは負の半径を持つ親より少し小さい球を作ることで中が空洞のガラス球を表現しています.)
f:id:taiga006:20200426011621p:plain:w400 ついにカメラを動かします. ①視野(=Field of View : θ) と画像の高さ(h)には h = tan(θ/2)の関係が成り立つのを利用してcameraクラスの画角を式にします.②カメラを置く位置と視点の先、そしてカメラのupベクトルを決め、cameraクラスで使えるようにします.
f:id:taiga006:20200426011634p:plain:w400 被写体深度を設定できるようにして終わりです.先程定義した「カメラを置く位置」の周辺に円の領域を考えそこからレイを飛ばす、と考えることでレンズを表現するというやり方に「なるほど」と思いました.
f:id:taiga006:20200426011647p:plain:w400 レイトレーサー完成(1280*720画素、レンダリング時間1.5h)

パラッとめくると「ただ写経するだけの教材かい?」と思われるかもしれませんが、重要な部分のコードしか書籍には書かれていないので、ちゃんと考えて書かないと時々つまづきます。(公式?のレポジトリも一応公開されてはいますが…。)

今回は、上のそれぞれの画像が出力できた時点でツイートするようにしてた2ので、お昼食べたりYoutube見たりして休んでた時間を考慮してだいたい9時間から10時間くらいで1周できました。まさに「週末レイトレーシング」という感じでした。一応、GitHubにもレポジトリを残しています。3

この『Ray Tracing in One Weekend』はRay Tracing Minibooksというシリーズ物の第1部で『Ray Tracing: the Next Week』が第2部に当たるようです。こちらもやっていくぞ!と思っていた矢先、Indie Visual Labの過去書籍が無料公開になった4ので「うう、一旦はそっちをやろうかな、あ、いやUnity Shader Programmingもまだ第3部積ん読してたなぁ~、うう、、、」という感じで世間はまだコロナで油断できない時期ですが、自宅でもやれること、やりたいことは盛り沢山です。

最後に、上のだと画像が小さいのであらためて最終的な出力結果です。 f:id:taiga006:20200426011647p:plain いやー、達成感すごい、気持ちいい。


  1. 達人出版会から600円+taxです。tatsu-zine.com

  2. こちらからスレッドになっています。

  3. github.com