2
\$\begingroup\$

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.

\$\endgroup\$
3
  • \$\begingroup\$ I thought a point light went in all directions? \$\endgroup\$
    – Polar
    Mar 18, 2013 at 21:40
  • 1
    \$\begingroup\$ @Polar It sounds like the OP is doing omnidirectional shadow mapping, with cube shadow map and a frustum for each face. \$\endgroup\$ Mar 18, 2013 at 22:40
  • \$\begingroup\$ Yea, that's exactly what I'm doing Nathan \$\endgroup\$
    – Telanor
    Mar 18, 2013 at 23:22

2 Answers 2

1
\$\begingroup\$

This is not a direct answer to your question, but may be a possible alternative with some added benefits.

You seem to be trying to generate a frustum bounding box so you can do some frustum culling before generating each face of a cubic shadow map. If you were to switch to using dual-paraboloid shadow maps you will get a performance gain by only using 2 render calls to generate the shadow map rather than 6.

The other benefit, which relates more to the question, is that culling is now a process of detecting whether an object is at the back, at the front or intersecting the plane which divides the two hemispheres of your dual-paraboloid shadow maps.

\$\endgroup\$
2
  • \$\begingroup\$ I've tried implementing that technique before unsuccessfully. I may try again in the future, but it does have a downside of creating seams. I'd like to try getting this basic method working first though. \$\endgroup\$
    – Telanor
    Mar 27, 2013 at 4:37
  • \$\begingroup\$ If doing the shadow map generation in a geometry shader, only one set of render calls is required (and you can frustum cull in a GS too) but this depends on what DX version the OP is targetting (I assume 10+ but confirmation would be nice). \$\endgroup\$ Sep 23, 2013 at 19:47
0
\$\begingroup\$

According to the MSDN documentation, the BoundingFrustum constructor only takes Left-Handed projection matrices:

http://msdn.microsoft.com/en-us/library/windows/desktop/hh855905(v=vs.85).aspx

\$\endgroup\$
4
  • \$\begingroup\$ I'm using SharpDX which has its own BoundingFrustum class. The documentation doesn't say whether it takes left or right handed matrices, though I have used it elsewhere and it seemed to work fine. \$\endgroup\$
    – Telanor
    Mar 19, 2013 at 21:24
  • \$\begingroup\$ That code is also based on DirectX's left-handed coordinate system, derived from this paper. \$\endgroup\$
    – Mokosha
    Mar 20, 2013 at 0:30
  • \$\begingroup\$ To follow up, there is a very interesting discussion about this topic at the Real-Time Rendering Blog \$\endgroup\$
    – Mokosha
    Mar 20, 2013 at 0:58
  • \$\begingroup\$ Hmm, the equations do indeed match that paper exactly, but the paper doesn't specify if its for left or right handed. It does mention OpenGL though, which seems to be right handed. In any case, I tried switching to the left-handed view/perspective matrix constructors and still got the same result. \$\endgroup\$
    – Telanor
    Mar 20, 2013 at 4:13

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .