1
\$\begingroup\$

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:

OLD

#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);
            }else{
                attenuation = 0.0;
            }
        }else{
            pointAttenuation = (1 + 15) / lightToSurfaceAngle; //Smooth color from center
            pointAttenuation = clamp(pointAttenuation, 0, 1);
        }
    }

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

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

    //specular
    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

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

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

    if(glossMapping)
        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);

    if(normalMapping){
        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);

    if(color.a<0.1)
    {
        discard;
    }

}

NEW

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

    //else
    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?

\$\endgroup\$
2
  • \$\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

1
\$\begingroup\$

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...).

\$\endgroup\$
1
  • \$\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
0
\$\begingroup\$

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.

\$\endgroup\$

You must log in to answer this question.

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