[光線追蹤] 06 -- BRDF類 & 光源類
在上一篇專欄里實(shí)現(xiàn)了第一個(gè)可以運(yùn)行的光追例子,? 這篇專欄將會(huì)分離 Material 和 BRDF,? 并且實(shí)現(xiàn)兩種常見的場(chǎng)景光源.

Sphere::hit 的化簡(jiǎn)
上一篇專欄里忘記了一件比較重要的事情,? 這里專門分開一小節(jié)來(lái)講.
求解光線與球體碰撞是求解方程?,? 求解一元二次方程的公式是:
,? 上下同除以 2 可以化簡(jiǎn) b 為 half_b,? 并且不需要額外乘 4.? 另外,? 因?yàn)閺?qiáng)制要求 Ray::direction 應(yīng)該是歸一化的,? 那么
.? 根據(jù)這些信息可以化簡(jiǎn) Sphere::hit 為

同理 Sphere::hit_record 里也一樣.

分離 BRDF
在上一篇專欄里實(shí)現(xiàn)了啞光材質(zhì) Matte 類,? 并在 Matte 里實(shí)現(xiàn)了 Lambertian 模型的半球渲染.? 但是我們需要的渲染模型?,? 包含了半球渲染和面積渲染兩項(xiàng),? 并且這兩項(xiàng)應(yīng)該是等價(jià)的.
由之前的討論可以得知,? 一個(gè)?BRDF 應(yīng)該在半球渲染 K??里實(shí)現(xiàn)采樣,? 而在面積渲染 K??里給出 f?.? 那么可以做一個(gè) BRDF 類同時(shí)提供相應(yīng)的采樣和 f?:

函數(shù) f? 需要局部坐標(biāo)系的方向 ω? 和 ω?,? 而 Hitrecord 里包含全局坐標(biāo)的 ω? (-ray.direcion) 和表面法線 normal,? 那么額外輸入全局坐標(biāo)的 ω? 就可以通過(guò)法線計(jì)算出相應(yīng)的局部坐標(biāo)下的 ω? 和 ω?.? 在 Sampler 里返回的采樣是在局部坐標(biāo),? 所以需要法線轉(zhuǎn)換為全局坐標(biāo).
至少這里的程序,? 只需要一個(gè)法線就可以完成局部坐標(biāo)與全局坐標(biāo)的轉(zhuǎn)換.? 當(dāng)只存在法線時(shí),? 立體角里的 θ 是可以求出的,? 但 φ 不能,? 也就是說(shuō)這里的 BRDF 與方向角 φ 無(wú)關(guān),? 這種 BRDF 被稱為各向同性 BRDF.? 為了實(shí)現(xiàn)各向異性?BRDF,? 就必須求出 φ,? 亦即需要碰撞點(diǎn)處的紋理坐標(biāo)系,? 程序修改起來(lái)也是比較簡(jiǎn)單的:? 在 HitRecord 里記錄局部紋理坐標(biāo)系,? 但這個(gè)專欄內(nèi)并沒(méi)有這樣的打算.
實(shí)現(xiàn)了 BRDF 的模板類后,? 就可以把 Lambertian 模型從 Matte 材質(zhì)類里分離出來(lái):

因?yàn)?Lambertian 模型的采樣必須要求是采樣集中度為 1 的半球采樣,? 所以這里選擇輸入 Sampler 的參數(shù)給 Lambertian 的初始化方法,? 而不是直接輸入 Samplerp.
在分離了 BRDF 后,? Matte 類里需要指定 BRDF,? 但 BRDF 是通用的,? 所以如果輸入任意一個(gè) BRDF 的話可能就不是 Matte 了,? 所以這里可以實(shí)現(xiàn)一個(gè)通用的不透明材質(zhì) Opaque:??


光源
在實(shí)現(xiàn)物體光源前,? 先來(lái)介紹兩個(gè)不是物體的光源:? 平行光和點(diǎn)光源.? 平行光可以看作從無(wú)限遠(yuǎn)處的光源,? 這意味著在場(chǎng)景內(nèi)任何地方光線都是從一個(gè)方向射入的.? 點(diǎn)光源可以看作無(wú)限小的球體光源.
因?yàn)楣庠葱枰褂妹娣e模型進(jìn)行渲染,? 而在面積模型里入射光線是從光源處得出的,? 所以只需給出光源照射到物體的位置,? 光源處的 HitRecord 就可以由光源本身給出.? 因?yàn)樽詈?HitRecord 都是被光源本身內(nèi)部消化的,? 所以干脆光源只給出一個(gè)方法?render_light 就足夠了.
看到面積模型:?,? 僅有?
?和
兩項(xiàng)是依靠入射方向 ω? 求出的,? 并且恰好這兩項(xiàng)是發(fā)生在 p 點(diǎn)上的.? 而?
?和?
?兩項(xiàng)是發(fā)生在 q 點(diǎn)上.?
?和?
?兩項(xiàng)則是發(fā)生在從 q 傳播到 p 的路上.? 那么分配計(jì)算時(shí),? 可以把發(fā)生在 p 點(diǎn)上的項(xiàng)放在 Material 里計(jì)算,? 而剩下的項(xiàng)放在光源里計(jì)算,? 也就是說(shuō)光源仍需要提供一個(gè)方法計(jì)算 ω?.? 那么光源類實(shí)現(xiàn)為下:

