这篇文章主要是参考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的结果。

运行结果:

  • Linear Z,16位浮点格式

从结果可以看出16位浮点的误差还是蛮大的,越靠近far-clip plane,误差越大

  • Linear Z,32位浮点格式

很明显,32位的精度很高,只有在非常接近far-clip plane时才有些许误差,理论上来讲32位定点数精度会更好。当然高精度的代价是高带宽

  • Linear Z,16位定点格式

16位定点格式的精度比浮点要好,误差分布也很均匀。如果某些情况必须使用16位的buffer时,16位定点数是不错的选择。

  • Perspective Z,16位浮点格式

这是所有测试结果中误差最严重的一种,这和MJP的结果是一样的,原因在于浮点数的分布和透视投影的特性。所以无论何时,都不要使用16位浮点数的非线性depth buffer。

  • Perspective Z,32位浮点格式

和之前的32位线性buffer一样,精度很高

  • Perspective Z,16位定点格式

这个测试结果表明:对于非线性的depth buffer,16位定点数格式远好于16位浮点数,并且在靠近near-clip plane的地方,比16位的线性buffer精度更好,缺点就是在靠近far-clip plane时精度就下降很多,这是透视投影的特性导致的

  • Perspective Z,24位定点格式

 

这是最常用的格式,从结果来看和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中的值变成了越大越靠近。

  • Position Buffer的精度

MJP的博客里还测试了把position直接存储到texture的精度,他测试的格式是DXGI_FORMAT_R16G16B16A16_FLOAT 效果如下(左下角和右下角分别为误差和误差乘以100):

可以看出误差并不小,结果和用16位浮点depth buffer构建position是差不多的,所以把position存到texture是糟糕的选择,不仅精度不够,而且占用带宽。

  • 线性和非线性的buffer

前面所说的线性和非线性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不是。

参考资料:

https://mynameismjp.wordpress.com/2010/03/22/attack-of-the-depth-buffer/

https://developer.nvidia.com/content/depth-precision-visualized

https://www.sjbaker.org/steve/omniv/love_your_z_buffer.html

http://www.humus.name/index.php?page=Comments&ID=255

http://dev.theomader.com/linear-depth/

测试不同格式下depth buffer的精度的更多相关文章

  1. D3D depth buffer的预览

    在使用D3D开发游戏的过程中,很多情况下都会用到depth buffer来完成特定的效果,比如DOF,Shadows,SSAO等等.在这些情况下我们就可能需要预览depth buffer来确定它是正确 ...

  2. [Zz] DX depth buffer

    声明:本文完全翻译自DX SDK Documentation depth buffer,通常被称为z-buffer或者w-buffer,是设备的一个属性,用来存储深度信息,被D3D使用.当D3D渲染一 ...

  3. Caffe学习系列(12):不同格式下计算图片的均值和caffe.proto

    均值是所有训练样本的均值,减去之后再进行训练会提高其速度和精度. 1.caffe下的均值 数据格式是二进制的binaryproto,作者提供了计算均值的文件compute_image_mean, 计算 ...

  4. gpt格式下通过U盘装win7系统

    首先下好一个64位的win7系统,可以是ghost版的,然后放到你的U盘,在U盘的根目录下添加bootmgr.efi,bootx64.efi.shell.efi这几个文件,其它都不要管,重启,你就在g ...

  5. 从depth buffer中构建view-space position

    观察透视投影矩阵: 对于x和y,矩阵变换只是一个缩放系数,那么逆变换就是缩放系数的倒数,所以 设Xndc Yndc为NDC空间中的XY坐标,Xview Yview Zview为view space中的 ...

  6. Cesium 中由 Logarithmic Depth Buffer 引起的模型显示不完整的问题

    当 Cesium 单个模型过长时,会遇到某些视角模型显示不完整的问题,如下图所示: 经过在官方论坛上询问,该问题由 viewer.scene.logarithmicDepthBuffer 开启造成,关 ...

  7. click 在网页测试手机模式下无效,不能执行。调成非手机模式即可

    click  在网页测试手机模式下无效,不能执行. 调成非手机模式即可

  8. Depth Buffer

    Up until now there is only one type of output buffer you've made use of, the color buffer. This chap ...

  9. 用实力燃爆暑期丨i春秋渗透测试工程师线下就业班开课了!

    i春秋&赛虎暑期渗透测试工程师线下就业班开课了! 本期开班地点:北京,面授脱产:四个月. 如果这次没来得及报名的同学也可以选择9月份广州的班次,具体开班时间请咨询谢老师:18513200565 ...

随机推荐

  1. PHPcms 把盛大登陆换成人人网登陆

    首先要确保你的 phpcms是比较新的版本, v9.3以后的吧 这里说明一个函数 rawurlencode() 本函数将字符串编码成 URL 的字符串专用格式,特殊的字符会转换成百分比符号后面加上二个 ...

  2. Struts2框架学习(三) 数据处理

    Struts2框架学习(三) 数据处理 Struts2框架框架使用OGNL语言和值栈技术实现数据的流转处理. 值栈就相当于一个容器,用来存放数据,而OGNL是一种快速查询数据的语言. 值栈:Value ...

  3. origin中把多个拟合曲线放在一张图

    双击其中一个.或者New一个graph.这里直接双击其中一个图. 右键,找到layer contents. 可以看到,一个scatter配一个polynomial fit line.把剩下的B,C,D ...

  4. I帧/P帧/B帧---术语解释

    视频压缩中,每帧代表一幅静止的图像.而在实际压缩时,会采取各种算法减少数据的容量,其中IPB就是最常见的.  简单地说,I帧是关键帧,属于帧内压缩.就是和AVI的压缩是一样的. P是向前搜索的意思.B ...

  5. android:在ViewPager中使用Button

    最近在项目用用到ViewPager ,其中页面包含有Button,因为之前也有使用个ViewPager ,所以这个也照搬之前的方式,测试后发现点击button无法执行,这个button是在第一页面的默 ...

  6. TypeScript教程2

    在TS中,我们允许开发人员使用面向对象技术. 1.类让我们看看一个简单的基于类的例子: class Greeter { greeting: string; constructor(message: s ...

  7. 通过判断cookie过期方式向Memcached中添加,取出数据(Java)

    应用场景:在数据驱动的web开发中,经常要重复从数据库中取出相同的数据,这种重复极大的增加了数据库负载.缓存是解决这个问题的好办法.但是ASP.NET中的虽然已经可以实现对页面局部进行缓存,但还是不够 ...

  8. Professional C# 6 and .NET Core 1.0 - 38 Entity Framework Core

    本文内容为转载,重新排版以供学习研究.如有侵权,请联系作者删除. 转载请注明本文出处:Professional C# 6 and .NET Core 1.0 - 38 Entity Framework ...

  9. ArcGIS制图表达Representation-制图表达介绍

    ArcGIS制图表达技术-制图表达介绍 by 李远祥 在基于GIS数据的制图中,大部分都是使用的数据+符号应用的这种模式.这种模式已经被应用很多年,而且也是非常成熟.对应在ArcGIS体系里面,就是数 ...

  10. 解析.NET对象的跨应用程序域访问(下篇)

    转眼就到了元宵节,匆匆忙忙的脚步是我们在为生活奋斗的写照,新的一年,我们应该努力让自己有不一样的生活和追求.生命不息,奋斗不止.在上篇博文中主要介绍了.NET的AppDomain的相关信息,在本篇博文 ...