Ray Sphere Intersect Problom

259 Views Asked by At

I'm making a 3D game and I have the following function to detect if a gun firing a laser beam (straight line) intersects a spherical object.

boolean intersect(Point3D raydir, Point3D rayorig, Point3D pos, double rad) {
        double a = raydir.X * raydir.X + raydir.Y * raydir.Y + raydir.X * raydir.X;
        double b = raydir.X * 2.0f * (rayorig.X - pos.X) + raydir.Y * 2.0f * (rayorig.Y - pos.Y) + raydir.Z * 2.0f * (rayorig.Z - pos.Z);
        double c = sum(pos, pos) + rayorig.X * rayorig.X + rayorig.Y * rayorig.Y + rayorig.Z * rayorig.Z -
                2.0f*(rayorig.X * pos.X + rayorig.Y * pos.Y + rayorig.Z * pos.Z) - rad * rad;
        double D = b*b + (-4.0f)*a*c;
        return ( D < 0 );
    }

So if my gun is located at (50,50,50) and the sphere is located at (50, 100, 50) with a radius of 5.0 and the weapon fires toward (50,40,50) which is the oposite direction to the sphere the math above return true.

intersect((50, 40, 50) , (50, 50, 50), (50, 100, 50), 5.0); //returns true
1

There are 1 best solutions below

0
On BEST ANSWER

Let's assume you have $$\begin{array} \vec{o} = (x_o, y_o, z_o) & \text{ Ray origin } \\ \vec{d} = (x_d, y_d, z_d) & \text{ Ray direction } \\ \vec{c} = (x_c, y_c, z_c) & \text{ Sphere center } \\ r \gt 0 & \text{ Sphere radius } \end{array}$$

This means the points $\vec{p}(t)$ on the ray fulfill $$\vec{p}(t) = \vec{o} + t \vec{d}$$ with $t \ge 0$.


The ray origin is within the sphere if and only if $$\left( \vec{c} - \vec{o} \right) \cdot \left( \vec{c} - \vec{o} \right) \le r^2$$ In pseudocode:

Boolean IsInsideSphere(Vec3D o, Vec3D c, Real r):
    return ( ( (o.x - c.x)*(o.x - c.x) + 
               (o.y - c.y)*(o.y - c.y) +
               (o.z - c.z)*(o.z - c.z) ) <= r*r )

The center of the sphere is in the same hemisphere (halfspace) as the direction of the ray, if and only if $$\left( \vec{c} - \vec{o} \right) \cdot \vec{d} \ge 0$$ In pseudocode:

Boolean SameHalfspace(Vec3D o, Vec3D d, Vec3D c):
    return ( ( (c.x - o.x) * d.x + 
               (c.y - o.y) * d.y +
               (c.z - o.z) * d.z ) >= 0 )

The minimum distance, squared, between the ray (infinitely long, extending to both directions) and the center of the sphere (from Point-Line Distance at Wolfram Mathworld) is $$r_{min}^2 = \frac{ \left\lvert \vec{d} \times \left( \vec{c} - \vec{o} \right) \right\rvert^2 }{ \left\lvert \vec{d} \right\rvert^2 } = \frac{ \left( \vec{d} \times \left( \vec{c} - \vec{o} \right) \right) \cdot \left( \vec{d} \times \left( \vec{c} - \vec{o} \right) \right) }{ \vec{d} \cdot \vec{d} }$$ In pseudocode:

Real MinimumDistanceSquared(Vec3D o, Vec3D d, Vec3D c)
    Vec3D t = (c.x - o.x, c.y - o.y, c.z - o.z)
    Real dd = d.x*d.x + d.y*d.y + d.z*d.z
    if (dd > 0.0):
        Vec3D p = ( d.y*t.z - d.z*t.y,
                    d.z*t.x - d.x*t.z,
                    d.x*t.y - d.y*t.x )
        return (p.x*p.x + p.y*p.y + p.z*p.z) / dd
    else:
        # No direction, return the distance squared.
        return t.x*t.x + t.y*t.y + t.z*t.z

The complete ray-hits-sphere test is then

Boolean RayHitsSphere(Vec3D o, Vec3D d, Vec3D c, Real r)

    # If you are inside the sphere, you cannot miss it.
    if (IsInsideSphere(o, c, r)):
        return True

    # We cannot hit the sphere, if we are facing away.
    if (not SameHalfSpace(o, d, c)):
        return False

    # Actual intersection test.
    return (MinimumDistanceSquared(o, d, c) <= r*r)

In a real-world game, you could give better score the closer the ray passes to the center of the sphere. Instead of the above Boolean function, you could have it return 0.0 for a miss, and 1.0 for a perfect hit (within some fraction of r of the center), and some value inbetween for imperfect hits.