c++ - GLSL 着色器不适用于 AMD/ATI,但适用于 NVIDIA

标签 c++ opengl glsl ati

我有一个非常奇怪的问题我不能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 种状态 - 当我使用灯光时,当我不使用灯光但使用每个顶点颜色时以及当我对所有顶点使用单一颜色时。我删除了所有的灯光计算代码,因为我将问题追溯到一个属性。这是着色器的输出:
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{

我得到以下信息:
Without in_Color attribute
所以你可以看到它在没有阴影的情况下渲染所有内容仍然是蓝色,但现在它也将使用灯光( 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 个属性。
另外,我的一些 friend 在更新的 AMD 上进行了测试。卡并遇到同样的问题。

更新 1

顶点属性绑定(bind)如下:
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;
}

所以没有分支。我明白了:
Without branching there is still that bug
如果我通过删除 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;
}

我明白了:
Without branching as expected

这最后一个是我所期望的,但即使我做了加法( + )而不是其他任何东西,前一个也没有绘制任何东西。所以如果 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 .所以我想这会更慢而且我在浪费带宽?但至少它有效......不过,关于如何更好地做到这一点的帮助会很有用。

最佳答案

这可能是因为您为着色器指定了 3 个输入值(in_Position、in_Color 和 in_TextureCoord),但您的顶点数据并不总是包含所有 3 个。当顶点属性与此格式不完全匹配时,您将进入未定义的行为领土和每个实现都可以做它想做的事。

看起来好像 Nvidia 驱动程序将它所拥有的内容绑定(bind)到适当的顶点属性并使用它进行渲染。

也就是说,您的数据在着色器中的排列如下所示:

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 个具有不同顶点属性的独立着色器,并根据是否有顶点绑定(bind)适当的一个颜色数据。

关于c++ - GLSL 着色器不适用于 AMD/ATI,但适用于 NVIDIA,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24286092/

相关文章:

c++ - 使用不同的 VAO 和 VBO 绘制多个三角形

c++ - glDrawArrays 访问错误

c++ - 你如何在 C++ 中比较多个字符串

c++ - 返回一个本地对象会导致垃圾,而返回同一个对象临时工作正常

opengl - 如何在 OpenGL 中正确绘制 3D 轴?

glsl - WebGL 2.0 最大图像纹理单位和最大组合纹理单位之间有什么区别?

glsl - WebGL:glsl 属性问题,getProgramParameter 返回错误的属性数

C++ 模板推导不起作用

c++ - 应用程序在 CCriticalSection::Lock 上中断

java - 在 OpenGL 中缩放纹理