GLSL着色器在AMD/ATI上无法工作,但在NVIDIA上可以。

6

我有一个非常奇怪的问题,几天来都无法确定。我正在制作一个简单的逐顶点光照效果,在英伟达(Nvidia)上运行良好,但在AMD/ATI上无法渲染任何受光影响的内容。我发现问题与属性有关,特别是颜色属性。
这是我的顶点着色器:

#version 140
uniform mat4 modelViewProjectionMatrix;
in vec3 in_Position;                 // (x,y,z)
in vec4 in_Color;                    // (r,g,b,a)
in vec2 in_TextureCoord;             // (u,v)

out vec2 v_TextureCoord;
out vec4 v_Color;

uniform bool en_ColorEnabled;
uniform bool en_LightingEnabled;

void main()
{
   if (en_LightingEnabled == true){
      v_Color = vec4(0.0,1.0,0.0,1.0);
   }else{
      if (en_ColorEnabled == true){
         v_Color = in_Color;
      }else{ 
         v_Color = vec4(0.0,0.0,1.0,1.0);
      }
   }
   gl_Position = modelViewProjectionMatrix * vec4( in_Position.xyz, 1.0);

   v_TextureCoord = in_TextureCoord;
}

这是像素着色器:
#version 140
uniform sampler2D en_TexSampler;
uniform bool en_TexturingEnabled;
uniform bool en_ColorEnabled;
uniform vec4 en_bound_color;
in vec2 v_TextureCoord;
in vec4 v_Color;
out vec4 out_FragColor;

void main()
{
   vec4 TexColor;
   if (en_TexturingEnabled == true){
      TexColor = texture2D( en_TexSampler, v_TextureCoord.st ) * v_Color;
   }else if (en_ColorEnabled == true){
      TexColor = v_Color;
   }else{
      TexColor = en_bound_color;
   }
   out_FragColor = TexColor;
}

所以这个着色器允许3种状态——当我使用灯光时,当我不使用灯光但使用每个顶点的颜色,以及当我对所有顶点使用单一颜色。我移除了所有的灯光计算代码,因为我把问题追溯到一个属性。这是着色器的输出结果:
Shader with bug 正如你所看到的,所有东西都是蓝色的(所以它会渲染所有没有阴影或颜色的东西(en_LightingEnabled == false and en_ColorEnabled == false))。

当我在顶点着色器中替换此代码时:

