【问题标题】:Z-fighting after depth prepass on GTX 980GTX 980 深度预通过后的 Z 战
【发布时间】:2018-04-05 12:10:04
【问题描述】:

我正在 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 已正确设置。

这听起来像是驱动程序错误还是您有什么建议我可能做错了什么?当这是一个驱动程序错误时,我如何才能实现深度预通?

【问题讨论】:

  • 为什么用glDepthMask(GL_FALSE)禁用深度写入?
  • 你为深度缓冲区分配了多少位?
  • 问题中的代码还远远不够,可能出错的地方太多了。例如,您是否在与两个通道相关的所有着色器中正确地将gl_Position 输出限定为invariant
  • @Rabbid76 我想到了glPolygonOffset,但由于代码在英特尔上运行,我认为肯定有其他问题,并想先探索一下。但是,我实施了您的解决方案(解决方法)并且它完成了工作。
  • @Ripi2 如果你问为什么:我禁用深度写入是因为主渲染通道不需要渲染深度,因为它已经由预通道完成。如果你问为什么glDepthMask(GL_FASLE):我想这是通常的方式。

标签: opengl rendering depth-testing


【解决方案1】:

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

GLSL 规范 4.4 解释的差异:

在本节中,方差是指从同一表达式中获得不同值的可能性 不同的程序。例如,假设两个顶点着色器,在不同的程序中,每个设置 gl_Position 与 两个着色器中的相同表达式,并且当两个着色器中的输入值相同时 着色器运行。由于两个着色器的独立编译,分配给 当两个着色器运行时,gl_Position 并不完全相同。在此示例中,这可能会导致问题 在多通道算法中对齐几何。

本例中使用的限定符如下:

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;

我解决了这个问题。

【讨论】:

    【解决方案2】:

    有些版本的 Sponza 很大。我记得我用两种解决方案之一解决了这个问题:

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

    【讨论】:

    • 正如对原始问题的评论中提到的:我正在使用 24 位深度缓冲区并将其更改为 32 位没有帮助。对数深度缓冲区很有趣,但太多了。场景被平截头体非常紧密地封装,因此(我认为)这些位得到了很好的利用。原来的场景其实很大,但也缩小了。
    猜你喜欢
    • 2021-10-06
    • 2018-01-06
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多