opengl-es - 线性深度缓冲区

标签 opengl-es webgl depth-buffer

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

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

但它在远剪切面附近的 float 精度存在问题。结果就是 z-fight。 使用 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 缓冲区的优点之一是我们可以在屏幕空间中线性插入结果深度值。如果将 gl_Position.z 乘以 gl_Position.w,生成的 z 值在屏幕空间中将不再是线性的,但深度测试仍将使用线性插值。这会导致您的图元在 z 维度上弯曲,从而导致附近图元之间完全错误的遮挡和相交(特别是当一个图元的顶点位于另一个图元的中心附近时)。

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

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

更新

来自您的评论:

Depth in screen space is linear interpolation of NDC, how I understand form here. In my case, it will be linear interpolation of linear interpolation of z from camera space. Thus, depth in screen space interpolated already.

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

enter image description here

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

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

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

由于深度测试将使用线性插值,因此它只会获取最右侧绘图中的点作为顶点着色器的输入,但会对它们进行线性插值 - 通过直线连接这些点。因此,基元之间的交集将不会出现在实际需要的位置。

另一种思考方式:假设您使用一些透视投影绘制了一些延伸到 z 维度的图元。由于透视的原因,远处的物体会显得更小。因此,如果您在屏幕空间中向右移动一个像素,如果图元距离较远,该步骤所覆盖的 z 范围实际上会更大,而随着距离越来越近,它会变得越来越小。因此,如果您只是向右迈出相同大小的步长,则您所做的 z 步长将根据基元的方向和位置而变化。然而,我们想要使用线性插值,因此我们想要为每个 x 步长设置相同的 z 步长大小。我们唯一能做到这一点的方法是扭曲 z 所在的空间 - 而除以 w 所引入的双曲线扭曲正是这样做的。

关于opengl-es - 线性深度缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47801957/

相关文章:

iphone - glDrawArray 工件

javascript - 你如何在 JavaScript 中转换为半 float ?

javascript - 使用中等精度在 RGBA 纹理中打包深度信息

unity3d - 提取 _CameraDepthTexture 值 Unity

javascript - WebGl 片段着色器 channel 数组

c - 嵌套模板 - OpenGL ES

iphone - 应用商店 : blocking the game for 3G- devices

基于 Canvas 的游戏的 JavaScript 缓冲 : mediocre performance

javascript - 如何调试 Angularjs ng-route 和 Threejs 崩溃/问题?

c++ - 在 OpenGl 中混合 GL_ALWAYS 和 GL_LESS