Chapter7 Sample Lights Directly
Preface
今天我們來講這個還算牛逼的技術——直接光源采樣
之前我們提到過,在2-7
前兩篇我們也提到要減少噪點,就是圖片上的黑點點,所以,所有的矛頭都指向了這一篇。
簡單說一下為什么會有那么多小點點,就是因為光線路徑中沒有觸碰到光源,路徑計算之后就會是黑色的點,可以通過發射大量的光線,比如計算每個像素點的時候發射8k~1w條采樣光線進行路徑計算;也可以路徑計算方面做文章,比如加深路徑計算遞歸深度;等等諸如此類。但是上述方法都是暴力解決法,相當耗時,我們可以運用數學對其進行優化,從而實現畫質和效率的雙面提升,這就是我們今天要講的——直接光源采樣!
Ready
可能您需要以下基礎:
1.微分
2.立體角 (蒙特卡羅(三))
沒了,剩下全靠想象
content
簡明扼要。
我們朝光源方向發送光線或者生成朝向光源的隨機方向都是很容易實現的,但是我們需要知道的是,pdf(direction)是什么呢?
引用書上一張圖:
對於一個光源區域A,如果我們均勻采樣該區域,那么這個pdf就等於1/A,意思就是每個點的概率均等
但是和我們的單位球體結合在一起的話,就比較麻煩了,見上圖
?為什么老是提到單位球體呢??
因為我們的光線和物體表面的交點,會作為下一個eye,然后新的視線方向是表面單位球隨機產生的方向,具體見1-5中的diagram7-3
好了,淵源就是醬紫,我們繼續
如果那個小的微分區域dA的采樣概率為
p_q(q)*dA(采樣比例乘以微分區域),也就是dA/A
而對應到單位球體表面的很小的區域,即我們所述的方位角。方位角微分dΩ對應的采樣概率為
p(direction)*dΩ
這里有一個用來描述dΩ 和 dA 的表達式:
dΩ = dA cosα / (distance(p,q)^2)
即:方位角微分區域:光源微分區域分成(球心到A中心距離平方)份,取其中的cosα代表的份額數
因為這個dA 和 dΩ的概率是相同的,所以就有如下等式
p(direction) * cosα * dA / (distance(p,q)^2) = p_q(q) * dA = dA / A
所以
p(direction) = distance(p,q)^2 / (cosα * A)
我們接下來就檢驗一下這個數學公式是否正確
但是代碼可能非常丑
我們需要之前的光源的區域參數
list[cnt++] = new xz_rect(200, 350, 220, 340, 550, light);
所以我們有以下的代碼
rtvec lerp(const ray& sight, intersect* world, int depth) { hitInfo info; if (world->hit(sight, (rtvar)0.001, rtInf(), info)) { ray scattered; rtvec emitted = info._materialp->emitted(info._u, info._v, info._p); rtvar pdf; rtvec albedo; if (depth < 50 && info._materialp->scatter(sight, info, albedo, scattered, pdf)) { rtvec on_light = rtvec(213 + lvgm::rand01() * (343 - 213), 554, 227 + lvgm::rand01() * (332 - 227)); rtvec to_light = on_light - info._p; double distance_squared = to_light.squar(); to_light.self_unitization(); if (dot(to_light, info._n) < 0) return emitted; double light_area = (343 - 213)*(332 - 227); double light_cosine = fabs(to_light.y()); if (light_cosine < 1e-6) return emitted; pdf = distance_squared / (light_cosine*light_area); scattered = ray(info._p, to_light, sight.time()); return emitted + albedo *info._materialp->scatter_pdf(sight, info, scattered)*lerp(scattered, world, depth + 1) / pdf; } else return emitted; } else return rtvec(); }
如下圖:
因為我們一路做測試,做圖形分析對比,所以我們上圖是sample為250時候的效果
據說,sample為10時,效果依舊很好
所以又超快速運行了一個sample為10的
不管怎樣,我們的圖形噪點已經做到了比較不錯的境地了,sample為10!!!
再看看之前的sample為250的圖形效果
簡直噪出天際線
關於本篇的那個圖
天花板上燈光周圍的噪聲是由於燈光是雙面的,燈光和天花板之間有一個狹窄空間。
我們可以通過將燈光法向量調至垂直向下來解決這一問題,同時讓我們的燈光發射函數也做相應的處理
virtual rtvec emitted(const ray& rIn, const hitInfo& info, const rtvar u, rtvar v, const rtvec& p)const { if(dot(info._n,rIn.direction())<0.) return _emit->value(u, v, p); else return rtvec(); }
記得一起改了material基類,以及lerp的emit函數調用根據上述參數描述
所以我們又得到了一個sample為10的新圖
沒什么大的變化
只是燈光周圍的噪點少了,解釋:
因為燈光的法向量垂直向下,而我們的反射光線與反射之后與法向量的夾角為銳角的時候才進行紋理計算
而來自屋頂上面的光線與燈光區域碰撞反射的方向與法向量呈鈍角(注意是反射之后的新方向不是入射光方向)則不計算返回黑色,默認光無法到達
感謝您的閱讀,生活愉快~