예제 #1
0
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
예제 #3
0
    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
예제 #4
0
 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
예제 #5
0
 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
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
 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
예제 #9
0
    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
예제 #10
0
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