protected OrthographicCamera CalculateFrustum(DirectionalLight light, Camera mainCamera, float minZ, float maxZ)
{
// Shorten the view frustum according to the shadow view distance
Matrix cameraMatrix;
mainCamera.GetWorldMatrix(out cameraMatrix);
for (int i = 0; i < 4; i++)
splitFrustumCornersVS[i] = frustumCornersVS[i + 4] * (minZ / mainCamera.FarClip);
for (int i = 4; i < 8; i++)
splitFrustumCornersVS[i] = frustumCornersVS[i] * (maxZ / mainCamera.FarClip);
Vector3.Transform(splitFrustumCornersVS, ref cameraMatrix, frustumCornersWS);
// Position the shadow-caster camera so that it's looking at the centroid,
// and backed up in the direction of the sunlight
Matrix viewMatrix = Matrix.CreateLookAt(Vector3.Zero - (light.Direction * 100), Vector3.Zero, new Vector3(0, 1, 0));
// Determine the position of the frustum corners in light space
Vector3.Transform(frustumCornersWS, ref viewMatrix, frustumCornersLS);
// Calculate an orthographic projection by sizing a bounding box
// to the frustum coordinates in light space
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;
}
// We snap the camera to 1 pixel increments so that moving the camera does not cause the shadows to jitter.
// This is a matter of integer dividing by the world space size of a texel
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)ShadowMapSize;
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;
// Create an orthographic camera for use as a shadow caster
const float nearClipOffset = 100.0f;
OrthographicCamera lightCamera = new OrthographicCamera(mins.X, maxes.X, mins.Y, maxes.Y, -maxes.Z - nearClipOffset, -mins.Z);
lightCamera.SetViewMatrix(ref viewMatrix);
return lightCamera;
}