OpenGL ES 片段着色器似乎无法返回白色

3

这是一个奇怪的问题。我有一个片段着色器,据我观察只能返回黑色或红色,但它却渲染为白色像素。如果我删除一行特定的代码,就会返回我期望的颜色。它在WebGL中可用,但在树莓派上的OpenGL ES中不行。

以下是着色器代码。如果像素代表Mandelbrot集合内的点,则返回黑色,否则返回红色。

precision mediump float;

varying vec2 vPosition;

void main(void) {
    float cx = vPosition.x;
    float cy = vPosition.y;

    float x = 0.0;
    float y = 0.0;
    float tempX = 0.0;
    int runaway = 0;
    for (int i=0; i < 100; i++) {
         tempX = x * x - y * y + float(cx);
         y = 2.0 * x * y + float(cy);
         x = tempX;
         if (runaway == 0 && x * x + y * y > 100.0) {
             runaway = i;
         }
    }

    if (runaway != 0) {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    } else {
        gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
    }
}

那个着色器会将所有像素都涂成白色。但是,如果我删除这一行:

             runaway = i;

然后,正如你所期望的那样,它会将所有像素都涂成黑色(因为它总是最终的if语句中的else分支中)。

渲染白色像素是否是某种错误条件的症状? 如果是,可能是什么呢?

[编辑] 在任何人问之前 - 在该行上设置runaway = 1;也会导致整个屏幕变成白色。


你确定它返回白色吗?也许你只是在代码中后处理了一帧?此外,在着色器中不建议使用条件和循环,特别是片段着色器。 - Reaper
我没有进行任何后处理,至少不是有意的 :-) 如果有帮助的话,您可以在https://raw.githubusercontent.com/gpjt/pi-opengles-lessons/mandelbrot-debug/example01/run.py上查看完整的代码。 我改编自原始的WebGL代码,该代码位于https://github.com/gpjt/webgl-lessons/blob/master/example01/index.html--您可以看到它具有相同的条件和循环。 - Giles Thomas
顺便提一下 - 据我所知,条件和循环的限制基本上是因为最好让所有着色器在锁定步骤中执行相同的计算,并且不遵循大不相同的代码路径。这就是为什么我只是在循环内设置了 runaway,而没有通过在循环上放置 break&& runaway == 0 条件来退出 - 即使它们不需要,着色器的所有实例也始终执行 100 次迭代。 - Giles Thomas
1个回答

2
我认为问题在于您为目标设备的片段着色器做了太多的工作。当您将runaway设置为i1时,实验允许优化器完全删除循环,这就是为什么您开始再次看到预期结果的原因。
OpenGL ES Shader Language spec中的一些相关引用如下:
总的来说,控制流程受限于......可以在编译时轻松确定最大迭代次数的循环。
对我来说(我有点推测),这意味着“某些实现根本不支持迭代,并将展开每个循环”。
非终止循环是允许的。非常长或非终止循环的后果取决于平台。
对我来说,这是在说,“您可以让着色器迭代任意次数,但不应该指望它能正常工作”。
是否应该指定迭代次数上限?决议:否
我觉得OpenGLES 2对于着色器复杂度限制有点含糊不清。我认为问题要么是:
您编译的着色器超出了实现的指令计数限制,这是第一种情况。第二种情况是您的着色器执行时间过长,在运行时被中断。如果是第一种情况,您可能会遇到着色器编译器错误、链接器错误或验证错误。您的代码是否充分检查了这些错误?这可能会使情况更加清晰。无论如何,我认为您需要简化着色器,以便在树莓派上运行。

非常感谢!那就是问题所在。当我将迭代次数降至18次(通过二分法确定了确切次数),它就正常工作了,尽管曼德博集看起来并不是很好。我猜管线的片段部分会返回全白色,如果硬件无法处理FS代码的话。 - Giles Thomas
哦,我刚意识到我没有回答你关于编译器/链接器/验证错误的问题——是的,我认为我正在彻底检查它们。至少,有错误的破损代码会生成一个错误。所以我猜测这是你的第二个选项。 - Giles Thomas
完整的代码(包括错误检查)在此处:https://raw.githubusercontent.com/gpjt/pi-opengles-lessons/mandelbrot-debug/example01/run.py - Giles Thomas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接