PowerVR GPU上的着色器执行流程问题

时间:2012-09-08 11:08:14

标签: android opengl-es opengl-es-2.0 glsl

我遇到了一个问题,我认为这与PowerVR GPU的GLSL编译优化有关。在Adreno和Tegra GPU上,片段着色器工作正常,但在PowerVR(Motorola Droid)上,它在条件语句中产生不正确的结果。 我通过更改片段着色器代码中的条件语句来解决问题。我没有在return语句的块中调用if,而是添加了else块,现在它在PowerVR上运行正常。

两个着色器的逻辑完全相同,在两种情况下都设置了gl_FragColor。 请解释一下PowerVR OpenGL驱动程序的这种行为,以便将来避免出现问题。为什么它以这种方式处理条件语句?

以下是旧的片段着色器,它在PowerVR GPU上运行不正确:

precision mediump float;

varying vec3 vNormal;
varying vec3 vViewVec;
varying vec2 vTextureCoord;

uniform sampler2D sTexturePumpkin;

void main(void)
{
const float sheen = 0.68;
const float noiseScale = 0.05;
const float furriness = 10.0;
const vec4 lightDir = vec4(0.267260, 0.267260, -0.925820, 0.0);

  vec4 color = texture2D(sTexturePumpkin, vTextureCoord/*vec2(0.0,0.0)*/);
  if(vTextureCoord.y > 0.7) { // in this case PowerVR displays incorrect color
     gl_FragColor = color;
     return;
  }

  float diffuse = 0.5 * (1.0 + dot(vNormal, vec3(lightDir.x, lightDir.y, -lightDir.z)));
  float cosView = clamp(dot(normalize(vViewVec), vNormal), 0.0, 1.0);
  float shine = pow(1.0 - cosView * cosView, furriness);

  gl_FragColor = (color + sheen * shine) * diffuse; // in this case PowerVR works correctly
}

新的片段着色器代码,可在Adreno和PowerVR GPU上正常工作:

precision mediump float;

varying vec3 vNormal;
varying vec3 vViewVec;
varying vec2 vTextureCoord;

uniform sampler2D sTexturePumpkin;

void main(void)
{
const float sheen = 0.68;
const float noiseScale = 0.05;
const float furriness = 10.0;
const vec4 lightDir = vec4(0.267260, 0.267260, -0.925820, 0.0);

  vec4 color = texture2D(sTexturePumpkin, vTextureCoord/*vec2(0.0,0.0)*/);
  if(vTextureCoord.y > 0.7) {
     gl_FragColor = color;
  }
  else {
    float diffuse = 0.5 * (1.0 + dot(vNormal, vec3(lightDir.x, lightDir.y, -lightDir.z)));
    float cosView = clamp(dot(normalize(vViewVec), vNormal), 0.0, 1.0);
    float shine = pow(1.0 - cosView * cosView, furriness);
    gl_FragColor = (color + sheen * shine) * diffuse;
  }
}

1 个答案:

答案 0 :(得分:3)

好的,经过深入调查后我发现它不是着色器编译器的错误,而是处理片段着色器执行的特定方法。放置一个简单的discard;return;语句并在其后面添加一些代码通常是个坏主意。它仍然可以被执行并导致不可预测的结果。

有些文章解释了适用于discard;语句的这种行为,因为我发现return;也会发生类似的行为。 请在此处阅读:http://people.freedesktop.org/~idr/OpenGL_tutorials/03-fragment-intro.html#infinite-loop 正如它在这里所说,在某些情况下,你甚至可以通过错误使用discard;来实现无限循环。

片段着色器由GPU执行,不是一次一个纹理元素,通常是2x2像素的批量。并行运行片段着色器可以导致return;之后的代码。

因此,要使片段着色器正确处理if语句,您必须始终在else运算符中使用if,而不是简单地按return;或{{1}退出函数}}。这正是我所做的。