Strange behaviour of Shader on ATI Radeon HD 5450

Hi,

I have the following shader that computes Phong illumination for Directional, Point and Spot Lights and applies shadowmaps. At most 1 light can cast shadowmaps.

It works correctly on an ATI FireProGL V3750, but on an ATI Radeon 5450 with latest Catalyst software (12.6, and also with 12.4) it behaves strangely: The SpotLight illumination is wrong (the diffuse component is all dark when the viewer is far from the objects and becomes lighter as he approaches the objects of the scene), while the illumination given by the Directional and Point lights is correct.

The shadow of the directional light is mapped correctly.
The shadow of the SpotLight is also mapped correctly (it shows as the viewer approaches the objects, because the diffuse of the spotlight becomes lighter and it makes the difference with the shadowed area visible).

If I don’t have any light with shadow, I use a slightly simpler shader that doesn’t do the shadowLookup call and the illumination is correct for all lights.

It should not be a problem of the algorithm since, as I said, it works fine on FireProGL V3750, and the code to compute the illumination of the SpotLight is the same in the version with and without the shadowLookup.

Is it due to some limitation of the graphics card?
If so, why don’t I get any error from the Link of the shader?

Vertex shader should not be meaningful, but I post it the same below.

Fragment Shader:


 
float materialShininess;


const int NumberOfLights = 8;




varying vec3 N;
varying vec3 ecPosition3; // in 3 space






uniform bool LightsEnabled[NumberOfLights];
uniform int  LightsType[NumberOfLights]; // 0 == directional, 1 == spot, 2 == point


void DirectionalLight(in int i,
                        in vec3 normal,
                        out vec4 ambient,
                        out vec4 diffuse,
                        out vec4 specular)
{
   float nDotVP;                       // normal . light direction
   float nDotHV;                       // normal. light half vector
   float pf;                           // power factor                        


    
   nDotVP = max(0.0, dot(normal, normalize(vec3(gl_LightSource[i].position))));
   nDotHV = max(0.0, dot(normal, vec3(gl_LightSource[i].halfVector)));  
   
   if (nDotVP == 0.0)
      pf = 0.0;
   else
      pf = pow(nDotHV, materialShininess);   
      
      ambient  = gl_LightSource[i].ambient;
      diffuse  = clamp(gl_LightSource[i].diffuse * nDotVP, 0.0, 1.0);
      specular = clamp (gl_LightSource[i].specular * pf, 0.0, 1.0);      


}






void ComputeSpotPointParameters( in int i,
                        in vec3 eye,
                        in vec3 ecPosition3,
                        in vec3 normal,
                        out vec3 VP,
                        out float nDotVP, 
                        out float pf,
                        out float attenuation)
{
   
    float nDotHV;
    float d;
    vec3 halfVector;
   
    VP = vec3(gl_LightSource[i].position) - ecPosition3;
    
    d = length(VP);
   
    VP = normalize(VP);
    
    attenuation = 1.0 / (gl_LightSource[i].constantAttenuation +
                        gl_LightSource[i].linearAttenuation * d +
                        gl_LightSource[i].quadraticAttenuation *d * d);
                        
    halfVector = normalize(VP + eye);
    
    nDotVP = max(0.0, dot(normal, VP));
    nDotHV = max(0.0, dot(normal, halfVector));
    
    if (nDotVP == 0.0)
      pf = 0.0;
    else
      pf = pow(nDotHV, gl_FrontMaterial.shininess);
}




void SpotLight(in int i,
               in vec3 eye,
               in vec3 ecPosition3,
               in vec3 normal,
               inout vec4 ambient,
               inout vec4 diffuse,
               inout vec4 specular)
{


    vec3 VP;
    float nDotVP, pf;
    float spotAttenuation;
    float attenuation;
    
    ComputeSpotPointParameters( i,
                                 eye,
                                 ecPosition3,
                                 normal,
                                 VP, nDotVP, pf,
                                 attenuation);
                        
   float spotDot = dot(-VP, normalize(gl_LightSource[i].spotDirection));
   
   if (spotDot < gl_LightSource[i].spotCosCutoff)
      spotAttenuation = 0.0;
   else
      spotAttenuation = pow(spotDot, gl_LightSource[i].spotExponent);
      
   attenuation *= spotAttenuation;
      
   ambient  = gl_LightSource[i].ambient * attenuation;
   diffuse  = gl_LightSource[i].diffuse * nDotVP * attenuation;
   specular = gl_LightSource[i].specular * pf * attenuation;
}


