# Basic shadow mapping fails on NVIDIA card?

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;

GLint sm_attr_coord3d;
GLint sm_uniform_mvp;

GLuint fbo_handle;
GLuint renderBuffer;

//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
{
return -1;

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

const char* uniform_name = "mvp";
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);

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
{
//Delete everything
glDeleteFramebuffers(1, &fbo_handle);
glDeleteRenderbuffers(1, &renderBuffer);

}

{
//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;

glUniformMatrix4fv(uniform_ls_mvp, 1, GL_FALSE, glm::value_ptr(lsMVP));

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

glDrawBuffer(GL_NONE);

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

int ticks = SDL_GetTicks();

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

//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);

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

• 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

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

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;