其中,? 如果當(dāng) has_shadow 為 false 時(shí),? v(p, q) 永遠(yuǎn)返回 1,? 并且這個(gè)值永遠(yuǎn)初始化為 true.
對(duì)于平行光和點(diǎn)光源來(lái)說(shuō),? 它們的面積都為 0,? 所以對(duì)它們進(jìn)行的渲染不應(yīng)該叫做 MC 積分了,? 盡管如此,? 但是代碼邏輯是沒(méi)有變化的.? 在面積模型里的??可以看作平方反比定律的體現(xiàn),? 但是平行光是不滿足平方反比定律的,? 所以平行光的這一項(xiàng)可以直接刪掉,??那么平行光類實(shí)現(xiàn)如下

可以看到在平行光里 get_direction 沒(méi)用上 Vec3 參數(shù),? 這說(shuō)明平行光對(duì)場(chǎng)景里所有地方都是一致的,??并且?direction 表示光的傳播方向,? 因?yàn)檫@里使用反向光追,? 所以 get_direction 方法返回的是 direction 的反向.
類似地,? 點(diǎn)光源可以實(shí)現(xiàn)如下

至于物體光源就可以留到下一篇專欄里了 (就嗯拖).

渲染部分
因?yàn)?Light 不同于 Object,? 所以在 World 里需要另外一個(gè) vector 存放光源:
并且需要在 Material 里實(shí)現(xiàn)相應(yīng)的面積模型:



至此代碼已經(jīng)完整,? 接下來(lái)又是愉快的排列組合環(huán)節(jié):

渲染結(jié)果如下

在代碼以及渲染結(jié)果里有幾個(gè)值得注意的地方:? 第一個(gè)就是因?yàn)槠叫泄夂忘c(diǎn)光源的面積為無(wú)限小,? 所以是不可見的,? 又所以習(xí)慣在點(diǎn)光源的位置放置一個(gè)渲染方式不太一樣的小球體展示點(diǎn)光源,? 如下圖所示 (相關(guān)代碼是在 render_figure 里被注釋掉的部分,?但這代碼完全沒(méi)考慮被遮擋的情況,?yysy,?也不是不能用):

第二個(gè)需要注意的是,? 看到點(diǎn)光源的顏色值:?45?* RGB(.6, .8, 1),? 說(shuō)明點(diǎn)光源本身亮度是非常高的,? 但盡管如此渲染出來(lái)的場(chǎng)景仍然比較昏暗,? 這是因?yàn)槠椒椒幢软?xiàng)對(duì)亮度起主要作用.? 所以大部分情況下,? 為了美觀會(huì)直接把平方反比項(xiàng)刪掉或改成反比,? 下面是兩種改進(jìn)的渲染圖:


可以看到觀感都比使用平方反比的要更好.? 商業(yè)用的光追器提供自定義參數(shù)控制光源隨距離衰減的強(qiáng)度 (當(dāng)然也不難實(shí)現(xiàn)),? 但為了真實(shí)性,? 在這個(gè)程序里還是繼續(xù)使用符合平方反比的光源.
第三個(gè)要注意的是溢色,? 之前說(shuō)過(guò)顏色值是定義在 [0,1] 里,? 而點(diǎn)光源的顏色是大于 1 的,? 那么當(dāng)儲(chǔ)存成圖片時(shí),? 大于 1 的值會(huì)被截?cái)喽槐A粜?shù)部分,? 這會(huì)產(chǎn)生極其嚴(yán)重的顏色錯(cuò)誤,? 比如說(shuō)把點(diǎn)光源的亮度乘數(shù)換成 75 進(jìn)行渲染:

可以看到盡管場(chǎng)景亮度有所增加,? 但光源直接照射的地方出現(xiàn)了錯(cuò)誤的顏色.? 因?yàn)椴淮蛩阋?HDR 渲染 (以及相應(yīng)的 map tonning 技術(shù)),? 所以在 LDR 范圍里有兩種常做的方法: 1) 所有顏色值 clamp 到 [0, 1] 里;? 2) 顏色值除以最大分量值,? 以保證色調(diào)正確.? 下面是兩種方法的實(shí)現(xiàn):

然后改寫 render_figure:

在 main 里指定 RemapColor 的實(shí)類就可以進(jìn)行渲染了,? 下面是兩種不同的渲染結(jié)果


在觀感上是 Clamp01 比較好,? 但是可以看到展示點(diǎn)光源的球體完全變成了白色;? MaxTo01 保持著色調(diào)正確,? 但在過(guò)曝區(qū)域失去了顏色變化.? 兩種方法可以按照個(gè)人喜好來(lái)使用,? 之后的程序如果沒(méi)有特別說(shuō)明就統(tǒng)一使用 Clamp01 了.

/* 請(qǐng)自行腦部結(jié)語(yǔ)?*/
項(xiàng)目倉(cāng)庫(kù):?https://github.com/nyasyamorina/nyasRT
群:?274767696
封面pid: 哦, 這次封面不是澀圖, 那沒(méi)事了