Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

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.)

share|improve this question
    
Have you tried inserting glGetError calls in your application, or using ARB_debug_output? More than likely, one or both of those will help you narrow down what's going wrong. –  Nathan Reed Jun 24 '13 at 0:29
    
As mentioned, the error state is not set at any point (my method of verifying this is to place error state checks at various places, then narrow them down by adding more between error state=0 and an error state) and since ARB_debug_output on NVIDIA cards is the equivalent of places a glGetError call after each gl call, it wouldn't help me. stackoverflow.com/questions/6801128/… –  James Jun 24 '13 at 8:03
add comment

1 Answer

up vote 1 down vote accepted

Solved. The problem as it happened was with alpha testing. Which makes a lot of sense:

In the code alpha testing is enabled on initialization and then isn't actually used. Removing it (and using the below mentioned matrix fix) fixes shadow mapping completely. The solution is to disable alpha when shadow mapping and re-enable it when rendering the scene with alpha. So my main loop looks more like this:

while (running) {
    glDisable(GL_ALPHA_TEST);

    //Shadow mapping

    glEnable(GL_ALPHA_TEST);

    //Standard draw
}

Although I'm not certain on this and have yet to find documentation on it I imagine the problem was probably caused by alpha testing on a non-existent colour buffer. While my AMD card coped with that just fine the NVIDIA card would always fail the GL_GREATER alpha test because retrieving alpha from a non-existent colour buffer would yield unpredictable results.

There was also a small issue with matrices; the order of multiplication was the wrong way round so where I'd written:

glm::mat4 mvp = view * projection;

It should've been:

glm::mat4 mvp = projection * view;
share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.