Shadow Mapping a Directional Light

Started by
6 comments, last by L. Spiro 14 years, 10 months ago
There seems to be plenty of articles about the shadow-mapping technique, and how to set OpenGL up to do it, but I find it exceptionally hard to find any material on how to create the light-source matrices. Here is an example of what I mean, from an online tutorial:
Pseudo-code in Pass 1:
         PushState(ViewPort);
         PushState(ViewMatrix);
         PushState(ProjectionMatrix);
         SetViewport(0, 0, shadowMapWidth, shadowMapHeight);
         SetTransformMatrix(VIEW_MATRIX, shadowLightViewMatrix);
         SetTransformMatrix(PROJECTION_MATRIX, shadowLightProjectionMatrix);
         glShadeModel(GL_FLAT);
         glColorMask(0, 0, 0, 0);
         DrawScene();
         PopState(RS_ProjectionMatrix);
         PopState(RS_ViewMatrix);
         PopState(RS_ViewPort);
The only thing holding me back right now is the creation of the shadowLightViewMatrix and shadowLightProjectionMatrix matrices, especially for directional lights. If I have a spotlight, it should be enough to match the cone of the light in my perspective matrix. I guess. But a directional light has no position. I must somehow position it to encapsulate the range of my main-camera frustum. I have never seen a tutorial explaining how to do this. Additionally, with either method, I need to select near/far planes that only barely encapsulate the geometry visible to them. And it needs to be orthogonal. How wide/tall do I make it? Can anyone explain how to derive these matrices? Thank you, L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Advertisement
The projection matrix for a directional light is just an orthographic projection - there isn't any need for perspective. To create it, you need to determine what the frustum size must be to fit your relevant geometry in it.

The view matrix can just be chosen to be from a position that allows your chosen frustum to encompass all of the needed geometry. Creation of the matrices is usually done by the D3DX library for me, but any standard graphics text should show the derivation of the matrices if you need that level of detail.
In OpenGL there is a function glOrtho for creating orthographic projection matrix. As Jason Z wrote you can use any position for the light that is far enough to "see" all geometry that you want, because created shadows don't depend on the position in that case. The same goes for clipping planes. So everything is up to you :)
I guess I was hoping for a sample of that math that appears in some texts, because it appears in none of the texts I have read. I left my books at work, but among them is Advanced Graphics Programming Using OpenGL (The Morgan Kaufmann Series in Computer Graphics), which again covers the concepts (which I understand clearly) but not the math.


I have a view frustum that is basically a pyramid with its top chopped off.
Then I have a box (representation of an orthogonal frustum projected from the directional light) at an arbitrary angle that needs to enclose that.
So I am guessing I need to take the dot product of the up/right vectors with the points that mark the corners of my view frustum to find the extremes in all directions, then take the objects that fit in that box and use a similar method to find the extreme far/near AABB’s of those objects.

Except there are no points in my view frustum; it is set up as 6 planes.
Calculating the intersection points of all the planes, while possible (and I know how to do it) seems slow, and I am sure there is a more efficient method.



As my engine is cross-platform (from iPhone to Nintendo Wii to Xbox 360), I will not be using any API-specific functions from OpenGL or DirectX.
I need the raw math behind this, or a pointer to a paper that has this mythical and mysterious math.
In the meantime I will of course continue searching for this, since you say it exists somewhere.


Thank you.
L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Hi!

like dys129 said, a common method go get the matrices in OpenGL is just to set the glOrtho and make a glGetMatrix(), but that's not really necessary. It is possible to set the matrices together by yourself, because a glOrtho() just multiplies an orthographic Matrix with the current Matrix (which is normally a Identity Matrix).

shadowLightProjectionMatrix = orthoMatrix * identityMatrix;

I don't know if you have seen that, but here you can see how OpenGL builds the orthographic Matrix:

http://www.opengl.org/sdk/docs/man/xhtml/glOrtho.xml

As said before, the values depend on you scene. For example I take:

glOrtho(-40,40,-40,40, 0.1f,500.0f);

I guess to calculate the best fit of your view frustum, it would be good to have the bounding spheres of the objects. Then you set the orthographic near plane on the nearest bounding sphere - radius (of it) and the farplane on the farthest bounding sphere + radius (of it).
Thank you all for your replies.

