使用具有多重采样纹理的gl_SampleMask并不能获得每个样本的混合?

时间:2016-01-03 17:16:44

标签: opengl

使用带有多重采样纹理的gl_SampleMask时出现问题。

为了简化问题,我举了这个例子。

将两个三角形绘制到帧缓冲区,并附加32x多重采样纹理。 三角形的顶点是(0,0)(100,0)(100,1)和(0,0)(0,1)(100,1)。

在片段着色器中,我有这样的代码,

#extension GL_NV_sample_mask_override_coverage : require
layout(override_coverage) out int gl_SampleMask[];

...

out_color = vec4(1,0,0,1);
coverage_mask = gen_mask( gl_FragCoord.x / 100.0 * 8.0 );
gl_SampleMask[0] = coverage_mask;

function int gen_mask(int X)生成一个整数,其中包含X 1s的二进制表示。

希望我能看到100像素充满全红色。 但实际上我得到了alpha混合输出。在(50,0)处的像素显示(1,0.25,0.25),这似乎是在(1,1,1,1)背景上绘制的两个(1,0,0,0.5)。

但是,如果我打破coverage_mask,请检查片段着色器中的gl_SampleID,并根据coverage_mask的gl_SampleID&#将(1,0,0,1)或(0,0,0,0)写入输出颜色39; s位,

if ((coverage_mask >> gl_SampleID) & (1 == 1) ) {
    out_color = vec4(1,0,0,1);
} else {
    out_color = vec4(0,0,0,0);
}

我按预期获得了100个红色像素。

我已经检查了OpenGL wiki和文档,但是没有找到行为在这里发生变化的原因。

并且,我在Windows 10上使用Nvidia GTX 980和驱动程序版本361.43。

如果需要,我稍后会将测试代码放到GitHub上。

1 个答案:

答案 0 :(得分:1)

  

当纹理有32个样本时,Nvidia的实现将一个像素分成四个小片段,每个片段有8个样本。因此,在每个片段着色器中,只有8位gl_SampleMask可用。

好的,我们假设这是真的。您如何假设NVIDIA实现了这一目标?

嗯,OpenGL规范 不允许他们通过更改gl_SampleMask的有效大小来实现这一点。很明显,样本掩码的大小必须足够大,以容纳实现支持的最大样本数。因此,如果GL_MAX_SAMPLES返回32,则gl_SampleMask 必须具有32位存储空间。

那么他们将如何实施呢?嗯,有一个简单的方法:覆盖掩码。它们为4个片段中的每个片段提供一个单独的8位覆盖掩码,并将它们的输出写入。哪个会完美无缺......

直到您使用override_coverage 覆盖覆盖率掩码。现在这意味着所有4个片段着色器调用都可以写入与其他FS调用相同的样本。

糟糕。

我没有直接测试NVIDIA的实现以确定这一点,但它与您获得的结果非常一致。代码中的每个FS实例最多会写入8个样本。 相同的 8个样本。 8/32是0.25,这正是你得到的:你写的颜色的0.25。即使4个FS可能正在为同一个像素写入,但每个FS都写入覆盖掩码的相同25%。

没有" alpha混合输出&#34 ;;它只是按照你的要求做的。

至于为什么你的第二个代码有效...好吧,你成为了经典的C / C ++(以及GLSL)错误之一的受害者:运营商优先权。请允许我将您的条件括起来,向您展示编译器认为您写的内容:

((coverage_mask >> gl_SampleID) & (1 == 1))

平等测试的优先级高于任何按位操作。所以它像这样分组。现在,由于1 == 1的结果是布尔值,因此一致的GLSL实现应该无法编译,因为它不能用于按位&操作。

当然,NVIDIA一直倾向于使用GLSL进行快速和宽松的播放,所以我们不会让他们感到惊讶,因为他们允许这些无意义的代码进行编译。很像C ++。我不知道这段代码实际上会做什么;它取决于true布尔值如何转换为整数。 GLSL没有定义这种隐式转换,因此由NVIDIA决定这意味着什么。

传统的测试条件是:

(coverage_mask & (0x1 << gl_SampleID))

如果coverage_mask不是无符号整数,它还可以避免未定义的行为。

当然,正确地完成这个条件应该会给你......与第一个完全相同的答案。