Problem with fitting light frustum to view frustum

Started by
4 comments, last by Matt_Aufderheide 8 years ago

Im having trouble with the process of generating a light view/projection matrix that fits my camera view proejction.. I want to do this to make my directional light shadow map fit better and have a higher resolution (also I can use it for cascaded shadow maps later). I have looked at a number of examples and so on, and put together some code. However it doesn't work as hoped yet...I should note that I have naïve shadow mapping working correctly if I simply calculate the light projection using a standard orthogonal matrix and look at matrix.

here's my working, naïve method for calculating my light view and projection:


 float interval = 256.0f;// 2000.0f*(1.0f / 512.0f);// 256.0f;
 shadow_view.x = floor(XMVectorGetX(camPosition) / interval)*interval;
 shadow_view.y = floor(XMVectorGetY(camPosition) / interval)*interval;
 shadow_view.z = floor(XMVectorGetZ(camPosition) / interval)*interval;

 shadow_view.pitch = 0.0f;
 shadow_view.yaw = -90.0f;
 shadow_view.roll = 0.0f;

 //hard coded for now..
 XMVECTOR lightvec = XMVectorSet(-2000.0f, 2500.0f, -2000.0f,0);
 lightvec = XMVector4Normalize(lightvec);
 lightvec = XMVectorMultiply(lightvec, XMVectorSet(800.0f, 800.0f, 800.0f, 0));

 XMVECTOR shadow_pos = XMVectorSet(shadow_view.x, shadow_view.y, shadow_view.z, 0);
 shadow_pos = XMVectorAdd(shadow_pos, lightvec);

 XMVECTOR shadow_look = XMVectorSet(shadow_view.x, shadow_view.y, shadow_view.z, 0);
 XMVECTOR shadow_up = XMVectorSet(0.0f, 0.0f, 1.0f, 0);

 shadow_view.matShadowView = XMMatrixLookAtLH(shadow_pos, shadow_look, shadow_up);
 shadow_view.matShadowProj = XMMatrixOrthographicLH(2000.0f, 2000.0f, 1.0f, 2049.0f);

But when I try this following code it just creates giant black area.. not working as hoped:


XMVECTOR vecFrustum[8];
 XMMATRIX mat;
 mat = XMMatrixInverse(NULL, camView*camProjection);

 //create the 8 points of a cube in unit-space
 vecFrustum[0] = XMVectorSet(-1.0f, -1.0f, 0.0f, 0.0f);
 vecFrustum[1] = XMVectorSet(1.0f, -1.0f, 0.0f, 0.0f);
 vecFrustum[2] = XMVectorSet(-1.0f, 1.0f, 0.0f, 0.0f);
 vecFrustum[3] = XMVectorSet(1.0f, 1.0f, 0.0f, 0.0f);
 vecFrustum[4] = XMVectorSet(-1.0f, -1.0f, 1.0f, 0.0f);
 vecFrustum[5] = XMVectorSet(1.0f, -1.0f, 1.0f, 0.0f);
 vecFrustum[6] = XMVectorSet(-1.0f, 1.0f, 1.0f, 0.0f);
 vecFrustum[7] = XMVectorSet(1.0f, 1.0f, 1.0f, 0.0f);

 for (int i = 0; i < 8; i++)
 {
  XMVector3TransformCoord(vecFrustum[i], mat);
 }

 XMVECTOR frustumCenter = vecFrustum[0];
 for (int i = 1; i < 8; i++)
 {
  frustumCenter += vecFrustum[i];
 }

 frustumCenter /= 8;

 shadow_pos = frustumCenter;
 shadow_pos = XMVectorAdd(shadow_pos, lightvec);
 shadow_view.matShadowView = XMMatrixLookAtLH(shadow_pos, frustumCenter, shadow_up);

 XMVECTOR transf = XMVector3TransformCoord(vecFrustum[0], shadow_view.matShadowView);
 float minZ = XMVectorGetZ(transf);
 float maxZ = XMVectorGetZ(transf);
 float minX = XMVectorGetX(transf);
 float maxX = XMVectorGetX(transf);
 float minY = XMVectorGetY(transf);
 float maxY = XMVectorGetY(transf);

 for (int i = 1; i < 8; i++)
 {
  transf = XMVector3TransformCoord(vecFrustum[i], shadow_view.matShadowView);

  if (XMVectorGetZ(transf) > maxZ) maxZ = XMVectorGetZ(transf);
  if (XMVectorGetZ(transf) < minZ) minZ = XMVectorGetZ(transf);
  if (XMVectorGetX(transf) > maxX) maxX = XMVectorGetX(transf);
  if (XMVectorGetX(transf) < minX) minX = XMVectorGetX(transf);
  if (XMVectorGetY(transf) > maxY) maxY = XMVectorGetY(transf);
  if (XMVectorGetY(transf) < minY) minY = XMVectorGetY(transf);
 }

 shadow_view.matShadowProj = XMMatrixOrthographicOffCenterLH(minX, maxX, minY, maxY, minZ, maxZ);