The numbers I am interested in finding are the numbers that you would plug into glOrtho(). I know how to build the matrices; I do not know what numbers I am supposed to feed them.


glOrtho(-40,40,-40,40, 0.1f,500.0f);

How did you come upon 40?
Does it always encompass your full scene from your view frustum’s point of view?
I have a feeling that if it does then you have a special situation in your game.

I know that one option is to make one orthogonal projection that fits the whole world and just use that all the time. It also prevents shadow flicker when the camera moves.
But it is also the worst possible way to go; you have terrible depth precision and do not make full use of the limited texture space you have, which results in terribly low-resolution shadows.


So I want to snap the orthogonal projection to essentially be an AABB of the view frustum, and this is the math I need.
I proposed my idea for how to do this above, by taking extreme points of the view frustum in the up/right directions of the light vector, but I felt that this might not be the most efficient way (although it will work).
This is where I would get the width/height to feed into glOrtho() (or rather my own matrix functions).



It seems that everyone is suggesting I use the actual objects inside my main view frustum to derive the width/height of the orthogonal projection from the light. But I would run into the same math problem regarding how to size my orthogonal projection to fit them (although it does become simpler if I use their bounding spheres rather than their AABB’s, and if that is the way I go I would not need help with that math), however this method causes shadows to disappear when the object casting them is no longer in view. For example a tall building no longer casting a shadow on the street just because the player looks down.

But that method has the advantage of not needlessly stretching the orthogonal projection out into the distance of there are no objects out there, which increases the quality of the shadows.


So I have a new plan to get the best of both worlds.
I will size the orthogonal projection to fit my main view frustum (which I still need the math to do), then cull objects from that box, with the far plane being derived from the fitted box and the near plane being set back to the edge of my world box.
Then among those objects I will tighten the orthogonal projection using the bounding spheres of those objects (for which I do not need the math).

I wonder if this will be too computationally expensive.


I guess all I need is the method/math for fitting an orthogonal projection around a view frustum.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

If I am in the wrong here, please forgive me, but I think I had a similar problem earlier.

These are a few steps to finding a good ortho frustum around your scene. Remember, that just because it is in the view frustum, there still exist objects outside the view frustum potentially that cast shadows into the view frustum (so you have to extend the box to include those).

This is how to calculate the minimum orthographic frustum around a arbitrary view frustum (as long as you can calculate it's eight corners).

1. Calculate the camera corners for your perspective transform in world space (To do this, calculate it in view space using trigonometry and then use the inverse view matrix to get the objects from view space to world space):

2. Rotate the vertices so that they are in the direction of the sun. To do this, I simply multiply every single corner by this matrix: gluLookAt (0, 0, 0, sx, sy, sz, 0, 1, 0) .

3. Now that you have the rotated vertices, it is a simple matter of finding the AABB around them, because you are in the light's view space. So find the maximum x, y, and z, values, and also the min x, y, and z values.

From here, you have the box, and creating an orthographic persepctive is trivial. The position of the camera will be at lightRotationInverse * (cx, cy, mz), where lightRotationInverse is the inverse of the matrix created in step 2, and (cx, cy, cz) is the center of the frustum in the light's view space, and mz is the minimum z value for the frustum's box.

There was a great XNA sample code that I used to determine how I should do this, but alas, I cannot find it. Search google for terms and add XNA maybe? I don't have the time to search.

Good luck!
Thank you; this is essentially what I wanted.

In my original explanation of my idea I thought that calculating the 8 corners of the view frustum would be pretty expensive so I thought there must be another way to do it to get the orthogonal dimensions directly from the 6 planes (of the view frustum).
But I guess not. Seems I will have to get the actual corner points anyway.


In that case I can proceed, but I think (I could be wrong) that it would be faster to use the dot products of the up/right vectors of the directional light against all 8 points (as opposed to transforming them via the inverse view matrix of the light). Both methods require a loop over the vertices and a min/max comparison, but the dot product is cheaper than a matrix multiply with a vector.

I think this is what you found before:
http://www.ziggyware.com/readarticle.php?article_id=235


If I am right, maybe you can even speed up your own implementation!
Just let me implement my idea first to see if it even works.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement