這篇文章主要是參考mjp的「attack of the depth buffer」,測試不同格式下depth buffer的精度。
測試的depth buffer包含兩類: 一是非線性的depth buffer,儲存著perspective z(也就是最常用的,透視投影後歸一化的z/w的buffer),二是線性的depth buffer,儲存著view space z(這裡的線性指的是在view space 中是否線性)。測試的格式包括16位浮點數,32位浮點數,16位定點數,還有最常用的24位定點數(dxgi_format_d24_unorm_s8_uint) 。
測試的方法是在pixel shader裡取樣depth buffer,然後構建出view space position,把這個值和vertex shader裡插值過來的position做對比,把兩者的差別輸出到rt的red分量,這樣越紅的部分誤差就越大。測試的near-clip plane為1,far-clip plane為300,場景模型用的是dxsdk裡的columns。
測試程式畫面的左半邊顯示精度誤差,右半邊顯示把精度誤值乘以100的結果。
執行結果:
從結果可以看出16位浮點的誤差還是蠻大的,越靠近far-clip plane,誤差越大
很明顯,32位的精度很高,只有在非常接近far-clip plane時才有些許誤差,理論上來講32位定點數精度會更好。當然高精度的代價是高頻寬
16位定點格式的精度比浮點要好,誤差分布也很均勻。如果某些情況必須使用16位的buffer時,16位定點數是不錯的選擇。
這是所有測試結果中誤差最嚴重的一種,這和mjp的結果是一樣的,原因在於浮點數的分布和透視投影的特性。所以無論何時,都不要使用16位浮點數的非線性depth buffer。
和之前的32位線性buffer一樣,精度很高
這個測試結果表明:對於非線性的depth buffer,16位定點數格式遠好於16位浮點數,並且在靠近near-clip plane的地方,比16位的線性buffer精度更好,缺點就是在靠近far-clip plane時精度就下降很多,這是透視投影的特性導致的
這是最常用的格式,從結果來看和32位的差不多。主要是這個測試結果的far-clip值並不大,誤差值不容易察覺,實際的誤差值在0.005%左右,如果far-clip非常大的話,誤差就會變大。
為什麼16位浮點數非線性buffer的誤差這麼之大?原因有兩個:1.透視投影的特性 2.浮點數的精度分布
根據透視投影矩陣,可以推導出view space z對應z buffer的函式影象如下
可以看出在靠近near的地方,曲線非常陡峭,斜率很大,在靠近far時,曲線很平穩,斜率很小。所以透視投影對於近處的物體有很好的精度,但隨著越來越靠近far時,精度就會不斷下降。
far和near的比值決定了曲線的陡峭程度,如果far/near越大,那麼曲線就越陡,z buffer的精度越差。比如16位浮點格式,當far/near為600時,誤差約為1%,當far/near為8000時,誤差高達10%。
對於浮點型別,其值在[0,1] 區間並不是均勻分布的,實際上是在靠近0時,精度最好,遠離0時,精度下降。如下圖:
而浮點型別的這種分布,和透視投影的特性剛好是相矛盾的——在靠近near(0)時,斜率很大,view space z只要變換一點點,z buffer就能有很大變換,所以並不需要很高的精度,而在遠離near時,斜率很小,就需要更多的精度。16位浮點本身精度就不如32位,再加上浮點的分布和透視投影特性,更加加劇了誤差,所以16位float的非線性buffer的精度才會如此之差。
這也說明了為什麼定點數的精度要好於浮點數——因為定點數是均勻分布的,不會有浮點數那樣的問題。
當使用非線性的浮點buffer時,實際上浮點數的很多精度都被浪費了。所以有一種做法就是把near plane和far plane對換,這樣近處的物體對映到1附近,遠處的物體對映到0附近,這就剛好符合了浮點數的精度分布,這在精度不夠時是一種很有效的優化手段。但在用的時候需要把depth test的條件從less改為greater,z buffer中的值變成了越大越靠近。
mjp的部落格裡還測試了把position直接儲存到texture的精度,他測試的格式是dxgi_format_r16g16b16a16_float 效果如下(左下角和右下角分別為誤差和誤差乘以100):
可以看出誤差並不小,結果和用16位浮點depth buffer構建position是差不多的,所以把position存到texture是糟糕的選擇,不僅精度不夠,而且占用頻寬。
前面所說的線性和非線性buffer,指的是在view space中,perspective z的buffer不是線性的,view space z的buffer是線性的。但是在螢幕空間,情況相反,perspective z的buffer是線性的,而view space z的buffer不是線性的。why ? 因為在螢幕空間,1/z才是線性的,而perspective z本身就是1/z的形式,所以是線性的,view space z不是1/z的形式,所以不是線性的。
螢幕空間中是線性的有什麼好處?很多螢幕空間的渲染就能收益,線性就意味著位於同一圖元表面的pixel的delta z是相同的(ddx(z), ddy(z)),那麼邊緣檢測之類的就變得很容易。而且螢幕空間的線性意味著插值簡單,無需做透視校正,那麼對硬體是很友好的。
關於線性和非線性,humus的「a couple of notes about z」中有詳細的論述。
1. 盡量減小far/near的值。
2. 16位浮點數的非線性depth buffer精度最差,避免使用。
3. 浮點格式精度不夠時,考慮交換near plane和far plane來提高精度
4. 在螢幕空間中,perspective z buffer是線性的,view space z buffer不是。
測試不同格式下depth buffer的精度
這篇文章主要是參考mjp的 attack of the depth buffer 測試不同格式下depth buffer的精度。測試的depth buffer包含兩類 一是非線性的depth buffer,儲存著perspective z 也就是最常用的,透視投影後歸一化的z w的buffer 二是...
測試不同格式下depth buffer的精度
這篇文章主要是參考mjp的 attack of the depth buffer 測試不同格式下depth buffer的精度。測試的depth buffer包含兩類 一是非線性的depth buffer,儲存著perspective z 也就是最常用的,透視投影後歸一化的z w的buffer 二是...
測試不同格式下depth buffer的精度
這篇文章主要是參考mjp的 attack of the depth buffer 測試不同格式下depth buffer的精度。測試的depth buffer包含兩類 一是非線性的depth buffer,儲存著perspective z 也就是最常用的,透視投影後歸一化的z w的buffer 二是...