Chapter7 Sample Lights Directlyhtml
Preface函數
今天咱們來說這個還算牛逼的技術——直接光源採樣測試
以前咱們提到過,在2-7優化
前兩篇咱們也提到要減小噪點,就是圖片上的黑點點,因此,全部的矛頭都指向了這一篇。spa
簡單說一下爲何會有那麼多小點點,就是由於光線路徑中沒有觸碰到光源,路徑計算以後就會是黑色的點,能夠經過發射大量的光線,好比計算每一個像素點的時候發射8k~1w條採樣光線進行路徑計算;也能夠路徑計算方面作文章,好比加深路徑計算遞歸深度;等等諸如此類。可是上述方法都是暴力解決法,至關耗時,咱們能夠運用數學對其進行優化,從而實現畫質和效率的雙面提高,這就是咱們今天要講的——直接光源採樣!code
Readyhtm
可能您須要如下基礎:blog
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的新圖
沒什麼大的變化
只是燈光周圍的噪點少了,解釋:
由於燈光的法向量垂直向下,而咱們的反射光線與反射以後與法向量的夾角爲銳角的時候才進行紋理計算
而來自屋頂上面的光線與燈光區域碰撞反射的方向與法向量呈鈍角(注意是反射以後的新方向不是入射光方向)則不計算返回黑色,默認光沒法到達
感謝您的閱讀,生活愉快~