def local_intersect(self, ray): a = ray.direction.x**2 - ray.direction.y**2 + ray.direction.z**2 b = 2 * ray.origin.x * ray.direction.x \ - 2 * ray.origin.y * ray.direction.y \ + 2 * ray.origin.z * ray.direction.z c = ray.origin.x**2 - ray.origin.y**2 + ray.origin.z**2 if abs(a) < EPSILON: if abs(b) < EPSILON: return Intersections() else: xs = [Intersection(-c / (2 * b), self)] + \ self._intersect_caps(ray) return Intersections(*xs) disc = b**2 - 4 * a * c if disc < 0: return Intersections() t0 = (-b - sqrt(disc)) / (2 * a) t1 = (-b + sqrt(disc)) / (2 * a) xs = [] y0 = ray.origin.y + t0 * ray.direction.y if self.minimum < y0 and y0 < self.maximum: xs.append(Intersection(t0, self)) y1 = ray.origin.y + t1 * ray.direction.y if self.minimum < y1 and y1 < self.maximum: xs.append(Intersection(t1, self)) xs = xs + self._intersect_caps(ray) return Intersections(*xs)
def local_intersect(self, ray): def check_axis(origin, direction): tmin_numerator = (-1 - origin) tmax_numerator = (1 - origin) if abs(direction) >= EPSILON: tmin = tmin_numerator / direction tmax = tmax_numerator / direction else: tmin = tmin_numerator * float('inf') tmax = tmax_numerator * float('inf') if tmin > tmax: tmin, tmax = tmax, tmin return (tmin, tmax) xtmin, xtmax = check_axis(ray.origin.x, ray.direction.x) ytmin, ytmax = check_axis(ray.origin.y, ray.direction.y) ztmin, ztmax = check_axis(ray.origin.z, ray.direction.z) tmin = max(xtmin, ytmin, ztmin) tmax = min(xtmax, ytmax, ztmax) if tmin > tmax: return Intersections() else: return Intersections(Intersection(tmin, self), Intersection(tmax, self))
def precomputing_the_reflection_vector(): # Given shape = Plane() r = Ray(Point(0, 1, -1), Vector(0, -sqrt(2) / 2, sqrt(2) / 2)) i = Intersection(sqrt(2), shape) # When comps = i.prepare_computations(r) # Then assert comps.reflectv == Vector(0, sqrt(2) / 2, sqrt(2) / 2)
def test_the_hit__when_an_intersection_occurs_on_the_outside(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = Sphere() i = Intersection(4, shape) # When comps = i.prepare_computations(r) # Then assert comps.inside is False
def test_the_hit__when_all_intersections_have_positive_t(): # Given s = Sphere() i1 = Intersection(1, s) i2 = Intersection(2, s) xs = Intersections(i2, i1) # When i = xs.hit() # Then assert i == i1
def test_the_schlick_approximation_with_a_perpendicular_viewing_angle(): # Given shape = glass_sphere() r = Ray(Point(0, 0, 0), Vector(0, 1, 0)) xs = Intersections(Intersection(-1, shape), Intersection(1, shape)) # When comps = xs[1].prepare_computations(r, xs) reflectance = comps.schlick() # Then assert equal(reflectance, 0.04)
def test_the_hit__when_some_intersections_have_negative_t(): # Given s = Sphere() i1 = Intersection(-1, s) i2 = Intersection(1, s) xs = Intersections(i2, i1) # When i = xs.hit() # Then assert i == i2
def test_the_hit__when_all_intersections_have_netative_t(): # Given s = Sphere() i1 = Intersection(-2, s) i2 = Intersection(-1, s) xs = Intersections(i2, i1) # When i = xs.hit() # Then assert i is None
def test_shading_an_intersection(): # Given w = default_world() r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = w.objects[0] i = Intersection(4, shape) # When comps = i.prepare_computations(r) c = w.shade_hit(comps) # Then assert c == Color(0.38066, 0.47583, 0.2855)
def test_the_hit_should_offset_the_point(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = Sphere() shape.transform = translation(0, 0, 1) i = Intersection(5, shape) # When comps = i.prepare_computations(r) # Then assert comps.over_point.z < -EPSILON / 2 assert comps.point.z > comps.over_point.z
def test_the_refracted_color_with_an_opaque_surface(): # Given w = default_world() shape = w.objects[0] r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) xs = Intersections(Intersection(4, shape), Intersection(6, shape)) # When comps = xs[0].prepare_computations(r, xs) c = w.refracted_color(comps, 5) # Then assert c == Color(0, 0, 0)
def test_aggregating_intersections(): # Given s = Sphere() i1 = Intersection(1, s) i2 = Intersection(2, s) # When xs = Intersections(i1, i2) # Then assert len(xs) == 2 assert xs[0].t == 1 assert xs[1].t == 2
def test_the_schlick_approximation_under_total_internal_reflection(): # Given shape = glass_sphere() r = Ray(Point(0, 0, sqrt(2) / 2), Vector(0, 1, 0)) xs = Intersections(Intersection(-sqrt(2) / 2, shape), Intersection(sqrt(2) / 2, shape)) # When comps = xs[1].prepare_computations(r, xs) reflectance = comps.schlick() # Then assert reflectance == 1.0
def test_the_reflected_color_for_a_nonreflective_material(): # Given w = default_world() r = Ray(Point(0, 0, 0), Vector(0, 0, 1)) shape = Sphere() shape.material.ambient = 1 i = Intersection(1, shape) # When comps = i.prepare_computations(r) color = w.reflected_color(comps) # Then assert color == Color(0, 0, 0) # black
def test_the_hit_is_always_the_lowest_nonnegative_intersection(): # Given s = Sphere() i1 = Intersection(5, s) i2 = Intersection(7, s) i3 = Intersection(-3, s) i4 = Intersection(2, s) xs = Intersections(i1, i2, i3, i4) # When i = xs.hit() # Then assert i == i4
def test_the_hit__when_an_intersection_occurs_on_the_inside(): # Given r = Ray(Point(0, 0, 0), Vector(0, 0, 1)) shape = Sphere() i = Intersection(1, shape) # When comps = i.prepare_computations(r) # Then assert comps.point == Point(0, 0, 1) assert comps.eyev == Vector(0, 0, -1) assert comps.inside is True assert comps.normalv == Vector(0, 0, -1)
def test_the_under_point_is_offset_below_the_surface(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = glass_sphere() shape.transform = translation(0, 0, 1) i = Intersection(5, shape) xs = Intersections(i) # When comps = i.prepare_computations(r, xs) # Then assert comps.under_point.z > EPSILON / 2 assert comps.point.z < comps.under_point.z
def test_shading_an_intersection_from_the_inside(): # Given w = default_world() w.light = PointLight(Point(0, 0.25, 0), Color(1, 1, 1)) r = Ray(Point(0, 0, 0), Vector(0, 0, 1)) shape = w.objects[1] i = Intersection(0.5, shape) # When comps = i.prepare_computations(r) c = w.shade_hit(comps) # Then assert c == Color(0.90498, 0.90498, 0.90498)
def test_precomputing_the_state_of_an_intersection(): # Given r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) shape = Sphere() i = Intersection(4, shape) # When comps = i.prepare_computations(r) # Then assert comps.t == i.t assert comps.object == i.object assert comps.point == Point(0, 0, -1) assert comps.eyev == Vector(0, 0, -1) assert comps.normalv == Vector(0, 0, -1)
def test_the_refracted_color_at_the_maximum_recursive_depth(): # Given w = default_world() shape = w.objects[0] shape.material.transparency = 1.0 shape.material.refractive_index = 1.5 r = Ray(Point(0, 0, -5), Vector(0, 0, 1)) xs = Intersections(Intersection(4, shape), Intersection(6, shape)) # When comps = xs[0].prepare_computations(r, xs) c = w.refracted_color(comps, 0) # Then assert c == Color(0, 0, 0)
def test_the_reflected_color_at_the_maximum_recursive_depth(): # Given w = default_world() shape = Plane() shape.material.reflective = 0.5 shape.transform = translation(0, -1, 0) w.objects.append(shape) r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2) / 2, sqrt(2) / 2)) i = Intersection(sqrt(2), shape) # When comps = i.prepare_computations(r) color = w.reflected_color(comps, 0) # Then assert color == Color(0, 0, 0)
def test__shade_hit__with_a_reflective_material(): # Given w = default_world() shape = Plane() shape.material.reflective = 0.5 shape.transform = translation(0, -1, 0) w.objects.append(shape) r = Ray(Point(0, 0, -3), Vector(0, -sqrt(2) / 2, sqrt(2) / 2)) i = Intersection(sqrt(2), shape) # When comps = i.prepare_computations(r) color = w.shade_hit(comps) # Then assert color == Color(0.87675, 0.92434, 0.82918)
def test__shade_hit__is_given_an_intersection_in_shadow(): # Given w = World() w.light = PointLight(Point(0, 0, -10), Color(1, 1, 1)) s1 = Sphere() w.objects.append(s1) s2 = Sphere() s2.transform = translation(0, 0, 10) w.objects.append(s2) r = Ray(Point(0, 0, 5), Vector(0, 0, 1)) i = Intersection(4, s2) # When comps = i.prepare_computations(r) c = w.shade_hit(comps) # Then assert c == Color(0.1, 0.1, 0.1)
def local_intersect(self, ray): # the vector from the sphere's center, to the ray origin # remember: the sphere is centered at the world origin sphere_to_ray = ray.origin - Point(0, 0, 0) a = ray.direction.dot(ray.direction) b = 2 * ray.direction.dot(sphere_to_ray) c = sphere_to_ray.dot(sphere_to_ray) - 1 discriminant = b**2 - 4 * a * c if discriminant < 0: return Intersections() t1 = (-b - sqrt(discriminant)) / (2 * a) t2 = (-b + sqrt(discriminant)) / (2 * a) return Intersections(Intersection(t1, self), Intersection(t2, self))
def test_an_intersection_encapsulates_t_and_object(): # Given s = Sphere() # When i = Intersection(3.5, s) # Then assert i.t == 3.5 assert i.object == s
def test_the_refracted_color_under_total_internal_reflection(): # Given w = default_world() shape = w.objects[0] shape.material.transparency = 1.0 shape.material.refractive_index = 1.5 r = Ray(Point(0, 0, sqrt(2) / 2), Vector(0, 1, 0)) xs = Intersections(Intersection(-sqrt(2) / 2, shape), Intersection(sqrt(2) / 2, shape)) # When # NOTE: this time you're inside the sphere, so you need # to look at the second intersection, xs[1], not xs[0] comps = xs[1].prepare_computations(r, xs) c = w.refracted_color(comps, 5) # Then assert c == Color(0, 0, 0)
def test_the_schlick_approximation_with_small_angle_and_n2_greater_than_n1(): # Given shape = glass_sphere() r = Ray(Point(0, 0.99, -2), Vector(0, 0, 1)) xs = Intersections(Intersection(1.8589, shape)) # When comps = xs[0].prepare_computations(r, xs) reflectance = comps.schlick() # Then assert equal(reflectance, 0.48873)
def _intersect_caps(self, ray): def check_cap(ray, t): x = ray.origin.x + t * ray.direction.x z = ray.origin.z + t * ray.direction.z return (x ** 2 + z ** 2) <= 1 xs = [] if self.closed is False or abs(ray.direction.y) < EPSILON: return xs t = (self.minimum - ray.origin.y) / ray.direction.y if check_cap(ray, t): xs.append(Intersection(t, self)) t = (self.maximum - ray.origin.y) / ray.direction.y if check_cap(ray, t): xs.append(Intersection(t, self)) return xs
def test_refracted_color_with_a_refracted_ray(): # Then w = default_world() A = w.objects[0] A.material.ambient = 1.0 A.material.pattern = MockPattern() B = w.objects[1] B.material.transparency = 1.0 B.material.refractive_index = 1.5 r = Ray(Point(0, 0, 0.1), Vector(0, 1, 0)) xs = Intersections(Intersection(-0.9899, A), Intersection(-0.4899, B), Intersection(0.4899, B), Intersection(0.9899, A)) # When comps = xs[2].prepare_computations(r, xs) c = w.refracted_color(comps, 5) # Then assert c == Color(0, 0.99887, 0.04721)
def local_intersect(self, ray): a = ray.direction.x ** 2 + ray.direction.z ** 2 # ray is parallel to the y axis if abs(a) < EPSILON: xs = self._intersect_caps(ray) return Intersections(*xs) b = 2 * ray.origin.x * ray.direction.x + \ 2 * ray.origin.z * ray.direction.z c = ray.origin.x ** 2 + ray.origin.z ** 2 - 1 disc = b ** 2 - 4 * a * c # ray does not intersect the cylinder if disc < 0: return Intersections() t0 = (-b - sqrt(disc)) / (2 * a) t1 = (-b + sqrt(disc)) / (2 * a) # if t0 > t1: # t0, t1 = t1, t0 xs = [] y0 = ray.origin.y + t0 * ray.direction.y if self.minimum < y0 and y0 < self.maximum: xs.append(Intersection(t0, self)) y1 = ray.origin.y + t1 * ray.direction.y if self.minimum < y1 and y1 < self.maximum: xs.append(Intersection(t1, self)) xs = xs + self._intersect_caps(ray) return Intersections(*xs)