启用 msaa 后 gl_FragDepth 等于 gl_FragCoord.z 吗?

Is gl_FragDepth equal gl_FragCoord.z when msaa enable?

我知道 gl_FragDepth 将从 opengl wiki 获取 gl_FragCoord.z 的值。 https://www.khronos.org/opengl/wiki/Fragment_Shader/Defined_Outputs

但是我有一个问题。如果我启用 MSAA 并在片段着色器中写入 gl_FragDepth = gl_FragCoord.z,显示将无法正常工作。您可以在白色三角形上看到一条黑线,如下所示:

如果我不在片段着色器中编写gl_FragDepth,它会工作正常。

如果我禁用 MSAA,无论我写 gl_FragDepth.

它也能正常工作

正确的显示图像没有黑线:

渲染场景很简单,我只画了2个白色三角形,它们相交在一条边上。

我在顶点着色器中添加了一个简单的光。代码如下:

const char *vertexShaderSource[] = {
    "#version 120\n",
    "varying vec4 lightColor;\n",
    "void main()\n",
    "{\n",
    "   vec3 n = normalize(gl_NormalMatrix * gl_Normal);\n",
    "   vec3 l = normalize(vec3(0.0, 1.0, 1.0));\n",
    "   float NdotL = clamp(dot(n, l), 0.001, 1.0);\n",
    "   lightColor = vec4(1.0)*(NdotL + 0.2);\n",
    "   gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n",
    "}\n"
};

const char *fragmentShaderSource[] = {
    "#version 120\n",
    "varying vec4 lightColor;\n",
    "void main(void)\n",
    "{\n",
    "   gl_FragColor = vec4(lightColor.rgb, 1.0);\n",
    "   gl_FragDepth = gl_FragCoord.z;\n"
    "}\n"
};

2个三角形的6个顶点的位置为(-5,-5,0),(5,-5,0),(-5,5,0),(-5,0,0) ,(5,0,0),(-5,0,-10)。 法线垂直于三角形。

我想知道为什么我在fragment shader中写gl_FragDepth显示的图片不一样?

你的两个三角形相交。具体来说,灰色三角形的边可以生成与白色三角形的深度值相等的深度值。因此,完全有可能从该交点处的灰色三角形中的特定样本生成与白色三角形的深度值相等的深度值。

因此,您永远无法保证不会看到那里的一行;你只是碰巧不是很多情况。

然而,这一切都假设:

  1. 灰色三角形在白色三角形之后渲染。

  2. 您的深度测试将通过相同的值。

即使在这两个条件之外,您在这里得到的结果也可能发生。原因很复杂。

看,whole point of multisampling是光栅化器生成的数字深度值和片段着色器执行的数字不一样。因此单个 FS 调用被映射到多个深度值。

但是,单个 FS 调用仍然可以写入 gl_FragDepth。如果这样做,那么映射到该 FS 调用的 所有样本 将接收相同的深度。此深度覆盖多重采样生成的深度值。

另外,interpolation at the edges of a primitive is weird under multisampling。在该像素处的三角形边界内的每个样本都将导致写入一个样本值(除非其他东西将其剔除)。但像素的中心点不必是这些样本位置之一。所以一个不通过像素中心的三角形仍然可以为该像素贡献一些样本,只要三角形通过该像素中的至少一个样本即可。

片段着色器根据像素内的某个位置获取插值。使用多重采样,此位置可能不在三角形的内部。例如,如果实现选择的 FS 在像素内插值的位置位于三角形的中心,并且三角形不通过该像素的中心,那么只要它通过,您仍然会调用 FS通过一些样本。

但这意味着内插值可以表示三角形区域外部 的位置。插值数学可以为不在三角形内的区域生成值;他们只是没有意义。

gl_FragCoord 作为一个插值,因此可以生成三角形之外的值。由于灰色三角形的目标是观察者,因此来自灰色三角形迎面而来的边缘“上方”的位置的值将比它们应该的更近。并且由于灰色三角形的边缘与白色三角形相交,因此比其实际边缘值更接近的值将被认为比白色三角形更接近

解决这个问题的正常方法是使用 centroid 插值限定符。但是,该标准实际上并不允许这样做;即使您使用 centroid 限定符重新声明 gl_FragCoordit won't have any effect:

The use of centroid does not further restrict this value to be inside the current primitive.

此外,如前所述,常规多重采样渲染中的深度替换无论如何都会破坏所有每个样本的深度信息。如果您的 FS 写入深度,像素中的每个样本都会获得相同的深度值。这并不是您真正想要的,即使您可以对 gl_FragCoord 进行 centroid 插值(这可能是他们不允许这样做的原因)。

因此,如果在用于多重采样的着色器中进行深度替换是绝对必要的(并且应尽可能避免这种情况),则需要使用 per-sample shading。您可以使用 sample 重新声明 gl_FragCoord 来实现此目的。