Sign in to follow this  

Shadow maps do not take perspective into account?

This topic is 1117 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello everybody,


I'm messing about with my 3D engine and decided that I want to add volumetric lighting to it. The technique I want to use is decribed here (, it looks simple and is easy to understand. However, when I started to delve into shadow maps I came accross something that I cannot figure out.


The main question I want to ask is:

When rendering a flat surface that is orthogonal from a pointlight's direction should I expect all the depth values in the depth buffer to be the same?


Here is an image to describe my problem and expected v.s. received outcome (sorry to everybody I offend with my horrible drawing skills).




To test the depth buffer I create a FrameBuffer attachment as detailed below. GL_TEXTURE_COMPARE_MODE is set to GL_NONE because I want to write the depth values to a texture and use them later.

// Initialise the texture to be used for shadow mapping.
glGenTextures(1, &texture_id_);
glBindTexture(GL_TEXTURE_2D, texture_id_);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32, depth_texture_size_, depth_texture_size_, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
// Set up depth comparison mode.
// Set up wrapping mode.

	// Set the texture id for the depth texture.
	glActiveTexture(GL_TEXTURE0 + light_->getDepthTextureId());
	glBindTexture(GL_TEXTURE_2D, texture_id_);

// Create FBO to render depth into.
glGenFramebuffers(1, &fbo_id_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
// Attach the depth texture to it.
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, texture_id_, 0);
// Disable colour rendering.

The surface is a plane with a point in (0, 0, 0). The light is 5.2 units above this plane and faces downwards (see above image). The render function is defined as such:

void ShadowRenderer::render(const FrustumCaster& cam)
	// Gather all the leaf nodes.
	unsigned int pre_rendered_objects_ = 0;

	glBindFramebuffer(GL_FRAMEBUFFER, fbo_id_);
 	glViewport(0, 0, depth_texture_size_, depth_texture_size_);

	glm::vec3 camera_location = cam.getLocation();
 	glm::mat4 view_matrix = cam.getViewMatrix();
	glm::mat4 perspective_matrix = cam.getPerspectiveMatrix();

	ShadowShader& shader = ShadowShader::getShader();
	frustum_->setFrustum(perspective_matrix * view_matrix);
	/ / New rendering method.
	Region* region = scene_manager_->getRoot().findRegion(cam.getLocation());
	if (region != NULL)
		std::stringstream ss;
		std::vector<const Portal*> processed_portals;
 		region->preRender(*frustum_, cam.getLocation(), *this, false, pre_rendered_objects_, 0, processed_portals, ss);
		for (std::vector<SceneNode*>::const_iterator ci = scene_manager_->getPlayers().begin(); ci != scene_manager_->getPlayers().end(); ++ci)
 			(*ci)->preRender(*frustum_, cam.getLocation(), *this, false, pre_rendered_objects_);
 	// If we cannot find a region we fall back on the true and tested... Although this
 	// is more a debuf feature and should be removed in future versions of this rendering
 	// engine.
 		SceneNode& root = scene_manager_->getRoot();
 		root.preRender(*frustum_, cam.getLocation(), *this, false, pre_rendered_objects_);

 	for (std::vector<const RenderableSceneLeaf*>::const_iterator ci = active_entities_.begin(); ci != active_entities_.end(); ++ci)
 		const RenderableSceneLeaf* leaf = *ci;

		if (leaf->getShadowType() == ShadowRenderer::NO_SHADOW || !leaf->isInFrustum(*frustum_))

		if (leaf->isDoubleSided())

		leaf->draw(view_matrix, perspective_matrix, active_lights_, &shader);
	// Unbind.
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glBindTexture(GL_TEXTURE_2D, 0);

	// Reset viewport.
	int orgWidth, orgHeight;
	glfwGetWindowSize(&orgWidth, &orgHeight);
	glViewport(0, 0, orgWidth, orgHeight);

The shader used for rendering the depth texture is very simple, as one would expect:

#version 420 core

uniform mat4 modelviewprojection_matrix;

layout (location = 0) in vec4 a_Vertex;

void main(void)
	gl_Position = modelviewprojection_matrix * a_Vertex;

#version 420 core

layout (location = 0) out vec4 colour;

void main(void)
	colour = vec4(0.0, 0.0, 0.0, 1.0);

modelviewprojection_matrix is calculated as: projection_matrix * view_matrix * model_matrix, where:

  • projection_matrix = glm::perspective(light_angle_ * 2, 1.0f, 0.1f, 60.0f).
  • view_matrix =
  • glm::vec3 camera_location(-location_);
    glm::mat4 view_matrix = glm::rotate(glm::mat4(1.0f), -pitch_, glm::vec3(1.0f, 0.0f, 0.0f));
    view_matrix = glm::rotate(view_matrix, -yaw_, glm::vec3(0.0f, 1.0f, 0.0f));
    view_matrix = glm::rotate(view_matrix, -roll_, glm::vec3(0.0f, 0.0f, 1.0f));
    view_matrix = glm::translate(view_matrix, camera_location);

    , where location = (0, 5.2, 0), pitch = -90, yaw = 0, roll = 0

  • model_matrix = I

Now, using a debugger I checked the values of the depth texture created in this scenario. To my surprise the texture contains the same depth value in every pixel (something like 0.9618)! I would expect this if the projection matrix was orthographical, but not when using a perspective matrix.


To highlight the problem I have created a shape that starts at the light's position and usesd the depth values to create a light volume. As you can see from the image belowthe length in the centre is perfect, but the length of the edge are too short. I attribute this to the depth map containing the wrong values, but I could be wrong.




Can somebody explain why this result is either correct or if I should expect different values and perhaps give me a pointer where things might have gone wrong.


Many thanks for your help!


Edited by Morloth

Share this post

Link to post
Share on other sites

The light calculation is independently of shadows, the shadow is just a (soft) mask for the light calculation. When it come to volumentric light, you will need to trace rays through the volume, the shadow map helps to define irregular volumes (eg when a box is blocking the light), a shadow map is in fact just a special type of volume texture. Doing the ray (cone) tracing is the more complexe and expensive part.

Share this post

Link to post
Share on other sites

This topic is 1117 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this