如何避免远距离的Z-fighting?

How to avoid Z-fighting in distance?

所以我最近看了一个关于 Z-fighting 的视频,学会了一个简单的方法来处理它——主要是。给出的解决方案实际上是倾斜投影,以便为更近的物体提供更多 space 以进行更准确的深度测试(因为浮点数只能如此精确),而更远的物体则被塞进投影的一小块区域。现在我对 OpenGL 和图形编程还很陌生(只是慢慢地工作),而且我实际上还没有做任何足够复杂的事情,这对我来说是个问题,但我将来可能需要知道这一点。无论如何,上述解决方案带来的新问题是更糟糕的远距离 Z 轴战斗(例如 Skyrim、Rust 等中的山脉)。有没有更好的解决方法,即使它确实具有成本效益,也不涉及图形妥协?假设地说(因为我对 OpenGL 管道还不是很满意),程序中的 Z 值是否可以在被钳位以进行深度测试之前加倍?

让我澄清一下。想想天际。注意到山脉有时是如何闪烁的吗?当使用 OpenGL 渲染场景时,所有对象都被塞满,或 "clamped" 到一个 Z 值从 -1.0 到 1.0 的小坐标平面中。然后对每个对象进行深度测试——树木、雪、山、动物、房屋,随便你怎么说,这样当它们被其他东西覆盖时就不会被绘制出来。然而,浮点数只能达到一定的精度,所以将数百个物体夹在一个微小的 space 中不可避免地会导致一些物体具有完全相同的 Z 坐标,并且两个物体在屏幕上一起闪烁的现象被称为 "Z-fighting"。我问是否每个对象的深度 (z-) 坐标都可以转换为双精度数,以便它们具有足够的精度(值得在可忽略的时间段内使用可忽略不计的额外内存)以便以正确的顺序准确地绘制对象,没有夹在一起。

When a scene is rendered with OpenGL, all the objects are crammed, or "clamped" into a small coordinate plane with Z-values from -1.0 to 1.0.

这只是故事的一部分。另一部分是 z 非线性存储:最高 'fidelity' 的部分是靠近近 Z 平面的部分,随着向后移动,保真度会降低。有关公式,请参阅 this page

OpenGL 常见问题解答 12. The Depth Buffer12.070 为什么深度缓冲区前端的精度更高?12.080[= 中对此进行了讨论25=] 提出针对不同 z 距离的多通道渲染。

您可以使用 glGetIntegerv(GL_DEPTH_BITS, &bits); 查询当前深度缓冲区大小,但(搜索后)似乎没有标准方法可以将其更改为使用更大(或更小)的深度。

可以通过不将远距离物体绘制为 3D 物体来应对 Z 战斗。例如,如果您示例中的山脉距离很远,则任何视差效果都将不可见。因此,在那种情况下,将远处的物体绘制到天空盒上可能会更好。

您可以解决。通常,您通过从最近到最远的排序一次性渲染所有对象。

您可以根据距离将对象分成 2 组,将它们命名为 FarGroupNearGroup

如果您的应用程序没有某些限制,例如:

  • 您没有为某些事情使用模板缓冲区
  • 你不需要深度来获得特殊效果(ScreenSpace Ambient Occlusion、景深等)

您可以使用模板缓冲区来解决 Z-fight 问题。

  • 清除模板、深度和颜色缓冲区
  • 您设置了一个模板功能,以便绘制的每个对象设置一个位

    glStencilOp(GL_KEEP,GL_KEEP, GL_INCR);

    glStencilFunc( GL_ALWAYS, 1, 0x01);

  • 然后渲染 NearGroup

  • 清除深度缓冲区
  • 设置模板测试,仅在未设置模板 位的地方绘制

    glStencilOp(GL_KEEP,GL_KEEP, GL_KEEP);

    glStencilFunc( GL_NOTEQUAL, 1, 0x01);

  • 渲染FarGroup

您可以通过反转渲染顺序以某种方式进行调整以不使用模板(由于像素透支而导致的性能损失),也许您可​​以将 SSAO 限制为 NearGroup 并为远处的对象使用烘焙的 AO 值,但是您明白了,你获得了一些东西,但你失去了做其他事情的能力(好吧,有许多以牺牲性能和一些头脑风暴为代价的限制的变通方法)。

对于相机,您只需设置 2 个不同的平截头体,它们只是将原始平截头体切成 2 个。

如果您没有太多限制并且能够使用上述技术,那么您既不会出现 z-fight,又会比使用 32 位 Z 缓冲区更快地渲染(由于 GPU 的内部优化)

首先,一个重要的细节:一个常见的错误是近平面真的太接近零了。由于 z 映射是非线性的,因此大部分动力学都在最前面并且在远处丢失。

对所有几何体进行排序以绕过 Z 缓冲区的成本很高。 一个经典的折衷方案是将场景分成 2 或 3 个大层(比如最前面的(驾驶舱/角色/武器)、近距离地形和远距离景观),并为它们中的每一个自定义 znear / zfar(在两者之间重置 Z ).