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

Opengl es GLSL片段着色器中的动态工作量,opengl-es,glsl,fragment-shader,Opengl Es,Glsl,Fragment Shader,我正在为2D冲击波开发一个后处理GLSL(ES)片段着色器。它很容易在一个圆圈内扭曲纹理,并且它已经适用于单个冲击波。现在我需要同时支持多个Wave,我的想法是: 第一个想法:对每个波浪进行后处理(绑定FBO->绑定上一个纹理->绘制调用)。很容易实现,但会有很多状态更改和draw调用 第二个想法:添加包含所有波的所有信息的数据纹理和一个包含波数的单一均匀纹理,这样我就可以这样做: uniform int count; uniform sampler2D dataTex; const float

我正在为2D冲击波开发一个后处理GLSL(ES)片段着色器。它很容易在一个圆圈内扭曲纹理,并且它已经适用于单个冲击波。现在我需要同时支持多个Wave,我的想法是:

第一个想法:对每个波浪进行后处理(绑定FBO->绑定上一个纹理->绘制调用)。很容易实现,但会有很多状态更改和draw调用

第二个想法:添加包含所有波的所有信息的数据纹理和一个包含波数的单一均匀纹理,这样我就可以这样做:

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);
[...]
统一整数计数;
均匀采样2d-dataTex;
常量浮点dataTexSize=32;
[...]
vec4像素;
for(int i=0;i
但显然GPU不喜欢带有非常量表达式的循环,并且驱动程序无法展开


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

只有少数ES2.0 GPU(可提供任何合理数量)不支持片段着色器中的动态循环。我特别知道的两个是Tegra2(但它已经很老了)和Broadcom VC4(覆盆子皮和亚马逊FireTV棒)。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);
[...]
int计数=%d;
均匀采样2d-dataTex;
常量浮点dataTexSize=32;
[...]
vec4像素;
for(int i=0;i

每次遇到需要不同循环计数的情况时,请使用所需循环计数替换
%d
,并编译一个新着色器。只要
count
有合理数量的可能值,就不会有那么多着色器。

您可以尝试在您可以访问的任何硬件上分析这两种实现。问题是,第二种想法根本不起作用,因为我无法使用for循环将着色器编译为非常量。但显然GPU不喜欢带有非常量表达式的循环“不,糟糕的GPU不喜欢这样。ES 2.0不允许非常量循环,但实际上现代GPU完全可以使用它。桌面GL 3.0在近十年前就放弃了这些要求。在移动领域,GL ES 3.0抛弃了这些要求。你完全正确。但不幸的是,我被限制在2.0ES。我检查了纹理读/写速率,很明显,我的桌面GPU驱动程序使用常量表达式优化了for循环,并使用了动态if-inside。幸运的是,我的目标受众只是桌面用户。谢谢