Pointlight Shadow Cube Mapping

852 views Asked by At

So i managed to create shadow maps for a directional light (extended at infinity). Now i am trying to create point light shadow cube map, so the shadows spread in the direction of the light->fragment. I created a new FBO like so:

        //gen new fbo
        glGenFramebuffers(1, &framebuffer_object);
        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_object);

        //gen color texture
        glGenTextures(1, &texture_color);
        glBindTexture(GL_TEXTURE_2D, texture_color);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexImage2D(GL_TEXTURE_2D, 0,GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);

        //gen cubemap
        glGenTextures(1, &shadow_cubemap);
        glBindTexture(GL_TEXTURE_2D_ARRAY, shadow_cubemap);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
        glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);

        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
        glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);     

        //bind color texture
        unsigned int attachment_index_color_texture = 0;
        glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+attachment_index_color_texture, texture_color,0);

        //add attachments
        std::vector<GLenum> drawbuffers;
        drawbuffers.push_back(GL_COLOR_ATTACHMENT0+attachment_index_color_texture);
        glDrawBuffers(drawbuffers.size(),&drawbuffers[0]);

        //check the fbo state
        if(glCheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE){
            std::cout<<"Error.FBO not integral."<<std::endl;
            std::cin.get();
            std::terminate();
        }

        //Unbind
        glBindFramebuffer(GL_FRAMEBUFFER,0);

This function is called upon reshaping(so you could say at init time) reshape is called once at program start.

Next i create my light_view matrixes , each view for a different cube face, and my projection matrix like so :

    //view matrix for +x
    lightView_matrix[0]=glm::lookAt(light_position,light_position+glm::vec3(1,0,0),glm::vec3(0,1,0));

    //view_matrix for -x
    lightView_matrix[1]=glm::lookAt(light_position,light_position+glm::vec3(-1,0,0),glm::vec3(0,1,0));

    //view_matrix for +z
    lightView_matrix[2]=glm::lookAt(light_position,light_position+glm::vec3(0,0,1),glm::vec3(0,1,0));

    //view_matrix for -z
    lightView_matrix[3]=glm::lookAt(light_position,light_position+glm::vec3(0,0,-1),glm::vec3(0,1,0));

    //view_matrix for +y
    lightView_matrix[4]=glm::lookAt(light_position,light_position+glm::vec3(0,1,0),glm::vec3(0,0,-1));

    //view_matrix for -y
    lightView_matrix[5]=glm::lookAt(light_position,light_position+glm::vec3(0,-1,0),glm::vec3(0,0,1));

    lightProjection_matrix = glm::perspective(90.0f, 1.0f, 0.5f, 140.0f);

Is the projection matrix ok? Should it be perspective when doing a cubemap instead of ortographic like i did for a directional light? I don't know if the values are ok : here is what i have, the width and height of the texture is 1366 x 768 which is my window size, my entire scene is from -70 to 70 on the z axis so 140 total,i chose a fov of pi/4 .

Moving on, to the first pass - hopefully generating the 6 faces of the cubemap:

    cube_fbo.bind();
    {

        for(int i=0;i<6;i++){

            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, cube_fbo.shadow_cubemap, 0);
            //clear screen
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

            //use shader
            glUseProgram(program_shader);

            //send uniform values
            glUniformMatrix4fv(glGetUniformLocation(program_shader,"light_view_matrix"),1,false,glm::value_ptr(lightView_matrix[i]));
            glUniformMatrix4fv(glGetUniformLocation(program_shader,"light_projection_matrix"),1,false,glm::value_ptr(lightProjection_matrix));

            //draw my objects with textures,i also have an alpha texture hence the has_alpha in the shaders (will post below )

        }
    }
    //unbind fbo
    cube_fbo.unbind();

