# Separating axis theorem OBB vs Sphere

I am trying to implement collision detection in 3D between OBB/AABB and sphere. For this i'm using sat/separating axis theorem. For sat to work in 3D i need face normals of obb (sphere to face), each vertex of obb - sphereCenter (sphere to vertex) and edge normals to sphereCenter (sphere to edge). The last i dont know how to calculate. First picture is working fine(sphere to vertex) and other two cases are giving false positives. Any other way to find minimum translation vector and distance of sphere and aabb would also be appreciated.

Box vs Sphere:

void Physics::colliderBoxVsSphere(BoxCollider* c1, SphereCollider* c2, glm::vec3& mtvAxis, float& mtvDist){
auto spPos = c2->getPosition();

std::vector<glm::vec3> obb;
c1->getOBB(obb);
auto mid = midPos2(obb);

// (1) identity axes
std::vector<glm::vec3> axes;
c1->getEntity()->transform()->appendIdentiyAxes(axes);

// (2) vertices
std::vector<glm::vec3> verts;
for (auto& vert : obb) {
auto axisVec = glm::normalize(spPos - vert);
verts.push_back(axisVec);
}

// (3) edges
//TODO

axes.insert(axes.end(), verts.begin(), verts.end());

CollisionMath::instance()->OBBCollideSphere(obb, spPos, c2->getRadius(), axes, mtvAxis, mtvDist);
}


SAT:

bool CollisionMath::OBBCollideSphere(const std::vector<glm::vec3>& obb, const glm::vec3& spherePos, float sphereRad, const std::vector<glm::vec3>& axes, glm::vec3& mtvAxisRet, float& mtvDistanceRet){
glm::vec3 pos1 = midPos(obb);
glm::vec3 pos2 = spherePos;

float mtvDist = std::numeric_limits<float>::max();
glm::vec3 mtvAxis = glm::vec3(0, 0, 0);
for (auto& axis : axes) {
if (axis == glm::vec3(0, 0, 0)) continue;

glm::vec2 p1 = Math::instance()->projectOnAxis(obb, axis);
glm::vec2 p2 = Math::instance()->projectSphereOnAxis(spherePos, sphereRad, axis);

float d1 = glm::dot(pos1, axis);
float d2 = glm::dot(pos2, axis);

if (d1 > d2) {
if (p1.x > p2.y)
return false;
else {      //intersect get depth
float depth = p2.y - p1.x;
if (depth < mtvDist) {
mtvDist = depth;
mtvAxis = axis;
}
}
}
else {
if (p2.x > p1.y)
return false;
else {      //intersect get depth
float depth = p1.y - p2.x;
if (depth < mtvDist) {
mtvDist = depth;
mtvAxis = -axis;
}
}
}
}

mtvAxisRet = mtvAxis;
mtvDistanceRet = mtvDist;

return true;
}


Project on axis:

float min = glm::dot(verts[0], axis);
float max = min;

for (unsigned i = 1; i < verts.size(); ++i) {
float d = glm::dot(verts[i], axis);
if (d > max) max = d;
if (d < min) min = d;
}

return glm::vec2(min, max);


project sphere on axis:

glm::vec3 A = center + axis * rad;
glm::vec3 B = center - axis * rad;

float min = glm::dot(B,axis);
float max = glm::dot(A,axis);

return glm::vec2(min, max);


Don't use separating axis theorem for this. It's overkill for spheres.

1. Use the OBB's orientation & translation information to inverse-transform the sphere's center into the box's local space.

localSphereCenter = box.InverseTransformPoint(sphere.worldCenter)

2. Find the closest point on the box to this point. Now that you're working on the box's local axes, this is as simple as a component-wise clamp on each axis:

closest.x = max(box.localMin.x, min(localSphereCenter.x, box.localMax.x));
... likewise for y, z

3. Take the offset vector from this closest point to the sphere center. If it's shorter than your radius, the sphere intersects the box.

The minimum translation vector (if the center is not in the box) is then this offset vector scaled to the remaining length:

translation = (sphere.radius - offset.length) * offset.normalized

If the sphere center is inside the box, then this displacement vector will be zero. You can clamp the point to the nearest face of the box, then form a penetration vector equal to the sphere center minus this closest face point. Your minimum translation vector is then:

translation = -1 * (sphere.radius + penetration.length) * penetration.normalized

Since we've computed these separations/penetrations in the box's local space, remember to transform them back to world space at the end to use them for moving objects.

• What if the sphere has no single radius? What if the sphere gets scaled by, say, (x=2, y=4, z=7)? – AudioGuy May 19 at 19:16
• Then it's no longer a sphere, and becomes what we call an ellipsoid. You can search "ellipsoid collision detection" for information about how to solve that related problem. – DMGregory May 19 at 19:42