jianliang79

09-08-2011, 12:29 AM

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:

#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);

}

fragment shader:

#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);

}

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.

vertex shader:

#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);

}

fragment shader:

#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);

}

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.