def view_transform(from_position: Point, to: Point, up: Vector) -> Matrix: forward = normalize(to - from_position) left = cross(forward, normalize(up)) true_up = cross(left, forward) orientation = Matrix([ [left.x, left.y, left.z, 0], [true_up.x, true_up.y, true_up.z, 0], [-forward.x, -forward.y, -forward.z, 0], [0, 0, 0, 1], ]) return orientation * translation(-from_position.x, -from_position.y, -from_position.z)
def normal_at(self, world_point: Point) -> Vector: transformer = inverse(self.transform) object_point = transformer * world_point object_normal = object_point - point(0, 0, 0) world_normal = transpose(transformer) * object_normal world_normal = vector(world_normal.x, world_normal.y, world_normal.z) # set w to 0 return normalize(world_normal)
def is_shadowed(world: World, point: Point) -> bool: if world.light is None: return False else: light_direction = world.light.position - point distance = magnitude(light_direction) ray = Ray(point, normalize(light_direction)) intersections = intersect_world(world, ray) hit = find_hit(intersections) if hit is None or hit.t > distance: return False return True
def lighting( m: Material, light: PointLight, point: Point, eye: Vector, normal: Vector, in_shadow: bool = False, ) -> Color: """ Compute color at a point on the surface using the Phong Reflection Model Composition made up of ambient light, diffused light and specular light """ effective_color = m.color * light.intensity ambient = effective_color * m.ambient if in_shadow: return ambient black = Color(0, 0, 0) light_direction = normalize(light.position - point) light_direction_cosine = dot(light_direction, normal) if light_direction_cosine < 0: # Light source is on other side of surface => only ambient lighting diffuse = black specular = black else: diffuse = effective_color * m.diffuse * light_direction_cosine reflection = reflect(-light_direction, normal) reflection_eye_cosine = dot(reflection, eye) if reflection_eye_cosine <= 0: # Light is reflected away from the eye => no specular lighting specular = black else: factor = reflection_eye_cosine**int(m.shininess) specular = light.intensity * m.specular * factor return ambient + diffuse + specular
def run() -> None: # Eye is at (0,0, 5) origin = point(0, 0, 5) shape = Sphere() # shape.set_transform(scaling(0.5, 1, 1)) shape.material.color = Color(0.9, 0.2, 1) light = PointLight(point(-10, 10, 10), Color(1, 1, 1)) canvas = Canvas(CANVAS_SIZE, CANVAS_SIZE) for i in range(CANVAS_SIZE): for j in range(CANVAS_SIZE): target = canvas_to_world(point(i, j, 0)) ray = Ray(origin, normalize(target - origin)) hit = find_hit(shape.intersect(ray)) if hit is not None: hit_point = position(ray, hit.t) normal = hit.shape.normal_at(hit_point) pixel_color = lighting(hit.shape.material, light, hit_point, -ray.direction, normal) canvas.write_pixel(i, j, pixel_color) PPM(canvas).save_to_file("sphere.ppm")
def check_normal(context, var_1, var_2): variable_1 = context.variables[var_1] variable_2 = context.variables[var_2] assert variable_1 == normalize(variable_2)
def assign_normalize(context, var, other_var): other = context.variables[other_var] context.variables[var] = normalize(other)
def check_approximate_normalize(context, var, x, y, z): my_variable = context.variables[var] assert normalize(my_variable).approximately_equals(vector(x, y, z))
def check_normalize(context, var, x, y, z): my_variable = context.variables[var] assert normalize(my_variable) == vector(x, y, z)