I am facing a really weird bug.

On an old copy of my engine, i was doing a loop to render all the lights in my scene:

    for(int i = 0; i < numLights; i++){
    finalColor += ApplyLight(lights[i], diffuseColor.rgb, normal, fragPosition, surfaceToCamera,specularColor,shininess);

Which was working just fine (i had 3 lights and they were all rendering).

But now, in a newer version that includes shadows for my lights, im trying to achieve the same thing:

    for(int i = 0; i < numLights; i++){
        color += ApplyLighting(lights[i], frag, normal, tex);

But this does not work!

I will explain:

Doing 1 index at a time works just fine (i = 0; i < 1; i++) will only execute one loop and therefore, will render the single light correctly.

But as soon as there is more than 1 light to be rendered, everything is black (compiler crash i guess).

I've heard about "constant array indexing" restriction but why in the world is it working in my other shader but not on my new one?

Here are both the shaders:


#version 430

#define MAX_LIGHTS 15
uniform int numLights;

uniform struct Light{
    vec4 position;
    vec3 intensities;
    float ambientCoefficient;
    float attenuation;
    float coneAngle;
    vec3 coneDirection;
} lights[MAX_LIGHTS];

uniform float materialShininess;
uniform vec3 materialSpecularColor;

uniform vec3 cameraPosition;
uniform mat4 model;

uniform bool specularMapping;
uniform bool normalMapping;
uniform bool glossMapping;
uniform bool ambientOcclusionMapping;

uniform sampler2D diffuseTexture;
uniform sampler2D specularTexture;
uniform sampler2D normalTexture;
uniform sampler2D glossTexture;
uniform sampler2D aoTexture;

in vec2 fragTexcord;
in vec3 fragNormal;
in vec3 fragVert;

out vec4 color;

vec3 ApplyLight(Light light, vec3 diffuseColor, vec3 normal, vec3 fragPosition, vec3 surfaceToCamera, vec4 specularColor, float shininess){
    vec3 surfaceToLight;
    float attenuation = 1.0;
    float pointAttenuation = 1.0f;

    if(light.position.w == 0.0) {
        //directional light
        surfaceToLight = normalize(light.position.xyz);
        attenuation = 1.0; //no attenuation for directional lights
    } else {
        //point light
        surfaceToLight = normalize(light.position.xyz - fragPosition);
        float distanceToLight = length(light.position.xyz - fragPosition);
        attenuation = 1.0 / (1.0 + light.attenuation * pow(distanceToLight, 2));

        //cone restrictions (affects attenuation)
        float lightToSurfaceAngle = degrees(acos(dot(-surfaceToLight, normalize(light.coneDirection))));
        if(lightToSurfaceAngle > light.coneAngle){      
            if(lightToSurfaceAngle - light.coneAngle < 2.5f){ //Smooth around the spotlight by 2.5 degree
                pointAttenuation = 1 / lightToSurfaceAngle;
                pointAttenuation = clamp(pointAttenuation, 0, 1);
                attenuation = 0.0;
            pointAttenuation = (1 + 15) / lightToSurfaceAngle; //Smooth color from center
            pointAttenuation = clamp(pointAttenuation, 0, 1);

    vec3 ambient = diffuseColor.rgb * light.ambientCoefficient * light.intensities * pointAttenuation;

    float brightness = max(0,dot(normal, surfaceToLight));
    vec3 diffuse = brightness * light.intensities * diffuseColor.rgb;

    float specularCoefficient = 0.0f;
    float cosAngle = dot(surfaceToCamera, reflect(-surfaceToLight, normal));
    if(shininess < 255){
        if(brightness > 0.0)
            specularCoefficient = pow(max(0.0, cosAngle), shininess);

    vec3 specular = specularCoefficient * materialSpecularColor * light.intensities * specularColor.rgb;

    //Attenuation relative to distance
    float distanceToLight = length(vec3(light.position.xyz) - fragPosition);

    return ambient + attenuation * (diffuse + specular);

void main(){
    float shininess = materialShininess;

    vec4 diffuseColor = vec4(1);
    vec4 specularColor = vec4(1);
    vec4 normalColor = vec4(1);

    diffuseColor = texture(diffuseTexture,fragTexcord); //Color on the diffuse texture

        diffuseColor *= texture(aoTexture, fragTexcord) * 1.2;

        specularColor = texture(specularTexture,fragTexcord); //Color on the specular texture

        shininess =  texture(glossTexture, fragTexcord).r;

    mat3 normalMatrix = transpose(inverse(mat3(model)));
    vec3 normal = normalize(normalMatrix * fragNormal);
    vec3 fragPosition = vec3(model * vec4(fragVert,1));

    vec3 surfaceToCamera = normalize(cameraPosition - fragPosition);

        normal = normalize(normalMatrix * fragNormal);

    //Calculate all the lights
    vec3 finalColor = vec3(0);

    for(int i = 0; i < numLights; i++){
        finalColor += ApplyLight(lights[i], diffuseColor.rgb, normal, fragPosition, surfaceToCamera,specularColor,shininess);

    //HDR (high dynamic range)
    finalColor = vec3(1.0) - exp(-finalColor * 0.8);

    //Gamma correction
    vec3 gamma = vec3(1.0/2.2);

    //Final color calculation

    //Gamma calculation : color = vec4(pow(linearColor,gamma),diffuseColor.a);

    color = vec4(finalColor,diffuseColor.a);




#version 430

out vec4 FragColor;

#define MAX_LIGHTS 15
uniform int numLights;

in VS_OUT {
    vec3 FragPos;
    vec3 Normal;
    vec2 TexCoords;
} fs_in;

uniform sampler2D diffuseTexture;

uniform struct Light{
    int lightType;
    vec4 position;
    vec3 intensities;
    float ambientCoefficient;
    float attenuation;
    float coneAngle;
    vec3 coneDirection;
    float far_plane;
    sampler2D shadowMap;
    samplerCube depthMap;
    mat4 lightMatrix;
} lights[MAX_LIGHTS];

uniform vec3 viewPos;

float ShadowCalculation(Light light, vec3 fragPos)
    // Get vector between fragment position and light position
    vec3 fragToLight = fragPos - light.position.xyz;

    // Use the light to fragment vector to sample from the depth map    
    float closestDepth = texture(light.depthMap, fragToLight).r;

    // It is currently in linear range between [0,1]. Re-transform back to original value
    closestDepth *= light.far_plane;

    // Now get current linear depth as the length between the fragment and light position
    float currentDepth = length(fragToLight);

    // Now test for shadows
    float bias = 0.15; 
    float shadow = currentDepth -  bias > closestDepth ? 1.0 : 0.0;

    return shadow;

vec3 ApplyLighting(Light light, vec3 FragPosition, vec3 norm, vec2 texCoords){
    vec3 color = texture(diffuseTexture, texCoords).rgb;
    vec3 normal = normalize(norm);
    vec3 lightColor = vec3(1.0);

    // Ambient
    vec3 ambient = 0.1 * color;

    // Diffuse
    vec3 lightDir = normalize(vec3(light.position) - FragPosition);
    float diff = max(dot(lightDir, normal), 0.0);
    vec3 diffuse = diff * lightColor;

    // Specular
    vec3 viewDir = normalize(viewPos - FragPosition);
    vec3 reflectDir = reflect(-lightDir, normal);
    float spec = 0.0;
    vec3 halfwayDir = normalize(lightDir + viewDir);  
    spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0);
    vec3 specular = spec * lightColor;    

    // Calculate shadow
    float shadow = ShadowCalculation(light, FragPosition);              
    //shadow = 0;

    vec3 lighting = (ambient + (1.0 - shadow ) * (diffuse + specular)) * color; 

    return lighting;

void main()
    vec3 color = vec3(0);

    vec3 frag = fs_in.FragPos;
    vec3 normal = fs_in.Normal;
    vec2 tex = fs_in.TexCoords;

    for(int i = 0; i < numLights; i++){
        color += ApplyLighting(lights[i], frag, normal, tex);

    //color = ApplyLighting(lights[0], fs_in.FragPos, fs_in.Normal, fs_in.TexCoords);
    //color += ApplyLighting(lights[1], fs_in.FragPos, fs_in.Normal, fs_in.TexCoords);

    //FragColor = vec4(numLights*0,numLights*255,0,1);

    FragColor = vec4(color, 1.0f);


As i said, the old one works perfectly fine (dont know why!).

I also noticed that if i set my MAX_LIGHTS define (on the new shader) to exactly the amount of light being used (2 in this case), it works!

But as soon as i set the size of define to (numLights+1), it doesnt work!

Any idea?

  • \$\begingroup\$ Not sure if it's related, but NUM_LIGHTS is not used in the new shader and count is not defined. \$\endgroup\$ Sep 16, 2016 at 5:08
  • \$\begingroup\$ I edited the post, that was stuff i was trying out at the time and forgot to remove it before posting. \$\endgroup\$
    – Dominic
    Sep 16, 2016 at 15:09

2 Answers 2


Make sure that you correctly set the "numLights" uniform on the CPU side.

To get further help, you should post your CPU code (glUniforms, glUseProgram, glDraw...).

  • \$\begingroup\$ I dont have the code with me right now, but i am setting it as i would do with any other uniform. Using glUniform1i(location, value). It it also correctly set because i tried to debug it just to be sure by setting the "out vec4 color" from the fragment shader by vec4(numLights * 255,0,0,1) and everything in the scene was red. So im pretty sure that the uniform is set correctly \$\endgroup\$
    – Dominic
    Sep 16, 2016 at 23:16

The problem lies within the shader, a lot of implementations don't allow for dynamic looping in GLSL, therefore numLights, when used in "i < numLights" is being evaluated as zero, an easy way to fix it would be to use a constant number, another could be to use SSBOs or something like that, not sure if that would work either.


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .