对数深度缓冲OpenGL

8
我已经成功在OpenGL中实现了对数深度缓冲,主要参考了Outerra的文章(您可以在这里, 这里, 和 这里阅读)。然而,我遇到了一些问题,我不确定这些问题是否与使用对数深度缓冲有关,或者是否有一些解决方法我无法想到。
首先,这是我在顶点着色器中计算对数深度的方法:
gl_Position = MVP * vec4(inPosition, 1.0);
gl_Position.z = log2(max(ZNEAR, 1.0 + gl_Position.w)) * FCOEF - 1.0;
flogz = 1.0 + gl_Position.w;

这是我在片元着色器中修复深度值的方法:
gl_FragDepth = log2(flogz) * HALF_FCOEF;

ZNEAR = 0.0001ZFAR = 1000000.0FCOEF = 2.0 / log2(ZFAR + 1.0),以及HALF_FCOEF = 0.5 * FCOEF。在我的情况下,C为1.0,以简化我的代码并减少计算。

首先,我非常满意获得的精度水平。使用普通深度缓存(znear = 0.1,zfar = 1000.0),我在视距边缘处遇到了很多z-fighting。现在,由于我的 znear:zfar 远远更远,我在第一个地面平面下方放置了第二个地面平面(间距为0.01单位),无论我将摄像机拉远多远,我都找不到任何z-fighting(当它离0.0001(0.1毫米)时,我会有一些z-fighting,但不那么严重)。

然而,我确实有一些问题和顾虑。

1)我使用正常深度缓冲时没有的近平面裁剪更多,看起来很丑。这种情况在逻辑上不应该发生。以下是我所说的一些截图:

地面被裁剪。

Clipping the ground.

剪裁网格。

Clipping a mesh.

这两种情况都是我在普通深度缓冲区中没有遇到过的,而且我不想看到它们(尤其是前者)。编辑:问题1已通过使用glEnable(GL_DEPTH_CLAMP)正式解决。

2)为了使其工作,我需要写入gl_FragDepth。我尝试不这样做,但结果令人无法接受。写入gl_FragDepth意味着我的图形卡不能进行早期的z优化。这将不可避免地让我发疯,所以我想尽快解决它。

3)我需要能够检索深度缓冲区中存储的值(我已经有一个帧缓冲区和纹理),然后将其转换为线性视图空间坐标。我真的不知道从哪里开始,我以前所做的方式涉及到逆投影矩阵,但我在这里真的做不到。有什么建议吗?

3个回答

3

我在此宣布问题#1已正式解决!非常感谢!我需要担心深度夹紧会减少可用精度吗? - Haydn V. Harach
@HaydnV.Harach:噢,现在你不必使用夹紧写入 gl_FragDepth。关于第三点,当你读取深度值并进行线性化时,对于所有被剪切的内容,你将会得到它们位于近裁剪面的深度值。 - datenwolf
链接不可用。 - Tara
@Dudeson:感谢你提醒。由于原网站在几个月前关闭,我制作了一个镜像。我已经在我的镜像上更新了问题的链接。 - datenwolf
@datenwolf:非常感谢! - Tara
显示剩余3条评论

2

1) 在你使用的方程中: gl_Position.z = log2(max(ZNEAR, 1.0 + gl_Position.w)) * FCOEF - 1.0; 不应该出现ZNEAR,因为它与此无关。那里的常量仅是为了避免log2参数为零,例如可以在那里使用1e-6。

但是,使用深度夹紧(depth clamping)将解决这个问题。

2) 您可以通过自适应细分(adaptive tesselation)来避免使用gl_FragDepth,以保持插值误差控制在一定范围内。例如,在Outerra中,地形是自适应细分的,因此永远不会出现在地形上有可见的错误。但是,在缩小时需要在对象上进行片段深度写入操作,因为长的屏幕空间三角形的线性插值值和正确的对数值之间的差异相当大。

请注意,最新的AMD驱动程序现在支持NV_depth_buffer_float扩展,因此现在可以使用反向浮点深度缓冲设置。但是,据我所知,它尚未支持Intel GPU。

3) 将视图空间深度转换的过程在此处描述:https://dev59.com/XXXYa4cB1Zd3GeqP7pHa#18187212


  1. 我尝试使用1e-6、1e-1、1等代替ZNEAR,但无论我输入什么,问题都基本相同。具有讽刺意味的是,1e-6比1e-1给我的错误更多。我只是使用ZNEAR,因为它是我定义的常量,通常喜欢使用常量而不是魔数。
  2. 好注意,但我不能真正细分所有的几何图形。墙壁等往往是大型多边形,我不能指望所有用户都有镶嵌着色器可用。我考虑使用反向浮点深度缓冲区,但只有在我能够与模板缓冲区一起使用时才可行(续)。
- Haydn V. Harach
不要浪费24位。3) 谢谢! - Haydn V. Harach
看起来我说得太早了,当我更仔细地比较它(0.5 + (reconstructedDepth - positionBuffer.z))时,场景看起来非常白(意味着重建的深度值比实际深度值高得多)。 - Haydn V. Harach
在所有现代的显卡上,深度和模板数据是分开的,而OpenGL标识符则建议填充已经过时且误导人。 - camenomizoratojoakizunewake
我没有改变reconstructedDepth的符号,但我改变了positionBuffer.z的符号(我将它设为vec3(positionBuffer.xy, -(positionBuffer.z + 1.0),这样可以产生我可以看到的效果(因为z值通常是负数))。 - Haydn V. Harach
显示剩余4条评论

2
也许有点晚了,但是无论如何,要使用log2版本重建Z:
realDepth = pow(2,(LogDepthValue + 1.0)/Fcoef) - 1.0;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接