背景:
我发现使用线性深度缓冲区非常容易,只需对规范的顶点变换稍作修改即可。最简单的方法位于https://www.mvps.org/directx/articles/linear_z/linearz.htm的底部。

不过,警告是,它仅适用于不具有三角形的三角形无需修剪在近机或远机上。 (另外一种解决方案是,在顶点着色器中执行透视划分,将对其他四个视锥面产生类似的问题。)

因为裁剪需要线性插值才能在所有四个裁剪空间坐标上工作,我认为仅使用顶点着色器来处理线性深度是不可能的。但是原因完全是Z除以W。

为什么这样做? X和Y需要按距相机的距离进行划分,但Z坐标不需要如此,以使其完全适合NDC框。

#1 楼

如果您正在制作透视图并且模型具有隐式相交,那么,如果使用“线性Z”,则这些相交将出现在错误的位置。

例如,考虑一个简单的地平面,该地平面有一排电话线杆,后退到远处,它刺穿地面(并在下面继续)。隐式交点将由插值深度值确定。如果未使用1/Z进行插值,则当使用透视图计算投影顶点时,图像看起来将不正确。

我为以下插图的非美学品质表示歉意,但我将其做了在97年。

第一张图片显示了所需的渲染效果。 (请注意,蓝色的“塔”在地平面下方走了很长一段距离,因此被剪裁在图像的底部)。



第二个图像显示了结果使用不可逆的深度缓冲区的说明:(比例更改的道歉-这些是从旧的MS Word文档中复制的,我不知道缩放发生了什么。)



如您所见,结果不正确。

请注意,确定要使用线性Z表示吗?如果要渲染透视图,肯定要比离距离更近的照相机更精确吗?

再发表评论:


“如果不是那样的话插值到1 / Z”,我不明白。那是什么插值?


首先要注意的是,在使用标准透视图投影的情况下,世界空间中的直线仍然是透视空间中的直线。但是,距离/长度不会保留。

为简单起见,让我们假设使用简单的透视变换投影顶点,即
$$ X_ {Screen} = \ frac { X_ {World}} {Z_ {World}} $$
$$ Y_ {Screen} = \ frac {Y_ {World}} {Z_ {World}} $$
我们还应该计算屏幕空间的倒数,例如$$ Z_ {Screen} = \ frac {1} {Z_ {World}} $$,但是对我来说,深度缓冲区中的线性Z需要类似以下内容:$$ Z_ {Screen} = scale * Z_ {World} $ $(我们可以在这里假设scale = 1)

我们假设我们有一条与世界空间端点有关的线$$
\ begin {matrix} \ begin {bmatrix}
0 \\
0 \\
1 \\
\ end {bmatrix}和
\ begin {bmatrix}
200 \\
0 \\
10 \\
\ end {bmatrix} \\
\ end {matrix}
$$
通过透视图将这些映射映射到屏幕空间坐标
$$ \ begin {矩阵} \ begin {bmatrix}
0 \\
0 \\
1 \\
\ end {bmatrix}和\ begin {bmatrix}
20 \\
0 \\
0.1 \\
\ end {bmatrix}
\ end {matrix} $$

渲染系统/硬件将线性插值屏幕空间z,因此在该行在屏幕上出现的1/2方向点上,即在像素(10,0)处,我们将获得投影的Z值(倒数) 0.55,更正对应于〜1.818的世界空间Z值。给定开始和结束Z值,则沿线的长度大约为20%。

如果相反,我们尝试使用原始Z值进行插值,则最终得到Z对应于世界空间值为5.5。只要没有任何相交,您可能会没事的(我还没有考虑得太彻底),但是任何隐含相交的东西都是不正确的。

我没有提到的是,一旦您介绍了视角正确的纹理化(甚至透视正确的阴影),您必须按像素进行1 / w的插值,此外,还必须按像素计算该插值的倒数。

评论


$ \ begingroup $
没有更多的数学/图表,我认为我将无法理解这个答案。是的,更高的精度,更接近的精度可能是有道理的,但是从线性/ far / z缩放(这是标准的)是没有意义的。这将产生一个深度缓冲区,该深度缓冲区在两个剪切平面彼此靠近时变得越来越线性。似乎将两个概念混为一谈:屏幕空间线性Z和用于性能破解的非恒定深度缓冲区映射。
$ \ endgroup $
–杰西
18-09-4在16:16



$ \ begingroup $
具体来说就是我不理解的“如果未使用1 / Z进行插值”。那是什么插值?
$ \ endgroup $
–杰西
18-09-5在2:51

$ \ begingroup $
我将添加一些其他文字以希望解释
$ \ endgroup $
–西蒙F
18-09-5在11:48

$ \ begingroup $
谢谢!我认为问题归结为“渲染系统/硬件将线性插值屏幕空间z”。我的印象是NDC位置将按每片段计算(x,y,z)/ w,但显然,我们必须处理(x / w,y / w, z / w)?在我看来,这在2018年似乎并不合理,但是很高兴知道这是否是我们现在必须忍受的hack!
$ \ endgroup $
–杰西
18-09-6在20:26

$ \ begingroup $
要执行透视校正贴图/明暗处理/其他操作,您需要线性插值(Val / w),然后按每个片段对线性插值1 / w进行除法。仅在评论中很难解释,但是在computergraphics.stackexchange.com/a/4799/209中有一些解释。或者,搜索Jim Blinn的文章“双曲插值”
$ \ endgroup $
–西蒙F
18-09-7在8:01



#2 楼

对深度缓冲区使用Z / W比剪切近平面和远平面要深得多。正如西蒙(Simon)提到的,这与在栅格化过程中三角形的顶点之间的插值有关。

Z / W是唯一的选项,它可以为内部的点正确计算NDC深度值通过在屏幕空间中简单地线性插值顶点的NDC深度值,即可生成三角形的三角形。原则上,我们可以使用任何我们想将相机空间Z映射到深度缓冲区值的函数-但除Z / W以外的任何其他选择都需要对每个像素执行更复杂的数学运算,这会更慢,并且更难于

请注意,如果您使用线性深度缓冲区,那么线性插值深度值在世界空间中当然是正确的,但通常在屏幕空间中是不正确的!屏幕空间对于光栅化很重要,因为我们需要能够在a的屏幕空间范围内为每个像素中心或其他采样点生成透视校正的深度值(以及其他属性值,例如UV)。三角形被栅格化。

评论


$ \ begingroup $
我不知道如何设计GPU,但是在我看来,对于线性深度,所需要做的就是对Z而不是Z / W进行插值,以实现线性深度,然后在可见的情况下仍然可以进行Z / W插值。我仍然无法确定这是一个合理的问题,还是“没人在乎,所以我们不会打扰更新”之一。
$ \ endgroup $
–杰西
'18 Sep 9'在10:28

$ \ begingroup $
内插Z而不是Z / W不能在屏幕空间中给出正确的结果。 Z / W可以。
$ \ endgroup $
–内森·里德(Nathan Reed)
18年9月9日在18:42

$ \ begingroup $
对。但是,如果深度缓冲区的量化精度低于位置精度,那么除了在工作时表现出色之外,存储缩放的屏幕空间Z块也不是一个好主意。如果线性插值是我们所能获得的,则需要裁剪发生在视图空间中。 Z需要在除以W之前进行插值,以进行深度缓冲,然后再进行深度插补。因此,我的问题的答案是否是“因为GPU始终只在片段空间内插值,因为它是第一个GPU上唯一的实用解决方案,并且自此以来效果很好”?
$ \ endgroup $
–杰西
18/09/10 5:00



$ \ begingroup $
我没有遵循您的意思:“量化到比位置低的精度”,或“存储缩放后的屏幕空间Z的块”。
$ \ endgroup $
–内森·里德(Nathan Reed)
18 Sep 10'在5:17

$ \ begingroup $
另外,“对于深度缓冲区,Z需要先除以W,然后再除以W”。这就是我一直试图解释的。如果在屏幕空间中插值Z(或其他任何值)而未先将其除以W,则会得到错误的答案。您似乎一直坚持这样的想法:如果不将W除以线性Z缓冲区就可以工作。但是它不能工作-无法在屏幕空间中正确插值。
$ \ endgroup $
–内森·里德(Nathan Reed)
18/09/10在5:21