Now the second pass which should draw the shadows based on the fragment position(should i do this in the drawing function or in the shader? )

    {

        //clear screen
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(program_shadow);

        //send uniform values
        glUniformMatrix4fv(glGetUniformLocation(program_shadow,"view_matrix"),1,false,glm::value_ptr(view_matrix));
        glUniformMatrix4fv(glGetUniformLocation(program_shadow,"projection_matrix"),1,false,glm::value_ptr(projection_matrix));


        glUniformMatrix4fv(glGetUniformLocation(program_shadow,"light_view_matrix"),1,false,glm::value_ptr(light_view_matrix));
        glUniformMatrix4fv(glGetUniformLocation(program_shadow,"light_projection_matrix"),1,false,glm::value_ptr(light_projection_matrix));
        glUniformMatrix4fv(glGetUniformLocation(program_shadow,"BiasMatrix"),1,false,glm::value_ptr(BiasMatrix));
        glUniform3f(glGetUniformLocation(program_shadow,"light_position"),light_position.x,light_position.y,light_position.z);

        //sphere at light position for reference------------------------
        glUniformMatrix4fv(glGetUniformLocation(program_shadow,"model_matrix"),1,false,glm::value_ptr(light_model_matrix));
        lab::drawSolidSphere(2,6,6);

        //glEnable(GL_CULL_FACE);
        //glCullFace(GL_BACK);
        //ground----------------------------
        glUniformMatrix4fv(glGetUniformLocation(program_shadow,"model_matrix"),1,false,glm::value_ptr(ground_model_matrix));
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, ground_texture);
        glUniform1i( glGetUniformLocation(program_shadow, "textura1"), 1 );
        glUniform1i( glGetUniformLocation(program_shadow, "has_alpha"), 0);
        //glDisable(GL_CULL_FACE);

        //send cubemap texture(GLuint bound in the first part (shadow_cubemap))
        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_CUBE_MAP, cube_fbo[0].getCubeMapTexture());

        glUniform1i(glGetUniformLocation(program_shadow, "textura_depth"), 2);

        glBindVertexArray(ground_vao);
        glDrawElements(GL_TRIANGLES, ground_num_indices, GL_UNSIGNED_INT, 0);

        //bamboo(s)----------------------------
        glActiveTexture(GL_TEXTURE0+1);
        glBindTexture(GL_TEXTURE_2D, bamboo_texture_color);
        glActiveTexture(GL_TEXTURE0+2);
        glBindTexture(GL_TEXTURE_2D, bamboo_texture_alpha);
        glUniform1i( glGetUniformLocation(program_shadow, "textura1"), 1 );
        glUniform1i( glGetUniformLocation(program_shadow, "textura2"), 2 );

        glUniform1i( glGetUniformLocation(program_shadow, "has_alpha"), 1); 

        //send cubemap texture
        glActiveTexture(GL_TEXTURE3);
        glBindTexture(GL_TEXTURE_2D, cube_fbo[0].getCubeMapTexture());

        glUniform1i(glGetUniformLocation(program_shadow, "textura_depth"), 3);

        glBindVertexArray(bamboo_vao);
        for(int i=0;i<10;i++) for(int j=0;j<10;j++) {
            if(j==5){
            glUniformMatrix4fv(glGetUniformLocation(program_shadow,"model_matrix"),1,false,glm::value_ptr(bamboo_model_matrix[i*10+j]));
            glDrawElements(GL_TRIANGLES, bamboo_num_indices, GL_UNSIGNED_INT, 0);
            }
        }
                }

As you can see in this last part i draw from the camera position and pass to the shader the cubemap generate from the first pass. I have a few misunderstandings and probably some mistakes untill this point.

  1. What do i return from the first pass (from the shader), when i did shadow mapping for a directional light i returned the depth (glFragcoord.z) but now i have to return a vec3 direction ? How do i compute that.Do i need something else to pass to the shader?

  2. Is the second part ok ? Do i just draw the objects and pass the cube map to the shader and let him calculate which fragment is where in relation to the light ? If so , how do i compute this ?

Here is my shader for the first pass(so generate the cubemap from the 6 light views):

#version 330 /*VERTEX SHADER*\

layout(location = 0) in vec3 in_position;       
layout(location = 2) in vec2 in_texcoord;

uniform mat4 model_matrix;
uniform mat4 light_view_matrix,light_projection_matrix;

out vec2 texcoord;
out vec4 v_position;

void main(){

texcoord = in_texcoord;

gl_Position = light_projection_matrix*light_view_matrix*model_matrix*vec4(in_position,1); 


}
#version 330 /*FRAGMENT SHADER*\


layout(location = 0) out vec4 out_color;
//layout(location = 1) out float depth;

uniform sampler2D textura1;
uniform sampler2D textura2;
uniform int has_alpha;

in vec2 texcoord;
in vec4 v_position;

void main(){


vec3 tex1 = texture(textura1, texcoord).xyz;
vec3 tex2 = texture(textura2, texcoord).xyz;

if(has_alpha>0.5) if((tex2.r<0.1) && (tex2.g<0.1) && (tex2.b<0.1)) discard;

out_color = vec4(tex1, 1);
}

And the shader for the second pass (tried to calculate shadow by getting the vec3 from pixel position in world space - light position in world space)

#version 330 core /*VERTEX SHADER*\

layout(location = 0) in vec3 in_position;
layout(location = 1) in vec3 in_normal;     
layout(location = 2) in vec2 in_texcoord;

out vec3 lightPosw;
out vec3 pixelPosw;
out vec2 texcoord;

uniform mat4 model_matrix,view_matrix,projection_matrix;
uniform vec3 light_position;

void main(){

texcoord=in_texcoord;
gl_Position = projection_matrix*view_matrix*model_matrix*vec4(in_position,1);
lightPosw= (model_matrix * vec4(light_position,1)).xyz;
pixelPosw = (model_matrix * vec4(in_position, 1)).xyz;
}

#version 330 core /*FRAGMENT SHADER*\

in vec3 lightPosw;
in vec3 pixelPosw;
in vec2 texcoord;

out vec4 out_color;

uniform samplerCube textura_depth;
uniform sampler2D textura1,textura2;

uniform mat4 view_matrix,model_matrix;

uniform int has_alpha;



void main(){

vec3 lightToPixel=pixelPosw-lightPosw;

vec3 tex1 = texture(textura1, texcoord).xyz;
vec3 tex2 = texture(textura2, texcoord).xyz;

if(has_alpha>0.5) if((tex2.r<0.1) && (tex2.g<0.1) && (tex2.b<0.1)) discard;

float shadowDepth = texture(textura_depth, normalize(lightToPixel));

float shadowFactor=1.0;
if(length(lightToPixel) < shadowDepth)
    shadowFactor=0.5;

out_color = vec4(tex1*shadowFactor,1);

   }

Sorry for the wall of code, i wanted to post all the relevant stuff so i can get better help. My problem is , everything renders semi-black, because i set the output color in the fragment shader to 0.5, if I wouldn't , then it all renders black.

0

There are 0 answers