I have a point in 3d space and I have a Vector3 that is closeby. I need to find the point (drawn in blue) along that Vector3 which is exactly 60 meters from the point I already have (labeled “my point” below).
I realize that there will actually be two points along the Vector3 that are exactly 60 meters from the point I have. I’d be thrilled if I could figure out how to get either or both.
var firstSphereIntersection = raySphereIntersection(SphereCenter, SphereRadius, lineOrigin, unitVectorOfForwardFromLineOrigin);
Above is how you can call into the function below. It’s yet another way, that you probably shouldn’t do. It’s moving a point along a ray in a loop until it is radius away from the center of the sphere. The performance is surly poor compared to single iteration methods.
private Vector3 raySphereIntersection(Vector3 sphereCenter, float sphereRadius, Vector3 rayStart, Vector3 rayUnitForward)
{
Ray ray = new Ray(rayStart, rayUnitForward);
float rayDistance;
if (raySphereIntersection(sphereCenter, sphereRadius, ray, out rayDistance))
{
return ray.GetPoint(rayDistance);
}
return Vector3.zero;
}
private bool raySphereIntersection(Vector3 sphereCenter, float sphereRadius, Ray ray, out float rayDistance)
{
float distanceOnRay = 0;
float distanceToSphereCenter = Vector3.Distance(sphereCenter, ray.origin);
float distanceToEdge = sphereRadius - distanceToSphereCenter;
while (distanceToEdge > 0.01)
{
float lastDistance = distanceToSphereCenter;
if (distanceToSphereCenter < sphereRadius)
{
// add distance if ray was going directly towards sphere edge
distanceOnRay += (sphereRadius - distanceToSphereCenter);
distanceToSphereCenter = Vector3.Distance(sphereCenter, ray.GetPoint(distanceOnRay));
}
else // distanceToSphereCenter must be > sphereRadius
{
// subtract distance if ray was going directly towards sphere center
distanceOnRay -= (distanceToSphereCenter - sphereRadius);
distanceToSphereCenter = Vector3.Distance(sphereCenter, ray.GetPoint(distanceOnRay));
// if ray is not going towards the sphere, it'll never hit it
if (distanceToSphereCenter > lastDistance)
{
rayDistance = 0;
return false;
}
}
distanceToEdge = sphereRadius - distanceToSphereCenter;
}
rayDistance = distanceOnRay;
return true;
}
Thanks @GameVortex. This was SUPER helpful for my little project, but I am getting a strange result that I was hoping someone could explain… I have concentric circles in which I have stored into a list of Radius’s (or is it Radii?). Anyway, I run through each one and work out my discriminant and all that… and I get my little test to print our the Vectors of each point that my line crosses, in order of collision (outermost, into the smallest radii, then back out again to the outermost).
It works for the most part… except that If my point StartPoint is inside the largest circle then my first result is always at the opposite side of that circle… then it goes back to printing the correct order.
I thought at first, it might have been my sort function, but then it occurred to me, my EndPoint never actually crosses the outer circle. The line can start outside but always finishes inside… so that opposite side should not be calculated as a hit point…
Also, for some reason, unless my path either points at the middle radius or cuts through it, my Discriminant comes back with a negative result…
public Vector3[] CircleLineCollision(Vector3 sunPos, float sunRadius, Vector3 shipStart, Vector3 DestinationPlanet)
{
// This is the function that takes a position and radius of a circle and the start and end of a line and tells you any points where they intersect.
Vector3 directionOfShip = DestinationPlanet - shipStart;
Vector3 centerToShipStart = shipStart - sunPos;
Vector3[] IntersectingPoints;
float a = Vector3.Dot(directionOfShip, directionOfShip);
float b = 2 * Vector3.Dot(centerToShipStart, directionOfShip);
float c = Vector3.Dot(centerToShipStart, centerToShipStart) - (sunRadius * sunRadius);
float discriminant = (b * b) - (4 * a * c);
Debug.Log("discriminant : " + discriminant.ToString());
if (discriminant >= 0)
{
//Ship Hits The Sun
discriminant = Mathf.Sqrt(discriminant);
//Where do the journey path intersections happen
float t1 = (-b - discriminant) / (2 * a);
float t2 = (-b + discriminant) / (2 * a);
if (t1 >= 0 && t2 >= 0)
{
// Debug.Log("You will intersect the Sun's orbit twice");
//total intersection, return both points
IntersectingPoints = new Vector3[2];
IntersectingPoints[0] = shipStart + (directionOfShip * t1);
IntersectingPoints[1] = shipStart + (directionOfShip * t2);
}
else
{
// Debug.Log("You will only intersect the Sun's Orbit once");
//Only one intersected, return one point
IntersectingPoints = new Vector3[1];
if (t1 >= 0)
{
IntersectingPoints[0] = shipStart + (directionOfShip * t1);
}
else if (t2 >= 0)
{
IntersectingPoints[0] = shipStart + (directionOfShip * t2);
}
}
return IntersectingPoints;
}
//No hits
return null;
}