我有一个非常奇怪的问题,几天来都无法确定。我正在制作一个简单的逐顶点光照效果,在英伟达(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](https://istack.dev59.com/8Nc4U.webp)
(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属性](https://istack.dev59.com/PQKLL.webp)
你可以看到它将所有不带阴影的蓝色渲染出来,但现在还会将所有使用灯光(
en_LightingEnabled == true
)的物体渲染成绿色。这就是应该的效果。问题是,in_Color
属性不应该干扰灯光,因为如果启用了灯光,它甚至都不会运行。你也可以看到图像中没有任何红色,这意味着在此场景中,en_ColorEnabled
始终为false
。因此,在这里in_Color
完全无用,但它会在逻辑上不应该有问题的地方导致错误。在Nvidia
上它正常工作,绿色区域与或不与in_Color
一起绘制。
我是否在使用一些AMD
不支持的东西?我是否在使用GLSL
规范之外的东西?我知道AMD
对GL
规范比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
上我并不需要它。所以我猜这样做会更慢,也浪费带宽?但至少它能够工作... 希望能提供更好的方法。
glGetProgramiv(shader, GL_COMPILE_STATUS, &compiled);
返回 compiled==false),但它仍然找到了所有的 uniform 和 attribute,并且渲染到屏幕上(给出了截图)。在 Nvidia 上,当编译器返回 false 时,它会在日志中返回错误,并且不会渲染任何内容。所以我觉得这真的很奇怪。我拥有的 ATI Mobility HD 5000 应该支持高达 GL3.3。 - user1214513