# Orthographic projection for shadow mapping?

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

## Recommended Posts

Hello. I am working on shadow mapping and it works pretty nice with perspective projection. However, the light source should be directional, so I need orthographic projection. From the papers I read, they mostly just say "use ortho projection" without any example code. My question is: how should I set glOrtho parameters so that the projection is equivalent to this perspective projection?
  gluPerspective(45.0f,float(screenX/screenY),1.00f,1000.0f);



##### Share on other sites
Typically with a directional light (like the sun) you'll come up with a new orthographic projection based on where the camera is and which way it's facing. The idea is that you figure out how wide and how tall your projection needs to be so that the entirety of the view frustum is contained in the area being rendered to the shadow map.

Doing this isn't terribly difficult, but it can be a little tricky. The basic algorithm goes like this:

-Figure out the world-space location of all 8 corners of the view frustum (this tutorial can show you how to do it)

-Find the centroid of the frustum (add all points together, divide by 8)

-Start at the centroid, and move back in the opposite direction of the light by an amount equal to the camera's farClip. This is the temporary working position for the light

-Create a view matrix using a LookAt function. The position is the temporary working position derived in the last step, the lookAt point is the centroid.

-Use this view matrix to find the location of the frustum corners with respect to our temporary working position (multiply the positions with the view matrix)

-Loop through the 8 light-space frustum corners we just calculated, and find the Min and Max X, Y, and Z.

-Create an off-center orthographic projection matrix (using glOrtho) based on the min and max values. Left would be min X, Right would be the max X, Bottom would be min Y, Top would be max Y, near would -maxZ, far would -minZ.

-Use these view and projection matrices to render geometry to the shadow buffer.

##### Share on other sites
I'll try, will post later

##### Share on other sites
Actually, there's a catch here. Yes, that algorithm will give you a light-aligned box that fits the camera's view frustum, but it may need to be more than that.

There may be shadow-casters that fall outside the camera's view frustum, yet their shadow can be seen. If you fail to take these into account, they could end up outside the light's frustum, get discarded, don't write into the depth buffer and thus don't cast a shadow.

So you may need to move the near clip plane further back (towards to light) to render all shadow casters. How you determine this depends on what you're doing in your application (i.e., what the shadow casters are).

##### Share on other sites
Now when I read MJP's post, it looks like it's exactly what I found on gpwiki:

// loop through all vertices in terrain and find min and max points with respect to screen space                     float minX = 999999, maxX = -999999;                     float minY = 999999, maxY = -999999;                     double X, Y, Z;                      // get pointer to terrain vertices                     float *vertices = Terrain.vertices()->lock();                                              for(int i = y0-1; i < y1+1; i++)                     {                         if(i < 0) continue;                         for(int j = x0-1; j < x1+1; j++)                         {                              if(j < 0) continue;                              int index = i * Terrain.size() + j;                                                            // get screen coordinates for current vertex                              static GLint viewport[4];                              static GLdouble modelview[16];                              static GLdouble projection[16];                              static GLfloat winX, winY, winZ;                               glGetDoublev( GL_MODELVIEW_MATRIX, modelview );                              glGetDoublev( GL_PROJECTION_MATRIX, projection );                              glGetIntegerv( GL_VIEWPORT, viewport );                               gluProject(vertices[index*3+0], vertices[index*3+1], vertices[index*3+2], modelview, projection, viewport, &X, &Y, &Z);                                                             if(X < minX) minX = X;                              if(X > maxX) maxX = X;                              if(Y < minY) minY = Y;                              if(Y > maxY) maxY = Y;                         }                     }

they loop through the terrain vertices to make sure that whole terrain fits into shadowmap, I think it's the same method, just a little bit simplified (your is for arbitrary camera, I can assume that all shadow casters lie on the terrain). I'll try this first, if it won't work, I'll try your solutions.

##### Share on other sites
Yes, that's a good point Mike and I really should have mentioned it. What I posted is really just a base to start from...getting a good projection may require some tweaking for your scene. It's also very common to use techniques Cascaded Shadow Maps to make better use of shadow resolution, so don't expect fantastic results right off the bat.

BTW if you need any code snippets or anything like that just let me know...unfortunately I don't code that's OpenGL but it should be similar enough for you to get the idea.

##### Share on other sites
Well, code snippets never hurt ^^

##### Share on other sites
Here's the relevant code from what I use:

// Find the centroidVector3 frustumCentroid = new Vector3(0,0,0);for (int i = 0; i < 8; i++)	frustumCentroid += frustumCornersWS;frustumCentroid /= 8;// Position the shadow-caster camera so that it's looking at the centroid,// and backed up in the direction of the sunlightconst float nearClipOffset = 50.0f;float distFromCentroid = farZ + nearClipOffset;Matrix viewMatrix = Matrix.CreateLookAt(frustumCentroid - (light.Direction * distFromCentroid), frustumCentroid, new Vector3(0,1,0));// Determine the position of the frustum corners in light spaceVector3.Transform(frustumCornersWS, ref viewMatrix, frustumCornersLS);// Calculate an orthographic projection by sizing a bounding box // to the frustum coordinates in light spaceVector3 mins = frustumCornersLS[0];Vector3 maxes = frustumCornersLS[0];for (int i = 0; i < 8; i++){	if (frustumCornersLS.X > maxes.X)		maxes.X = frustumCornersLS.X;	else if (frustumCornersLS.X < mins.X)		mins.X = frustumCornersLS.X;	if (frustumCornersLS.Y > maxes.Y)		maxes.Y = frustumCornersLS.Y;	else if (frustumCornersLS.Y < mins.Y)		mins.Y = frustumCornersLS.Y;	if (frustumCornersLS.Z > maxes.Z)		maxes.Z = frustumCornersLS.Z;	else if (frustumCornersLS.Z < mins.Z)		mins.Z = frustumCornersLS.Z;}     // Create an orthographic camera for use as a shadow casterOrthographicCamera newCamera;newCamera = new OrthographicCamera(mins.X, maxes.X, mins.Y, maxes.Y, -maxes.Z - nearClipOffset, -mins.Z);newCamera.SetViewMatrix(ref viewMatrix);

This is C# and XNA btw...however it should all translate to GL functions and whatever math library you're using. My OrthographicCamera constructor takes the same parameters as glOrtho.

##### Share on other sites
i can't get it yet to work but... why is near and far plane in ortho negative? wouldn't that place the planes BEHIND the camera, thus invisible at all?

##### Share on other sites
No, because mins.z and maxes.z should both be negative already (negating them makes them postive). With right-handed coordinates, points that in front of the camera have a negative Z value.

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 13
• 14
• 40
• 63