【问题标题】:Linear depth buffer线性深度缓冲区
【发布时间】:2018-05-27 20:53:09
【问题描述】:

许多人使用带有第三行的常用透视矩阵,如下所示:

(0  0  (n+f)/(n-f) 2*n*f/(n-f))

但它在远裁剪面附近的浮点精度方面存在问题。结果是z-fighting。 如何使用 z 的线性变换?让我们把矩阵第三行改成这样:

(0  0  -2/(f-n) (-f-n)/(f-n))

这将是从 [-n, -f] 到 [-1, 1] 的线性变换 z。然后,我们将在顶点着色器中添加这条线:

gl_Position.z *= gl_Position.w;

透视分割后z值会恢复。

为什么不到处使用它?我在网上找到了很多文章。他们都使用了通常的矩阵。 我描述的线性变换是否有我看不到的问题?

更新:这不是this 的副本。我的问题不是关于如何做线性深度缓冲。就我而言,缓冲区已经是线性的。我不明白,为什么不使用这种方法?内部 webgl 管道中是否存在陷阱?

【问题讨论】:

  • 如果您看到我链接的副本,它应该会回答您的问题。如果您没有看到它:如果您对片段中的值进行线性化,那么您会失去精度,因为您已经获得了四舍五入的值。这个想法是将线性化的值存储到 Z 缓冲区,而不是在存储后对其进行线性化。这样你就可以绕过透视分割,一切都很好......线性Z缓冲区仅用于大深度视图。对于较小的,最好使用 OpenGL 非线性的,因为它在视觉上具有相同的深度分辨率。
  • @Spektre:这个问题不是您链接的问题的重复(在我看来),所以我重新打开它。虽然这两个问题都与线性深度缓冲区有关,但这里的这个问题专门询问通过修改顶点着色器中的剪辑空间 z 来线性化深度(无法完成),而链接的问题(正确地)在每个片段上线性化 z水平。
  • @derhass OK btw +1 答案这些准确度图很好

标签: opengl-es webgl depth-buffer


【解决方案1】:

您描述的方法根本行不通。双曲线 Z 缓冲区的一个优点是我们可以在屏幕空间中线性 插入结果深度值。如果将gl_Position.z 乘以gl_Position.w,则生成的z 值在屏幕空间中将不再是线性的,但深度测试仍将使用线性插值。这会导致您的图元在 z 维度上发生弯曲,从而导致附近图元之间出现完全错误的遮挡和相交(尤其是当一个图元的顶点位于另一个图元的中心附近时)。

使用线性深度缓冲区的唯一方法是在片段着色器中自己实际对 Z 值进行非线性插值。这可以做到(归结为只是线性变换每个片段的透视校正插值 w 值,因此有时称为“W 缓冲”),但是您正在失去早期 Z 测试的好处,而且更糟- 分层深度测试。

提高深度测试精度的一种有趣方法是将浮点缓冲区与反向 Z 投影矩阵结合使用,如Depth Precision Visualized blog article 中所述。

更新

来自您的评论:

屏幕空间的深度是NDC的线性插值,我在这里理解的形式。在我的情况下,它将是来自相机空间的 z 线性插值的线性插值。因此,屏幕空间的深度已经被插值了。

您误解了这一点。可能的要点是,屏幕空间中的线性插值仅在如果您使用的是已经双曲线扭曲的 Z 值(如 NDC Z)时才有效。如果您想使用眼睛空间 Z,这可以进行线性插值。我画了一些情况的图:

这是关于眼空间和 NDC 的自上而下的视图。所有图纸实际上都是按比例绘制的。绿色光线是穿过某个像素的视图光线。这个像素恰好是直接代表那个三角形的中点(绿点)的像素。

在应用投影矩阵并发生除以w 之后,我们处于标准化设备坐标中。请注意,现在视线的方向只是+z,所有像素的所有视线都变得平行(这样我们在光栅化时可以忽略Z)。由于 z 值的双曲线关系,绿点现在不再精确地位于中心,而是向远平面挤压。然而,重要的一点是,这个点现在位于由图元的(双曲扭曲的)端点形成的直线上 - 因此我们可以简单地在屏幕空间中线性插值 z_ndc

如果您使用线性深度缓冲区,绿点现在再次位于图元中心的 z 处,但该点不在直线上 - 您实际上弯曲了图元。

由于深度测试将使用线性插值,它只会将最右边绘图中的点作为顶点着色器的输入,但会线性插值 - 用直线连接这些点。因此,图元之间的交集将不会是它实际必须在的位置。

另一种思考方式:假设您绘制了一些延伸到 z 维度的图元,并带有一些透视投影。由于透视,更远的东西会显得更小。因此,如果您只在屏幕空间中向右移动一个像素,那么如果图元越远,该步骤所覆盖的 z 范围实际上会更大,而随着您靠近,它会变得越来越小。因此,如果您只是向右以相同大小的步长走,您正在制作的 z 步长将根据图元的方向和位置而有所不同。但是,我们想使用线性插值,因此我们希望为每个 x 步设置相同的 z 步长。我们唯一要做的就是扭曲 z 所在的空间 - 而由 w 除法引入的双曲线扭曲正是这样做的。

【讨论】:

  • 屏幕空间的深度是NDC的线性插值,我如何理解here。在我的情况下,它将是来自相机空间的 z 线性插值的线性插值。因此,屏幕空间的深度已经被插值了。也许,我没听懂你的意思吗?
  • 我明白了。非常感谢。以及我的理解,如果没有三角形交叉,那么没有问题。你能给出任何数学证明的链接吗,请相机空间中的哪些线转换为 NDC 中的线?只是为了完全确定。
  • @lzv:“如果三角形没有交叉,那么就没有问题”。不,那不是真的。想象一个非常大的三角形,和一个非常小的三角形,紧靠在它后面,靠近大三角形的中心。如果您只是线性插值眼睛空间 z,那么小三角形可以出现在大三角形的前面。这就是透视正确的插值(除了与图像平面完全平行的图元外,它变成非线性的)。
  • 你说得对,我没想到。我认为所有三角形的大小都大致相同。可能是特殊情况。但总的来说不是。
【解决方案2】:

我们不使用线性变换,因为这样会在所有距离上同样产生精度问题。至少现在,精度问题只出现在很远的地方,你不太可能注意到。线性映射将错误均匀地隔开,这使得错误更有可能发生在相机附近。

【讨论】:

  • 我怀疑线性映射有那么糟糕。 AFAIK 它通常被认为是一件好事,就像 @derhass 链接的 the article 一样(尽管它提出了一种不同的实现方式)。
  • @HolyBlackCat:我链接的文章根本没有实现线性映射。它只是解释了如何通过使用可表示的浮点数在接近零时变得越来越密集的属性来针对浮点格式优化双曲线映射。
  • @derhass 我这边选词不当。我应该说精度的线性分布,这就是OP考虑线性映射的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-12
相关资源
最近更新 更多