【问题标题】:GLSL shader not working on AMD/ATI, but works on NVIDIAGLSL 着色器不适用于 AMD/ATI,但适用于 NVIDIA
【发布时间】:2014-06-18 12:51:39
【问题描述】:

我有一个很奇怪的问题,我几天都不能pin down。我正在制作一个简单的逐顶点光照,它在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 种状态——当我使用灯光时,当我不使用灯光但使用逐顶点颜色时,以及当我对所有顶点使用单一颜色时。我删除了所有光计算代码,因为我将问题追溯到一个属性。这是着色器的输出:
如您所见,所有内容都是蓝色的(因此它会渲染没有阴影或颜色的所有内容(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{

我得到以下信息:

所以你可以看到它渲染所有没有阴影的东西仍然是蓝色的,但现在它也把所有使用灯光(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 上测试并正常工作,在ATI mobility 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 上不需要那个。所以我想这更慢而且我在浪费带宽?但至少它有效......不过,如何更好地做到这一点的帮助会很有用。

【问题讨论】:

  • 展示如何绑定顶点属性。同时尝试验证您的程序并显示验证日志。
  • 尝试在不使用条件的情况下重写...(改为切换程序)...我刚刚遇到了英特尔驱动程序和条件的疯狂问题:stackoverflow.com/questions/24222343/…
  • 我更新了话题。所以即使没有分支,我仍然会遇到问题。
  • 如果您将版本更改为 3.3 或 4+ 范围内的版本会怎样? (e.g.#version 330.)
  • 更改版本不会改变任何东西。尽管我确实注意到即使日志为空,编译显示为失败(glGetProgramiv(shader, GL_COMPILE_STATUS, &compiled); 返回已编译==false),但它仍然找到所有制服和属性,以及渲染到屏幕(给出的屏幕截图)。在 Nvidia 上,当编译器返回 false 时,它​​会在日志中返回错误并且不渲染任何内容。所以我觉得这真的很奇怪。我拥有的硬 ATI Mobility HD 5000 应该支持 GL3.3。

标签: c++ opengl glsl ati


【解决方案1】:

这可能是因为您为着色器指定了 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

可能的解决方案是执行您已经完成的操作并在不需要时指定垃圾颜色数据,或者您可以将顶点着色器拆分为具有不同顶点属性的 2 个单独的着色器,并根据是否绑定适当的着色器你有顶点颜色数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2012-04-03
    • 1970-01-01
    相关资源
    最近更新 更多