在 GTX 980 上进行深度预传后的 Z-fighting

Z-fighting after depth prepass on GTX 980

我正在 OpenGL 中实现深度预通道。在 Intel HD Graphics 5500 上,此代码可以正常工作,但在 Nvidia GeForce GTX 980 上则不能(下图显示了由此产生的 z-fighting)。我正在使用以下代码生成图像。 (与问题无关的都省略了。)

// ----------------------------------------------------------------------------
// Depth Prepass
// ----------------------------------------------------------------------------

glEnable(GL_DEPTH_TEST);
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
glDepthFunc(GL_LESS);
glDepthMask(GL_TRUE);

glUseProgam(program1); // The problem turned out to be here!

renderModel(...);

// ----------------------------------------------------------------------------
// Scene Rendering
// ----------------------------------------------------------------------------

glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_FALSE);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);

glUseProgam(program2); // The problem turned out to be here!

renderModel(...);

似乎 glDepthFunc 没有更改为 GL_LEQUAL。但是,当我在 RenderDoc 中单步执行 GL 调用时,glDepthFunc 被正确设置。

这听起来像是驱动程序错误,或者您对我可能做错的地方有什么建议吗?当这是一个驱动程序错误时,我怎样才能实施深度预通道?

Sponza 的某些版本很大。我记得我使用以下两种解决方案之一解决了该问题:

第二种方法在利用 early depth test for hidden surface removal 的平铺架构上较差。在移动平台上,性能下降可能非常明显。

当深度预通道使用不同的着色器程序时,必须明确确保该程序生成与主通道程序相同的深度值(尽管在相同的几何体上调用)。这是通过在 gl_Position.

上使用 invariant 限定符来完成的

GLSL 规范 4.4 解释的方差:

In this section, variance refers to the possibility of getting different values from the same expression in different programs. For example, say two vertex shaders, in different programs, each set gl_Position with the same expression in both shaders, and the input values into that expression are the same when both shaders run. It is possible, due to independent compilation of the two shaders, that the values assigned to gl_Position are not exactly the same when the two shaders run. In this example, this can cause problems with alignment of geometry in a multi-pass algorithm.

在这种情况下使用如下限定符:

invariant gl_Position;

此行保证 gl_Position 是根据着色器中给出的精确表达式计算的,没有任何优化,因为这会改变操作,因此很可能会以一些小的方式改变结果。

在我的具体案例中,作业是问题的根源。主通道程序的顶点着色器包含以下几行:

fWorldPosition = ModelMatrix*vPosition; // World position to the fragment shader
gl_Position = ProjectionMatrix*ViewMatrix*fWorldPosition;

prepass 程序的顶点着色器在一个表达式中计算 gl_Position

gl_Position = ProjectionMatrix*ViewMatrix*ModelMatrix*vPosition;

通过将其更改为:

vec4 worldPosition = ModelMatrix*vPosition;
gl_Position = ProjectionMatrix*ViewMatrix*worldPosition;

我解决了问题。