/// <summary> /// Calculates the distance along the specified ray the first intersection with this object occurs. /// </summary> /// <param name="ray">The ray to test.</param> /// <returns>The distance along the ray the first intersection with this object occurs. If no intersection, returns a negative value.</returns> public override float IntersectDistance(Ray ray) { if (ray == null) { throw new ArgumentNullException("ray"); } var P = ray.Start - Centre; var A = ray.Direction * ray.Direction; var B = 2 * P * ray.Direction; var C = P * P - Radius * Radius; var t = SolveQuadraticMinimumNonnegative(A, B, C); if (!t.HasValue) { return -1.0f; } return t.Value; }
/// <summary> /// Calculates the distance along the specified ray the first intersection with this object occurs. /// </summary> /// <param name="ray">The ray to test.</param> /// <returns>The distance along the ray the first intersection with this object occurs. If no intersection, returns a negative value.</returns> public abstract float IntersectDistance(Ray ray);
/// <summary> /// Calculates the distance along the specified ray the first intersection with this object occurs. /// </summary> /// <param name="ray">The ray to test.</param> /// <returns>The distance along the ray the first intersection with this object occurs. If no intersection, returns a negative value.</returns> public override float IntersectDistance(Ray ray) { if (ray == null) { throw new ArgumentNullException("ray"); } var nd = normal * ray.Direction; if (nd == 0) { return -1.0f; } var start = new Vector(ray.Start.X, ray.Start.Y, ray.Start.Z); var t = (d - normal * start) / (nd); var q = ray.Start + t * ray.Direction; if (((P2 - P1) % (q - P1)) * normal < 0.0f) { return -1.0f; } if (((P3 - P2) % (q - P2)) * normal < 0.0f) { return -1.0f; } if (((P1 - P3) % (q - P3)) * normal < 0.0f) { return -1.0f; } return t; }
/// <summary> /// Shoot a ray into the scene and determine the colour at the impact point. /// </summary> /// <param name="ray">The ray to shoot into the scene.</param> /// <returns>The colour of the impacted point in the scene.</returns> private Colour ShootRay(Ray ray) { Colour finalOutput = new Colour(0.0f, 0.0f, 0.0f); float coefficient = 1.0f; uint count = 0u; do { Colour output = new Colour(0.0f, 0.0f, 0.0f); IObject closestObject = null; float closestDistance = float.MaxValue; foreach (var o in Objects) { float t = o.IntersectDistance(ray); if (t > 0.001f && t < closestDistance) { closestObject = o; closestDistance = t; } } if (closestObject == null) { break; } // Calculate the point of intersection. Point intersection = ray.Start + closestDistance * ray.Direction; // Get normal of object surface at impact of ray. Vector normal = closestObject.GetNormalAtPoint(intersection); foreach (var l in Lights) { Vector toLightSource = (l.Location - intersection).Normalise(); float cosineLightAngle = normal * toLightSource; if (cosineLightAngle <= 0.0f) { // The light is below the surface. continue; } bool inShadow = false; foreach (var o in Objects) { if (o == closestObject) { continue; } if (o.IntersectDistance(new Ray(intersection, toLightSource)) > 0.0f) { inShadow = true; break; } } if (!inShadow) { var lambertianReflectance = (Colour)closestObject.Material.Colour * (Colour)l.Colour * cosineLightAngle; output += lambertianReflectance; var halfwayVector = (toLightSource - ray.Direction).Normalise(); var halfwayNormalAngle = halfwayVector * normal; var blinnTerm = (Colour)l.Colour * closestObject.Material.SpecularTerm * (float)Math.Pow(Math.Max(halfwayNormalAngle, 0.0f), closestObject.Material.SpecularPower); output += blinnTerm; } } finalOutput += output * coefficient; var newRayDirection = (ray.Direction - 2 * normal * (normal * ray.Direction)).Normalise(); ray = new Ray(intersection, newRayDirection); coefficient *= closestObject.Material.Reflectance; } while (++count < MAX_ITERATIONS && coefficient > 0.0f); return finalOutput; }