locked
DirectX Math Collision - BoundingFrustum interface

    Question

  • Hi,

    My application uses Right-Handed coordinates system.
    All was fine until I came across the BoundindFrustum interface in Directx Math Collision : BoundingFrustum interface

    Frustum culling does not work as intended if I use my View, Projection, Right-Handed matrices.

    All is working fine if I compute extra Left-Handed matrices for frustum culling (see code below) :

    ProjectionMatrixLH = XMMatrixPerspectiveFovLH(fovAngleY, AspectRatio, NearClip, FarClip);
    ViewMatrixLH = XMMatrixLookAtLH(Position, Target, Up);
    XMVECTOR Determinant;
    XMMATRIX InvViewMatrixLH = XMMatrixInverse(&Determinant, ViewMatrixLH);
    BoundingFrustum boundingFrustum = BoundingFrustum();
    boundingFrustum.CreateFromMatrix(boundingFrustum, ProjectionMatrixLH);
    boundingFrustum.Transform(boundingFrustum, InvViewMatrixLH);

    Anyway, is there a "trick" to use the BoundingFrustum interface with Right-Handed matrices ?

    Is there more documentation about the Orientation property (public: XMFLOAT4 Orientation) ?

    Thanks for advices



    Monday, November 05, 2012 12:21 AM

