Closest point on OBB to given point

Hi,

I found a very nice class implementing an OBB here (BoundingOrientedBox). I would like now to find the point on the OBB which is the closest to a given point. The point I give might be outside or inside the OBB, but the result point must always be on the OBB (not outside or inside). I don’t know if we can call that some kind of projection?

I think that this link has what I am asking for but I was not able to implement/rewrite it.

Thank you in advance for any help.

Edit:
I have found this:

void ClosestPtPointOBB(Point p, OBB b, Point &q)
{
Vector d = p - b.c;
// Start result at center of box; make steps from there
q = b.c;
// For each OBB axis…
for (int i = 0; i < 3; i++) {
// …project d onto that axis to get the distance
// along the axis of d from the box center
float dist = Dot(d, b.u[i]);
// If distance farther than the box extents, clamp to the box if (dist > b.e[i]) dist = b.e[i];
if (dist < -b.e[i]) dist = -b.e[i];
// Step that distance along the axis to get world coordinate
q += dist * b.u[i];
}
}

But it works only when the given point is outside the box.

Maybe one of these will help …

https://duckduckgo.com/?q=obb+c%23+algorithm&t=ffhp&ia=qa

https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-box-intersection

Is the code in the link of the snippet what your trying to translate ?

this…

// Given point p, return point q on (or in) OBB b, closest to p
void ClosestPtInOBB(D3DXVECTOR3 p,
    		  D3DXVECTOR3 bc,
    		  D3DXVECTOR3* bu,	 
                  float* be,
    		  D3DXVECTOR3 &q )
    { // ... ect ...

The code you posted is an excerpt from the book “Real Time Collision Detection”. Here are the pages you want. You can always buy the book if you want more.

1 Like

@willmotil I have actually looked at most links Google gave me, and no success. And yes this is the code I wanted to use, which is the same as below in fact.

@LithiumToast I have read that yes, the code I get is (put inside the OBB class):

    public Vector3 Closest(Vector3 p)
    {
        Vector3 d = p - this.Center;
        // Start result at center of box; make steps from there
        Vector3 q = this.Center;

        var u = new Vector3[]
        {
            Forward(this.Orientation), Up(this.Orientation), Right(this.Orientation)
        };

        var e = new float[]
        {
            this.HalfExtent.X, this.HalfExtent.Y, this.HalfExtent.Z
        };

        // For each OBB axis...
        for (int i = 0; i < 3; i++)
        {
            // ...project d onto that axis to get the distance
            // along the axis of d from the box center
            float dist = Vector3.Dot(d, u[i]);
            // If distance farther than the box extents, clamp to the box if (dist > b.e[i]) dist = b.e[i];
            //if (dist > e[i]) dist = e[i];
            if (dist < -e[i]) dist = -e[i];
            // Step that distance along the axis to get world coordinate
            q += dist * u[i];
        }

        return q;
    } 

  //from http://blog.diabolicalgame.co.uk/2013_06_01_archive.html
    public static Vector3 Forward(Quaternion q)
    {
        return new Vector3(
          -2 * (q.X * q.Z + q.W * q.Y),
          -2 * (q.Y * q.Z - q.W * q.X),
          -1 + 2 * (q.X * q.X + q.Y * q.Y));
    }
    public static Vector3 Up(Quaternion q)
    {
        return new Vector3(
          2 * (q.X * q.Y - q.W * q
               .Z),
          1 - 2 * (q.X * q.X + q.Z * q.Z),
          2 * (q.Y * q.Z + q.W * q.X));
    }
    public static Vector3 Right(Quaternion q)
    {
        return new Vector3(
          1 - 2 * (q.Y * q.Y + q.Z * q.Z),
          2 * (q.X * q.Y + q.W * q.Z),
          2 * (q.X * q.Z - q.W * q.Y));
    }

But it does not work at all, I maybe have made a mistake but I am not able to figure it out.

I recommend to not use arrays willy nilly in C#; they are not just pointers with offsets to some memory like C.

// Region R = { x | x = c+r*u[0]+s*u[1]+t*u[2] } , |r|<=e[0], |s|<=e[1], |t|<=e[2]
// in XNA Vector3 is commonly used for vectors, points, and triple scalar values as tuples
// notice that the size of this struct is larger than 16-24 bytes...
// consider using a class instead or pass the struct by ref
public struct OBB 
{ 
    // OBB center point
    public Vector3 Center; 
    // Local x-, y-, and z-axes (vectors)
    // Make sure these vectors are normalized
    public Vector3 XAxis;
    public Vector3 YXxis;
    public Vector3 ZAxis; 
    // Positive halfwidth extents of OBB along each axis (scalar values)
    public Vector3 HalfExtents;  

    // Given point p, return point q on (or in) OBB b, closest to p
    public Vector3 ClosestPointTo(Vector3 point)
    {
        // vector from box centre to point
        var directionVector = point - Center;

        // for each OBB axis...
        // ...project d onto that axis to get the distance 
        // along the axis of d from the box center
        // then if distance farther than the box extents, clamp to the box 
        // then step that distance along the axis to get world coordinate

        var distanceX = Vector3.Dot(directionVector, XAxis);
        if (distanceX > HalfExtents.X) distanceX = HalfExtents.X; 
        else if (distanceX < -HalfExtents.X) distanceX = -HalfExtents.X;

        var distanceY = Vector3.Dot(directionVector, YAxis);
        if (distanceY > HalfExtents.Y) distanceY = HalfExtents.Y; 
        else if (distanceY < -HalfExtents.Y) distanceY = -HalfExtents.Y;

        var distanceZ = Vector3.Dot(directionVector, ZAxis);
        if (distanceZ > HalfExtents.Z) distanceZ = HalfExtents.Z; 
        else if (distanceZ < -HalfExtents.Z) distanceZ = -HalfExtents.Z;

        return Center + distanceX * XAxis + distanceY * YAxis + distanceZ * ZAxis; 
    }
}
1 Like

Thank you for your enhancements. It is actually working, the problem was that I was giving some wrong axes.

For those who would like to implement the solution above to the sample MG BoundingOrientedBox class, you can get the axes like that:

Matrix M = Matrix.CreateFromQuaternion(Orientation);
Vector3 XAxis = M.Right;
Vector3 YAxis = M.Up;
Vector3 ZAxis = M.Forward;

Although there might be a better way?

But the thing is that, as I said in my first post, this method returns the same given point if that given point is inside the OBB. I would like in that case to have the closest point lying on the OBB. I had some solution previously, but in 2D and was not so good written, if you have any idea?

You could check which distance for each axis is largest then use that axis to get the edge of the OBB from the centre. The other distances for the other two axes will behave the same as before. It’s easier to reason if you just draw some examples on a piece of paper.