Our Application use uniform block to pass light source parameters to fragment shader when rendering a scene. Our code run perfectly in nVidia video card but can't render the correct result with an ATI 5770 card (catalyst 11.8, windows 7 x64) when light count is greater than 1. The vertex shader and fragment shader is pasted below:
vertex shader:
[b]#version 150
uniform mat4 modelViewMatrix;
uniform mat3 normalMatrix;
uniform mat4 projectionMatrix;
uniform float normalMultiplier; // may be 1 or -1
in vec3 vertex;
in vec3 normal;
in vec2 texCoord;
out vec3 fragPosition; // in eye coordinates
out vec3 fragNormalVector; // in eye coordinates
out vec3 fragEyeVector; // in eye coordinates
out vec2 fragTexCoord;
void main()
{
// transform vertex position into eye coordinates
vec3 posEye = (modelViewMatrix * vec4(vertex, 1)).xyz;
fragPosition = posEye;
fragNormalVector = normalize(normalMatrix * (normal * normalMultiplier));
fragEyeVector = -normalize(posEye);
fragTexCoord = texCoord;
gl_Position = projectionMatrix * vec4(posEye, 1);
}[/b]
fragment shader:
[b]#version 150
struct SLightSource {
vec4 color;
vec3 position; // light source position in eye coordinates
vec3 lightDir; // unit vector for light direction(toward light), in eye coordinates
float lightDirMixFactor; // 0 for directional light; 1 for point or spot light
float cosTheta; // cosTheta must be greater than cosPhi
float cosPhi;
float constantAttenuation;
float linearAttenuation;
float quadraticAttenuation;
};
layout(std140) uniform LightSourcesBlock {
SLightSource lightSources[32];
};
//
// material light reflection property
//
uniform vec4 diffuseColor;
uniform vec4 specularColor;
uniform vec4 ambientColor;
uniform float glossiness; // must be greater than 0
vec4 CalcLightColor(const in int lightIdx,
const in vec3 pos,
const in vec3 normalVector,
const in vec3 eyeVector)
{
vec4 lightSourceColor = lightSources[lightIdx].color;
// calculate vector from this fragment point to light source
vec3 lightVector = lightSources[lightIdx].position - pos;
float lightDistance = length(lightVector);
lightVector = normalize(lightVector);
lightVector = mix(lightSources[lightIdx].lightDir, lightVector, lightSources[lightIdx].lightDirMixFactor);
// calculate diffuse light color
float diffuseIntensity = max(dot(normalVector, lightVector), 0);
vec4 lightColor = diffuseColor * lightSourceColor * diffuseIntensity;
// calculate specular light color
vec3 reflection = normalize(reflect(-lightVector, normalVector));
float specularItensity = pow(max(dot(reflection, eyeVector), 0), glossiness);
lightColor += specularColor * lightSourceColor * specularItensity;
// calculate radial intensity attenuation
float attenuation = 1 / (lightSources[lightIdx].constantAttenuation
+ lightSources[lightIdx].linearAttenuation * lightDistance
+ lightSources[lightIdx].quadraticAttenuation * lightDistance * lightDistance);
// calculate angular intensity attenuation
float cosAlpha = dot(lightVector, lightSources[lightIdx].lightDir);
attenuation *= smoothstep(
lightSources[lightIdx].cosPhi,
lightSources[lightIdx].cosTheta,
cosAlpha);
lightColor *= attenuation;
// add ambient color
lightColor += ambientColor * lightSourceColor;
return lightColor;
}
uniform vec4 emittedColor;
uniform float opacity;
uniform sampler2D texSampler;
uniform float texMultiplier;
in vec3 fragPosition; // in eye coordinates
in vec3 fragNormalVector; // in eye coordinates
in vec3 fragEyeVector; // in eye coordinates
in vec2 fragTexCoord;
out vec4 fragColor;
void main()
{
vec4 texColor = texture(texSampler, fragTexCoord);
texColor.rgb = texColor.rgb * texMultiplier;
texColor.a *= opacity;
if (texColor.a == 0)
discard;
vec3 normalVector = normalize(fragNormalVector);
vec3 eyeVector = normalize(fragEyeVector);
// calculate light color for all light sources
vec4 lightColor = emittedColor;
lightColor += CalcLightColor(0, fragPosition, normalVector, eyeVector);
lightColor += CalcLightColor(1, fragPosition, normalVector, eyeVector);
lightColor.a = 1;
vec4 color = lightColor * texColor;
fragColor = vec4(color.rgb * color.a, color.a);
}
[/b]
As you can see from the code, in our fragment shader, I use a uniform block to pass light source information, the block contain a structure array. I use std140 layout for this uniform block, I will fill the uniform block with the light sources’ information before issuing GL drawing call. The code above show the case where we have two light sources, it run perfectly in all the nVidia cards we have used but will render incorrect result in my ATI radeon HD 5770 card (with catalyst 11.8 driver, windows 7 x64), the final result will only show the color lit by the first light source, the second light sources seems to be ignored by GLSL compiler. After some hack, I found that the fragment shader read wrong value for the second light sources’ parameter (but it can always read correct value for the first light), I suspect this is due to the incorrect std140 layout. The layout of my structure we used to fill the uniform block is listed below:
[name] [offset]
color 0
position 16
lightDir 32
lightDirMixFactor 44
cosTheta 48
cosPhi 52
constantAttenuation 56
linearAttenuation 60
quadraticAttenuation 64
and the stride of my structure array is 80
I calculate these offset and stride according to openGL 3.2 specification, Is my calculation correct? And how can I do to resovle my problem with ATI card? thanks.