/// <summary> /// Test if a collision sphere intersects with the given ray /// </summary> /// <param name="ray">A ray in 3D space</param> /// <param name="intersectionPoint">The intersection point on the surface of the sphere, if one exists</param> /// <returns>True if an intersection occurs, false otherwise</returns> public bool intersectsWith(Ray ray, ref Vector3 intersectionPoint) { // If p0 is the origin of the sphere, and r is the radius, with p being the base point of the ray // and d the unit direction of the ray... // An intersection point [x, y, z] will be found if (x - p0_x)^2 + (y - p0_y)^2 + (z - p0_z)^2 = r^2 // The points [x, y, z] can be defined as some point p + n * d, for some number n. // If the number is positive we have an intersection at that point. // ((p_x + nd_x) - p0_x)^2 + ((p_y + nd_y) - p0_y)^2 + ((p_z + nd_z) - p0_z)^2 = r^2 // Do some algebra and apply the quadratic formula Vector3 d = ray.Direction; Vector3 p = ray.Position; Vector3 p0 = Origin; float r = Radius; float a = 1.0f; // Go figure - it turns out to be (d_x^2 + d_y^2 + d_z^2), the square magnitude of a unit vector... Which is one. float b = 2.0f * (d.X * (p.X - p0.X) + d.Y * (p.Y - p0.Y) + d.Z * (p.Z - p0.Z)); float c = -(r * r) + p0.X * p0.X + p0.Y * p0.Y + p0.Z * p0.Z - 2.0f * (p.X * p0.X + p.Y * p0.Y + p.Z * p0.Z) + p.X * p.X + p.Y * p.Y + p.Z * p.Z; float sqrtnum = Sqrt(b * b - 4.0f * a * c); // No roots - not a collision if (float.IsNaN(sqrtnum)) { return false; } float sol1 = (-b - sqrtnum) / (2.0f * a); float sol2 = (-b + sqrtnum) / (2.0f * a); if (sol1 < 0.0f) { if (sol2 < 0.0f) { return false; } else { intersectionPoint = p + d * sol2; return true; } } else { if (sol2 < 0.0f) { intersectionPoint = p + d * sol1; return true; } else { intersectionPoint = p + d * Math.Min(sol1, sol2); return true; } } }
public void RayIntersectionDetailedTest() { Sphere unitOriginSphere = new Sphere(Vector3.Zero, 1.0f); Sphere positiveXSphere = new Sphere(Vector3.UnitX * 10.0f, 5.0f); // // Unit intersection tests // // Test one: ray along positive x axis from negative x direction Ray tr1 = new Ray(Vector3.UnitX * -5.0f, Vector3.UnitX); Vector3 c1 = Vector3.Zero; Assert.IsTrue(unitOriginSphere.intersectsWith(tr1, ref c1), "Unit X ray from negative X origin should intersect unit origin sphere"); Assert.AreEqual(Vector3.UnitX * -1.0f, c1, "Collision point should be at [-1, 0, 0]"); // Test two: ray along negative y axis from positive y direction Ray tr2 = new Ray(Vector3.UnitY * 5.0f, Vector3.UnitY * -1.0f); Vector3 c2 = Vector3.Zero; Assert.IsTrue(unitOriginSphere.intersectsWith(tr2, ref c2), "Negative unit Y ray from positive Y origin should intersect unit origin sphere"); Assert.AreEqual(Vector3.UnitY * 1.0f, c2, "Collision point should be at [0, 1, 0]"); // Test three: Ray along positive z axis from negative z direction Ray tr3 = new Ray(Vector3.UnitZ * -5.0f, Vector3.UnitZ); Vector3 c3 = Vector3.Zero; Assert.IsTrue(unitOriginSphere.intersectsWith(tr3, ref c3), "Unit Z ray from negative Z origin should intersect unit origin sphere"); Assert.AreEqual(Vector3.UnitZ * -1.0f, c3, "Collision point should be at [0, 0, -1]"); // // Direct miss tests // // Near miss Ray tr4 = new Ray(new Vector3(0.0f, 5.1f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Vector3 c4 = Vector3.Zero; Assert.IsFalse(positiveXSphere.intersectsWith(tr4, ref c4), "A close miss of a ray with a sphere should yield no collision"); Assert.AreEqual(Vector3.Zero, c4, "Output array should remain zero vector (assuming input zero vector) after a failed near miss collision"); // Orthogonal miss Ray tr5 = new Ray(new Vector3(1.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f)); Vector3 c5 = Vector3.Zero; Assert.IsFalse(positiveXSphere.intersectsWith(tr5, ref c5), "An orthogonal miss of a ray with a sphere should yield no collision"); Assert.AreEqual(Vector3.Zero, c5, "Output array should remain zero vector (assuming input zero vector) after a failed orthogonal collision"); // Wrong direction miss Ray tr6 = new Ray(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(-1.0f, 0.0f, 0.0f)); Vector3 c6 = Vector3.Zero; Assert.IsFalse(positiveXSphere.intersectsWith(tr6, ref c6), "An opposite direction miss of a ray with a sphere should yield no collision"); Assert.AreEqual(Vector3.Zero, c6, "Output array should remain zero vector (assuming input zero vector) after a failed wrong direction collision"); // // Barely hit test // Ray tr7 = new Ray(new Vector3(0.0f, 5.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Vector3 c7 = Vector3.Zero; Assert.IsTrue(positiveXSphere.intersectsWith(tr7, ref c7), "A collision with a sphere with a barely intersects point should yield a collision"); Assert.AreEqual(new Vector3(10.0f, 5.0f, 0.0f), c7, "The collision point of a radius 5 sphere with origin [10, 0, 0] on a ray going along the y=5 axis should be [10, 5, 0])"); // // Ray origin on edge of sphere test // Ray tr8 = new Ray(new Vector3(-1.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Vector3 c8 = Vector3.Zero; Assert.IsTrue(unitOriginSphere.intersectsWith(tr8, ref c8), "A collision with a sphere where the ray begins immediately on the sphere should yield a collision"); Assert.AreEqual(new Vector3(-1.0f, 0.0f, 0.0f), c8, "The collision point of a ray begining on the sphere should be at the origin point of the sphere"); // // Ray origin inside sphere test // Ray tr9 = new Ray(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Vector3 c9 = Vector3.Zero; Assert.IsTrue(unitOriginSphere.intersectsWith(tr9, ref c9), "A collision with a sphere from a ray based inside the sphere should yield a collision"); Assert.AreEqual(Vector3.UnitX, c9, "The collision point of an intersection with a ray based inside a sphere should be on the closest edge"); }
/// <summary> /// Test if the collision sphere intersects the given ray /// </summary> /// <param name="ray"></param> /// <returns></returns> public bool intersectsWith(Ray ray) { // TODO KAM: Try to use a more efficient ray/sphere collision method Vector3 foo = Vector3.Zero; return intersectsWith(ray, ref foo); }
public void RayIntersectionTest() { Sphere unitOriginSphere = new Sphere(Vector3.Zero, 1.0f); Sphere positiveXSphere = new Sphere(Vector3.UnitX * 10.0f, 5.0f); // // Unit intersection tests // // Test one: ray along positive x axis from negative x direction Ray tr1 = new Ray(Vector3.UnitX * -5.0f, Vector3.UnitX); Assert.IsTrue(unitOriginSphere.intersectsWith(tr1), "Unit X ray from negative X origin should intersect unit origin sphere"); // Test two: ray along negative y axis from positive y direction Ray tr2 = new Ray(Vector3.UnitY * 5.0f, Vector3.UnitY * -1.0f); Assert.IsTrue(unitOriginSphere.intersectsWith(tr2), "Negative unit Y ray from positive Y origin should intersect unit origin sphere"); // Test three: Ray along positive z axis from negative z direction Ray tr3 = new Ray(Vector3.UnitZ * -5.0f, Vector3.UnitZ); Assert.IsTrue(unitOriginSphere.intersectsWith(tr3), "Unit Z ray from negative Z origin should intersect unit origin sphere"); // // Direct miss tests // // Near miss Ray tr4 = new Ray(new Vector3(0.0f, 5.1f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Assert.IsFalse(positiveXSphere.intersectsWith(tr4), "A close miss of a ray with a sphere should yield no collision"); // Orthogonal miss Ray tr5 = new Ray(new Vector3(1.0f, 0.0f, 0.0f), new Vector3(0.0f, 1.0f, 0.0f)); Assert.IsFalse(positiveXSphere.intersectsWith(tr5), "An orthogonal miss of a ray with a sphere should yield no collision"); // Wrong direction miss Ray tr6 = new Ray(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(-1.0f, 0.0f, 0.0f)); Assert.IsFalse(positiveXSphere.intersectsWith(tr6), "An opposite direction miss of a ray with a sphere should yield no collision"); // // Barely hit test // Ray tr7 = new Ray(new Vector3(0.0f, 5.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Assert.IsTrue(positiveXSphere.intersectsWith(tr7), "A collision with a sphere with a barely intersects point should yield a collision"); // // Ray origin on edge of sphere test // Ray tr8 = new Ray(new Vector3(-1.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Assert.IsTrue(unitOriginSphere.intersectsWith(tr8), "A collision with a sphere where the ray begins immediately on the sphere should yield a collision"); // // Ray origin inside sphere test // Ray tr9 = new Ray(new Vector3(0.0f, 0.0f, 0.0f), new Vector3(1.0f, 0.0f, 0.0f)); Assert.IsTrue(unitOriginSphere.intersectsWith(tr9), "A collision with a sphere from a ray based inside the sphere should yield a collision"); }