Multi lights shadow mapping does not work correctly using GLSL
Asked Answered
C

1

10

I have implemented the basic shadow mapping algorithm but it works correctly with only one light.

I want to render a scene with two following point lights :

  • Light_1 - position : vec3(-8.0f, 5.0f, 8.0f), direction : vec3(1.3f, -1.0f, -1.0f)
  • Light_2 - position : vec3(8.0f, 5.0f, 8.0f), direction : vec3(1.3f, -1.0f, -1.0f)

If I render separately the two lights I have the following results:

Rendering with Light_1 :

enter image description here

Rendering with Light_2 :

enter image description here

But the two light together it looks like this :

enter image description here

As you can see the first shadow seems to be rendered correctly, but it is below the shadow of the light_2 which is not correct. To sum up the situation I have the texture of my box which is bound to the texture unit 0. The shadow depth texture is bound from the texture unit 1 and if there are more than one depth texture (so at least two ligths, like in this example), there are bound to the texture unit 1 + 1 (GL_TEXTURE1 + 1). Here's the code that represent what I said :

for (int idy = 0; idy < this->m_pScene->getLightList().size(); idy++)

[...]

Light *light = this->m_pScene->getLightList()[idy];
FrameBuffer *frameBuffer = light->getFrameBuffer();

glActiveTexture(GL_TEXTURE1 + idy);
glBindTexture(GL_TEXTURE_2D, frameBuffer->getTexture()->getTextureId()); //To unbind

shaderProgram->setUniform(std::string("ShadowMatrix[").append(Convertor::toString<int>       (idy)).append("]").c_str(), this->m_pScene->getLightList()[idy]->getBiasViewPerspectiveMatrix() * modelMatrix);
                    shaderProgram->setUniform(std::string("ShadowMap[").append(Convertor::toString<int>(idy)).append("]").c_str(), (int)idy + 1);

It corresponds in our case to :

shaderProgram->setUniform("ShadowMatrix[0]", <shadow_matrix_light_1>);
shaderProgram->setUniform("ShadowMap[0]", 1); (GL_TEXTURE1)
shaderProgram->setUniform("ShadowMatrix[1]", <shadow_matrix_light_2>);
shaderProgram->setUniform("ShadowMap[1]", 2); (GL_TEXTURE2)

The vertex shader is the following (available for just 2 lights):

#version 400

#define MAX_SHADOW_MATRIX 10
#define MAX_SHADOW_COORDS 10

layout (location = 0) in vec4 VertexPosition;
layout (location = 1) in vec3 VertexNormal;
layout (location = 2) in vec2 VertexTexture;

uniform mat3 NormalMatrix;
uniform mat4 ModelViewMatrix;
uniform mat4 ShadowMatrix[MAX_SHADOW_MATRIX];
uniform mat4 MVP;

uniform int lightCount;

out vec3 Position;
out vec3 Normal;
out vec2 TexCoords;
out vec4 ShadowCoords[MAX_SHADOW_COORDS];

void main(void)
{
    TexCoords = VertexTexture;
    Normal = normalize(NormalMatrix * VertexNormal);
    Position = vec3(ModelViewMatrix * VertexPosition);
    for (int idx = 0; idx < lightCount; idx++)
        ShadowCoords[idx] = ShadowMatrix[idx] * VertexPosition;
    gl_Position = MVP * VertexPosition;
}

And a piece of code of the fragment shader :

[...]

vec3 evalBasicFragmentShadow(vec3 LightIntensity, int idx)
{
    vec3 Ambient = LightInfos[idx].La * MaterialInfos.Ka;

    if (ShadowCoords[idx].w > 0.0f)
    {
        vec4 tmp_shadow_coords = ShadowCoords[idx];

        tmp_shadow_coords.z -= SHADOW_OFFSET;

        float shadow = textureProj(ShadowMap[idx], tmp_shadow_coords);

        LightIntensity = LightIntensity * shadow + Ambient;
    }
    else
    {
        LightIntensity = LightIntensity + MaterialInfos.Ka;
    }
    return (LightIntensity);
}

vec3 getLightIntensity(vec3 TexColor)
{
    vec3 LightIntensity = vec3(0.0f);

    for (int idx = 0; idx < lightCount; idx++)
    {
        vec3 tnorm = (gl_FrontFacing ? -normalize(Normal) : normalize(Normal));
        vec3 lightDir = vec3(LightInfos[idx].Position) - Position;
        vec3 lightDirNorm = normalize(lightDir);
        float lightAtt = getLightAttenuation(lightDir, LightInfos[idx]);

        LightIntensity += Point_ADS_Shading(lightAtt, -tnorm, lightDirNorm, TexColor, idx);
        LightIntensity = evalBasicFragmentShadow(LightIntensity, idx);
    }
    return (LightIntensity);
}

[...]

It's look like a texture unit problem because separatly the two shadows have been rendered perfectly and I use glActiveTexture correctly (I think so). Plus, I noticed that if I change the loading order of the lights, the bad shadow is caused by 'the other light' (it's the contrary). So it seems to comes from the texture unit 2, but I don't understand why. Does anyone can help me, please ? Thanks a lot in advance for your help.

Crissycrist answered 23/12, 2013 at 16:17 Comment(3)
I wish I had time to update my OpenGL knowledge to OpenGL 4, this looks so beautiful!Iverson
You do not have to keep writing: vec4(VertexPosition, 1.0f), just declare VertexPosition as vec4 to begin with. GLSL automatically fills in 1.0 for W if your glVertexAttribPointer (...) call only gives enough data for XYZ. Also this whole complicated thing you are doing where you convert idy to a character and build a string (e.g. "ShadowCoords[" idy "]") from it is unnecessary - you can get the uniform location for ShadowCoords and then add idy to that value because each element in a uniform array is guaranteed to be allocated a sequential location.Precedency
Thank you. I updated my vertex shader code like above according to your advices (I agree with you, it's so much better). However I found your second advice very interesting and applied it for ShadowCoords. But is it possible to apply it with a structure like 'LightInfos['idy'].position' ? Thanks in advance.Crissycrist
C
14

I solved my problem. Actually, I just filled the first depth texture (for the first loaded light). So for the second light, the shadow map was not filled and this is what explains the black area on the third image above.

Here's the final result :

enter image description here

enter image description here

I hope this post will be usefull for someone. Thanks for your attention.

Crissycrist answered 24/12, 2013 at 13:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.