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();
}