David Sullivan
David Sullivan

Reputation: 508

OpenGL everything in shadow

I am trying to add shadows to my OpenGL scene by generating a shadow map, and using that to add shadows into my scene.

To do that, I am following the learnopengl.com shadowmap tutorial and trying to get shadowmaps working in my existing project.

The idea is that you place a camera where your light source should be. then you render the scene from the perspective of the light-source-camera, and save its depth buffer. Then you render the scene using the actual camera you want to view the scene through, passing along the light-scene-depth-buffer as a texture. Then in the shader, you do some math to figure out where each fragment in camera-space would be in light-space, and if the depth buffer has recorded a depth value lower than the fragment in question, that implies the fragment is occluded from the light by some other object and is thus in shadow.

My understanding is that there are two ways to transform the fragment into the lightspace.

  1. If the fragment shader has the position of the fragment in world-coordinates, then you can just apply the light-camera's transformation to that directly. so lightCoord = light_camera_perspective * light_camera_view * worldCoord

  2. If the fragment shader does not have the fragment in world coordinates, but does have the camera and the light-space-camera, then you undo the camera's transformation and then apply the light-space-camera to that.


mat4x4 LIGHT = light_camera_perspective * light_camera_view;
mat4x4 CAMERA = camera_perspective * camera_view;
lightCoord = LIGHT * inverse(CAMERA) * fragCoord;

In either 1 or 2, I believe there is a little additional normalization that is needed to make these into proper UV coordinates for the shadow map. This is a part I'm a bit hazy on. I've done my best, with reference from the tutorial and some of its comments.

Anyways, the behavior I am hoping for is a diffuse render except everything that is occluded from the light (eg is in shadow) is rendered as black.

I have the depth buffer from the light's POV rendering correctly. It looks like this,

light depth buffer

Then when I render the scene from the Camera's point of view, trying to use the light's depth buffer as a shadow map, everything is black and looks like this.

models rendered with shadows

I have checked using RenderDoc to make sure that all textures are bound correctly including the light's depth buffer as a shadow map. Indeed, if I manually set shadow_value = 1.0; in the fragment shader, it renders as you would expect (just a diffuse render pass) and looks like this,

diffuse render when shadow_value = 1.0

I strongly suspect that the problem is in how I am transforming the fragment's position into light-space, or how I take that and turn it into uv coordinates. In fact, when I set FragColor to be the uv coordinates I calculate, I get the same output as the second image. So something is clearly going wrong there.

I've read a lot of answers debugging artifacts you get from shadow map techniques, as well as explinations of various detail about how this process works and specifically how the change of coordinate system works. I've also spent quite a bit of time in render doc trying to make sure everything is loading correctly, and I've tried many little variations of the shaders I include below.

Below are the shader files I'm using, and then the part of my main loop where I render. I am going to omit call my glError calls to make the code cleaner, but rest assured, I check for errors often and there are none.

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

Upvotes: -2

Views: 71

Answers (0)

Related Questions