Exemple #1
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
Exemple #2
0
    def hit(self, r: RayList, t_min: float, t_max: Union[float, cp.ndarray]) \
            -> HitRecordList:
        if isinstance(t_max, (int, float, cp.floating)):
            t_max_list = cp.full(len(r), t_max, cp.float32)
        else:
            t_max_list = t_max

        oc: Vec3List = r.origin() - self.center
        a: cp.ndarray = r.direction().length_squared()
        half_b: cp.ndarray = oc @ r.direction()
        c: cp.ndarray = oc.length_squared() - self.radius**2
        discriminant_list: cp.ndarray = half_b**2 - a*c

        discriminant_condition = discriminant_list > 0
        if not discriminant_condition.any():
            return HitRecordList.new(len(r)).set_compress_info(None)

        # Calculate t
        positive_discriminant_list = (
            discriminant_list * discriminant_condition
        )
        root = cp.sqrt(positive_discriminant_list)
        non_zero_a = a - (a == 0)
        t_0 = (-half_b - root) / non_zero_a
        t_1 = (-half_b + root) / non_zero_a

        # Choose t
        t_0_condition = (
            (t_min < t_0) & (t_0 < t_max_list) & discriminant_condition
        )
        t_1_condition = (
            (t_min < t_1) & (t_1 < t_max_list)
            & (~t_0_condition) & discriminant_condition
        )
        t = cp.where(t_0_condition, t_0, 0)
        t = cp.where(t_1_condition, t_1, t)

        # Compression
        condition = t > 0
        full_rate = condition.sum() / len(t)
        if full_rate > 0.5:
            idx = None
        else:
            idx = cp.where(condition)[0]
            t = t[idx]
            r = RayList(
                Vec3List(r.orig.get_ndarray(idx)),
                Vec3List(r.dir.get_ndarray(idx))
            )

        # Wrap up result
        point = r.at(t)
        outward_normal = (point - self.center) / self.radius

        result = HitRecordList(
            point, t, cp.full(len(r), self.material.idx, dtype=cp.int32)
        ).set_face_normal(r, outward_normal).set_compress_info(idx)

        return result
Exemple #3
0
 def new(length: int) -> HitRecordList:
     return HitRecordList(
         Vec3List.new_empty(length),
         cp.zeros(length, dtype=cp.float32),
         cp.zeros(length, dtype=cp.int32),
         Vec3List.new_empty(length),
         cp.empty(length, dtype=cp.bool)
     )
Exemple #4
0
 def new_from_t(t: cp.ndarray) -> HitRecordList:
     length = len(t)
     return HitRecordList(
         Vec3List.new_empty(length),
         t,
         cp.zeros(length, dtype=cp.int32),
         Vec3List.new_empty(length),
         cp.empty(length, dtype=cp.bool)
     )
Exemple #5
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
Exemple #6
0
    def compress(self, r: RayList, closest_so_far: cp.ndarray) \
            -> Tuple[RayList, cp.ndarray]:
        condition = r.dir.length_squared() > 0
        full_rate = condition.sum() / len(r)
        if full_rate > 0.5:
            self.idx = None
            return r, closest_so_far

        self.idx = cp.where(condition)[0]
        self.old_length = len(condition)
        new_r = RayList(Vec3List(r.orig.get_ndarray(self.idx)),
                        Vec3List(r.dir.get_ndarray(self.idx)))
        new_c = closest_so_far[self.idx]
        return new_r, new_c
def compress(r, rec):
    condition = rec.t > 0
    full_rate = condition.sum() / len(r)
    if full_rate > 0.5:
        return r, rec, None

    idx = cp.where(condition)[0]
    new_r = RayList(Vec3List(r.origin().get_ndarray(idx)),
                    Vec3List(r.direction().get_ndarray(idx)))
    new_rec = HitRecordList(Vec3List(rec.p.get_ndarray(idx)), rec.t[idx],
                            rec.material[idx],
                            Vec3List(rec.normal.get_ndarray(idx)),
                            rec.front_face[idx])
    return new_r, new_rec, idx
Exemple #8
0
def compress(r: RayList, rec: HitRecordList) \
        -> Tuple[RayList, HitRecordList, Optional[np.ndarray]]:
    condition = rec.t > 0
    full_rate = condition.sum() / len(r)
    if full_rate > 0.6:
        return r, rec, None

    idx: np.ndarray = np.where(condition)[0]
    new_r = RayList(Vec3List(r.orig.get_ndarray(idx)),
                    Vec3List(r.dir.get_ndarray(idx)))
    new_rec = HitRecordList(Vec3List(rec.p.get_ndarray(idx)), rec.t[idx],
                            rec.material[idx],
                            Vec3List(rec.normal.get_ndarray(idx)),
                            rec.front_face[idx])
    return new_r, new_rec, idx
Exemple #9
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
Exemple #10
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
    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)
Exemple #12
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
Exemple #13
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
Exemple #14
0
 def __init__(self, point: Vec3List, t: cp.ndarray, mat: cp.ndarray,
              normal: Vec3List = Vec3List.new_zero(0),
              front_face: cp.ndarray = cp.array([])) -> None:
     self.p = point
     self.t = t
     self.material = mat
     self.normal = normal
     self.front_face = front_face
 def __init__(self,
              point,
              t,
              mat,
              normal=Vec3List.new_zero(0),
              front_face=cp.array([])):
     self.p = point
     self.t = t
     self.material = mat
     self.normal = normal
     self.front_face = front_face
Exemple #16
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
def decompress(r, a, idx, length):
    if idx is None:
        return r, a

    old_idx = cp.arange(len(idx))
    new_r = RayList.new_zero(length)
    new_r.origin().e[idx] = r.origin().e[old_idx]
    new_r.direction().e[idx] = r.direction().e[old_idx]

    new_a = Vec3List.new_zero(length)
    new_a.e[idx] = a.e[old_idx]

    return new_r, new_a
Exemple #18
0
def decompress(r: RayList, a: Vec3List, idx: Optional[np.ndarray],
               length: int) -> Tuple[RayList, Vec3List]:
    if idx is None:
        return r, a

    old_idx = np.arange(len(idx))

    new_r = RayList.new_zero(length)
    new_r.orig.e[idx] = r.orig.e[old_idx]
    new_r.dir.e[idx] = r.dir.e[old_idx]

    new_a = Vec3List.new_zero(length)
    new_a.e[idx] = a.e[old_idx]

    return new_r, new_a
Exemple #19
0
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
Exemple #20
0
 def new_empty(length: int) -> RayList:
     return RayList(Vec3List.new_empty(length), Vec3List.new_empty(length))
 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
Exemple #22
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 new_zero(length):
     return RayList(Vec3List.new_zero(length), Vec3List.new_zero(length))
 def new_empty(length):
     return RayList(Vec3List.new_empty(length), Vec3List.new_empty(length))
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
Exemple #26
0
 def new_zero(length: int) -> RayList:
     return RayList(Vec3List.new_zero(length), Vec3List.new_zero(length))
Exemple #27
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