void PointLight(in int i,
                in vec3 eye,
                in vec3 ecPosition3,
                in vec3 normal,
                inout vec4 ambient,
                inout vec4 diffuse,
                inout vec4 specular)
{


    vec3 VP;
    float nDotVP, pf, attenuation;
    
    ComputeSpotPointParameters( i,
                                 eye,
                                 ecPosition3,
                                 normal,
                                 VP, nDotVP, pf,
                                 attenuation);
    
   ambient  = gl_LightSource[i].ambient * attenuation;
   diffuse  = gl_LightSource[i].diffuse * nDotVP * attenuation;
   specular = gl_LightSource[i].specular * pf * attenuation;          
}


    
    
const int NumberOfSplits = 1;


uniform bool FirstPass;


bool doShadow;
uniform sampler2DShadow ShadowMap;
uniform float Epsilon;
varying vec4  ShadowCoord[NumberOfSplits]; 
uniform float SplitDepths[NumberOfSplits];
varying float vertexDepth;


uniform float ShadowAmbientFactor;
uniform bool YieldShadow[NumberOfLights];




bool shadowLookup(vec3 shadowCoord, vec4 diff, float x, float y, out vec4 color)
{
   float depth = shadow2D(ShadowMap, shadowCoord).x;
   
   if (depth != 1.0) // in shadow
   {        
        color = vec4(diff * ShadowAmbientFactor);        
        return false;




   }
   else // in light
   {
      color = vec4(diff);
      return true;
   }
}




uniform bool BackFaceColor;


void main(void)
{
    vec4 finalColor;    




    vec3 normal;
    vec4 ambient  = vec4(0.0);  
    vec4 sceneColor, materialAmbient;
    vec4 materialSpecular, materialDiffuse;


    if (gl_FrontFacing)
        normal = normalize(N);        
    else
        normal = -normalize(N); // for backfaces with same color as the front ones        
      
   if (gl_FrontFacing || !BackFaceColor) // front face
    {
        sceneColor          = gl_FrontLightModelProduct.sceneColor;
        materialAmbient     = gl_FrontMaterial.ambient;
        materialSpecular    = gl_FrontMaterial.specular;
        materialDiffuse     = gl_FrontMaterial.diffuse;
        materialShininess   = gl_FrontMaterial.shininess;
    }


    else   // back Face
    {
        sceneColor          = gl_BackLightModelProduct.sceneColor;
        materialAmbient     = gl_BackMaterial.ambient;
        materialSpecular    = gl_BackMaterial.specular;
        materialDiffuse     = gl_BackMaterial.diffuse;
        materialShininess   = gl_BackMaterial.shininess;
    }


    


    if (FirstPass)
        finalColor = sceneColor;
    else
        finalColor = vec4(0,0,0,1);




    vec4 DiffuseColor [NumberOfLights];
    vec4 SpecularColor[NumberOfLights];
    vec4 AmbientColor [NumberOfLights];


    vec3 eye = vec3(0.0, 0.0, 1.0);


    for (int i=0; i<NumberOfLights; i++)
    {
        if (LightsEnabled[i])
        {
            switch(LightsType[i])
            {
                case 0: 
                    DirectionalLight(i, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
                    break;


                case 1: 
                    SpotLight(i, eye, ecPosition3, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
                    break;              


                case 2:
                    PointLight(i, eye, ecPosition3, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
                    break;
            }


            SpecularColor[i] = vec4((SpecularColor[i] * materialSpecular).xyz, 0); // for Pictures with transparent textures, otherwise the Specular Alpha would make them become opaque.
                
                   
            DiffuseColor[i]  *=  materialDiffuse;
        }
   }
   
        ambient = vec4(0,0,0,0);
        for (int i=0; i < NumberOfLights; i++)
        {
               
        //  The ambient for the spotlight with shadow is added later after evaluating if it's in shadow
            if (LightsEnabled[i] && (LightsType[i] != 1 || !YieldShadow[i]))
                ambient += AmbientColor[i];
           
        }


        finalColor += ambient * materialAmbient;


   
    // lookup the shadowMaps
    vec4 diffuseForShadow;


    bool spotLightWithShadow = false;
    vec4 spotAmbient;


    for (int i=0; i<NumberOfLights; i++)
    {
        if (LightsEnabled[i])
        {
            if (YieldShadow[i])
            {
                diffuseForShadow = DiffuseColor[i];


                if (LightsType[i] == 1) // Spot Light
                {
                    spotLightWithShadow = true;
                    spotAmbient = AmbientColor[i];
                }
            }
            else
                finalColor += DiffuseColor[i];
        }
    }


    vec4 coord;
    
    for (int i = 0; i < NumberOfSplits; i++)
    {                
        if (vertexDepth > SplitDepths[i]) // negative values
        {               
            
            coord = ShadowCoord[0];


    
            break;
        }
    
    }  


    vec3 shadowCoord = vec3(coord / coord.w);                                    


    bool inLight = shadowLookup(shadowCoord, diffuseForShadow, 0.0, 0.0, diffuseForShadow);


    if (inLight && spotLightWithShadow)
        finalColor += spotAmbient * materialAmbient;




    finalColor += diffuseForShadow;


   
        for (int i=0; i < NumberOfLights; i++)
        {
            if (LightsEnabled[i] && YieldShadow[i])
            {
                if (inLight)
                    finalColor += SpecularColor[i];
            }
            else
                finalColor += SpecularColor[i];
        }




       
    finalColor.w = materialDiffuse.w; 
  
    finalColor = clamp (finalColor, 0.0, 1.0);


    gl_FragColor = finalColor;
   
}



Vertex Program:


            
const int NumberOfLights = 8;


   
    uniform bool LightsEnabled[NumberOfLights];


    // ShadowTextureScale
    // (0.5, 0.5) if NumberOfSplits is 1
    // (0.25, 0.5) if it is 2, 
    // (0.25, 0.25) if it is 4.
    uniform vec2 ShadowTextureScale;


    
    const int NumberOfSplits = 1;




    varying vec4  ShadowCoord[NumberOfSplits];    
    uniform mat4  TextureMatrix[NumberOfSplits];
    uniform bool YieldShadow[NumberOfLights];
    //uniform mat4  ModelViewMatrix;
    varying float vertexDepth;
    
    uniform vec4 ShadowOffsetForSplitInX;
    uniform vec4 ShadowOffsetForSplitInY;


    uniform vec2 ShadowOffsetForLight;
    // offset in X: (0, 0.5, 0.5, 0);
    // offset in Y: (0, 0, 0.5, 0.5);






varying vec2 TexCoord;
uniform mat4 blockRefMatrix;




varying vec3 N;
varying vec3 ecPosition3; // in 3 space




void main(void)
{
   vec4 ecPosition = gl_ModelViewMatrix * gl_Vertex;


   gl_ClipVertex = ecPosition; // userclipping




        N = normalize(gl_NormalMatrix * gl_Normal);
        ecPosition3 = (vec3(ecPosition)) / ecPosition.w;






vec4 vertex = blockRefMatrix * gl_Vertex;


   




        vertexDepth = ecPosition.z;


        for(int i=0; i<NumberOfSplits; i++)
        {
            vec4 shadowTexCoord = TextureMatrix[i] * vertex;
            
            // Map the texture coordinates to one of the 4 sub-textures of the big Texture map
            shadowTexCoord.s *= ShadowTextureScale.x;
            shadowTexCoord.t *= ShadowTextureScale.y;
                                                    
            shadowTexCoord.s += ShadowOffsetForLight.x + ShadowOffsetForSplitInX[i];
            shadowTexCoord.t += ShadowOffsetForLight.y + ShadowOffsetForSplitInY[i];
            
            ShadowCoord[i] = shadowTexCoord;
        }


   
   TexCoord  = gl_MultiTexCoord0.st;
   
   gl_Position = ftransform();
}



It seems the problem was this part:


 for (int i=0; i<NumberOfLights; i++)
    {
        if (LightsEnabled[i])
        {
            switch(LightsType[i])
            {
                case 0: 
                    DirectionalLight(i, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
                    break;




                case 1: 
                    SpotLight(i, eye, ecPosition3, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
                    break;              




                case 2:
                    PointLight(i, eye, ecPosition3, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
                    break;
            }
 

Replacing it with this one, it works:


 for (int i=0; i<NumberOfLights; i++)
    {
        if (LightsEnabled[i])
        {
            float spotCutoff = gl_LightSource[i].spotCutoff;


            if (spotCutoff >= 0.0 && spotCutoff <= 90.0)
            {
                SpotLight(i, eye, ecPosition3, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
            }
            else
            {
                if (spotCutoff == 180)
                    PointLight(i, eye, ecPosition3, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
                else
                    DirectionalLight(i, normal, AmbientColor[i], DiffuseColor[i], SpecularColor[i]);
            }