GLSL片段着色器中的动态工作量

4
我正在为2D冲击波制作一个后处理GLSL(ES)片段着色器,与单个冲击波相比,将纹理略微扭曲很容易,并且已经可以实现。现在我需要同时支持多个冲击波,我的想法如下: 第一种想法: 对每个冲击波进行后处理(绑定FBO-》绑定先前的纹理-》绘制调用)。易于实现,但需要大量状态更改和绘制调用。 第二种想法:添加包含所有冲击波信息的数据纹理和一个表示波数的统一变量,这样我就可以像这样做:
uniform int count;
uniform sampler2D dataTex;
const float dataTexSize = 32;
[...]
    vec4 pixel;
    for(int i = 0; i < count; i++)
    {
        vec2 dataTexPos = vec2(i / dataTexSize, 0);
        pixel += shockwave(texture2D(dataTex, dataTexPos));
    }
    pixel /= float(count);
[...]

但显卡似乎不喜欢带有非常量表达式的循环,而驱动程序无法展开循环。

是否有最佳实践可以为着色器提供“动态量的工作”?我的OpenGL版本是2.0 ES。


你可以尝试在你能够访问的任何硬件上对这两个实现进行性能分析。 - Colonel Thirty Two
问题在于,第二个想法根本行不通,因为我无法将带有for循环的着色器编译为非常量。我想知道是否有方法可以实现这一点。 - Bastian Born
“但显然,GPU不喜欢带有非常量表达式的循环。” 不,糟糕的 GPU不喜欢那样。 ES 2.0不允许非常量循环,但实际上现代GPU完全可以胜任。桌面GL 3.0在近十年前放弃了这些要求。在移动领域,GL ES 3.0也放弃了这些要求。 - Nicol Bolas
你说得完全正确。但不幸的是,我只能使用2.0 ES。 - Bastian Born
1个回答

2
只有少数几个ES2.0 GPU(以合理数量可用)不支持片段着色器中的动态循环。我知道的其中两个是Tegra 2(Android GLSL fragment shader on NVIDIA tegra2 - 但它已经相当老了)和Broadcom VC4(Raspberry Pi和Amazon FireTV Stick)。ES 2.0规范不要求支持,但正如@Nicol Bolas所提到的,绝大多数移动GPU都支持它。因此,如果您没有针对这些芯片进行定位,那么您很可能可以使用该着色器。
不幸的是,没有GLES扩展来广告支持动态循环。您基本上只需编译具有动态循环的着色器,并查看其是否成功。在目标不支持它的情况下,您可以“伪造”动态循环,通过编译具有常量循环表达式的多个着色器,而不是使用统一变量。例如:
int count = %d;
uniform sampler2D dataTex;
const float dataTexSize = 32;
[...]
    vec4 pixel;
    for(int i = 0; i < count; i++)
    {
        vec2 dataTexPos = vec2(i / dataTexSize, 0);
        pixel += shockwave(texture2D(dataTex, dataTexPos));
    }
    pixel /= float(count);
[...]

每当您遇到需要不同循环次数的情况时,请将%d替换为所需的循环次数并编译新着色器。只要count具有合理小的可能值,您就不会得到太多的着色器。

1
我检查了纹理的读写速率,显然我的台式机GPU驱动程序完美地优化了一个带有常量表达式和动态if的for循环。幸运的是,我的目标受众只有桌面用户。谢谢! - Bastian Born

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