Basic shadow mapping fails on NVIDIA card?

Posted by James on Game Development See other posts from Game Development or by James
Published on 2013-06-23T12:10:01Z Indexed on 2013/06/27 16:29 UTC
Read the original article Hit count: 452

Filed under:
|
|
|
|

Recently I switched from an AMD Radeon HD 6870 card to an (MSI) NVIDIA GTX 670 for performance reasons.

I found however that my implementation of shadow mapping in all my applications failed. In a very simple shadow POC project the problem appears to be that the scene being drawn never results in a draw to the depth map and as a result the entire depth map is just infinity, 1.0 (Reading directly from the depth component after draw (glReadPixels) shows every pixel is infinity (1.0), replacing the depth comparison in the shader with a comparison of the depth from the shadow map with 1.0 shadows the entire scene, and writing random values to the depth map and then not calling glClear(GL_DEPTH_BUFFER_BIT) results in a random noisy pattern on the scene elements - from which we can infer that the uploading of the depth texture and comparison within the shader are functioning perfectly.)

Since the problem appears almost certainly to be in the depth render, this is the code for that:

const int s_res = 1024;

GLuint shadowMap_tex;

GLuint shadowMap_prog;
GLint sm_attr_coord3d;
GLint sm_uniform_mvp;

GLuint fbo_handle;
GLuint renderBuffer;

bool isMappingShad = false;

//The scene consists of a plane with box above it
GLfloat scene[] = {
    -10.0, 0.0, -10.0, 0.5, 0.0,
     10.0, 0.0, -10.0, 1.0, 0.0,
     10.0, 0.0,  10.0, 1.0, 0.5,

    -10.0, 0.0, -10.0, 0.5, 0.0,
    -10.0, 0.0,  10.0, 0.5, 0.5,
     10.0, 0.0,  10.0, 1.0, 0.5,
    ...
};

//Initialize the stuff used by the shadow map generator
int initShadowMap()
{
    //Initialize the shadowMap shader program
    if (create_program("shadow.v.glsl", "shadow.f.glsl", shadowMap_prog) != 1)
        return -1;

    const char* attribute_name = "coord3d";
    sm_attr_coord3d = glGetAttribLocation(shadowMap_prog, attribute_name);
    if (sm_attr_coord3d == -1) {
        fprintf(stderr, "Could not bind attribute %s\n", attribute_name);
        return 0;
    }

    const char* uniform_name = "mvp";
    sm_uniform_mvp = glGetUniformLocation(shadowMap_prog, uniform_name);
    if (sm_uniform_mvp == -1) {
        fprintf(stderr, "Could not bind uniform %s\n", uniform_name);
        return 0;
    }

    //Create a framebuffer
    glGenFramebuffers(1, &fbo_handle);
    glBindFramebuffer(GL_FRAMEBUFFER, fbo_handle);

    //Create render buffer
    glGenRenderbuffers(1, &renderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);

    //Setup the shadow texture
    glGenTextures(1, &shadowMap_tex);
    glBindTexture(GL_TEXTURE_2D, shadowMap_tex);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, s_res, s_res, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    return 0;
}

//Delete stuff
void dnitShadowMap()
{
    //Delete everything
    glDeleteFramebuffers(1, &fbo_handle);
    glDeleteRenderbuffers(1, &renderBuffer);
    glDeleteTextures(1, &shadowMap_tex);

    glDeleteProgram(shadowMap_prog);
}

int loadSMap()
{
    //Bind MVP stuff
    glm::mat4 view = glm::lookAt(glm::vec3(10.0, 10.0, 5.0),
                                 glm::vec3(0.0, 0.0, 0.0),
                                 glm::vec3(0.0, 1.0, 0.0));

    glm::mat4 projection = glm::ortho<float>(-10,10,-8,8,-10,40);

    glm::mat4 mvp = projection * view;

    glm::mat4 biasMatrix(
        0.5, 0.0, 0.0, 0.0,
        0.0, 0.5, 0.0, 0.0,
        0.0, 0.0, 0.5, 0.0,
        0.5, 0.5, 0.5, 1.0
    );

    glm::mat4 lsMVP = biasMatrix * mvp;

    //Upload light source matrix to the main shader programs
    glUniformMatrix4fv(uniform_ls_mvp, 1, GL_FALSE, glm::value_ptr(lsMVP));

    glUseProgram(shadowMap_prog);
    glUniformMatrix4fv(sm_uniform_mvp, 1, GL_FALSE, glm::value_ptr(mvp));


    //Draw to the framebuffer (with depth buffer only draw)
    glBindFramebuffer(GL_FRAMEBUFFER, fbo_handle);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
    glBindTexture(GL_TEXTURE_2D, shadowMap_tex);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, shadowMap_tex, 0);

    glDrawBuffer(GL_NONE);
    glReadBuffer(GL_NONE);

    GLenum result = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (GL_FRAMEBUFFER_COMPLETE != result) {
        printf("ERROR: Framebuffer is not complete.\n");
        return -1;
    }

    //Draw shadow scene
    printf("Creating shadow buffers..\n");

    int ticks = SDL_GetTicks();

    glClear(GL_DEPTH_BUFFER_BIT); //Wipe the depth buffer
    glViewport(0, 0, s_res, s_res);

    isMappingShad = true;

    //DRAW
    glEnableVertexAttribArray(sm_attr_coord3d);
    glVertexAttribPointer(sm_attr_coord3d, 3, GL_FLOAT, GL_FALSE, 5*4, scene);

    glDrawArrays(GL_TRIANGLES, 0, 14*3);

    glDisableVertexAttribArray(sm_attr_coord3d);

    isMappingShad = false;

    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    printf("Render Sbuf in %dms (GLerr: %d)\n", SDL_GetTicks() - ticks, glGetError());

    return 0;
}

This is the full code for the POC shadow mapping project (C++) (Requires SDL 1.2, SDL-image 1.2, GLEW (>1.5) and GLM development headers.)

initShadowMap is called, followed by loadSMap, the scene is drawn from the camera POV and then dnitShadowMap is called.

I followed this tutorial originally (Along with another more comprehensive tutorial which has disappeared as this guy re-configured his site but used to be here (404).)

I've ensured that the scene is visible (as can be seen within the full project) to the light source (which uses an orthogonal projection matrix.) Shader utilities function fine in non-shadow-mapped projects. I should also note that at no point is the GL error state set.

What am I doing wrong here and why did this not cause problems on my AMD card?

(System: Ubuntu 12.04, Linux 3.2.0-49-generic, 64 bit, with the nvidia-experimental-310 driver package. All other games are functioning fine so it's most likely not a card/driver issue.)

© Game Development or respective owner

Related posts about c++

Related posts about opengl