def ray_color(r: RayList, world: HittableList, depth: int) \ -> Tuple[Optional[RayList], Optional[Vec3List], Vec3List]: length = len(r) if not r.direction().e.any(): return None, None, Vec3List.new_zero(length) # Calculate object hits rec_list: HitRecordList = world.hit(r, 0.001, cp.inf) # Useful empty arrays empty_vec3list = Vec3List.new_zero(length) empty_array_float = cp.zeros(length, cp.float32) empty_array_bool = cp.zeros(length, cp.bool) empty_array_int = cp.zeros(length, cp.int32) # Background / Sky unit_direction = r.direction().unit_vector() sky_condition = Vec3List.from_array((unit_direction.length() > 0) & (rec_list.material == 0)) t = (unit_direction.y() + 1) * 0.5 blue_bg = (Vec3List.from_vec3(Color(1, 1, 1), length).mul_ndarray(1 - t) + Vec3List.from_vec3(Color(0.5, 0.7, 1), length).mul_ndarray(t)) result_bg = Vec3List(cp.where(sky_condition.e, blue_bg.e, empty_vec3list.e)) if depth <= 1: return None, None, result_bg # Material scatter calculations materials: Dict[int, Material] = world.get_materials() scattered_list = RayList.new_zero(length) attenuation_list = Vec3List.new_zero(length) for mat_idx in materials: mat_condition = (rec_list.material == mat_idx) mat_condition_3 = Vec3List.from_array(mat_condition) if not mat_condition.any(): continue ray = RayList( Vec3List(cp.where(mat_condition_3.e, r.orig.e, empty_vec3list.e)), Vec3List(cp.where(mat_condition_3.e, r.dir.e, empty_vec3list.e))) rec = HitRecordList( Vec3List( cp.where(mat_condition_3.e, rec_list.p.e, empty_vec3list.e)), cp.where(mat_condition, rec_list.t, empty_array_float), cp.where(mat_condition, rec_list.material, empty_array_int), Vec3List( cp.where(mat_condition_3.e, rec_list.normal.e, empty_vec3list.e)), cp.where(mat_condition, rec_list.front_face, empty_array_bool)) ray, rec, idx_list = compress(ray, rec) scattered, attenuation = materials[mat_idx].scatter(ray, rec) scattered, attenuation = decompress(scattered, attenuation, idx_list, length) scattered_list += scattered attenuation_list += attenuation return scattered_list, attenuation_list, result_bg
def ray_color(r, world, depth): length = len(r) if not r.direction().e.any(): return None, None, Vec3List.new_zero(length) rec_list = world.hit(r, 0.001, cp.inf) empty_vec3list = Vec3List.new_zero(length) empty_array_float = cp.zeros(length, cp.float32) empty_array_bool = cp.zeros(length, cp.bool) empty_array_int = cp.zeros(length, cp.int32) unit_direction = r.direction().unit_vector() sky_condition = Vec3List.from_array((unit_direction.length() > 0) & (rec_list.material == 0)) t = (unit_direction.y() + 1) * 0.5 blue_bg = (Vec3List.from_vec3(Color(1, 1, 1), length).mul_ndarray(1 - t) + Vec3List.from_vec3(Color(0.5, 0.7, 1), length).mul_ndarray(t)) result_bg = Vec3List(cp.where(sky_condition.e, blue_bg.e, empty_vec3list.e)) if depth <= 1: return None, None, result_bg materials = world.get_materials() scattered_list = RayList.new_zero(length) attenuation_list = Vec3List.new_zero(length) for mat_idx in materials: mat_condition = (rec_list.material == mat_idx) mat_condition_3 = Vec3List.from_array(mat_condition) if not mat_condition.any(): continue ray = RayList( Vec3List( cp.where(mat_condition_3.e, r.origin().e, empty_vec3list.e)), Vec3List( cp.where(mat_condition_3.e, r.direction().e, empty_vec3list.e))) rec = HitRecordList( Vec3List( cp.where(mat_condition_3.e, rec_list.p.e, empty_vec3list.e)), cp.where(mat_condition, rec_list.t, empty_array_float), cp.where(mat_condition, rec_list.material, empty_array_int), Vec3List( cp.where(mat_condition_3.e, rec_list.normal.e, empty_vec3list.e)), cp.where(mat_condition, rec_list.front_face, empty_array_bool)) ray, rec, idx_list = compress(ray, rec) scattered, attenuation = materials[mat_idx].scatter(ray, rec) scattered, attenuation = decompress(scattered, attenuation, idx_list, length) scattered_list += scattered attenuation_list += attenuation return scattered_list, attenuation_list, result_bg
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 scan_line(j: int, world: HittableList, cam: Camera, image_width: int, image_height: int, samples_per_pixel: int, max_depth: int) -> Img: img = Img(image_width, 1) row_pixel_color = Vec3List.from_vec3(Color(), image_width) for s in range(samples_per_pixel): u: np.ndarray = (random_float_list(image_width) + np.arange(image_width)) / (image_width - 1) v: np.ndarray = (random_float_list(image_width) + j) / (image_height - 1) r: RayList = cam.get_ray(u, v) row_pixel_color += ray_color(r, world, max_depth) img.write_pixel_list(0, row_pixel_color, samples_per_pixel) return img
def ray_color(r: RayList, world: HittableList, depth: int) -> Vec3List: length = len(r) if not r.direction().e.any(): return Vec3List.new_zero(length) # Calculate object hits rec_list: HitRecordList = world.hit(r, 0.001, np.inf) # Useful empty arrays empty_vec3list = Vec3List.new_zero(length) empty_array_float = np.zeros(length, np.float32) empty_array_bool = np.zeros(length, np.bool) empty_array_int = np.zeros(length, np.int32) # Background / Sky unit_direction = r.direction().unit_vector() sky_condition = Vec3List.from_array((unit_direction.length() > 0) & (rec_list.material == 0)) t = (unit_direction.y() + 1) * 0.5 blue_bg = (Vec3List.from_vec3(Color(1, 1, 1), length).mul_ndarray(1 - t) + Vec3List.from_vec3(Color(0.5, 0.7, 1), length).mul_ndarray(t)) result_bg = Vec3List(np.where(sky_condition.e, blue_bg.e, empty_vec3list.e)) if depth <= 1: return result_bg # Per-material preparations materials: Dict[int, Material] = world.get_materials() material_dict: Dict[int, Tuple[RayList, HitRecordList]] = dict() for mat_idx in materials: mat_condition = (rec_list.material == mat_idx) mat_condition_3 = Vec3List.from_array(mat_condition) if not mat_condition.any(): continue raylist_temp = RayList( Vec3List(np.where(mat_condition_3.e, r.orig.e, empty_vec3list.e)), Vec3List(np.where(mat_condition_3.e, r.dir.e, empty_vec3list.e))) reclist_temp = HitRecordList( Vec3List( np.where(mat_condition_3.e, rec_list.p.e, empty_vec3list.e)), np.where(mat_condition, rec_list.t, empty_array_float), np.where(mat_condition, rec_list.material, empty_array_int), Vec3List( np.where(mat_condition_3.e, rec_list.normal.e, empty_vec3list.e)), np.where(mat_condition, rec_list.front_face, empty_array_bool)) material_dict[mat_idx] = raylist_temp, reclist_temp # Material scatter calculations scattered_list = RayList.new_zero(length) attenuation_list = Vec3List.new_zero(length) for key in material_dict: ray, rec = material_dict[key] ray, rec, idx_list = compress(ray, rec) scattered, attenuation = materials[key].scatter(ray, rec) scattered, attenuation = decompress(scattered, attenuation, idx_list, length) scattered_list += scattered attenuation_list += attenuation result_hittable = (attenuation_list * ray_color(scattered_list, world, depth - 1)) return result_hittable + result_bg