def __init__(self, p1, p2, p3, n1=None, n2=None, n3=None): super().__init__() self.p1 = p1 self.p2 = p2 self.p3 = p3 self.n1 = n1 self.n2 = n2 self.n3 = n3 self.smoothed = False # pre-compute edge vectors self.e1 = subtract(p2, p1) self.e2 = subtract(p3, p1) # pre-compute normal self.normal = normalize(cross(self.e2, self.e1))
def is_shadowed(self, light_position, hit_point): vec = subtract(light_position, hit_point) distance = magnitude(vec) direction = normalize(vec) ray = Ray(hit_point, direction) intersections = self.intersect(ray) hit_intersection = shadow_hit(intersections) return hit_intersection is not None and hit_intersection.t < distance
def ray_for_pixel(self, pixel_x, pixel_y): xoffset = (pixel_x + 0.5) * self.pixel_size yoffset = (pixel_y + 0.5) * self.pixel_size world_x = self.half_width - xoffset world_y = self.half_height - yoffset pixel = multiply_tuple(self.__inverse_transform, point(world_x, world_y, -1)) origin = multiply_tuple(self.__inverse_transform, point(0, 0, 0)) direction = normalize(subtract(pixel, origin)) return Ray(origin, direction)
def local_intersect(self, local_ray): sphere_to_ray = subtract(local_ray.origin, point(0, 0, 0)) a = dot(local_ray.direction, local_ray.direction) b = 2 * dot(local_ray.direction, sphere_to_ray) c = dot(sphere_to_ray, sphere_to_ray) - 1 discriminant = b**2 - 4 * a * c if discriminant < 0: return [] t1 = (-b - sqrt(discriminant)) / (2 * a) t2 = (-b + sqrt(discriminant)) / (2 * a) i1 = Intersection(t1, self) i2 = Intersection(t2, self) return [i1, i2]
def main(): canvas_pixels = 500 canvas = Canvas(canvas_pixels, canvas_pixels) shape = Sphere() # assign material shape.material = Material() shape.material.color = color(1, 0.2, 1) light_position = point(-10, 10, -10) light_color = color(1, 1, 1) light = PointLight(light_position, light_color) ray_origin = point(0, 0, -5) wall_z = 10 wall_size = 7.0 pixel_size = wall_size / canvas_pixels half = wall_size / 2 for y in range(canvas_pixels): world_y = half - pixel_size * y for x in range(canvas_pixels): world_x = -half + pixel_size * x pos = point(world_x, world_y, wall_z) r = Ray(ray_origin, normalize(subtract(pos, ray_origin))) xs = shape.intersect(r) shape_hit = hit(xs) if shape_hit is not None: hit_point = r.position_at(shape_hit.t) normal = shape_hit.object.normal_at(hit_point) eye = negate(r.direction) px_color = lighting(shape_hit.object.material, shape_hit.object, light, hit_point, eye, normal) canvas.set_pixel(x, y, px_color) with open('render_phong_sphere.ppm', 'w') as out_file: out_file.write(canvas.to_ppm())
def main(): canvas_pixels = 400 canvas = Canvas(canvas_pixels, canvas_pixels) red = color(1, 0, 0) shape = Sphere() # shrink it along the y axis #shape.set_transform(scaling(1, 0.5, 1)) # shrink it along the x axis #shape.set_transform(scaling(0.5, 1, 1)) # shrink it, and rotate it! # shape.set_transform(multiply_matrix(rotation_z(pi / 4), scaling(0.5, 1, # 1))) # shrink it, and skew it! # shape.set_transform( # multiply_matrix(shearing(1, 0, 0, 0, 0, 0), scaling(0.5, 1, 1))) ray_origin = point(0, 0, -5) wall_z = 10 wall_size = 7.0 pixel_size = wall_size / canvas_pixels half = wall_size / 2 for y in range(canvas_pixels): world_y = half - pixel_size * y for x in range(canvas_pixels): world_x = -half + pixel_size * x pos = point(world_x, world_y, wall_z) r = Ray(ray_origin, normalize(subtract(pos, ray_origin))) xs = shape.intersect(r) if hit(xs) is not None: canvas.set_pixel(x, y, red) with open('render_sphere.ppm', 'w') as out_file: out_file.write(canvas.to_ppm())
def local_intersect(self, local_ray): dir_cross_e2 = cross(local_ray.direction, self.e2) determinant = dot(self.e1, dir_cross_e2) if abs(determinant) < self.EPSILON: return [] f = 1.0 / determinant p1_to_origin = subtract(local_ray.origin, self.p1) u = f * dot(p1_to_origin, dir_cross_e2) if u < 0.0 or u > 1.0: return [] origin_cross_e1 = cross(p1_to_origin, self.e1) v = f * dot(local_ray.direction, origin_cross_e1) if v < 0.0 or (u + v) > 1.0: return [] t = f * dot(self.e2, origin_cross_e1) if self.smoothed: return [Intersection(t, self, u, v)] return [Intersection(t, self)]
def refracted_color(self, hit, remaining=5): if remaining <= 0: return color(0, 0, 0) if hit.object.material.transparency == 0.0: return color(0, 0, 0) n_ratio = hit.n1 / hit.n2 cos_i = dot(hit.eyev, hit.normalv) sin2_t = n_ratio**2 * (1 - cos_i**2) if sin2_t > 1.0: return color(0, 0, 0) cos_t = sqrt(1.0 - sin2_t) direction = subtract(multiply(hit.normalv, (n_ratio * cos_i - cos_t)), multiply(hit.eyev, n_ratio)) refract_ray = Ray(hit.under_point, direction) return multiply(self.color_at(refract_ray, remaining - 1), hit.object.material.transparency)
def step_impl(context): actual = subtract(context.c1, context.c2) assert_tuple(actual, color(0.2, 0.5, 0.5))
def step_create_eye_vector_v1_from_eye_to_p(context): context.v1 = normalize(subtract(context.eye, context.p))
def pattern_at(self, point): distance = subtract(self.color_b, self.color_a) fraction = point[0] - floor(point[0]) return add(self.color_a, multiply(distance, fraction))
def local_normal_at(self, local_point, hit=None): return subtract(local_point, point(0, 0, 0))