片段着色器输出干扰条件语句

Posted

技术标签:

【中文标题】片段着色器输出干扰条件语句【英文标题】:Fragment shader output interferes with conditional statement 【发布时间】:2018-02-27 11:03:57 【问题描述】:

上下文:我在 ios 11 上使用 OpenGLES 2 执行以下所有操作

在实现用于将两种纹理混合在一起的不同混合模式时,我遇到了一个奇怪的问题,我设法将其简化为以下问题:

我正在尝试将以下两个纹理混合在一起,仅使用片段着色器而不是 OpenGL 混合函数或方程。 GL_BLEND 已禁用。

底部 - dst:

顶部 - src:

(底部图像与顶部图像相同,但使用“正常”(如 Photoshop 中的“正常”)混合旋转并混合到不透明的白色背景上)

为了进行混合,我使用了

#extension GL_EXT_shader_framebuffer_fetch

extension,这样我就可以在我的片段着色器中写:

void main()

    highp vec4 dstColor = gl_LastFragData[0];
    highp vec4 srcColor = texture2D(textureUnit, textureCoordinateInterpolated);

    gl_FragColor = blend(srcColor, dstColor);

blend 本身不执行任何混合。它仅根据统一的blendMode 整数值选择要应用的正确混合函数。在这种情况下,第一个纹理使用已经测试过的正常混合函数绘制,然后第二个纹理使用以下blendTest 函数绘制在顶部:

这就是问题所在:

highp vec4 blendTest(highp vec4 srcColor, highp vec4 dstColor) 

    highp float threshold = 0.7; // arbitrary
    highp float g = 0.0;

    if (dstColor.r > threshold && srcColor.r > threshold) 
        g = 1.0;
    

    //return vec4(0.6, g, 0.0, 1.0); // no yellow lines (Case 1)
    return vec4(0.8, g, 0.0, 1.0); // shows yellow lines (Case 2)

这是我期望的输出(在 Photoshop 中制作):

所以到处都是红色,在两个纹理都包含大于任意阈值的红色量的区域中是绿色/黄色。

但是,由于某种原因,我得到的结果取决于我为红色组件(0.60.8)选择的输出值,并且这些输出都不符合预期。 这是我看到的(灰色边框只是背景):

案例 1:

案例 2:

总结一下:如果我返回一个大于阈值的红色值,例如

return vec4(0.8, g, 0.0, 1.0);

我看到垂直的黄线,而如果红色分量小于阈值,则结果中将不会出现黄色/绿色。

问题:

为什么我的片段着色器的输出决定了条件语句是否被执行,即使那样,为什么我以绿色垂直线而不是绿色框结束(这表明 dstColor 没有被正确读取)?

是否与我正在使用的扩展程序有关?

我还想指出,纹理都被正确加载和绑定。如果我只返回单独的纹理信息而不进行混合,甚至使用我已经实现的正常混合功能,我就能很好地看到它们。

【问题讨论】:

这仍然表明您的问题出在您的代码中...您是否正在交换您使用过的程序?如果我理解正确,则不应使用此片段着色器绘制第一个纹理。看来这个问题可能正是因为如此。如果您的第一个绘图调用将片段设置为小于或大于阈值,那么您的第二个调用将简单地绘制任何内容(绿色),例如示例 1,并且将绘制所有垂直线,或者在其他情况下绘制完整的垂直线。 . @MaticOblak 与我拥有的完整着色器相比,我实际上简化了这里的代码,以便专注于这个特定问题,但你是对的,我认为我为此付出了太多示范。在我的实际着色器中,我没有直接调用 blendTest,而是调用了一个混合函数,它只不过是使用传入的统一整数确定要应用的混合类型。第一个纹理是使用普通混合函数绘制的(经过测试和它有效),然后使用blendTest 函数绘制第二个。 我们为应用程序构建的管道/着色器经常会遇到这些错误,这些错误通常是您难以追踪的最小错误之一。如果可能的话,我会尝试迭代整个事情...... 1 绘制单个纹理。 2. 绘制另一个纹理 3. 用另一个覆盖一个纹理 4. 添加扩展并重新绘制帧缓冲区。 5. 为红色和绿色添加步进着色器...我相信您是在暗示 (4) 是问题所在。 @MaticOblak 3 你的意思是先绘制底部纹理,然后绘制第二个纹理,以便它替换所有以前的帧缓冲区内容?如果是这样,那么它也可以正常工作。我还可以执行完全准确的正常混合和变暗混合,这意味着扩展应该也能正常工作。我能想到着色器输出如何影响条件语句的唯一方法是输出值是否由于某种原因再次被读入。但即便如此,这也不能完全解释我所看到的输出。 是的,这就是我所说的 (3)。我同意唯一的方法是该值被再次读取或被读取,因为它已被写入两次。正如我所说,如果您在第一次绘制调用时错误地使用了相同的着色器,那么第二次将产生您所看到的结果......另一种方法是让您根据纹理坐标在此着色器中使用不同的方法......就像y 【参考方案1】:

我发现了问题所在(并且我意识到这不是任何人仅通过阅读问题就能知道的):

您可以在上面看到的两个纹理之间绘制一个额外的完全透明的纹理,我忘记了。

如果srcColor alpha 为0,则不考虑这一点并仅返回dstColor,而是在混合时使用透明纹理的颜色信息(即(0.0, 0.0, 0.0, 0.0)),因此会更改帧缓冲区内容.

透明纹理和最终纹理都是使用blendTest 函数绘制的,因此在混合最终纹理时会读取第一个函数调用的输出。

【讨论】:

以上是关于片段着色器输出干扰条件语句的主要内容,如果未能解决你的问题,请参考以下文章

条件语句会减慢着色器的速度吗?

满足条件时是不是可以在 GLSL 着色器中回调 C/C++ 函数/代码? [关闭]

python - 条件语句/循环语句/迭代器

顶点着色器+片段着色器

Python语句(输出语句条件语句循环语句)

在片段着色器中输出数据时切换布局位置