Camera frustum calculation coming out wrong
- by Telanor
I'm trying to calculate a view/projection/bounding frustum for the 6 directions of a point light and I'm having trouble with the views pointing along the Y axis. Our game uses a right-handed, Y-up system. For the other 4 directions I create the LookAt matrix using (0, 1, 0) as the up vector. Obviously that doesn't work when looking along the Y axis so for those I use an up vector of (-1, 0, 0) for -Y and (1, 0, 0) for +Y. The view matrix seems to come out correctly (and the projection matrix always stays the same), but the bounding frustum is definitely wrong. Can anyone see what I'm doing wrong?
This is the code I'm using:
camera.Projection = Matrix.PerspectiveFovRH((float)Math.PI / 2, ShadowMapSize / (float)ShadowMapSize, 1, 5);
for(var i = 0; i < 6; i++)
{
var renderTargetView = shadowMap.GetRenderTargetView((TextureCubeFace)i);
var up = DetermineLightUp((TextureCubeFace) i);
var forward = DirectionToVector((TextureCubeFace) i);
camera.View = Matrix.LookAtRH(this.Position, this.Position + forward, up);
camera.BoundingFrustum = new BoundingFrustum(camera.View * camera.Projection);
}
private static Vector3 DirectionToVector(TextureCubeFace direction)
{
switch (direction)
{
case TextureCubeFace.NegativeX:
return -Vector3.UnitX;
case TextureCubeFace.NegativeY:
return -Vector3.UnitY;
case TextureCubeFace.NegativeZ:
return -Vector3.UnitZ;
case TextureCubeFace.PositiveX:
return Vector3.UnitX;
case TextureCubeFace.PositiveY:
return Vector3.UnitY;
case TextureCubeFace.PositiveZ:
return Vector3.UnitZ;
default:
throw new ArgumentOutOfRangeException("direction");
}
}
private static Vector3 DetermineLightUp(TextureCubeFace direction)
{
switch (direction)
{
case TextureCubeFace.NegativeY:
return -Vector3.UnitX;
case TextureCubeFace.PositiveY:
return Vector3.UnitX;
default:
return Vector3.UnitY;
}
}
Edit: Here's what the values are coming out to for the PositiveX and PositiveY directions:
Constants:
Position = {X:0 Y:360 Z:0}
camera.Projection =
[M11:0.9999999 M12:0 M13:0 M14:0]
[M21:0 M22:0.9999999 M23:0 M24:0]
[M31:0 M32:0 M33:-1.25 M34:-1]
[M41:0 M42:0 M43:-1.25 M44:0]
PositiveX:
up = {X:0 Y:1 Z:0}
target = {X:1 Y:360 Z:0}
camera.View =
[M11:0 M12:0 M13:-1 M14:0]
[M21:0 M22:1 M23:0 M24:0]
[M31:1 M32:0 M33:0 M34:0]
[M41:0 M42:-360 M43:0 M44:1]
camera.BoundingFrustum:
Matrix =
[M11:0 M12:0 M13:1.25 M14:1]
[M21:0 M22:0.9999999 M23:0 M24:0]
[M31:0.9999999 M32:0 M33:0 M34:0]
[M41:0 M42:-360 M43:-1.25 M44:0]
Top = {A:0.7071068 B:-0.7071068 C:0 D:254.5584}
Bottom = {A:0.7071068 B:0.7071068 C:0 D:-254.5584}
Left = {A:0.7071068 B:0 C:0.7071068 D:0}
Right = {A:0.7071068 B:0 C:-0.7071068 D:0}
Near = {A:1 B:0 C:0 D:-1}
Far = {A:-1 B:0 C:0 D:5}
PositiveY:
up = {X:0 Y:0 Z:-1}
target = {X:0 Y:361 Z:0}
camera.View =
[M11:-1 M12:0 M13:0 M14:0]
[M21:0 M22:0 M23:-1 M24:0]
[M31:0 M32:-1 M33:0 M34:0]
[M41:0 M42:0 M43:360 M44:1]
camera.BoundingFrustum:
Matrix =
[M11:-0.9999999 M12:0 M13:0 M14:0]
[M21:0 M22:0 M23:1.25 M24:1]
[M31:0 M32:-0.9999999 M33:0 M34:0]
[M41:0 M42:0 M43:-451.25 M44:-360]
Top = {A:0 B:0.7071068 C:0.7071068 D:-254.5585}
Bottom = {A:0 B:0.7071068 C:-0.7071068 D:-254.5585}
Left = {A:-0.7071068 B:0.7071068 C:0 D:-254.5585}
Right = {A:0.7071068 B:0.7071068 C:0 D:-254.5585}
Near = {A:0 B:1 C:0 D:-361}
Far = {A:0 B:-1 C:0 D:365}
When I use the resulting BoundingFrustum to cull regions outside of it, this is the result:
Pass PositiveX: Drew 3 regions
Pass NegativeX: Drew 6 regions
Pass PositiveY: Drew 400 regions
Pass NegativeY: Drew 36 regions
Pass PositiveZ: Drew 3 regions
Pass NegativeZ: Drew 6 regions
There are only 400 regions to draw and the light is in the center of them. As you can see, the PositiveY direction is drawing every single region. With the near/far planes of the perspective matrix set as small as they are, there's no way a single frustum could contain every single region.