如何解決光線跟蹤中浮點數誤差導致的渲染錯誤?

下圖為自己的渲染器渲染的標準康奈爾盒,開啟伽馬矯正,跟蹤深度固定為12(沒有開啟Russian roulette),spp = 256(優化較差,速度較慢)

問題出現在光線求交上。第一幅圖中,未做任何浮點數剔除操作。可見到左邊的鏡面反射球與玻璃球上的反射有嚴重的錯誤,光斑異常

第二張圖為,對每個求交點p,做 vec{p} = vec{p} + epsilon * vec{wo}
(wo為單位出射向量方向),以保證不出現求交點因為誤差導致的,在球內做反射的錯誤操作。

此處EPSILON取1e-3。嘗試使用1e-4,1e-5,1e-6這些,均會出現錯誤渲染結果

參考[smallpt](smallpt: Global Illumination in 99 lines of C++)與[smallpaint](https://cg.tuwien.ac.at/~zsolnai/gfx/smallpaint/)中的實現,他們均在遞歸跟蹤時採用double而非float,因此並沒有做上述的浮點數誤差判斷。

[PBRT](Physically Based Rendering: From Theory to Implementation)中使用float,但似乎隱約在實現中只見到1e-5的偏移,並不清楚它具體是如何保證畫面精確的。

因此想詢問

  1. 想要使用float,那麼如何才能從根本上避免這樣的問題出現?

  2. 有沒有不使用我採用的笨拙的手工偏移的方法,更為標準的消除誤差的做法?


float換成double也可以


最高效的辦法就是加bias,當然不夠robust,比如無法處理兩個反射面夾角出的光線

同Milo Yip的回答類似,如果是一般的三角建模的話,每個面加上一個id,觸發反射/折射時忽略出射面的id即可,如果你使用kd-tree之類的加速結構,並且某個片可能佔據多個tree nodes,那麼本身在實現的時候也會需要避免重複求交計算,應該可以重用該部分的代碼把這個出射面當作已經經過求交計算來做,這樣基本是0開銷的。這個方法對於一般的場景是比較robust的,不過也有無法解決的特殊結構(比如薄膜),以及極大入射角時也可能出現誤差。

如果是參數化建模,則情況太過複雜了,不過也可以用同樣的思路,記錄出射點的參數鄰域,求交時忽略,不過也不比bias大法健壯,而且效率很低


光線在進行變換的同時一定要補償浮點數誤差,pbrt 第三版第三章有解釋。

這個後面有一個小例子,渲染引擎雜記(1)——浮點數誤差分析及補償


轉個ompf上的討論,Matt Pharr 貼了pbrt3的新章節pdf

ompf2.com ? View topic


沒用的,上定點數吧


謝邀。沒有什麼更標準的做法了。大多渲染器都有一個可以調整的Ray Bias參數(例如PRman: https://renderman.pixar.com/view/raytracing-fundamentals)用於避免Ray和發射出Ray的表面出現自交。

之所以是可調整的是因為根據不同的場景讓用戶可以微調。你也可以用一些簡單的heuristic去動態的設置這個bias,例如讓根據場景的大小去縮放這個值。Bias=Scale 	imes WorldBoundingSphereRadius

還有另一種做法就是,不在Ray的方向上Bias,而在表面法線的方向上Bias vec{p} = vec{p} + bias* vec{normal}
,這樣做的好處是哪怕在Ray相對於表面的頂角特別低的時候,也不會自交。當然壞處是你破壞的Ray光線的採樣得到的方向。

一般都會同時實現這些選項讓用戶自交選,沒有一個是robust,因為就算不robust這個問題也並不難解決。


如果只考慮反射光線時返回相同幾何形狀交點的問題,而形狀是凸的,可以簡單地在投射反射光線時忽略該形狀。

穿透光線則要忽略進入形狀的交點,只求離開該形狀的光線。

這隻算是trick,並不robust。


我以為第二張圖在反射中出現了ao,求摺疊。。。


推薦閱讀:

c++里有沒有類似python中list的結構?
本人熟悉window下C/C++開發,windows網路/多線程編程,怎麼快速轉到linux下做開發?
Python 和 C++ 如何選擇?
C++ 有哪些性能分析工具?

TAG:C | 計算機圖形學 | 三維渲染 | 光線跟蹤 |