if (en_ColorEnabled == true){
   v_Color = in_Color;
}else{ 

使用这个:

if (en_ColorEnabled == true){
   v_Color = vec4(1.0,0.0,0.0,1.0);
}else{

我得到了以下内容:
未使用in_Color属性
你可以看到它将所有不带阴影的蓝色渲染出来,但现在还会将所有使用灯光(en_LightingEnabled == true)的物体渲染成绿色。这就是应该的效果。问题是,in_Color属性不应该干扰灯光,因为如果启用了灯光,它甚至都不会运行。你也可以看到图像中没有任何红色,这意味着在此场景中,en_ColorEnabled始终为false。因此,在这里in_Color完全无用,但它会在逻辑上不应该有问题的地方导致错误。在Nvidia上它正常工作,绿色区域与或不与in_Color一起绘制。

我是否在使用一些AMD不支持的东西?我是否在使用GLSL规范之外的东西?我知道AMDGL规范比Nvidia更严格,所以可能我做了一些未定义的事情。我尽可能简化了代码,但仍然有问题,所以我不知道问题在哪里。
另外,我注意到如果禁用in_Color顶点属性(没有glEnableVertexAttribArray),它将不会绘制任何东西(即使是蓝色区域)在屏幕上,但为什么呢?我甚至没有使用颜色。规范要求禁用的属性返回(0,0,0,1),对吗?我尝试使用glVertexAttrib4f来更改该颜色,但仍然是黑色。编译器和链接器没有返回任何错误或警告。使用glGetAttribLocation找到了in_Color属性。但正如我所说的那样,在我的代码中,这个分支根本不应该被执行。有什么想法吗?

Nvidia660Ti上测试时,一切正常,在ATImobility radeon HD 5000笔记本电脑上测试时出现了错误。我唯一的想法是也许超出了GPU的能力(太多属性等等),但当我将代码简化到这个程度时,我不再相信这个理论了。我只在这里使用了3个属性。
此外,我的一些朋友在更新的AMD卡上测试时也遇到了同样的问题。

更新1

顶点属性绑定如下:

if (useColors){
   glUniform1i(uni_colorEnable,1);  
   glEnableVertexAttribArray(att_color);
   glVertexAttribPointer(att_color, 4, GL_UNSIGNED_BYTE, GL_TRUE, STRIDE, OFFSET(offset));
}else{
   glUniform1i(uni_colorEnable,0);  
}

此外,我找到了一种更容易展示异常的方式——使用这个顶点着色器:
void main()
{
   v_Color = clamp(vec4(1.,1.,1.,1.) * vec4(0.5,0.5,0.25,1.),0.0,1.0) + in_Color;

   gl_Position = modelViewProjectionMatrix * vec4( in_Position.xyz, 1.0);

   v_TextureCoord = in_TextureCoord;
}

因此不需要分支。我得到了这个结果:没有分支,但仍然存在该漏洞 如果我删除 in_Color 这段代码,那么就会变成这样:

void main()
{
   v_Color = clamp(vec4(1.,1.,1.,1.) * vec4(0.5,0.5,0.25,1.),0.0,1.0);

   gl_Position = modelViewProjectionMatrix * vec4( in_Position.xyz, 1.0);

   v_TextureCoord = in_TextureCoord;
}

我得到的是这个: 未按预期分支

最后一个示例是我所期望的,但是在我使用加法 (+) 而不是其他内容时,上一个示例并没有绘制任何东西。因此,如果 attrib 没有绑定,并且给出了 (0,0,0,0) 或 (0,0,0,1),那么它不应该做任何事情。然而,它仍然失败了。

此外,所有日志都是空的 - 编译、链接和验证。没有错误或警告。尽管我注意到 AMD/ATI 给出的着色器调试信息远不如 Nvidia 的。

更新2

我找到的唯一解决方法是在所有情况下启用 attribarray(因此当颜色未启用时它基本上发送垃圾到 GPU),但由于我使用统一变量来检查颜色,因此我就不使用它了。因此代码如下:

if (useColors){
   glUniform1i(uni_colorEnable,1);  
}else{
   glUniform1i(uni_colorEnable,0);  
}
glEnableVertexAttribArray(att_color);
glVertexAttribPointer(att_color, 4, GL_UNSIGNED_BYTE, GL_TRUE, STRIDE, OFFSET(offset));

我不知道为什么会发生这种情况,也不知道为什么需要使用它。在 Nvidia 上我并不需要它。所以我猜这样做会更慢,也浪费带宽?但至少它能够工作... 希望能提供更好的方法。


1
尝试不使用条件语句进行重写,而是使用程序开关...我刚刚遇到了一个有关英特尔驱动程序和条件语句的疯狂问题:https://dev59.com/ooDba4cB1Zd3GeqPG6Qj - user755921
我更新了主题。所以即使没有分支,我仍然遇到问题。 - user1214513
更改版本并没有改变任何东西。虽然我注意到即使日志为空,编译仍然显示为失败(glGetProgramiv(shader, GL_COMPILE_STATUS, &compiled); 返回 compiled==false),但它仍然找到了所有的 uniform 和 attribute,并且渲染到屏幕上(给出了截图)。在 Nvidia 上,当编译器返回 false 时,它会在日志中返回错误,并且不会渲染任何内容。所以我觉得这真的很奇怪。我拥有的 ATI Mobility HD 5000 应该支持高达 GL3.3。 - user1214513
@user1214513:我认为如果您展示更多客户端GL代码,特别是着色器设置和属性位置查询以及有关绘制调用的一些内容,这将非常有帮助。我并不100%确信这是AMD的错误(当然不能排除这种可能性),但仍然看到您可能存在某些错误的可能性。 - derhass
@user1214513:另外,“规范需要禁用属性返回(0,0,0,1)”并不完全正确。这是初始值,但在启用此属性的绘制调用之后,它将变为未定义状态(NVidia和AMD可能会有所不同)。“我尝试使用glVertexAttrib4f来更改颜色,但仍然是黑色。”不清楚您何时调用了此函数。如果在该属性被禁用的每个绘制调用中重新指定,那么在之前使用该属性数组的其他绘制调用之后,您必须重新指定它。 - derhass
显示剩余3条评论
1个回答

2
这可能是因为您在着色器中指定了3个输入值(in_Position、in_Color和in_TextureCoord),但您的顶点数据并不总是包含全部3个。当顶点属性与此格式不完全匹配时,您将进入未定义的行为领域,每个实现都可以自行处理。
看起来Nvidia驱动程序会将其所拥有的内容绑定到相应的顶点属性,并进行渲染。
也就是说,您的数据在着色器中的排列方式如下:
in_Position      = Position.xyz
in_Color         = ???
in_TextureCoords = TextureCoords.xy

AMD驱动程序可能会尝试将顶点数据解释为所有3个属性,即使不包括颜色。在第一个顶点之后,位置与数据数组中的位置不同步,这会导致其几乎出现在任何地方。我怀疑在去除in_Color属性使用时它可以在AMD上工作的原因是着色器编译器看到它没有被使用并优化了您指定的事实。

in_Position      = Position[0].xyz
in_Color         = TextureCoords[0].xy,Position[1].xy
in_TextureCoords = Position[1].z,TextureCoords[1].x

可能的解决方案是已经做了的,即在您不需要时指定垃圾颜色数据,或者您可以将顶点着色器拆分为两个不同的着色器,具有不同的顶点属性,并根据是否有顶点颜色数据绑定适当的着色器。

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