def random_scene(aspect_ratio: float, time0: float, time1: float) \ -> Tuple[BVHNode, Camera]: world = HittableList() # ground_material = Lambertian(SolidColor(0.5, 0.5, 0.5)) ground_material = Lambertian( CheckerTexture(SolidColor(0.2, 0.3, 0.1), SolidColor(0.9, 0.9, 0.9))) world.add(Sphere(Point3(0, -1000, 0), 1000, ground_material)) for a in range(-11, 11): for b in range(-11, 11): choose_mat = random_float() center = Point3(a + 0.9 * random_float(), 0.2, b + 0.9 * random_float()) if (center - Vec3(4, 0.2, 0)).length() > 0.9: if choose_mat < 0.6: # Diffuse albedo = Color.random() * Color.random() sphere_material_diffuse = Lambertian(SolidColor(albedo)) center2 = center + Vec3(0, random_float(0, 0.5), 0) world.add( MovingSphere(center, center2, 0, 1, 0.2, sphere_material_diffuse)) elif choose_mat < 0.8: # Metal albedo = Color.random(0.5, 1) fuzz = random_float(0, 0.5) sphere_material_metal = Metal(albedo, fuzz) world.add(Sphere(center, 0.2, sphere_material_metal)) else: # Glass sphere_material_glass = Dielectric(1.5) world.add(Sphere(center, 0.2, sphere_material_glass)) material_1 = Dielectric(1.5) world.add(Sphere(Point3(0, 1, 0), 1, material_1)) material_2 = Lambertian(SolidColor(0.4, 0.2, 0.1)) world.add(Sphere(Point3(-4, 1, 0), 1, material_2)) material_3 = Metal(Color(0.7, 0.6, 0.5), 0) world.add(Sphere(Point3(4, 1, 0), 1, material_3)) world_bvh = BVHNode(world.objects, time0, time1) lookfrom = Point3(13, 2, 3) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = 10 aperture: float = 0.1 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, time0, time1) return world_bvh, cam
def __init__(self, point: Point3, t: float, mat_idx: int, normal: Vec3 = Vec3(), front_face: bool = False) -> None: self.p = point self.t = t self.material_idx = mat_idx self.normal = normal self.front_face = front_face
def main() -> None: aspect_ratio = 16 / 9 image_width = 256 image_height = int(image_width / aspect_ratio) samples_per_pixel = 20 max_depth = 10 world: HittableList = three_ball_scene() lookfrom = Point3(13, 2, 3) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = 10 aperture: float = 0.1 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus) print("Start rendering.") start_time = time.time() n_processer = multiprocessing.cpu_count() img_list: List[Img] = Parallel(n_jobs=n_processer)( delayed(scan_line)(j, world, cam, image_width, image_height, samples_per_pixel, max_depth) for j in range(image_height - 1, -1, -1)) final_img = Img(image_width, image_height) final_img.set_array(np.concatenate([img.frame for img in img_list])) end_time = time.time() print(f"\nDone. Total time: {round(end_time - start_time, 1)} s.") final_img.save("./output.png", True)
def __init__(self, lookfrom: Point3, lookat: Point3, vup: Vec3, vfov: float, aspect_ratio: float, aperture: float, focus_dist: float, t0: float = 0, t1: float = 0) -> None: """ vfov: vertical field-of-view in degress """ theta: float = degrees_to_radians(vfov) h: float = np.tan(theta / 2) viewport_height: float = 2 * h viewport_width: float = aspect_ratio * viewport_height self.w: Vec3 = (lookfrom - lookat).unit_vector() self.u: Vec3 = vup.cross(self.w).unit_vector() self.v: Vec3 = self.w.cross(self.u) self.origin: Point3 = lookfrom self.horizontal: Vec3 = self.u * viewport_width * focus_dist self.vertical: Vec3 = self.v * viewport_height * focus_dist self.lower_left_corner: Point3 = ( self.origin - self.horizontal/2 - self.vertical/2 - self.w * focus_dist ) self.lens_radius: float = aperture / 2 self.time0 = t0 self.time1 = t1
def three_ball_scene(aspect_ratio: float, time0: float, time1: float) \ -> Tuple[BVHNode, Camera]: world = HittableList() world.add( Sphere(Point3(0, 0, -1), 0.5, Lambertian(SolidColor(0.1, 0.2, 0.5)))) world.add( Sphere(Point3(0, -100.5, -1), 100, Lambertian(SolidColor(0.8, 0.8, 0)))) world.add(Sphere(Point3(1, 0, -1), 0.5, Metal(Color(0.8, 0.6, 0.2), 0.3))) world.add(Sphere(Point3(-1, 0, -1), 0.5, Dielectric(1.5))) world.add(Sphere(Point3(-1, 0, -1), -0.45, Dielectric(1.5))) world_bvh = BVHNode(world.objects, time0, time1) lookfrom = Point3(3, 3, 2) lookat = Point3(0, 0, -1) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = (lookfrom - lookat).length() aperture: float = 0 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, time0, time1) # lookfrom = Point3(13, 2, 3) # lookat = Point3(0, 0, 0) # vup = Vec3(0, 1, 0) # vfov = 20 # dist_to_focus: float = 10 # aperture: float = 0.1 # cam = Camera( # lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, # time0, time1 # ) return world_bvh, cam
def __init__(self, origin: Point3 = Point3(), direction: Vec3 = Vec3(), time: float = 0) -> None: self.orig = origin self.dir = direction self.tm = time
def scatter(self, r_in, rec): condition = (rec.t > 0) & rec.front_face scatter_direction = rec.normal + Vec3.random_unit_vector(len(r_in)) scattered = RayList(rec.p.mul_ndarray(condition), scatter_direction.mul_ndarray(condition)) attenuation = Vec3List.from_array(condition) * self.albedo return scattered, attenuation
def main() -> None: aspect_ratio = 16 / 9 image_width = 720 image_height = int(image_width / aspect_ratio) samples_per_pixel = 48 max_depth = 5 world = random_scene() lookfrom = Point3(13, 2, 3) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus = 10 aperture = 0.1 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus) print("Start rendering.") start_time = time.time() img_list = Parallel(n_jobs=2, verbose=20)( delayed(scan_frame)(world, cam, image_width, image_height, max_depth) for s in range(samples_per_pixel)) end_time = time.time() print(f"\nDone. Total time: {round(end_time - start_time, 1)} s.") final_img = Img(image_width, image_height) for img in img_list: final_img.write_frame(img) final_img.average(samples_per_pixel).gamma(2) final_img.save("./output.png", True)
def __init__(self, obj: Hittable, angle: float) -> None: radians = degrees_to_radians(angle) self.sin_theta = np.sin(radians) self.cos_theta = np.cos(radians) self.obj = obj self.bbox = obj.bounding_box(0, 1) if self.bbox is None: raise ValueError point_min = Point3(np.inf, np.inf, np.inf) point_max = Point3(-np.inf, -np.inf, -np.inf) for i in range(2): for j in range(2): for k in range(2): x = i * self.bbox.max().x() + (1 - i) * self.bbox.min().x() y = j * self.bbox.max().y() + (1 - j) * self.bbox.min().y() z = k * self.bbox.max().z() + (1 - k) * self.bbox.min().z() newx = self.cos_theta * x + self.sin_theta * z newz = -self.sin_theta * x + self.cos_theta * z new = Vec3(newx, y, newz) for c in range(3): point_min[c] = np.minimum(point_min[c], new[c]) point_max[c] = np.maximum(point_max[c], new[c]) self.bbox = AABB(point_min, point_max)
def hit(self, r: Ray, t_min: float, t_max: float) -> Optional[HitRecord]: rec1 = self.boundary.hit(r, -np.inf, np.inf) if rec1 is None: return None rec2 = self.boundary.hit(r, rec1.t + 0.0001, np.inf) if rec2 is None: return None if rec1.t < t_min: rec1.t = t_min if rec1.t < 0: rec1.t = 0 if rec2.t > t_max: rec2.t = t_max if rec1.t >= rec2.t: return None ray_length = r.direction().length() distance_inside_boundary = (rec2.t - rec1.t) * ray_length hit_distance = self.neg_inv_density * np.log(random_float()) if hit_distance > distance_inside_boundary: return None t = rec1.t + hit_distance / ray_length p = r.at(t) rec = HitRecord(p, t, self.phase_function) rec.normal = Vec3(1, 0, 0) rec.front_face = True return rec
def main() -> None: aspect_ratio = 16 / 9 image_width = 256 image_height = int(image_width / aspect_ratio) samples_per_pixel = 20 max_depth = 10 world: HittableList = three_ball_scene() lookfrom = Point3(13, 2, 3) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = 10 aperture: float = 0.1 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus) print("Start rendering.") start_time = time.time() n_processer = multiprocessing.cpu_count() img_list: List[Img] = Parallel(n_jobs=n_processer, verbose=10)( delayed(scan_line)(j, world, cam, image_width, image_height, samples_per_pixel, max_depth) for j in range(image_height - 1, -1, -1)) # # Profile prologue # import cProfile # import pstats # import io # from pstats import SortKey # pr = cProfile.Profile() # pr.enable() # img_list: List[Img] = list() # for j in range(image_height-1, -1, -1): # img_list.append( # scan_line( # j, world, cam, # image_width, image_height, # samples_per_pixel, max_depth # ) # ) # # Profile epilogue # pr.disable() # s = io.StringIO() # sortby = SortKey.CUMULATIVE # ps = pstats.Stats(pr, stream=s).sort_stats(sortby) # ps.print_stats() # print(s.getvalue()) final_img = Img(image_width, image_height) final_img.set_array(np.concatenate([img.frame for img in img_list])) end_time = time.time() print(f"\nDone. Total time: {round(end_time - start_time, 1)} s.") final_img.save("./output.png", True)
def scatter(self, r_in, rec): condition = (rec.t > 0) & rec.front_face scatter_direction = Vec3.random_in_hemisphere(rec.normal) scattered = RayList(rec.p.mul_ndarray(condition), scatter_direction.mul_ndarray(condition)) attenuation = Vec3List.from_array(condition) * self.albedo return scattered, attenuation
def scatter(self, r_in: Ray, rec: HitRecord) \ -> Optional[Tuple[Ray, Color]]: if not rec.front_face: return None scatter_direction: Vec3 = rec.normal + Vec3.random_unit_vector() scattered = Ray(rec.p, scatter_direction, r_in.time()) attenuation = self.albedo.value(rec.u, rec.v, rec.p) return scattered, attenuation
def scatter(self, r_in: Ray, rec: HitRecord) \ -> Optional[Tuple[Ray, Color]]: reflected: Vec3 = r_in.direction().unit_vector().reflect(rec.normal) \ + Vec3.random_in_unit_sphere() * self.fuzz scattered = Ray(rec.p, reflected) attenuation = self.albedo if scattered.direction() @ rec.normal > 0: return scattered, attenuation return None
def cornell_box(aspect_ratio: float, time0: float, time1: float) \ -> Tuple[BVHNode, Camera]: world = HittableList() # Colors red = Lambertian(SolidColor(0.65, 0.05, 0.05)) white = Lambertian(SolidColor(0.73, 0.73, 0.73)) green = Lambertian(SolidColor(0.12, 0.45, 0.15)) light = DiffuseLight(SolidColor(15, 15, 15)) # Outer box world.add(FlipFace(YZRect(0, 555, 0, 555, 555, green))) world.add(YZRect(0, 555, 0, 555, 0, red)) world.add(XZRect(213, 343, 227, 332, 554, light)) world.add(XZRect(0, 555, 0, 555, 0, white)) world.add(FlipFace(XZRect(0, 555, 0, 555, 555, white))) world.add(FlipFace(XYRect(0, 555, 0, 555, 555, white))) # Objects in the box box1: Hittable = Box(Vec3(0, 0, 0), Point3(165, 330, 165), white) box1 = RotateY(box1, 15) box1 = Translate(box1, Point3(265, 0, 295)) box1 = ConstantMedium(box1, 0.01, SolidColor(0, 0, 0)) world.add(box1) box2: Hittable = Box(Point3(0, 0, 0), Point3(165, 165, 165), white) box2 = RotateY(box2, -18) box2 = Translate(box2, Point3(130, 0, 65)) box2 = ConstantMedium(box2, 0.01, SolidColor(1, 1, 1)) world.add(box2) world_bvh = BVHNode(world.objects, time0, time1) lookfrom = Point3(278, 278, -800) lookat = Point3(278, 278, 0) vup = Vec3(0, 1, 0) vfov = 40 dist_to_focus: float = 10 aperture: float = 0 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, time0, time1) return world_bvh, cam
def __init__(self) -> None: self.point_count: int = 256 self.ranvec: List[Vec3] = list() for i in range(self.point_count): self.ranvec.append(Vec3.random(-1, 1).unit_vector()) self.perm_x: List[int] = self.perlin_generate_perm() self.perm_y: List[int] = self.perlin_generate_perm() self.perm_z: List[int] = self.perlin_generate_perm()
def get_ray(self, s: float, t: float) -> Ray: rd: Vec3 = Vec3.random_in_unit_disk() * self.lens_radius offset: Vec3 = 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 main() -> None: aspect_ratio = 16 / 9 image_width = 1920 image_height = int(image_width / aspect_ratio) samples_per_pixel = 48 max_depth = 10 world: HittableList = random_scene() lookfrom = Point3(13, 2, 3) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = 10 aperture: float = 0.1 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus) print("Start rendering.") start_time = time.time() img_list: List[Vec3List] = Parallel(n_jobs=4, verbose=20)( delayed(scan_frame)(world, cam, image_width, image_height, max_depth) for s in range(samples_per_pixel)) # # Profile prologue # import cProfile # import pstats # import io # from pstats import SortKey # pr = cProfile.Profile() # pr.enable() # img_list: List[Vec3List] = list() # for sample_num in range(samples_per_pixel): # img_list.append( # scan_frame(world, cam, image_width, image_height, max_depth) # ) # # Profile epilogue # pr.disable() # s = io.StringIO() # sortby = SortKey.CUMULATIVE # ps = pstats.Stats(pr, stream=s).sort_stats(sortby) # ps.print_stats() # print(s.getvalue()) end_time = time.time() print(f"\nDone. Total time: {round(end_time - start_time, 1)} s.") final_img = Img(image_width, image_height) for img in img_list: final_img.write_frame(img) final_img.average(samples_per_pixel).gamma(2).up_side_down() final_img.save("./output.png", True)
def scatter(self, r_in, rec): condition = (rec.t > 0) & rec.front_face reflected = (r_in.direction().unit_vector().reflect(rec.normal) + Vec3.random_in_unit_sphere_list(len(r_in)) * self.fuzz) condition = condition & (reflected @ rec.normal > 0) scattered = RayList(rec.p.mul_ndarray(condition), reflected.mul_ndarray(condition)) attenuation = Vec3List.from_array(condition) * self.albedo return scattered, attenuation
def hit(self, r: Ray, t_min: float, t_max: float) -> Optional[HitRecord]: t = (self.k - r.origin().x()) / r.direction().x() if t < t_min or t > t_max: return None y = r.origin().y() + t*r.direction().y() z = r.origin().z() + t*r.direction().z() if (y < self.y0) or (y > self.y1) or (z < self.z0) or (z > self.z1): return None rec = HitRecord(r.at(t), t, self.material) rec.set_face_normal(r, Vec3(1, 0, 0)) rec.u = (y - self.y0) / (self.y1 - self.y0) rec.v = (z - self.z0) / (self.z1 - self.z0) return rec
def get_ray(self, s, t): if len(s) != len(t): raise ValueError rd = Vec3.random_in_unit_disk(len(s)) * self.lens_radius u = Vec3List.from_vec3(self.u, len(s)) v = Vec3List.from_vec3(self.v, len(s)) offset_list = u.mul_ndarray(rd[0]) + v.mul_ndarray(rd[1]) origin_list = offset_list + self.origin horizontal_multi = Vec3List.from_vec3(self.horizontal, len(s)) vertical_multi = Vec3List.from_vec3(self.vertical, len(s)) direction_list = (horizontal_multi.mul_ndarray(s) - vertical_multi.mul_ndarray(t) + self.top_left_corner - self.origin - offset_list) return RayList(origin_list, direction_list)
def trilinear_interp(c: List[List[List[Vec3]]], u: float, v: float, w: float) -> float: uu = u * u * (3 - 2 * u) vv = v * v * (3 - 2 * v) ww = w * w * (3 - 2 * w) accum: float = 0 for i in range(2): for j in range(2): for k in range(2): weight_v = Vec3(u - i, v - j, w - k) accum += ((i * uu + (1 - i) * (1 - uu)) * (j * vv + (1 - j) * (1 - vv)) * (k * ww + (1 - k) * (1 - ww)) * (c[i][j][k] @ weight_v)) return accum
def two_perlin_spheres(aspect_ratio: float, time0: float, time1: float) \ -> Tuple[BVHNode, Camera]: world = HittableList() pertext = NoiseTexture(4) world.add(Sphere(Point3(0, -1000, 0), 1000, Lambertian(pertext))) world.add(Sphere(Point3(0, 2, 0), 2, Lambertian(pertext))) world_bvh = BVHNode(world.objects, time0, time1) lookfrom = Point3(13, 2, 3) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = 10 aperture: float = 0 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, time0, time1) return world_bvh, cam
def two_spheres(aspect_ratio: float, time0: float, time1: float) \ -> Tuple[BVHNode, Camera]: world = HittableList() checker = CheckerTexture(SolidColor(0.2, 0.3, 0.1), SolidColor(0.9, 0.9, 0.9)) world.add(Sphere(Point3(0, -10, 0), 10, Lambertian(checker))) world.add(Sphere(Point3(0, 10, 0), 10, Lambertian(checker))) world_bvh = BVHNode(world.objects, time0, time1) lookfrom = Point3(13, 2, 3) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = 10 aperture: float = 0 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, time0, time1) return world_bvh, cam
def earth(aspect_ratio: float, time0: float, time1: float) \ -> Tuple[BVHNode, Camera]: world = HittableList() earth_texture = ImageTexture("earthmap.jpg") earth_surface = Lambertian(earth_texture) globe = Sphere(Point3(0, 0, 0), 2, earth_surface) world.add(globe) world_bvh = BVHNode(world.objects, time0, time1) lookfrom = Point3(0, 0, -5) lookat = Point3(0, 0, 0) vup = Vec3(0, 1, 0) vfov = 50 dist_to_focus: float = 10 aperture: float = 0 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, time0, time1) return world_bvh, cam
def random_scene() -> HittableList: world = HittableList() ground_material = Lambertian(Color(0.5, 0.5, 0.5), 1) world.add(Sphere(Point3(0, -1000, 0), 1000, ground_material)) sphere_material_glass = Dielectric(1.5, 2) for a in range(-11, 11): for b in range(-11, 11): choose_mat = random_float() center = Point3(a + 0.9 * random_float(), 0.2, b + 0.9 * random_float()) if (center - Vec3(4, 0.2, 0)).length() > 0.9: idx = (a * 22 + b) + (11 * 22 + 11) + 6 if choose_mat < 0.6: # Diffuse albedo = Color.random() * Color.random() sphere_material_diffuse = Lambertian(albedo, idx) world.add(Sphere(center, 0.2, sphere_material_diffuse)) elif choose_mat < 0.8: # Metal albedo = Color.random(0.5, 1) fuzz = random_float(0, 0.5) sphere_material_metal = Metal(albedo, fuzz, idx) world.add(Sphere(center, 0.2, sphere_material_metal)) else: # Glass world.add(Sphere(center, 0.2, sphere_material_glass)) material_1 = Dielectric(1.5, 3) world.add(Sphere(Point3(0, 1, 0), 1, material_1)) material_2 = Lambertian(Color(0.4, 0.2, 0.1), 4) world.add(Sphere(Point3(-4, 1, 0), 1, material_2)) material_3 = Metal(Color(0.7, 0.6, 0.5), 0, 5) world.add(Sphere(Point3(4, 1, 0), 1, material_3)) return world
def simple_light(aspect_ratio: float, time0: float, time1: float) \ -> Tuple[BVHNode, Camera]: world = HittableList() pertext = NoiseTexture(4) world.add(Sphere(Point3(0, -1000, 0), 1000, Lambertian(pertext))) world.add(Sphere(Point3(0, 2, 0), 2, Lambertian(pertext))) difflight = DiffuseLight(SolidColor(4, 4, 4)) world.add(Sphere(Point3(0, 7, 0), 2, difflight)) world.add(XYRect(3, 5, 1, 3, -2, difflight)) world_bvh = BVHNode(world.objects, time0, time1) lookfrom = Point3(23, 4, 5) lookat = Point3(0, 2, 0) vup = Vec3(0, 1, 0) vfov = 20 dist_to_focus: float = 10 aperture: float = 0 cam = Camera(lookfrom, lookat, vup, vfov, aspect_ratio, aperture, dist_to_focus, time0, time1) return world_bvh, cam
def scatter(self, r_in: Ray, rec: HitRecord) \ -> Optional[Tuple[Ray, Color]]: scatter_direction: Vec3 = Vec3.random_in_hemisphere(rec.normal) scattered = Ray(rec.p, scatter_direction) attenuation = self.albedo return scattered, attenuation
def scatter(self, r_in: Ray, rec: HitRecord) \ -> Optional[Tuple[Ray, Color]]: scattered = Ray(rec.p, Vec3.random_in_unit_sphere(), r_in.time()) attenuation = self.albedo.value(rec.u, rec.v, rec.p) return scattered, attenuation
def __init__( self, origin: Point3 = Point3(), direction: Vec3 = Vec3()) -> None: self.orig = origin self.dir = direction