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 update(self, new: HitRecordList) -> HitRecordList: if new.compress_idx is not None: idx = new.compress_idx old_idx = cp.arange(len(idx)) else: idx = slice(0, -1) old_idx = slice(0, -1) change = (new.t[old_idx] < self.t[idx]) & (new.t[old_idx] > 0) if not change.any(): return self change_3 = Vec3List.from_array(change) self.p.e[idx] = cp.where( change_3.e, new.p.e[old_idx], self.p.e[idx] ) self.t[idx] = cp.where( change, new.t[old_idx], self.t[idx] ) self.material[idx] = cp.where( change, new.material[old_idx], self.material[idx] ) self.normal.e[idx] = cp.where( change_3.e, new.normal.e[old_idx], self.normal.e[idx] ) self.front_face[idx] = cp.where( change, new.front_face[old_idx], self.front_face[idx] ) return self
def set_face_normal(self, r: RayList, outward_normal: Vec3List) \ -> HitRecordList: self.front_face = (r.direction() * outward_normal).e.sum(axis=1) < 0 front_face_3 = Vec3List.from_array(self.front_face) self.normal = Vec3List( np.where(front_face_3.e, outward_normal.e, -outward_normal.e)) return self
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 scatter(self, r_in: RayList, rec: HitRecordList) \ -> Tuple[RayList, Vec3List]: etai_over_etat = cp.where( rec.front_face, 1 / self.ref_idx, self.ref_idx ) unit_direction = r_in.direction().unit_vector() cos_theta = -unit_direction @ rec.normal cos_theta = cp.where(cos_theta > 1, 1, cos_theta) sin_theta = cp.sqrt(1 - cos_theta**2) reflect_prob = self.schlick(cos_theta, etai_over_etat) reflect_condition = ( (etai_over_etat * sin_theta > 1) | (random_float_list(len(r_in)) < reflect_prob) ) # total internal reflection reflected = (unit_direction.mul_ndarray(reflect_condition)).reflect( rec.normal.mul_ndarray(reflect_condition) ) # refraction refracted = (unit_direction.mul_ndarray(~reflect_condition)).refract( rec.normal.mul_ndarray(~reflect_condition), etai_over_etat ) direction = reflected + refracted condition = rec.t > 0 scattered = RayList( rec.p.mul_ndarray(condition), direction.mul_ndarray(condition) ) attenuation = Vec3List.from_array(condition) * Color(1, 1, 1) return scattered, attenuation
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 update(self, new: HitRecordList) -> HitRecordList: change = (new.t < self.t) & (new.t > 0) if not change.any(): return self change_3 = Vec3List.from_array(change) self.p = Vec3List(np.where(change_3.e, new.p.e, self.p.e)) self.t = np.where(change, new.t, self.t) self.material = np.where(change, new.material, self.material) self.normal = Vec3List( np.where(change_3.e, new.normal.e, self.normal.e)) self.front_face = np.where(change, new.front_face, self.front_face) return self
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 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
def set_face_normal(self, r, outward_normal): self.front_face = (r.direction() * outward_normal).e.sum(axis=1) < 0 front_face_3 = Vec3List.from_array(self.front_face) self.normal = Vec3List( cp.where(front_face_3.e, outward_normal.e, -outward_normal.e)) return self