Hiya again guys,
I have a niggling problem with my CSM. The middle an far cascades noticeably "swim" as you move around. It's not really noticeable on the close one but i'm that's probably just due to the filtering and the factor it has such a high quality map to work from.
Anyway I have 3 cascades and i'm using a bounding box for each. They all overlap with they near clip and this works fairly nicely but I was looking at the csm sample that some nicely ported over to monogame. The problem is I can't get the sphere based bounding boxes working at all for me. My debug rendertargets for all the cascades are just blank. If I switch my camera to be the shadow light camera (the sun) it seems fine (and it works using my bounding box method so i think that's fine). I THINK i'm just calculating the bounding box (using a sphere) incorrectly some how.
Could someone help me make this method use sphere's instead please:
public void GenerateCSMOrthoSlice(float pfarClip)
{
Vector3[] frustumCornersWS = new Vector3[8];
Vector3[] frustumCornersLS = new Vector3[8];
BoundingFrustum viewFrustum = new BoundingFrustum(_Camera.CameraView * Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, _Camera._aspectRatio, 10, pfarClip));
frustumCornersWS = viewFrustum.GetCorners();
Vector3 frustumCentroid = new Vector3(0, 0, 0);
for (int i = 0; i < 8; i++)
frustumCentroid += frustumCornersWS[i];
frustumCentroid /= 8;
lightsView = Matrix.Identity;
lightsViewProjectionMatrix = Matrix.Identity;
ShadowLightPos = frustumCentroid + (SunlightDirection * 100);
ShadowLookAt = frustumCentroid;
ShadowLightView = Matrix.CreateLookAt(ShadowLightPos, ShadowLookAt, new Vector3(0, 1, 0));
Vector3.Transform(frustumCornersWS, ref ShadowLightView, frustumCornersLS);
Vector3 mins = frustumCornersLS[0];
Vector3 maxes = frustumCornersLS[0];
for (int i = 0; i < 8; i++)
{
if (frustumCornersLS[i].X > maxes.X)
maxes.X = frustumCornersLS[i].X;
else if (frustumCornersLS[i].X < mins.X)
mins.X = frustumCornersLS[i].X;
if (frustumCornersLS[i].Y > maxes.Y)
maxes.Y = frustumCornersLS[i].Y;
else if (frustumCornersLS[i].Y < mins.Y)
mins.Y = frustumCornersLS[i].Y;
if (frustumCornersLS[i].Z > maxes.Z)
maxes.Z = frustumCornersLS[i].Z;
else if (frustumCornersLS[i].Z < mins.Z)
mins.Z = frustumCornersLS[i].Z;
}
float diagonalLength = (frustumCornersWS[0] - frustumCornersWS[6]).Length();
diagonalLength += 2; //Without this, the shadow map isn't big enough in the world.
float worldsUnitsPerTexel = diagonalLength / (float)4096;
Vector3 vBorderOffset = (new Vector3(diagonalLength, diagonalLength, diagonalLength) - (maxes - mins)) * 0.5f;
maxes += vBorderOffset;
mins -= vBorderOffset;
mins /= worldsUnitsPerTexel;
mins.X = (float)Math.Floor(mins.X);
mins.Y = (float)Math.Floor(mins.Y);
mins.Z = (float)Math.Floor(mins.Z);
mins *= worldsUnitsPerTexel;
maxes /= worldsUnitsPerTexel;
maxes.X = (float)Math.Floor(maxes.X);
maxes.Y = (float)Math.Floor(maxes.Y);
maxes.Z = (float)Math.Floor(maxes.Z);
maxes *= worldsUnitsPerTexel;
ShadowLightProjection = Matrix.CreateOrthographicOffCenter(mins.X, maxes.X, mins.Y, maxes.Y, -maxes.Z - 500f, -mins.Z);
lightsView = Matrix.CreateLookAt(ShadowLightPos, ShadowLookAt, new Vector3(0, 1, 0));
lightsViewProjectionMatrix = lightsView * ShadowLightProjection;
}
What I tried (which doesn't work at all) is:
public void GenerateCSMOrthoSlice(float pfarClip)
{
bool StabilizeCascades = true;
Vector3 minExtents = Vector3.Zero;
Vector3 maxExtents = Vector3.Zero;
Vector3[] frustumCornersWS = new Vector3[8];
Vector3[] frustumCornersLS = new Vector3[8];
BoundingFrustum viewFrustum = new BoundingFrustum(_Camera.CameraView * Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, _Camera._aspectRatio, 10, pfarClip));
frustumCornersWS = viewFrustum.GetCorners();
Vector3 frustumCentroid = new Vector3(0, 0, 0);
for (int i = 0; i < 8; i++)
frustumCentroid += frustumCornersWS[i];
frustumCentroid /= 8;
// sphere based cascade
if (StabilizeCascades)
{
// This needs to be constant for it to be stable
var upDir = Vector3.Up;
// Calculate the radius of a bounding sphere surrounding the frustum corners
var sphereRadius = 0.0f;
for (var i = 0; i < 8; ++i)
{
var dist = (_frustumCorners[i] - frustumCentroid).Length();
sphereRadius = Math.Max(sphereRadius, dist);
}
sphereRadius = (float)Math.Ceiling(sphereRadius * 16.0f) / 16.0f;
maxExtents = new Vector3(sphereRadius);
minExtents = -maxExtents;
}
lightsView = Matrix.Identity;
lightsViewProjectionMatrix = Matrix.Identity;
//Vector3 sunlightdirection = new Vector3(0.21f, 0.11f, -0.5f);
ShadowLightPos = frustumCentroid + (SunlightDirection * 100);
//ShadowLookAt = _SLLookAt;
ShadowLookAt = frustumCentroid;
ShadowLightView = Matrix.CreateLookAt(ShadowLightPos, ShadowLookAt, new Vector3(0, 1, 0));
Vector3.Transform(frustumCornersWS, ref ShadowLightView, frustumCornersLS);
Vector3 mins = frustumCornersLS[0];
Vector3 maxes = frustumCornersLS[0];
for (int i = 0; i < 8; i++)
{
if (frustumCornersLS[i].X > maxes.X)
maxes.X = frustumCornersLS[i].X;
else if (frustumCornersLS[i].X < mins.X)
mins.X = frustumCornersLS[i].X;
if (frustumCornersLS[i].Y > maxes.Y)
maxes.Y = frustumCornersLS[i].Y;
else if (frustumCornersLS[i].Y < mins.Y)
mins.Y = frustumCornersLS[i].Y;
if (frustumCornersLS[i].Z > maxes.Z)
maxes.Z = frustumCornersLS[i].Z;
else if (frustumCornersLS[i].Z < mins.Z)
mins.Z = frustumCornersLS[i].Z;
}
float diagonalLength = (frustumCornersWS[0] - frustumCornersWS[6]).Length();
diagonalLength += 2; //Without this, the shadow map isn't big enough in the world.
float worldsUnitsPerTexel = diagonalLength / (float)4096;
Vector3 vBorderOffset = (new Vector3(diagonalLength, diagonalLength, diagonalLength) - (maxes - mins)) * 0.5f;
maxes += vBorderOffset;
mins -= vBorderOffset;
mins /= worldsUnitsPerTexel;
mins.X = (float)Math.Floor(mins.X);
mins.Y = (float)Math.Floor(mins.Y);
mins.Z = (float)Math.Floor(mins.Z);
mins *= worldsUnitsPerTexel;
maxes /= worldsUnitsPerTexel;
maxes.X = (float)Math.Floor(maxes.X);
maxes.Y = (float)Math.Floor(maxes.Y);
maxes.Z = (float)Math.Floor(maxes.Z);
maxes *= worldsUnitsPerTexel;
lightsView = Matrix.CreateLookAt(ShadowLightPos, ShadowLookAt, new Vector3(0, 1, 0));
lightsViewProjectionMatrix = lightsView * ShadowLightProjection;
ShadowLightProjection = Matrix.CreateOrthographicOffCenter(mins.X, maxes.X, mins.Y, maxes.Y, -maxes.Z - 500f, -mins.Z);
if (StabilizeCascades)
{
ShadowLightProjection = Matrix.CreateOrthographicOffCenter(minExtents.X, minExtents.Y, maxExtents.X, maxExtents.Y, 0.0f, pfarClip);
// Create the rounding matrix, by projecting the world-space origin and determining
// the fractional offset in texel space
var shadowMatrixTemp = lightsViewProjectionMatrix;
var shadowOrigin = new Vector4(0.0f, 0.0f, 0.0f, 1.0f);
shadowOrigin = Vector4.Transform(shadowOrigin, shadowMatrixTemp);
shadowOrigin = shadowOrigin * (4096 / 2.0f);
var roundedOrigin = Round(shadowOrigin);
var roundOffset = roundedOrigin - shadowOrigin;
roundOffset = roundOffset * (2.0f / 4096);
roundOffset.Z = 0.0f;
roundOffset.W = 0.0f;
ShadowLightProjection.M41 += roundOffset.X;
ShadowLightProjection.M42 += roundOffset.Y;
ShadowLightProjection.M43 += roundOffset.Z;
ShadowLightProjection.M44 += roundOffset.W;
}
//
//lightsView = Matrix.CreateLookAt(ShadowLightPos, ShadowLookAt, new Vector3(0, 1, 0));
//lightsViewProjectionMatrix = lightsView * ShadowLightProjection;
}
↧