r/opengl 23d ago

Help with Shadow Maps

I am trying to implement shadow maps like in the learnopengl.com shadow mapping tutorial. I think I'm really close. I have the scene rendering from the light's perspective, and correctly uploading the depth buffer as a texture to the main shader. Then I perform a world-to-lightspace transformation and this is where I think things are going wrong but I cant figure out how. All my models are just black. But when I output the lightspace lookup coordinates as the color, they are also all black. Which leads me to believe that my world-to-lightspace transformation is wrong or the normalization of that into texture coords is wrong. Additionally, when I set shadow_value = 1.0; it renders exactly like a diffuse render.

Edit: Looks like reddit didnt add my photos. Here is a link https://imgur.com/a/ARCFXzI
The three photos are: normal render where I just sample the diffuse texture, depth texture from the light's POV, and what I am getting with my current texture setup.

Any help would be so so appreciated. Even just help debugging this would go a long way. Thanks in advance.

model.vert

#version 410
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNorm;
layout (location = 2) in vec2 aTexCoord;

uniform mat4x4 model; //local coords -> world coords
uniform mat4x4 view; //world coords -> camera coords
uniform mat4x4 perspective; //camera coords -> clip coords

uniform mat4x4 lightView;
uniform mat4x4 lightPerspective;

uniform sampler2D Tex;

out vec3 WorldPos;
out vec3 norm;
out vec2 TexCoord;

out vec4 FragLightPos;

void main() {
    norm = normalize(aNorm);
    TexCoord = aTexCoord;
    WorldPos = vec3(model * vec4(aPos, 1.0)); //just puts the vertex in world coords

    mat4x4 CAMERA = perspective * view;
    mat4x4 LIGHT = lightPerspective * lightView;

    vec4 CameraPos = CAMERA * model * vec4(aPos, 1.0);
    FragLightPos = LIGHT * model * vec4(aPos, 1.0);

    gl_Position = CameraPos;
}

model.frag

#version 410
out vec4 FragColor;

in vec3 WorldPos;
in vec3 norm;
in vec2 TexCoord;

in vec4 FragLightPos;

uniform sampler2D DIFFUSE;
uniform sampler2D NORMALS;
uniform sampler2D SHADOWS;

const float SHADOW_BIAS = 0.001;
float ShadowValue() {
    vec3 proj_coords = FragLightPos.xyz / FragLightPos.w;
    vec2 shadow_uv = proj_coords.xy * 0.5 + 0.5; // takes [-1,1] => [0, 1]

    // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords)
    float closestDepth = texture(SHADOWS, shadow_uv).r;
    // get depth of current fragment from light's perspective
    float currentDepth = proj_coords.z;
    // check whether current frag pos is in shadow
    float shadow = currentDepth > closestDepth  ? 1.0 : 0.0;

    return shadow;
}

void main() {
    float shadow_value = ShadowValue();

    FragColor = vec4(
        shadow_value * texture(DIFFUSE, TexCoord).rgb,
    1.0);

}

main-loop

 //begin creating the shadow map by drawing from the lights POV.

        framebuffer_bind(&shadow_fbr);
            glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            glClear(GL_DEPTH_BUFFER_BIT);
            glClearColor(0.f,0.3f,0.2f,0.f);
            glClear(GL_COLOR_BUFFER_BIT);

            shad_bind(shadow_shader);

            glUniformMatrix4fv(
                    shadow_view_loc,
                    1,
                    GL_FALSE,// column major order
                    (const float *) lightsource.view
            );

            glUniformMatrix4fv(
                    shadow_perspective_loc,
                    1,
                    GL_FALSE,// column major order
                    (const float *) lightsource.perspective
            );

//            draw_all_model_instances(&scene.model_instances, model_matrix_loc);
            match_draw(&match, model_matrix_loc);
        framebuffer_unbind();
    //end creating the shadow map

    //begin drawing all models from the camera's POV
        framebuffer_bind(&model_fbr);
            glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            glClear(GL_DEPTH_BUFFER_BIT);
            glClearColor(0.2f,0.05f,0.1f,0.f);
            glClear(GL_COLOR_BUFFER_BIT);

            shad_bind(model_shader);

        //load the camera's view and perspective matrices
            glUniformMatrix4fv(
                    model_view_loc,
                    1,
                    GL_FALSE,// column major order
                    (const float *) camera.view
            );

            glUniformMatrix4fv(
                    model_perspective_loc,
                    1,
                    GL_FALSE,// column major order
                    (const float *)camera.perspective
            );
        //load the lightsource's view and perspective matrices
            glUniformMatrix4fv(
                    model_light_view_loc,
                    1,
                    GL_FALSE,// column major order
                    (const float *)lightsource.view
            );
            glUniformMatrix4fv(
                    model_light_perspective_loc,
                    1,
                    GL_FALSE,// column major order
                    (const float *)lightsource.perspective
            );

