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
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:
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:
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:
The complete ray-hits-sphere test is then
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.0for a miss, and1.0for a perfect hit (within some fraction ofrof the center), and some value inbetween for imperfect hits.