Advertisement

Bear with me, it's been awhile since I've touched camera code. But somethings do seem a bit off.

Your up vector is specified as 1.0 on the Z-Axis I would assume it's some kind of top down light source. Since the definition of Shadow_View isn't included in your source I can assume somewhere your doing the degress -> radian conversion?

Also what is the value of CamPosition before this code? Your taking a position, flooring it, than multiplying it? That would yeild: Say camPosition.x = 24, 24 / 256 = 0.09375, than you multiply that by 256, which yeilds 24. That's not what you meant to do was it?

shadow_view.x = floor(XMVectorGetX(camPosition) / interval)*interval;
shadow_view.y = floor(XMVectorGetY(camPosition) / interval)*interval;
shadow_view.z = floor(XMVectorGetZ(camPosition) / interval)*interval;

Then you pipe this value into your lookAt Vector

XMVECTOR shadow_look = XMVectorSet(shadow_view.x, shadow_view.y, shadow_view.z, 0);

Now, I may be mistaken, but i'm pretty sure you want some normalized value in your lookAt Vector, i.e. XMVECTOR shadow_look = XMVectorSet(0.0f, -1.0f, 0, 0);

Similar to how you construct the lightView's up vector.

Marcus

Thanks for the response, but actually that first snippet of code works just fine.. the second code snippet is what the problem is.. it is supposed to replace that first snippet, I just wanted to show how I was doing things... the floor , divide , multiply just rounds the light view position to increments to reduce shadow swim (this is not really truly stable however)...

Maybe I should explain the basic process that going on in the second snippet, at least as I understand it :)

1) first I invert the camera view*projection matrix to put it into world space

2) then transform the 8 corners of a unit cube into that space.. supposedly this gives me the world space position of the corners of the camera view/projection matrix

3) then get the centroid of the view frustum .. this will be the new lookat (shadow_look) point for the light

4) add the light vector to the lookat (shadow_look) point (which is a normalized vector multiplied by a factor to get some distance )..this is the new light view positon (called "shadow_pos" here) and construct a look at matrix... I use the up vector of (0,0,1) here...isn't that right because its a Left Handed matrix? maybe this is wrong and I have to find the up vector some other way?

5) transform the 8 corners of the camera view frustum into the new light view space, then find the bounds by find min/max for each x,y and z cords

6) finally construct an orthographic off center matrix using the min/max values ...

then I use these matrices to render the shadow objects and finally render the projected shadow in the shaders... this stage works fine when I use the first way of calculating the view/projection matrices so I think the shaders are fine.

haha, that makes sense.

float interval = 256.0f;// 2000.0f*(1.0f / 512.0f);// 256.0f;
shadow_view.x = floor(XMVectorGetX(camPosition) / interval)*interval;

What I am referring to here is that floor is already rounding down your value, so say you have camPosition.X = 27.85934, floor will than take that, and make it 27.

Then you proceed to divide that result from floor by 256. 27 / 256 = 0.10546875, but than you take that value, and multiply it by the same variable (256), 0.10546875 * 256 = 27, you see what i'm getting at, it just seems a bit redundant, floor() by itself should work for rounding. Unless I'm really missing something here.

I use the up vector of (0,0,1) here...isn't that right because its a Left Handed matrix? maybe this is wrong and I have to find the up vector some other way?

The main discrepancy between left vs right hand coordinate systems is the inverse of the direction of the Z-Axis. +Z for left hand, -Z for right hand.

In conclusion try (0.0f, 1.0f //on the yAxis, 0.0f); for the up vector if your lightView matrixs lookAt is on the +Z

Marcus

OK, after trying some stuff i realized i made a dumb mistake...this line:

XMVector3TransformCoord(vecFrustum, mat);

should be like this:

vecFrustum=XMVector3TransformCoord(vecFrustum, mat);

it works basically the way it should now....

I keep forgetting that the directX math functions don't actually change the value of the input....they just return a value.

The rounding thing.. the reason it is like that: floor(value/increment)*increment ... this rounds to nearest integer increment, its not simply rounding.. floor on its own just cuts off the decimals..

anyway.. now I have to figure out if there is a way to scale my projection matrix so it has a higher texel to sample rate..

OK i can just use different projection matrices as the basis... i can now do cascaded shadow maps....

Here's my next problem, i have figure out how to stabilize the shadows so it doesn't "swim" when camera moves... I get the basic idea...

This topic is closed to new replies.

Advertisement