Answers

  • I wouldn't expect that you need to modify the slopes.

    It is possible that the Contains test itself makes assumptoins about LH vs. RH in the way it sets up the planes.

    inline ContainmentType BoundingFrustum::Contains( FXMVECTOR Point ) const
    {
        // Build frustum planes.
        XMVECTOR Planes[6];
        Planes[0] = XMVectorSet( 0.0f, 0.0f, -1.0f, Near );
        Planes[1] = XMVectorSet( 0.0f, 0.0f, 1.0f, -Far );
        Planes[2] = XMVectorSet( 1.0f, 0.0f, -RightSlope, 0.0f );
        Planes[3] = XMVectorSet( -1.0f, 0.0f, LeftSlope, 0.0f );
        Planes[4] = XMVectorSet( 0.0f, 1.0f, -TopSlope, 0.0f );
        Planes[5] = XMVectorSet( 0.0f, -1.0f, BottomSlope, 0.0f );
    
        // Load origin and orientation.
        XMVECTOR vOrigin = XMLoadFloat3( &Origin );
        XMVECTOR vOrientation = XMLoadFloat4( &Orientation );
    
        assert( DirectX::Internal::XMQuaternionIsUnit( vOrientation ) );
    
        // Transform point into local space of frustum.
        XMVECTOR TPoint = XMVector3InverseRotate( Point - vOrigin, vOrientation );
    
        // Set w to one.
        TPoint = XMVectorInsert<0, 0, 0, 0, 1>( TPoint, XMVectorSplatOne() );
    
        XMVECTOR Zero = XMVectorZero();
        XMVECTOR Outside = Zero;
    
        // Test point against each plane of the frustum.
        for( size_t i = 0; i < 6; ++i )
        {
            XMVECTOR Dot = XMVector4Dot( TPoint, Planes[i] );
            Outside = XMVectorOrInt( Outside, XMVectorGreater( Dot, Zero ) );
        }
    
        return XMVector4NotEqualInt( Outside, XMVectorTrueInt() ) ? CONTAINS : DISJOINT;
    }
    

    Tuesday, November 06, 2012 7:09 PM
  • Thanks a lot for the source code Chuck.

    Now I understand !

    "It is possible that the Contains test itself makes assumptoins about LH vs. RH in the way it sets up the planes."

    Absolutly, this code is not designed for a Right-Handed camera (frustum).

    In the souce code we have :
    Near Plane :
    Planes[0] = XMVectorSet( 0.0f, 0.0f, -1.0f, Near );
    Near Plane equation : -z + Near = 0
    Near Plane Normal : (0, 0, -1)
    Far Plane :    
    Planes[1] = XMVectorSet( 0.0f, 0.0f, 1.0f, -Far );
    Far Plane equation : z - Far = 0
    Far Plane Normal : (0, 0, 1)

    For a Right Handed camera the normals of the far and near planes should be inverted like this:
    Near : (0, 0, 1) -> Planes[0] = XMVectorSet( 0.0f, 0.0f, 1.0f, -Near );
    Far : (0, 0, -1) -> Planes[1] = XMVectorSet( 0.0f, 0.0f, -1.0f, Far );

    I also checked DirectXCollison.h and in the BoundingFrustum constructor we find :
    { assert( _Near <= _Far ); }
    And again this is not compatible with a Right handed frustum :(



    Wednesday, November 07, 2012 2:27 AM

All replies

  • For most of DirectXMath, the model is row-major matricies with either Left-handed or Right-handed matricies. In this case, the bounding types assume left-handed matrices.

    The orientation property is a quaternion.

    I'll make sure to note this in the docs.

    You should be able to create the RH version pretty easily. I haven't tested it, so let me know if something else needs to change.

    void CreateFromMatrixRH( BoundingFrustum& Out, CXMMATRIX Projection )
    {
        // Corners of the projection frustum in homogenous space.
        static XMVECTORF32 HomogenousPoints[6] =
        {
            {  1.0f,  0.0f, -1.0f, 1.0f },   // right (at far plane)
            { -1.0f,  0.0f, -1.0f, 1.0f },   // left
            {  0.0f,  1.0f, -1.0f, 1.0f },   // top
            {  0.0f, -1.0f, -1.0f, 1.0f },   // bottom
    
            { 0.0f, 0.0f, 0.0f, 1.0f },     // near
            { 0.0f, 0.0f, -1.0f, 1.0f }      // far
        };
    
        XMVECTOR Determinant;
        XMMATRIX matInverse = XMMatrixInverse( &Determinant, Projection );
    
        // Compute the frustum corners in world space.
        XMVECTOR Points[6];
    
        for( size_t i = 0; i < 6; ++i )
        {
            // Transform point.
            Points[i] = XMVector4Transform( HomogenousPoints[i], matInverse );
        }
    
        Out.Origin = XMFLOAT3( 0.0f, 0.0f, 0.0f );
        Out.Orientation = XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f );
    
        // Compute the slopes.
        Points[0] = Points[0] * XMVectorReciprocal( XMVectorSplatZ( Points[0] ) );
        Points[1] = Points[1] * XMVectorReciprocal( XMVectorSplatZ( Points[1] ) );
        Points[2] = Points[2] * XMVectorReciprocal( XMVectorSplatZ( Points[2] ) );
        Points[3] = Points[3] * XMVectorReciprocal( XMVectorSplatZ( Points[3] ) );
    
        Out.RightSlope = XMVectorGetX( Points[0] );
        Out.LeftSlope = XMVectorGetX( Points[1] );
        Out.TopSlope = XMVectorGetY( Points[2] );
        Out.BottomSlope = XMVectorGetY( Points[3] );
    
        // Compute near and far.
        Points[4] = Points[4] * XMVectorReciprocal( XMVectorSplatW( Points[4] ) );
        Points[5] = Points[5] * XMVectorReciprocal( XMVectorSplatW( Points[5] ) );
    
        Out.Near = XMVectorGetZ( Points[4] );
        Out.Far = XMVectorGetZ( Points[5] );
    }

    Monday, November 05, 2012 8:09 PM
  • Thanks Chuck,

    I think I'm missing something ...

    I manually build a LH Bounding Frustum like this :

    BF.Origin = XMFLOAT3( 0.0f, 0.0f, 0.0f );
    BF.Orientation = XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f );
    BF.RightSlope = 1.0f;
    BF.LeftSlope = -1.0f;
    BF.TopSlope = 1.0f;
    BF.BottomSlope = -1.0f;
    BF.Near = 0.0f;
    BF.Far = 100.0f;
    I make a simple containement test (a point with positive Z):
    ContainmentType CT = BF.Contains(XMVectorSet(0.0f, 0.0f, 1.0f, 1.0f)); 
    The function returns CONTAINS (2) and it's ok.

    Now I manually build a RH Bounding Frustrum (Right and Left slopes inverted and a negative value for the far plane) :

    BF.Origin = XMFLOAT3( 0.0f, 0.0f, 0.0f );
    BF.Orientation = XMFLOAT4( 0.0f, 0.0f, 0.0f, 1.0f );
    BF.RightSlope = -1.0f;
    BF.LeftSlope = 1.0f;
    BF.TopSlope = 1.0f;
    BF.BottomSlope = -1.0f;
    BF.Near = 0.0f;
    BF.Far = -100.0f;
    I make a simple containement test (a point with negative Z):
    ContainmentType CT = BF.Contains(XMVectorSet(0.0f, 0.0f, -1.0f, 1.0f));

    The Contains function should still return CONTAINS (2), but it returns DISJOINT (0) ...


    Tuesday, November 06, 2012 2:17 AM
  • I wouldn't expect that you need to modify the slopes.

    It is possible that the Contains test itself makes assumptoins about LH vs. RH in the way it sets up the planes.

    inline ContainmentType BoundingFrustum::Contains( FXMVECTOR Point ) const
    {
        // Build frustum planes.
        XMVECTOR Planes[6];
        Planes[0] = XMVectorSet( 0.0f, 0.0f, -1.0f, Near );
        Planes[1] = XMVectorSet( 0.0f, 0.0f, 1.0f, -Far );
        Planes[2] = XMVectorSet( 1.0f, 0.0f, -RightSlope, 0.0f );
        Planes[3] = XMVectorSet( -1.0f, 0.0f, LeftSlope, 0.0f );
        Planes[4] = XMVectorSet( 0.0f, 1.0f, -TopSlope, 0.0f );
        Planes[5] = XMVectorSet( 0.0f, -1.0f, BottomSlope, 0.0f );
    
        // Load origin and orientation.
        XMVECTOR vOrigin = XMLoadFloat3( &Origin );
        XMVECTOR vOrientation = XMLoadFloat4( &Orientation );
    
        assert( DirectX::Internal::XMQuaternionIsUnit( vOrientation ) );
    
        // Transform point into local space of frustum.
        XMVECTOR TPoint = XMVector3InverseRotate( Point - vOrigin, vOrientation );
    
        // Set w to one.
        TPoint = XMVectorInsert<0, 0, 0, 0, 1>( TPoint, XMVectorSplatOne() );
    
        XMVECTOR Zero = XMVectorZero();
        XMVECTOR Outside = Zero;
    
        // Test point against each plane of the frustum.
        for( size_t i = 0; i < 6; ++i )
        {
            XMVECTOR Dot = XMVector4Dot( TPoint, Planes[i] );
            Outside = XMVectorOrInt( Outside, XMVectorGreater( Dot, Zero ) );
        }
    
        return XMVector4NotEqualInt( Outside, XMVectorTrueInt() ) ? CONTAINS : DISJOINT;
    }
    

    Tuesday, November 06, 2012 7:09 PM
  • Thanks a lot for the source code Chuck.

    Now I understand !

    "It is possible that the Contains test itself makes assumptoins about LH vs. RH in the way it sets up the planes."

    Absolutly, this code is not designed for a Right-Handed camera (frustum).

    In the souce code we have :
    Near Plane :
    Planes[0] = XMVectorSet( 0.0f, 0.0f, -1.0f, Near );
    Near Plane equation : -z + Near = 0
    Near Plane Normal : (0, 0, -1)
    Far Plane :    
    Planes[1] = XMVectorSet( 0.0f, 0.0f, 1.0f, -Far );
    Far Plane equation : z - Far = 0
    Far Plane Normal : (0, 0, 1)

    For a Right Handed camera the normals of the far and near planes should be inverted like this:
    Near : (0, 0, 1) -> Planes[0] = XMVectorSet( 0.0f, 0.0f, 1.0f, -Near );
    Far : (0, 0, -1) -> Planes[1] = XMVectorSet( 0.0f, 0.0f, -1.0f, Far );

    I also checked DirectXCollison.h and in the BoundingFrustum constructor we find :
    { assert( _Near <= _Far ); }
    And again this is not compatible with a Right handed frustum :(



    Wednesday, November 07, 2012 2:27 AM
  • I found there do exist an issue in this code snip, the homogenous coordinate for near <= far constraint should stay with RH coordinate : and thus the near/far point in the coordinate should be swap in the CreateFromMatrixRH(...) like:

     // Corners of the projection frustum in homogenous space.
     static XMVECTORF32 HomogenousPoints[6] =
     {
      { 1.0f,  0.0f, -1.0f, 1.0f },   // right (at far plane)
      { -1.0f,  0.0f, -1.0f, 1.0f },   // left
      { 0.0f,  1.0f, -1.0f, 1.0f },   // top
      { 0.0f, -1.0f, -1.0f, 1.0f },   // bottom
    
      { 0.0f, 0.0f, 1.0f, 1.0f },    // near
      { 0.0f, 0.0f, 0.0f, 1.0f }     // far
     };
    

    This gives result like LeftSlope=1 RrightSlope=-1 TopSlope=-1 BottomeSlope=1 Near=-100 Far=-0.1, and the constraint (Near < Far) stays

    Tuesday, January 20, 2015 4:24 PM
  • Thanks. If you make that change, do all the other Frustum methods operate as expected?

    Can you sum up the changes that you believe are needed to the existing code to make it work as you expect with RH?

    Tuesday, January 20, 2015 8:33 PM