昌平哪有做网站的公司,盐城网站建设官网,寻找赣州网站建设,查公司的网站有哪些在3D渲染管线中#xff0c;Z这个家伙几乎无处不在#xff0c;如Z-Buffer#xff0c;Early-Z#xff0c;Z-Cull#xff0c;Z-Test#xff0c;Z-Write等等#xff0c;稍有接触图形学的人都会对这些术语有所耳闻。 那么Z到底是什么呢#xff1f;首先Z当然可以是任意坐标系… 在3D渲染管线中Z这个家伙几乎无处不在如Z-BufferEarly-ZZ-CullZ-TestZ-Write等等稍有接触图形学的人都会对这些术语有所耳闻。 那么Z到底是什么呢首先Z当然可以是任意坐标系下的z坐标值但我们这里要说的Z值就是深度值上面几个包含Z的术语里面的Z也都是深度值的意思深度值是物体变换到屏幕空间后的z坐标的值因为NDC空间转屏幕空间时并不会改变z值所以也可以说是NDC空间中z坐标的值有些读者可能认为在屏幕空间中Z值已经不存在了这也是有道理的因为屏幕是一个2d空间没有z轴但我们在这里不做2d3d区别认为都有z轴。在DirectX中Z值得取值范围是[0,1]在OpenGL中其取值范围为[-1,1]这篇拟在DirectX环境下讨论Z。 Z值的推导请参见 http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c10123/Deriving-Projection-Matrices.htm 这里我们直接用上文中的一个结果建议没推导过的读者按照这一篇的思路推导一遍必定会受益匪浅 即Z值在透视投影后的结果 $$ZZ_{c}{\frac{f}{f-n}Z_{c}}-{\frac{fn}{f-n}}$$ 上面的方程中$Z$即我们要求的深度值$Z_{c}$是物体在Eye Space中的z坐标f是视锥体远裁剪平面在Eye Space中的z坐标n是视锥体近裁剪平面在Eye Space中的z坐标。由上式可求得($ZZ_{c}$其实是Clip Space中的z值除以$Z_{c}$就是透视除法得到NDC空间的z值也即是深度值Z) $$Z{\frac{f}{f-n}}-{\frac{fn}{(f-n)*Z_{c}}}\quad ①$$ 对于$Z_{c}$我们可以证明其关于物体在World Space中的z值$Z_{w}$为线性关系那么根据上式可知$Z$与$Z_{c}$、$Z_{w}$皆不为线性关系。简单起见我们取f1000n0.01有 $$Z≈-{\frac{0.01}{Z_{c}}}1\quad②$$ 其函数图像如下$Z_{c}0$: 图1 图中A点表明了$Z_{c}$∈[0.01,0.1]的物体占用了十分之九0~0.9的深度值这说明在z轴方向上与相机距离为0.1到1000的物体只用到了十分之一0.9~1.0的深度值。这个结果是令人印象深刻的因为Z值的分布太不均匀了就好像世界上的绝大部分钱都被一个人占有了一样。那Z值的分布情况对于3d渲染来说重要吗它意味着什么呢 深度值的不均分分配会导致非常严重的后果那就是Z-Fighting。深度值的取值范围是[0,1]但这并不代表它存到Z-Buffer里面后也一定是[0,1]的浮点数事实上在过去很长一段时间乃至现在很多时候深度值被保存在16位或者24位的无符号整数中。这里我们用范围更小的16位来存储深度值因为这能更好的凸显出问题。当深度值存储为16位无符号整型格式时其取值范围是[0,65535]现在我们来算一算当深度值为65534时$Z_{c}$是多少?65534映射到[0,1]中值为65534/65535。连同f1000n0.01代入①式①比②可获得更精确的结果可解得$Z_{c}$≈395.9005401718437≈395.9这说明在Eye Space中在z轴方向上距离相机395.9到1000的物体的深度值都是65535当两个物体拥有同样的深度值时就会产生非常丑陋的Z-Fighting(详见https://en.wikipedia.org/wiki/Z-fighting: (相同的深度值导致GPU不能正确分辨哪个在前哪个在后)。 在3d渲染中应该尽可能的避免产生Z-Fighting即应该尽可能的改善深度值分布的均匀程度。提高用来保存深度值类型的精度可以起到改善z值冲突的情况比如用24位甚至32位的数据类型来存储深度会比16位好很多但由于硬件条件的限制和Z值的非线性增长目前来说不可能用太多位的硬件出现。有的人也许会想到用浮点数来保存深度值但其实这毫无作用的甚至可以说更为浪费因为对于32位浮点数其尾数Mantissa只有23位二进制数规格化浮点数加上一位保留位也只有24位这与24位无符号整数表示的精度是一样的而浮点数还多使用了8位来存储其他信息。另外虽然浮点数本身表示的范围更广但我们知道深度值的范围不过为[0,1]当我们用浮点数来存储深度值时当然不会再去做映射这样深度值其实只占到了范围在[0,1]的浮点数所占的精度这势必就更少了不过好在浮点数的精度分布也主要分布在0值附近0值附近的符点数拥有更好的精度但不管怎样目前来说想依靠浮点数来改善状况是不可取的。 除了提高Z-Buffer的精度以外还有一些方法也可以改善Z值冲突的情况如增大近裁面与相机位置z值距离即n值就是一种方法。对①式 我们取n0.1f1000不变有 $$Z≈-\frac{0.1}{Z_{c}}1$$ 其图像如下 图2 对比图1图2的情况好了很多对比两个图中的点A前0.9的深度值表示的范围从0.1扩大到了1说明有更多的深度值用来表示$Z_{c}$比较大的情况如果还以16位无符号整数来存储深度值计算后可得$Z_{c}$在区间[868,1000]时共享65535这个深度值这比[396,1000]的冲突少了非常多降低了出现Z-Fighting的概率。而我们仅仅是将n从0.01提高到0.1而已这对一般的应用场景几乎不会产生影响。 既然如此我们将n值继续增大比如取n100会怎样呢?我们将n100f1000不变代入1式得 $$Z-\frac{1000}{9Z_{c}}\frac{10}{9}$$ 图像如下我必须把x轴压缩400倍才能截个图 图3 可以看到0到0.9的深度值已经可以表示到大约600的时候了要知道n0.01的时候 0.9的深度值$Z_{c}$只能表示到0.1n0.1的时候$Z_{c}$只能表示到1。依然将深度值存入到无符号整型中我们可以计算出当物体的$Z_{c}$∈[999.863,1000]时它们才共用65535这个深度值通过取n100我们很好地改善了Z值的分布情况。至少看起来已经是个很好——甚至可以说近乎完美的办法了。但是事实并非如此由于取得n100我们舍弃了整个$Z_{c}$∈[0,100]的物体我们将永远看不到那些离相机z轴距离少于100的物体增大近裁剪面的值以换取深度值的分布均匀程度难言利弊得失。 难道就没有更好的改善深度值分布的办法了吗当然有了办法就是神奇的Reversed-ZReversed-Z的做法其实是很简单的即将原本近裁剪平面映射到深度值0远裁剪平面映射到深度值1的映射关系反过来让近裁剪平面映射到深度值1远裁剪平面映射到深度值0。即将[n,f]映射到[1,0]按照上文给出的投影矩阵推导链接中的方法我们可以推导出Reversed-Z的情况下Z与$Z_{c}$的关系其实就是①式中n与f互换 $$Z{\frac{n}{n-f}}-{\frac{fn}{(n-f)*Z_{c}}}$$ 我们取n0.1f1000有 $$Z≈\frac{0.1}{Z_{c}}$$ 函数图像如下 图4 看到上面的图细心的读者可能会发现这不跟图2一样嘛都是$Z_{c}$1的时候深度值Z就用了十分之九0.9了不过是前者是[0, 0.9]这里是[0.1, 1]而已有区别吗如果我们还是以无符号整型来存储深度值的确对我们达成目的没有帮助依然是靠近近裁剪平面的少数物体占据了大多数深度值。但是我说过Reversed-Z是神奇的它的神奇之处是当它搭配上我前面否定过的浮点数时Reversed-Z在提高深度值均分分布程度 这件事上就变得非常有效了。 让我们回到浮点数前面有提到过 0值附近的符点数拥有更好的精度这是有依据的浮点数具体介绍请参考维基百科https://en.wikipedia.org/wiki/IEEE_floating_point这里以单精度符点类型做简单说明。规约化单精度浮点数的有效位数只有7位实际是7点多位这里简单起见取7当一个浮点数小于1的时候它可以确保有6位小数位是精确的也就是说在(0,1)这个开区间内至少可以包含9999996位个误差允许的单精度浮点数1~9同理但由于非规约化浮点数主要是在0值左右的存在使得(0,1)这个区间内的浮点数个数要比(1,2),(2,3)…(9,10)这些区间内的符点数要多。在(10, 11)这个区间内由于整数位占去了两位所以这个区间内至少只可以包含999995位个有效单精度浮点数以此类推(100,101)开区间内包含9999个有效单精度浮点数(1000,1001)开区间内包含999个有效单精度浮点数等等当数量级来到[1000000,1000001]时(注意这里是闭区间)这个区间内能保证有效的单精度浮点数不过就两个1000000与1000001本身。这说明浮点数的分布与深度值的分布一样是不均与的越靠近0的浮点数分布越密集越远离0的浮点数分布越稀疏 浮点数分布情况图 当我们用正常的Z值系统[n,f]映射到[0,1]与浮点数配合时符点数没有任何帮助(原谅这个不一样的画风由于我还不太会使用GeogeBra作图我把本文的主要参考文章Depth Precision Visualized的图拿过来用了) 图5 图5中z1到z2这么远的距离依然只共享一个深度值0.99。 但当我们将Reversed-Z[n,f]映射到[1,0]与浮点数结合起来情况就变成了 图6 随着$Z_{c}$的增大深度值Z的降幅越来越小看似又要陷入精度不够的死胡同但浮点数的分布规律恰好弥补了这一不足使得较大的也有足够的精度表示图6中z1到z2比之图5中多获得了5个深度值。这样距离相机近和远的物体分得的深度值就比较平均了变相的实现了改善深度值分布状况这一目的从而也达到了降低Z-Fighting出现的概率是的虽然Reversed-Z这么神奇但Z-Fighting还是不能完全避免的虽然概率已经降到很低。 作为依赖Unity引擎的开发者很高兴看到Unity在其5.5以及以后的版本中引入了Reversed-Z的做法在这里也提醒一下大家以后为Unity写shader的时候如果用到深度值Z一定要记得 [n,f] 是映射到[1,0]否则就会写出错误的效果J。 参考与说明 本文参考Depth Precision Visualized对Reversed-Z进行思考与分析希望能对读者有所帮助。 文中的函数图像使用GeoGeBra软件绘制公式用LaTex 语法写成。 转载于:https://www.cnblogs.com/jackmaxwell/p/6851728.html