Ray Tracing: The Next Week
GitHub:
reference:
AC.icon やりきった!
https://scrapbox.io/files/66b9686a9531e2001c22fb72.png
方針
VSCodeを使う
指示にはできるだけ従う
あまり深く掘り下げない
下のメモは初見で読んでわかるように書いていない(時間がかかるので)
referenceを読み進めると同時に下のメモを使うのがちょうど良いと思うappbird.icon
Ray Tracing in One Weekendよりももっとざっくりと進める
(そうでないと分量が多すぎて一生終わらない)
わからないことに突き当たった時は
証明系:15分考えてわからなかったら一旦飛ばす
今回の目的はレイトレーシングをハンズオンで理解することであって、理論を突き詰めて理解することではない
そのほかの疑問:書いてとりあえず飛ばす
わからないことに引っかかった時だけメモする
AC.icon 2. Motion Blur
目的:時間[0, 1)において動く球体を収めたモーションブラーのある写真を1枚取る
Once you have taken a step down that road, the good news is that almost all effects can be similarly brute-forced
ゴリ押しで行くということすか....appbird.icon
Brute force wins again!
草
モンテカルロ法 / Monte Carlo法でいく
Since the "engine" of the ray tracer can just make sure the objects are where they need to be for each ray, the intersection guts don't change much. To accomplish this, we need to store the exact time for each ray:
どういう意味だろこれ
code:cpp
ray(
const point3& origin,
const vec3& direction
):
ray(origin, direction, 0)
{}
初期化子って既存のコンストラクタの定義流用して書けるんだ....appbird.icon
もっと複雑な機能のカメラは考えられるけど、今のところは単純なカメラを考える
時刻0から時刻1までの間シャッターが開いていて、
位置が固定されていて
一枚写真を撮るカメラを想定する。
Not many changes are needed to camera because for now it is not allowed to move
なるほど
というわけで
とりあえずまずはレイに時間を設定できるようにして
球を線形に動かせるようにして
そして反射するたびに、マテリアルが次のレイに前のレイの時間を渡すようにする
本来なら光速で動く光を考慮して時間を遅らせる必要はあるけど近似しているappbird.icon
レンダリングにだいぶ時間かかる
先に次の章読み進めておこうか
https://scrapbox.io/files/66b4440a0ecd5200221b44fe.png
image_width: 400, samples_per_pixel 50
8m19s
492s
https://scrapbox.io/files/66b45ff90aac3e001c038966.png
AC.icon 3. Bounding Volume Hierarchies (BVH)
目的:レイと物体の衝突検出の高速化を行う
This part is by far the most difficult
おぉっとマジかいappbird.icon
にぶたん(二分探索)きたわね!!
対数時間での探索を目指す
オブジェクトらをBounding Boxによって分割する。
Bounding Boxはシーン内のオブジェクトをいくつか包括する
もしBounding Boxにレイが当たらなければ、その内部のオブジェクトにレイが当たることはない
スクリーンやシーンに従って分割するわけではない
オーバーラップする可能性があるし、ボックス間に順序があるわけでもない
3.3 Axis-Aligned Bounding Boxes (AABBs)
目的:良い分割方法とは何か? & どうレイとAABBの交差を判定するか?
レイとAABBの交差については衝突検出ができればよくて、どの点・法線で衝突したかを考慮する必要はない
slab法(スラブを用いた分割)
それぞれの軸においてAABBと交差するレイの$ tの区間に重なりがあれば、AABBと交差していると言える
レイが負の方向から飛んでくる場合$ t値が反転することに注意とか
$ t値の大小関係とか色々注意することはある
overlaps関数ってなんのために定義したんだろappbird.icon
3.5 Constructing Bounding Boxes for Hittables
目的:Hittableに対してAABBを定義する
まず元クラスにそれを返す関数を定義
AABBのヒエラルキーを作る
子AABBが親AABBに含まれる必要がある
葉にプリミティブが入ることを期待する
オブジェクトが動くことに注意、指定期間中に通る領域をAABBで内包する。
まず球クラスにAABBを定義する
移動するものの場合はその可動域まで考慮する
それを実装するにあたり、開始点と終了点における球のAABBを含む最小のAABBをそのオブジェクトのAABBとして選ぶ。
Hittable_listについてもAABBを定義する。
オブジェクトが追加されるたびに、今までのオブジェクトを含むAABBと追加されたオブジェクトのAABBを含む最小のAABBを、そのオブジェクトのAABBとして更新すればよい。
Hittableのコンテナとしてbvh_nodeを定義する。これもまたHittableである。
レイがHittableのbboxに当たるかどうかを判定するためのラッパークラス。
本来のHittableのhit関数はそのオブジェクトらの当たり判定を
BVH_NodeのHittable関数はそのオブジェクトらのAABBの当たり判定を
行う。
これによって、Sphereを大きく変更せずに済むappbird.icon
一つのノードがhittable_listに対応する。
各ノードにどう言ったhittable_listを作っておくかによってオブジェクトの分割を表現する
では、どうその分割を作るか?
ある軸を選択して、その軸に従ってオブジェクトをソートして、その結果に基づいてBVH木の右か左かに仕分ける
not have to check for null pointers, so if I just have one element I duplicate it in each subtree.
そんな二つにどっちも同じノードを置いておくっていう方法アリなのか...。appbird.icon
どう動くのだろう
objectsが渡される
axisが選択され、x, y, zの中から比較対象の軸が選ばれる
objectsがaxisに沿ってソートされる
基準はAABBのその軸における最小値らしい
objectsを半分に分割して、leftに前半部分、rightに後半部分を渡す
leftにてobjects前半が渡される
axisが選択され、x, y, zの中から比較対象の軸が選ばれる
objectsがaxisに沿ってソートされる
objects前半を半分に分割して....
rightにてobjects後半が渡される。
axisが選択され、x, y, zの中から比較対象の軸が選ばれる
objectsがaxisに沿ってソートされる
objects後半を半分に分割して....
と分岐して行って、最後に一つのプリミティブが格納される
それぞれのbboxには、left, rightを内包する最小のAABBが指定される。
実際に辿る時は
右と左にレイが当たってるかを調べて
bboxに当たっていればどんどんバウンディングボックスの階層を辿って行って、
最終的になんかしらのオブジェクトに当たっていればtrueを返す。
さて、なぜこれは早くなるか?appbird.icon
最悪のケース:
全ノードを探索し切ったけどどこにも当たっていなかった
オブジェクトが狭い一箇所に集中していて、一つの飛ばされたレイがそのAABBを全て通過していた場合が相当
同階層のAABBが重なりまくっている時はこれが発生する。
ノード数は$ O(n)なので、探索に$ O(n)かかる
しかし実際には:
同階層のAABBはほとんど重なっていないものと考えられる
なので、理想的には階層あたり一回しか探索されない
木の深さは$ O(\log n)なので、探索には$ O(\log n)で済む。
あ、最終的にプリミティブのhit関数に行き着いて、そこでrecに衝突情報が新しく書き込まれるのな
爆速で草 Scanlinesの減りがエグいappbird.icon
なんでだよ!!
https://scrapbox.io/files/66b489988c653d001c445798.png
みた感じ描画順序の問題っぽいが
一番最後の球に当たるように調節されている?appbird.icon
あっ
code:cpp
bool hit(
const ray& r,
interval ray_t,
hit_record& rec
) const override {
if (not bbox.hit(r, ray_t)) { return false; }
const bool hit_left = left->hit(r, ray_t, rec);
if (hit_left) { return true; }
const bool hit_right = right->hit(r, ray_t, rec);
return hit_left or hit_right;
}
そういえばここ本来のコードよりももっと早くしようと思ったんだった
だけど、実際にはrecもここで代入されているから、hit_leftで発見されようともhit_rightでもっと近いオブジェクトが発見されることがある
だから、right->hitも同様に実行しなくてはならない。(早期に探索を打ち切ってはならない。)
65 sec
というわけで7.6倍の高速化に成功 やったね
https://scrapbox.io/files/66b48c4abb56d5001dcdd077.png
3.10 Another BVH Optimization
Instead of choosing a random splitting axis, let's split the longest axis of the enclosing bounding box to get the most subdivision.
なるほどかしこいappbird.icon
construct an axis-aligned bounding box of the span of objects in the BVH constructor
ん?すでにあるAABBの長さを測れば終わりなんでは?その割には複雑なことをやってそうな感じがする
あ〜わかったかも
今までleft, rightを内包するオブジェクト境界を取ってたけど、これは本来のプリミティブが存在するスパンとは必ずしも一致しない
WJ.iconまあleftを構築する順序とかによればそうなるのか...?本当か?
ここからは全てのプリミティブのAABBを全て含む最小のaabb境界を、BVHのノードのAABBに設定することで、本来のプリミティブが存在するスパンを求める。
おお、ちょっと早くなった気がする。時間がかかってた行に対しても比較的早くこなしている気がするappbird.icon
53.96 s (20%高速化)
これ、-O2最適化をこの上にやったらどうなるのだろう?
code:cmake
add_executable(main ./src/main.cpp)
target_compile_options(main PUBLIC -Wall -Wextra -O2)
というわけでね
6.4s(8.3倍高速)
草appbird.icon そんなはよなる????
https://scrapbox.io/files/66b4440a0ecd5200221b44fe.png
https://scrapbox.io/files/66b4924ef2097e001dce8115.png
最適化しゅごい............appbird.icon
AC.icon 4. Texture Mapping
Texture mapping in computer graphics is the process of applying a material effect to an object in the scene.
へえ、もっと広い意味なのかこれ
単純に絵を貼っつけるだけじゃないのね
マテリアルのどんなプロパティにもなりうると
hit_recordにuv座標載っけるの!?
レイに衝突した段階でマッピング説明できるんかえ?
4.2 Solid Textures: A Checker Texture
空間を対応づけることで色付けを行うらしい
オブジェクトが動くと柄がずれていくやつappbird.icon
This is in the spirit of shader networks introduced by Pat Hanrahan back in the 1980s.
テクスチャを別のテクスチャによって定義する様子をそう言ってるっぽいな
というわけでマテリアルに適用していくわよ
光の反射を計算するのがmaterial
attenuationを計算するのがtexture
ってことかいな
https://scrapbox.io/files/66b611d6e841b8001cd6fa80.png
1m36s
https://scrapbox.io/files/66b6111efd0958001c0a09e0.png
3.82s
十分平面だとみなせる物体にはこれの方法でも問題ないけど、そうじゃないオブジェクトに対しては問題があるってことかなappbird.icon
4.4 Texture Coordinates for Spheres
$ f: S \to [0, 1\rbrack^2; (x, y, z) \mapsto (u,v)なる写像を定義する。
この関数は表面からテクスチャ平面への対応を説明する写像である。
uv展開とかって多分ここあたりの話だよね
https://youtu.be/mFJNdIKApPc?feature=shared
ただし
テクスチャが拡大縮小・回転などできるように
$ S \sub \R^3;: モデルの表面
連続性を保ったまま
今回は球$ S = \{\bm p: |\bm p - \bm a| = r\}に対して$ fを定義する。
球面座標系$ (r, \theta, \phi)のうち、$ \theta, \phiを$ u, vに比例させることで定義する。
なるほど、衝突点を計算する際についでにuv座標も計算しちゃうのか
4.5. Accessing Texture Image Data
おぉっ、画像読み込むのか...!
今回は画像の読み込みを学びたいわけじゃないので丸コピさせてもらおう
if (image.height() <= 0) { return color{0, 1, 1}; }
いやあ、ここは赤紫でしょ
if (image.height() <= 0) { return color{1, 0, 1}; }
v = 1.0 - interval(0,1).clamp(v);
flipするのは上下方向が逆だからかなappbird.icon
使わない引数には[[maybe_unused]]属性を使うと警告を解消できる
意図して使っていない引数に対してはこれを使うと便利appbird.icon
https://cpprefjp.github.io/lang/cpp17/maybe_unused.html
すごい、attenuation計算するだけでこれできるのか
反射とは独立してテクスチャを割り当てられるのね...appbird.icon
https://scrapbox.io/files/66b61f411d17fe001ca8fba4.png
よし!!テクスチャエラーで赤紫が出ることも確認
https://scrapbox.io/files/66b61fee99a8da001d620b50.png
AC.icon 5. Perlin Noise
パーリンノイズだ!
Perlin noise :
参照透過性をもち、
連続性がある
シンプルかつ早い
パーリンノイズの作り方
前準備
1からNまでの順列$ P_x, P_y, P_zを用意する。
$ [0, 1)の乱数列$ Xを用意する。
乱数生成
ただ単純に乱数を生成するのではなく、ハッシュ化みたいなことをやる
与えられた位置に基づいて$ P_x, P_y, P_zのアクセスする要素を決める
それらのxorをとった最終値をrandfloatの要素とする。
https://scrapbox.io/files/66b62c71738b51001d97185a.png
2.61s
これを線形補間するらしい
ええっと.....こういうことか
code:cpp
accum += (i*u + (1-i)*(1-u))
* (j*v + (1-j)*(1-v))
* (k*w + (1-k)*(1-w))
* cijk;
これめっちゃゴツいことやってるように見えるけど実際にはそうでもなくて、
(1-i), iが0, 1しか取りえないことに着目して
簡単のために二次元で書き起こすと
code:cpp
accum += (i*u + (1-i)*(1-u))
* (j*v + (1-j)*(1-v))
* cij;
code:cpp
accum ==
(1-u)*(1-v)*c00
+ u*(1-v)*c10
+ (1-u)*v*c01
+ u*v*c11
となる。こう書くと単純な二次元の線形補間であることがわかる。
一次元に帰着して書くと確かにそうなっていることが確認できる。
ミラーボールになっちまったよ なんでだよ
https://scrapbox.io/files/66b632e01ec1a0001d616dd4.png
四つの縁が全て暗いのが気になる
連続じゃない
線形補間自体はうまく行ってそうなんだけどな
code:cpp
rep(i, 2) {
rep(j, 2) {
rep (k, 2) {
accum +=
(i*u + (1 - i)*(1 - u)) *
(i*v + (1 - i)*(1 - v)) *
(i*w + (1 - i)*(1 - w)) *
cijk;
}
}
}
おっと....ijkを正しくセットできていなかった
いけたわね
https://scrapbox.io/files/66b6341c738b51001d972f6b.png
5.3. Improvement with Hermitian Smoothing
まあでもグリッド感が残る(線形補間ゆえに)
アーティファクト
Mach bandsというらしい
線形じゃなくて、Hermite cubicを使って丸めることで解消する!
エルミート補間のことかこれ
3次関数を用いて
こうすることで微分可能になるんだな
$ fをHermite補間関数とした時、$ f'(t)は連続なので、これで補間すれば繋げれば"滑らか"に見える
https://scrapbox.io/files/66b6365aa71d57001c966ef7.png
5.4. Tweaking The Frequency
周波数遅くない?もっと早くしたい
えっと、生成機構を何か変えるのか...?appbird.icon
return color(1,1,1) * noise.noise(scale * p);
かしこいいappbird.icon
https://scrapbox.io/files/66b6391d03f78e001de79280.png
2.69s
5.5 Using Random Vectors on the Lattice Points
なんかまだブロック感ある わかる
最小値最大値の現れる箇所が格子点上にしかないため?
格子上の点に、float値じゃなくてランダムなベクトルを対応させ、輝度値の計算に内積を使う
そうすれば、ノイズの最大値最小値が格子点に現れなくなりやすい?
ランダムなベクトルにかける内積はvec3 weight_v(u-i, v-j, w-k);と定義するらしい
それぞれのキー点からどれぐらい離れているかを表すベクトルっぽいappbird.icon
(u-i, v-j, w-k)がそのランダムなベクトルと一致するとより大きい値が出やすいようだ
確かにこうなると最大値が格子点以外の場所で現れる可能性も出てくるappbird.icon
ところで、これによって計算された関数負の値になりうるらしいので、$ [0, 1)区間にマッピングし直しておく
まあ内積が-1になりうるからねぇappbird.icon
https://scrapbox.io/files/66b63de418db5f001d1c4e9e.png
ところで、u, v, wにhermite補間関数を(2回誤って)掛けてしまうとこう言う図が撮れる
https://scrapbox.io/files/66b63e561647de001d92d0a9.png
この模様はこの模様でまた何かに使えそうappbird.icon
壁紙の模様とか、妙に四角い感じがいい感じでかわいい
5.6. Introducing Turbulence
Turbulence ... 気象学における大気の乱れ・乱気流のことらしい
異なる周波数のノイズを足し合わせる ---> Turbulanceというらしい
リアルタイムグラフィックスの数学の本だと非整数ブラウン運動がそれに当たるのか...?appbird.icon
fBM / fractional Brownian motion
フラクタルノイズ
The Book of Shadersでも同じ項目があるな
同様のテクニックを使って「乱流(turbulence)」と呼ばれる効果を作り出すこともできます。基本的にはfBMなのですが、鋭い谷間を作り出すために符号付きノイズ(signed noise)の絶対値を用いています。
(accessed at 2024/8/10 01:18)
ほほう
https://scrapbox.io/files/66b642596c875e001c8aeb32.png
色をうまく選んだらおようふくの模様になりそう
5.7 Adjusting the Phase
位相の調節?
間接的に乱流が使われる
マーブルっぽい模様作りたい
---> 正弦関数(sin関数)の位相に乱流を指定しよう
undulate: 緩やかに波打つ
modulateの対義語的な存在?
おぉ....よく見る床のデザインだ....
color{0.5,0.5,0.5} * (1 + std::sin(scale * p.z() + 10 * noise.turb(p, 7)));
https://scrapbox.io/files/66b6440527bb2b001c5c1a76.png
これなんでこう言う模様になってるんだ?
z方向に移動すると周期的に変化する
color{0.5,0.5,0.5} * (1 + std::sin( scale * p.z() ));
これだと出てくるのはストライプ柄
そこにノイズの乱流を載せている
color{0.5,0.5,0.5} * (1 + std::sin( scale*p.z() + 10*noise.turb(p, 7) ));
10*noise.turb(p, 7)が、scale*p.z()を中心に揺らがすと解釈するのが良いのかなappbird.icon
AC.icon 6. Quadrilaterals
四辺形
$ Q, u, v(開始点・幅方向・高さ方向)の三つによって四辺形を定める(平行四辺形)
$ \{\bf x; \bf x = \bf Q + \alpha \bf u + \beta \bf v \land 0\leq \alpha\leq 1 \land 0 \leq \beta \leq 1\}
AABB: 無限に薄いと困る。 ---> ちょっと厚みを持たせる
ここでextendsメソッドが効くのねappbird.icon
方法
1. 平面$ \{\bf x; \bf x = \bf Q + \alpha \bf u + \beta \bf v\}とレイの交点を求める
2. 交点における$ \alpha, \beta値が$ 0以上$ 1以下かを調べる
平面を表す平面ベクトル$ \bf nと$ Dはどうやって探す?
$ \bf nは$ \bf u, \bf vの外積を取ればいい
$ Dは式$ \mathbf n \cdot \mathbf v = Dに代入すればOK
始点$ Qは四辺形の内部にある
四辺形を含む平面と交差しているかを調べる。
$ \mathbf n \cdot \mathbf v = Dなる$ \mathbf vが見つけられれば良い
解$ t=\frac{D-\mathbf{n} \cdot \mathbf{P}}{\mathbf{n} \cdot \mathbf{d}}が存在すれば、交差が発生する。
この方法は三角形でも円でも適用できる!
そうなんかappbird.icon
$ \alpha, \betaを求める
同じベクトル同士の外積が$ 0になることを用いれば$ \alpha, \betaをベクトル方程式から導き出すことができる。
なんかバグった
奥の平面しか現れない
D = dot(n, Q);
が原因だった
正しくはD = dot(normal, Q); (normalはnの正規化ベクトル)
あぁ、その後の交点の計算が、平面の係数$ A, B, Cを正規化したモノとして扱ってるからだ
ここで正規化していないベクトルを使ってしまうと計算がズレる
他のシェイプも追加してみよう
まず、平面図形を表すplane_figure仮想クラスを作る
set_bounding_box, is_interiorをvirtual
set_bounding_boxをoverrideできるようにしちゃうと、依存関係がややこしいことになるので
全ての図形を$ \alpha, \beta\in [0,1)内に描画することにした
円とかについては見つけた$ \alpha, \betaを適当にスケーリングすればOK
$ \alpha \leftarrow(\alpha - 0.5)\cdot2
そして、quadをplane_figureから継承
ついでにdisk, triangleも作っちゃう
リングもやっちゃう
はい
https://scrapbox.io/files/66b84907648db3001d8e50ca.png
テクスチャを参照して凹凸を表現するなんてこともできそうだ
Terrainかな それってTerrainだね Terrain大好き
AC.icon 7. Lights
おぉぉ!!!!まじか
しかし、光を発射するなんてどうやって...?
まさか光源の方からレイトレーシングするなんてことはないだろうし
レイが当たったらその色を1.0, 1.0, 1.0にするんですかね
こんな単純なアプローチでいけるんか?
7.1. Emissive Materials
emittedという光源専用のメソッドをマテリアルに用意する
もし光を放つなら、その放つ色を色として返す
反射するレイにはその色にemittedしたカラーを加算合成する。
従来のマテリアルは光を放たないので黒を返す
7.2 Adding Background Color to the Ray Color Function
カメラに背景色なるモノを考慮させる
今回は黒色にしたい
なるほど加算合成として光を表現するのねappbird.icon
そして背景色を設定する
7.3 Turning Objects into Lights
auto difflight = make_shared<diffuse_light>(color(4,4,4));
そんなに明るくするの!?
反射によって減衰していく分も加味してこうしてるんかねappbird.icon
でもなんかUnityでもこういう1の上限値を超えたセッティングができてたな
はえ〜光追加するとその度に重くなるって印象があったんだけど、処理見てるとそんなに重くならんのね
じゃあシーンに光源いっぱい置いてもスピードの面でも言うて問題になるわけではないのかappbird.icon
2.13 sec
https://scrapbox.io/files/66b852113bb855001cbf92f3.png
なんか暗い...暗くない?
いや、これでいいのか?
これ光源の明るさが10, 10, 10だったらどうなるんだろう
6.25sec(sample_per_pixel = 300)
https://scrapbox.io/files/66b8536a4f1b660022b11a81.png
ちゃんと明るくなってる!
Fool aroundで遊びまわるって意味になるのかappbird.icon
2.24sec
https://scrapbox.io/files/66b852f9852f92001dfc95ab.png
光の強度を(12, 12, 12)にしたら
https://scrapbox.io/files/66b853c4de979b001da840ee.png
今見ると、光の周辺がぼやけて見える現象(bloom現象)があるけど、このレイトレーサーにおいては光のある周辺の画素がサンプリングされた結果そう見えるんだな
となると、レンズ(dof)によるぼやけがあればもっとブルーム現象が激しくなるのか
7.4
Creating an Empty “Cornell Box”
71s
結構重いappbird.icon
レイが反射しまくってるからだな
https://scrapbox.io/files/66b856b775b010001d4b938d.png
おぉ...しかし結構ノイズが乗ってるなappbird.icon
光源が小さいせいでほとんどのレイは当たっていないからノイズが乗っている
しかし現実だとこんなに明るい光源が一個あれば実際にはそんなことは起こらない
何が原因なのだろう?
これはどうすればいいんだろう?
8. Instances
ボックスを作ろう
作った、けど傾きがない
162s
https://scrapbox.io/files/66b85afc37e3e4001c9c5f78.png
回転させよう
instanceなるものが使える
あるオブジェクトを指定して、それをアフィン変換した別のコピーを仮想的に作る。
交差を判定する時はオブジェクトじゃなくて逆にレイを移動させればいいじゃない
それで交差点があったら移動させた分を戻せばいいじゃない
衝突しているか考えたい対象に対して、レイがどういった位置にあるかを求める
並行移動
というわけで、回転させたいhittableがあったら、それをラップするクラスを別途作って、それにhit処理を代替させる(その時にレイを移動させた上でラップしたhittableの衝突処理を行う)
はぇ〜賢い.....appbird.icon
get your terms crossed条件を間違える可能性がある
termsが条件なので、crossedがこんがらがるとかそう言う意味なのかね
While normals and vectors may appear identical for an object undergoing rotation and translation, an object undergoing scaling requires special attention to keep the normals orthogonal to the surface. We won't cover that here, but you should research surface normal transformations if you implement scaling.
スケーリングの変換には注意が必要?
ある一点を中心に拡大縮小する場合は確かに法線に気をつける必要がありそう
rotate_yのinstanceを実装する
rotate_vector_ccwとrotate_vector_cwなるメソッドを別途定義
さすれば結構短く書ける
あれ?この回転って原点中心での回転になってるけど大丈夫なん?
あ、元のオブジェクトを原点に置くようにしてるんか なるほど
だからtranslateが必要だったわけね
https://scrapbox.io/files/66b87caa0012da001c4a9eb9.png
9. Volumes
目的:霧を作る
participating ?
ガス輻射のように、流体自身が輻射に影響を及ぼすことをParticipating(Participate)と言います。また輻射に関与する流体(媒体)のことをParticipating Mediaと呼びます。ガス輻射を行う場合、Participating Media(媒体)の設定として、吸収係数と散乱係数を設定する必要があります。
https://www.idaj.co.jp/glossary/participating/
なるほど、輻射に影響「を及ぼしている」の意味でparticipatingか
This usually adds software architectural mayhem because volumes are a different animal than surfaces,
but a cute technique is to make a volume a random surface.
cute ってどういうこっちゃ
「気がきいている」「利口な」って意味があるらしい?
霧 = 確率的に存在するかしないかが決まる表面としてモデル化する
密度一定のvolumeを作る
霧を通過する中で、一定確率で反射する
霧の内部をレイが通過する中で、途中で拡散するかも
こうなる確率は光学的密度に比例する量$ c, 距離$ \Delta Lに比例している
$ p = c dL
$ p/c = dL
$ pを$ Lと微小量で表せないか...??
$ L = -\frac{1}{C}\ln(1-r)
$ rは確率変数で$ [0, 1\rbrackを値域とする一様分布に従う。
$ \lnどっから出てきた?
日本語訳をあたってみると訳注を発見
なるほど$ n回の試行を無限回行うものとして近似するのか
二項分布 ---> ポアソン分布だね
https://inzkyk.xyz/ray_tracing_in_one_weekend/week_2/2_9_volumes/
散乱するまでに進んだ長さが$ Lになる確率を$ p(L)とする。
そして、散乱するまでに進んだ長さ$ Lのパスを$ n個の等しい長さ$ \Delta Lの区間に分割する。
$ L = n\Delta L
すると、$ \Delta Lの区間でレイが散乱する確率は一定($ C\Delta L)だとみなせ、
距離$ Lで散乱する確率は次のようにみなせる。
$ n回分割したときに距離$ Lまで進んだ状態で散乱する確率を$ p(L)として表現すると
$ p_n(L) \propto (1 - C\Delta L)^n = (1 - C\Delta L)^{L/\Delta L}
ここで、$ n \to \inftyとすると
$ p(L)
$ = \lim_{n \to \infty} p_n(L)
$ \propto\lim_{\Delta L \to 0} (1 - C\Delta L)^{L/\Delta L}
$ = \lim_{-C\Delta L \to 0} (1 + (- C\Delta L))^{(-CL)/(-C\Delta L)}
$ = \lim_{-C\Delta L \to 0} \left((1 + (- C\Delta L))^{1/(-C\Delta L)} \right)^{-CL}
$ = e^{-CL}
となる。
ここで、$ p(L)の正規化定数は$ Cであり、$ p(L) = Ce^{-CL}。
(日本語訳訳注の方を参照)
$ rを$ \lbrack 0, 1\rbrack上の一様乱数とする。
$ r = \int_0^x p(x) dxとなったときの確率密度は$ \frac{d}{dx}\int_0^x p(x) dx = p(x)。
(確率密度... 累積密度関数の微分係数とする)
すなわち、$ x=-\frac{1}{C} \log (1-r)なる確率密度もまた$ p(x)である。
($ r = \int_0^x p(x)dxと$ p(x) = Ce^{-Cx}から同値変形をして得られる)
従って、$ xの確率密度関数は$ p(x)であり
一様乱数$ rに基づいて$ xを生成すれば、適切な距離が求められる。
一般に
$ r = \int_0^x p(x) dxなる関係が成り立っている時、
$ pは確率密度関数
関数$ f: [0, \infty) \to [0, 1) ; x \mapsto rは全単射であるため逆写像が存在する
$ x = f^{-1}(r)という関係と同値である。
$ r = \int_0^x p(x) dxを満たす$ xの確率密度が$ p(x)であるということは
$ x = f^{-1}(r)を満たす$ xの確率密度が$ p(x)であることを意味している。
textureにphase_functionって名前つけてるけどこれはなんぞ?
位相関数...?
もしかしてこの散乱位相関数のことか?
scattering phase function
ある方向から入射した放射エネルギーが他の特定の方向に散乱される確率を表す関数.散乱では放射エネルギーは方向を変えるだけで減衰されないので,この関数を全空間で積分すると1となる.等方散乱では,あらゆる方向への散乱確率は等しく一定値となる.
https://www.jsme.or.jp/jsme-medwiki/doku.php?id=10:1004829
なるほど、確かにtextureの役目だな
異方体isotropicってなんぞや?
散乱位相関数のこと(どの方向から入射したかによって散乱するか否かを決定する関数)
とはいっても、今回は指向性のないモノを仮定するらしい
1回目にレイの直線上との交点、2回目にそれより奥にあるものを取り上げているのはなぜ?
code:cpp
if (!boundary->hit(r, interval::universe, rec1))
return false;
if (!boundary->hit(r, interval(rec1.t+0.0001, infinity), rec2))
return false;
霧境界が表側にあるか裏側にあるかを調べたい
$ t上のどの位置からどの位置までが境界になっているかを調べたい
そのあと、何回か比較することでray_tで与えられている区間との共通部分を求めている
code:cpp
if (rec1.t < ray_t.min) rec1.t = ray_t.min;
if (rec2.t > ray_t.max) rec2.t = ray_t.max;
この共通部分の中でレイが散乱するかを調べたい
最後のこの部分は、ray_tで与えられた区間が空だったり負の値を含むような区間であったときの対策として入れているのかな
code:cpp
if (rec1.t >= rec2.t) return false;
if (rec1.t < 0) rec1.t = 0;
https://scrapbox.io/files/66b8bd4d24e2cd001c807c50.jpeg
図形が凸 であることを前提としている。
任意の形状に対応させるには?
適当にアルゴリズムだけ記述
https://scrapbox.io/files/66b8bfca8dfbb2001daad444.jpeg
こんな感じで、区間について複数の区間の合併集合を定義できたら意外と楽にいけそうな気がする。
とはいえど、計算量には少し不安があるけどappbird.icon
3min 6s
https://scrapbox.io/files/66b8d174de979b001daa0db8.png
なんでだよ!
みた感じz-depthに矛盾があるので多分判定方法を間違えている
rec1.t = std::min(0.0, rec1.t); ---> rec1.t = std::max(0.0, rec1.t);
https://scrapbox.io/files/66b8d3138424fe001cd645a8.png
なんか絶対表面で反射するマンになってない?
code:cpp
constant_medium(
shared_ptr<hittable> boundary,
double density,
const color& albedo
):
boundary(boundary),
neg_inv_density(density),
phase_function(make_shared<isotropic>(albedo))
{}
あっ
neg_inv_density(density)お前じゃい!!
これそのままだとエグい濃度の霧が発生していることになるのか
いや負の符号も忘れてるよな....hit_distanceが負になるのか
あ〜つまり、絶対rec1.t以前の場所で衝突が発生したことになるんだ
きたァ!!
https://scrapbox.io/files/66b8d42eb5fec100224963d5.png
10. A Scene Testing All New Features
a blue subserface reflection sphereってなんすか?
volume inside a dielectricがsubsurface materialらしい
subsurface materialってなんだ?appbird.icon
shadow raysってなんぞ?
影のレイ?
よし!(35s; 100px 1000 samples)
https://scrapbox.io/files/66b8e844c783fd001c835f8a.png
このアルゴリズムは幅$ w, サンプル数$ nに対して$ O(w^2n)なので、
仮に600 pxの画像を5000samplesで出力しようとした場合、出力時間は6^2 * 5 = 180倍ほどになる
よって、6300sかかる計算で、これは1.75時間と想定される
いけそうな範囲なのでやってみよう
1hour 36min
https://scrapbox.io/files/66b96859b0db04001d69cae7.png
https://scrapbox.io/files/66b9686a9531e2001c22fb72.png
うおおおおおおおおおおお!!!!!!
オブジェクト1000個以上あるのにこれだけのレンダリング時間で済んでるのすごいな
完