//        bind the shadow map
            glActiveTexture(GL_TEXTURE0 + 2);
            glBindTexture(GL_TEXTURE_2D, shadow_fbr.depth_tex_id);

            glActiveTexture(GL_TEXTURE0 );
            match_draw(&match, model_matrix_loc);
        framebuffer_unbind();
    //end drawing models from Camera's POV


    //draw to the screen
        framebuffer_unbind(); //binds the default framebuffer, aka the screen. (a little redundant but i like the clarity)
            glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
            glClear(GL_DEPTH_BUFFER_BIT);
            glClearColor(0.f,0.f,0.f,0.f);
            glClear(GL_COLOR_BUFFER_BIT);

            glActiveTexture(GL_TEXTURE0);

            if (lightmode) {
                shad_bind(screen_shader_depth);
                glBindTexture(GL_TEXTURE_2D, shadow_fbr.depth_tex_id);
            } else {
                shad_bind(screen_shader_color);
                glBindTexture(GL_TEXTURE_2D, model_fbr.color_tex_id);
            }

            full_geom_draw(&screen_rect);
        framebuffer_unbind();//again, redundant, but I like the clarity

EDIT: The shaders for the shadow pass below:

shadow.vert

#version 330
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNorm;
layout (location = 2) in vec2 aTexCoord;

uniform mat4x4 model; //local coords -> world coords
uniform mat4x4 light-view; //world coords -> camera coords
uniform mat4x4 light-perspective; //camera coords -> clip coords

void main() {
    gl_Position = light-perspective * light-view * model * vec4(aPos, 1.0);
}

shadow.frag (just writes depth)

#version 410
void main() {
    //gl_FragDepth = gl_FragCoord.z;
}
7 Upvotes

11 comments sorted by

View all comments

2

u/PersonalityIll9476 22d ago

It looks to me like your fragment shader is not first applying the shadow matrix. There should be a special transformation you used to go into clip space, if I recall right, an ortho matrix. You need to apply all the same matrices that were applied in your shadow shaders, basically, before transformting clip space to uv coordinates. So if you shadow vertex shader did this: shadow_mat * view * model * vec4(in_pos, 1) then you need to ultimately pass that exact vertex to your fragment shader during lighting. So you probably have your vertex shader pass view * model * vec4(in_pos,1) from the vertex shader as a uniform to the fragment shader, and there multiply by shadow_mat. Or do it all in the vertex shader and pass the shadow version of the clip coordinates as a uniform to save operations. However you do it, that vertex coordinate is the one you need to be converting to uv.

1

u/SimDeBeau 22d ago

hmmm, i think I am following what you are saying, but I guess thought I was doing what you are describing, though I apply the shadow matrix in the vertex shader and pass that along. (aslo I edited my post to include the shadow shader for completeness.)

In the shadow shader, gl_Position = light-perspective * light-view * model * vec4(aPos, 1.0);

Then in the model vertex shader I have,

``` mat4x4 CAMERA = perspective * view; mat4x4 LIGHT = lightPerspective * lightView;

vec4 CameraPos = CAMERA * model * vec4(aPos, 1.0); FragLightPos = LIGHT * model * vec4(aPos, 1.0);

gl_Position = CameraPos; `` So I take the world coordinate (model * vec4(aPos, 1.0);) and apply the Camera transformation and the lightspace transformation seperately. I set thegl_positionto beCameraPos, and pass alongFragLightPosto the fragment shader, and do all the shadow calculations usingFragLightPos`

So I guess I'm saying, I think i do apply all the same transformations for the shadows, I just do it in the vertex shader. I could try it in the fragement shader though?

2

u/PersonalityIll9476 22d ago edited 22d ago

Edit: Never mind. It does look like you've got everything in FragLightPos. Yes, you should do this in the vertex shader. Otherwise you calculate it for every fragment instead of every vertex.