2013-04-03 37 views
4

我想传递一个统一的vec3数组,然后在每个像素上遍历它们。数组的大小因情况而异,所以我无法使循环的迭代次数保持不变。iPhone GLSL动态分支问题

下面是代码:

precision highp float; 
precision highp int; 

varying vec4 v_fragmentColor; 

varying vec4 v_pos; 

uniform int u_numberOfParticles; 

const int numberOfAccumsToCapture = 3; 
const float threshold = 0.15;    
const float gooCoeff = 1.19; 

uniform mat4 u_MVPMatrix; 
uniform vec3 u_waterVertices[100]; 

void main() 
{ 
    vec4 finalColor = vec4(0.0, 0.0, 0.0, 0.0); 

    vec2 currPos = v_pos.xy; 

    float accum = 0.0; 
    vec3 normal = vec3(0, 0, 0); 

    for (int i = 0; i < u_numberOfParticles; ++i) 
    { 
     vec2 dir2 = u_waterVertices[i].xy - currPos.xy; 
     vec3 dir3 = vec3(dir2, 0.1); 
     float q = dot(dir2, dir2); 

     accum += u_waterVertices[i].z/q; 
    } 

    float normalizeToEdge = 1.0 - (accum - threshold)/2.0; 

    if (normalizeToEdge < 0.4) 
     finalColor = vec4(0.1, normalizeToEdge + 0.5, 0.9-normalizeToEdge*0.4, 1.0); 

    if (normalizeToEdge < 0.2) 
    { 
     finalColor = vec4(120.0/255.0, 245.0/255.0, 245.0/255.0, 1.0); 
     float shade = mix(0.7, 1.0, normal.x); 
     finalColor *= shade; 
    } 

    gl_FragColor = vec4(finalColor); 
} 

的问题是在这里:

for (int i = 0; i < u_numberOfParticles; ++i) 
{ 
    vec2 dir2 = u_waterVertices[i].xy - currPos.xy; 
    vec3 dir3 = vec3(dir2, 0.1); 
    float q = dot(dir2, dir2); 

    accum += u_waterVertices[i].z/q; 
} 

当我做for循环这样

for (int i = 0; i < 2; ++i) 
{ 
    //... 
} 

我得到双倍帧速率,甚至虽然u_numberOfParticles也是2

使它像这样

for (int i = 0; i < 100; ++i) 
{ 
    if (i == u_numberOfParticles) 
     break; 
    //... 
} 

没有改善。

我知道应对这种情况的唯一方法是创建多个着色器。但是数组的大小可能在1到40之间变化,并且由于for循环是愚蠢的,所以可以制作40个不同的着色器。任何帮助或想法如何处理这种情况?

+0

u_numberOfParticles多久更改一次?你可以在运行期间设置一次吗? – Kimi

+0

@Kimi,当我添加新的粒子时,它会发生变化,所以很常见。它可以是15,然后是20,然后是30,然后是15,我可以将它总是设置为40。但效率不高。 – Terko

+0

将迭代次数设置为一个小常数时,您获得更好结果的原因是编译器循环展开和常量数组索引。如果您有动态断点子句,则循环无法展开。你在片段着色器(gl_FragClolor)中运行提供的代码吗?如果是,则计算顶点着色器中的值并将累加值作为变量传递将会更有效。 – Kimi

回答

1

我同意@badweasel你的方法不适合着色器。

根据我的理解,您正在计算从当前像素到每个粒子的距离,总结一些东西并使用结果确定颜色。

也许你可以渲染每个粒子的点精灵并通过智能混合来确定颜色。

您可以使用gl_PointSize来设置顶点着色器中点精灵的大小。在片段着色器中,您可以使用gl_PointCoord.xy(它在纹理坐标中,即[0..1])确定点精灵内当前像素的位置。通过知道点精灵的大小,您可以计算当前像素与粒子中心的距离并将颜色设置为某个值。通过额外启用混合,您可以实现在循环内进行的总和,但帧速率要高得多。

这里是顶点和片段着色器,我用它来渲染通过点精灵的“假”球体,作为如何使用点精灵的例子。

VS:

#version 150 
in vec3 InPosition; 

uniform mat4 ModelViewProjectionMatrix; 
uniform int Radius = 10; 

void main() 
{ 
    vec4 Vertex = vec4(InPosition, 1.0); 
    gl_Position = ModelViewProjectionMatrix * Vertex; 
    gl_PointSize = Radius; 
} 

FS:

#version 150 
out vec4 FragColor; 

void main() 
{ 
    // calculate normal, i.e. vector pointing from point sprite center to current fragment 
    vec3 normal; 
    normal.xy = gl_PointCoord * 2 - vec2(1); 
    float r2 = dot(normal.xy, normal.xy); 
    // skip pixels outside the sphere 
    if (r2 > 1) discard; 
    // set "fake" z normal to simulate spheres 
    normal.z = sqrt(1 - r2); 
    // visualize per pixel eye-space normal 
    FragColor = vec4(gl_PointCoord, normal.z, 1.0); 
} 

请注意,您需要启用:GL_POINT_SPRITEGL_PROGRAM_POINT_SIZE用点精灵。

+0

现在我使用你建议的方法。我有一些“特殊”纹理渲染,然后混合后,我分析每个像素的alpha值,并决定像素应该是什么样子。由于删除了片段着色器中的这些循环,这看起来要快得多。不过,谢谢。 – Terko