Exemple #1
0
    def hit(self, r: Ray, t_min: float, t_max: float) -> Optional[HitRecord]:
        oc: Vec3 = r.origin() - self.center
        a: float = r.direction().length_squared()
        half_b: float = oc @ r.direction()
        c: float = oc.length_squared() - self.radius**2
        discriminant: float = half_b**2 - a * c

        if discriminant > 0:
            root: float = np.sqrt(discriminant)
            t_0: float = (-half_b - root) / a
            t_1: float = (-half_b + root) / a

            if t_min < t_0 < t_max:
                t = t_0
            elif t_min < t_1 < t_max:
                t = t_1
            else:
                return None

            point: Point3 = r.at(t)
            outward_normal: Vec3 = (point - self.center) / self.radius

            rec = HitRecord(point, t, self.material)
            rec.set_face_normal(r, outward_normal)
            return rec

        return None
Exemple #2
0
    def hit(self, r: Ray, t_min: float, t_max: float) -> Optional[HitRecord]:
        origin = r.origin().copy()
        direction = r.direction().copy()

        origin[0] = self.cos_theta * r.origin()[0] - self.sin_theta * r.origin(
        )[2]
        origin[2] = self.sin_theta * r.origin()[0] + self.cos_theta * r.origin(
        )[2]
        direction[0] = \
            self.cos_theta*r.direction()[0] - self.sin_theta*r.direction()[2]
        direction[2] = \
            self.sin_theta*r.direction()[0] + self.cos_theta*r.direction()[2]

        rotated_r = Ray(origin, direction, r.time())
        rec = self.obj.hit(rotated_r, t_min, t_max)
        if rec is None:
            return None

        p = rec.p.copy()
        normal = rec.normal.copy()

        p[0] = self.cos_theta * rec.p[0] + self.sin_theta * rec.p[2]
        p[2] = -self.sin_theta * rec.p[0] + self.cos_theta * rec.p[2]
        normal[0] = \
            self.cos_theta*rec.normal[0] + self.sin_theta*rec.normal[2]
        normal[2] = \
            -self.sin_theta*rec.normal[0] + self.cos_theta*rec.normal[2]

        rec.p = p
        rec.set_face_normal(rotated_r, normal)

        return rec
Exemple #3
0
 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
Exemple #4
0
 def hit_deprecated(self, r: Ray, tmin: float, tmax: float) -> bool:
     for i in range(3):
         t0: float = min((self.min()[i] - r.origin()[i]) / r.direction()[i],
                         (self.max()[i] - r.origin()[i]) / r.direction()[i])
         t1: float = min((self.min()[i] - r.origin()[i]) / r.direction()[i],
                         (self.max()[i] - r.origin()[i]) / r.direction()[i])
         tmin = max(t0, tmin)
         tmax = min(t1, tmax)
         if tmax <= tmin:
             return False
     return True
Exemple #5
0
    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 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
Exemple #7
0
    def scatter(self, r_in: Ray, rec: HitRecord) \
            -> Optional[Tuple[Ray, Color]]:
        if rec.front_face:
            etai_over_etat = 1 / self.ref_idx
        else:
            etai_over_etat = self.ref_idx

        unit_direction: Vec3 = r_in.direction().unit_vector()
        cos_theta: float = min(-unit_direction @ rec.normal, 1)
        sin_theta: float = np.sqrt(1 - cos_theta**2)
        reflect_prob: float = self.schlick(cos_theta, etai_over_etat)

        if etai_over_etat * sin_theta > 1 or random_float() < reflect_prob:
            # total internal reflection
            reflected: Vec3 = unit_direction.reflect(rec.normal)
            scattered = Ray(rec.p, reflected)
        else:
            # refraction
            refracted: Vec3 = unit_direction.refract(
                rec.normal, etai_over_etat
            )
            scattered = Ray(rec.p, refracted)

        attenuation = Color(1, 1, 1)
        return scattered, attenuation
Exemple #8
0
    def hit(self, r: Ray, t_min: float, t_max: float) -> Optional[HitRecord]:
        # remove the offset to hit the real object
        moved_r = Ray(r.origin() - self.offset, r.direction(), r.time())
        rec = self.obj.hit(moved_r, t_min, t_max)
        if rec is None:
            return None

        # add the offset back to simulate the move
        rec.p += self.offset
        rec.set_face_normal(moved_r, rec.normal)
        return rec
Exemple #9
0
 def hit(self, r: Ray, tmin: float, tmax: float) -> bool:
     for i in range(3):
         invD: float = 1 / r.direction()[i]
         t0: float = (self.min()[i] - r.origin()[i]) * invD
         t1: float = (self.max()[i] - r.origin()[i]) * invD
         if invD < 0:
             t0, t1 = t1, t0
         tmin = max(t0, tmin)
         tmax = min(t1, tmax)
         if tmax <= tmin:
             return False
     return True
Exemple #10
0
def ray_color(r: Ray, world: HittableList, depth: int) -> Color:
    if depth <= 0:
        return Color(0, 0, 0)

    rec: Optional[HitRecord] = world.hit(r, 0.001, np.inf)
    if rec is not None:
        scatter_result = rec.material.scatter(r, rec)
        if scatter_result is not None:
            scattered, attenuation = scatter_result
            return attenuation * ray_color(scattered, world, depth - 1)
        return Color(0, 0, 0)

    unit_direction: Vec3 = r.direction().unit_vector()
    t = (unit_direction.y() + 1) * 0.5
    return Color(1, 1, 1) * (1 - t) + Color(0.5, 0.7, 1) * t
Exemple #11
0
 def set_face_normal(self, r: Ray, outward_normal: Vec3) -> HitRecord:
     self.front_face = (r.direction() @ outward_normal) < 0
     self.normal = outward_normal if self.front_face else -outward_normal
     return self