def get_ray(self, s, t, sample): rd = Vector3.random_in_unit_disk(s, t, sample) * self.lens_radius # rd = Vector3.concentric_sample_disk(s*2-1, t*2-1) * self.lens_radius offset = self.u * rd.x + self.v * rd.y return Ray( self.origin + offset, self.lower_left_corner + self.horizontal*s + self.vertical*t - self.origin - offset)
def __init__(self, pos, t): self.pos = pos self.normal = Vector3(0, 0, 0) self.t = t self.material = None self.front_face = False
def scatter(self, r_in, rec): reflected = Vector3.reflect(r_in.direction.normalized(), rec.normal) # Catch degenerate scatter direction if reflected.near_zero(): reflected = rec.normal scattered = Ray(rec.pos, reflected) attenuation = self.albedo return scattered.direction.dot(rec.normal) > 0, scattered, attenuation
def scatter(self, r_in, rec): scatter_direction = rec.normal + Vector3.random_unit_vector() # Catch degenerate scatter direction if scatter_direction.near_zero(): scatter_direction = rec.normal scattered = Ray(rec.pos, scatter_direction) attenuation = self.albedo return True, scattered, attenuation
def hit(self, r, t_min, t_max, recc=None): temp_rec = HitRecord(Vector3(0, 0, 0), 0.0) hit_anything = False closest_so_far = t_max rec = None for obj in self.objects: if obj.hit(r, t_min, closest_so_far, temp_rec): hit_anything = True closest_so_far = temp_rec.t rec = temp_rec return hit_anything, rec
def ray_colour(r, background, world, depth): hit, rec = world.hit(r, 0.001, utils.INFINITY) if depth <= 0: return Vector3(0, 0, 0) if not hit: return background emitted = rec.material.emitted() result, scattered, attenuation = rec.material.scatter(r, rec) if not result: return emitted return emitted + attenuation.mul(ray_colour(scattered, background, world, depth-1))
def scatter(self, r_in, rec): return True, Ray(Vector3(0, 0, 0), Vector3(0, 0, 0)), HitRecord(Vector3(0, 0, 0), 0.0)
def emitted(self): return Vector3(0, 0, 0)
def main(): # Image aspect_ratio = 16.0 / 9.0 image_width = 256 image_height = image_width/aspect_ratio background = Vector3(0, 0, 0) samples_per_pixel = 20 max_depth = 1 # World world = HittableList() # material_ground = Lambertian(Vector3(0.8, 0.8, 0.0)) # material_center = Lambertian(Vector3(0.7, 0.3, 0.3)) # material_left = Metal(Vector3(0.8, 0.8, 0.8)) material_emissive = DiffuseLight(Vector3(1,1,1), Vector3(1,1,1)) # material_right = Metal(Vector3(0.8, 0.6, 0.2)) # world.append(Sphere(Vector3(0, 0, -1), 0.5, material_center)) # world.append(Sphere(Vector3(1, 0.5, -0.5), 1, material_emissive)) # world.append(Sphere(Vector3(-1, 0, -1), 0.5, material_right)) # world.append(Sphere(Vector3(0, -100.5, -1), 100, material_ground)) sphere_count = 35 sphere_dist = 100 for i in range(0, sphere_count): world.append(Sphere(Vector3(utils.rand_range(-50, 50), utils.rand_range(-40, 40), sphere_dist), 1, material_emissive)) # Camera look_from = Vector3(0, 0, 0) look_at = Vector3(0, 0, 1) v_up = Vector3(0, 1, 0) dist_to_focus = (look_from - look_at).length() f_stop = 8 aperture = 1/f_stop cam = Camera(look_from, look_at, v_up, 30.0, aspect_ratio, aperture, dist_to_focus) # Render render_data = list() print("Commencing Rendering.") start_time = datetime.now() for j in reversed(range(0, int(image_height))): print("Scanlines remaining: %s" % j) for i in range(0, image_width): pixel_colour = Vector3(0, 0, 0) for s in range(0, samples_per_pixel): u = (i + utils.rand()) / (image_width-1) v = (j + utils.rand()) / (image_height-1) r = cam.get_ray(u, v, s) pixel_colour += ray_colour(r, background, world, max_depth) render_data.append(colour.write_colour(pixel_colour, samples_per_pixel)) print("\nDone.\nTime Spent: %s" % (datetime.now() - start_time)) file = image.write_image( width=image_width, height=image_height, data=render_data ) return file