示例#1
0
 def bounding_box_on_time(self, time: float) -> AABB:
     box = AABB(
         self.get_center(time) -
         Vector3(self._radius, self._radius, self._radius),
         self.get_center(time) +
         Vector3(self._radius, self._radius, self._radius))
     return box
示例#2
0
class HitRecord(object):
    t: float = 0.0
    hit_point: Vector3 = Vector3(0.0, 0.0, 0.0)
    hit_point_normal: Vector3 = Vector3(0.0, 0.0, 0.0)
    u: float = 0.0
    v: float = 0.0
    material: Material = None
示例#3
0
    def calculate_ray_color(self, ray: Ray, depth: float):
        has_hit, hit_record = self._bvh_world.hit(ray=ray,
                                                  t_min=0.001,
                                                  t_max=float('inf'))

        if has_hit is True:
            scattered, attenuation = hit_record.material.scatter(
                ray_incident=ray, hit_record=hit_record)

            emitted = hit_record.material.emitted(u=hit_record.u,
                                                  v=hit_record.v,
                                                  p=hit_record.hit_point)
            if depth < 50 and scattered is not None:
                return emitted + attenuation * self.calculate_ray_color(
                    ray=scattered, depth=depth + 1)
            else:
                return emitted

        else:
            # Ambient Color
            # return Vector3(0.0, 0.0, 0.0)
            ray_direction = Vector3.normalize(ray.direction)
            t = 0.5 * (ray_direction.y() + 1.0)
            return (1.0 - t) * Vector3(0.2, 0.2, 0.2) + t * Vector3(
                0.5, 0.7, 1.0)
示例#4
0
    def _render_patch(self, patch_number: int, begin_row: int,
                      begin_column: int, patch_width: int, patch_height: int):
        """Render a patch of the image defined by {row} {column} with {width} and {height}"""
        logging.info(
            f'Beginning patch {patch_number}: ({begin_row} {begin_column}) with width {patch_width} and height {patch_height}'
        )

        max_color_value = 255.99
        result = list()
        for row in range(begin_row, begin_row - patch_height, -1):
            for column in range(begin_column, begin_column + patch_width):
                final_color = Vector3()
                for s in range(self.num_samples):
                    u = float(column + random.random()) / float(self.width)
                    v = float(row + random.random()) / float(self.height)

                    r = self.camera.get_ray(u=u, v=v)
                    final_color = final_color + self.calculate_ray_color(
                        ray=r, depth=0)

                final_color = final_color / self.num_samples

                # make the sqrt for the gamma correction
                final_color = Vector3(
                    math.sqrt(final_color.r()) * max_color_value,
                    math.sqrt(final_color.g()) * max_color_value,
                    math.sqrt(final_color.b()) * max_color_value)
                result.append((row, column, final_color))

        logging.info(
            f'Ending patch {patch_number} ({begin_row} {begin_column}) with width {patch_width} and height {patch_height}'
        )
        return result
    def random_in_unit_sphere():
        p = Vector3(1.0, 1.0, 1.0)
        while p.length() >= 1.0:
            p = 2.0 * Vector3(random.random(), random.random(),
                              random.random()) - Vector3(1, 1, 1)

        return p
 def value(self, u: float, v: float, p: Vector3) -> Vector3:
     sines = math.sin(10 * p.x()) * math.sin(10 * p.y()) * math.sin(
         10 * p.z())
     if sines < 0:
         return self._texture0.value(u, v, p)
     else:
         return self._texture1.value(u, v, p)
示例#7
0
 def perlin_generate(self):
     p = list()
     for i in range(256):
         p.append(
             Vector3.normalize(
                 Vector3(-1 + 2 * random.random(), -1 + 2 * random.random(),
                         -1 + 2 * random.random())))
     return p
示例#8
0
    def first_light_scene():
        list_of_hitables = [
            Sphere(center=Vector3(0.0, -1000, 0.0), radius=1000, material=Lambertian(albedo=Vector3(0.9, 0.67, 0.25))),
            Sphere(center=Vector3(0.0, 2, 0.0), radius=2, material=Lambertian(albedo=Vector3(0.7, 0.8, 0.1))),
            Sphere(center=Vector3(0.0, 7, 0.0), radius=2, material=DiffuseLight(albedo=Vector3(1.0, 1.0, 1.0), texture=ConstantTexture(color=Vector3(4,4,4)))),
            XYRect(3, 5, 1, 3, -2, DiffuseLight(albedo=Vector3(1.0, 1.0, 1.0), texture=ConstantTexture(color=Vector3(1, 1, 1))))
        ]

        return list_of_hitables
示例#9
0
 def __init__(self,
              albedo: Vector3 = Vector3(1.0, 1.0, 1.0),
              texture: Texture = None):
     """ Lamberian Material
     :param albedo: albedo color of the material
     :type albedo: Vector3
     """
     super().__init__()
     self._albedo = albedo
     self._texture = ConstantTexture(Vector3(
         1.0, 1.0, 1.0)) if texture is None else texture
示例#10
0
    def surrounding_box(box0, box1):  # try to use annotations
        smallest = Vector3(
            min(box0.min.x(), box1.min.x()),
            min(box0.min.y(), box1.min.y()),
            min(box0.min.z(), box1.min.z()),
        )

        biggest = Vector3(
            max(box0.max.x(), box1.max.x()),
            max(box0.max.y(), box1.max.y()),
            max(box0.max.z(), box1.max.z()),
        )

        return AABB(min=smallest, max=biggest)
示例#11
0
    def trilineart_interp(self, c, u, v, w):
        uu = u * u * (3 - 2 * u)
        vv = v * v * (3 - 2 * v)
        ww = w * w * (3 - 2 * w)

        accum = 0
        for i in range(2):
            for j in range(2):
                for k in range(2):
                    weight_v = Vector3(u - i, v - j, w - k)
                    accum += (i * uu + (1 - i) * (1 - uu)) * \
                             (j * vv + (1 - j) * (1 - vv)) * \
                             (k * ww + (1 - k) * (1 - ww)) * Vector3.dot(c[i * 2 + j * 2 + k], weight_v)
        return accum
示例#12
0
    def hit(self, ray: Ray, t_min: float, t_max: float) -> (bool, HitRecord):
        """Checks if the ray 'ray' hits with the plane between t_min and t_max.
        Returns true if there is a collision, false otherwise.
        Return the hitRecord information if the collision is true.
        """
        if ray.direction.y() == 0.0:
            return False, None

        t = (self._k - ray.origin.y()) / ray.direction.y()
        if t < t_min or t > t_max:
            return False, None

        x = ray.origin.x() + t * ray.direction.x()
        z = ray.origin.z() + t * ray.direction.z()

        if x < self._x0 or x > self._x1 or z < self._z0 or z > self._z1:
            return False, None

        record = HitRecord()
        record.t = t
        record.hit_point = ray.point_at_parameter(record.t)
        record.hit_point_normal = Vector3(0, 1, 1)

        record.u = (x - self._x0) / (self._x1 - self._x0)
        record.v = (z - self._z0) / (self._z1 - self._z0)
        record.material = self._material

        return True, record
示例#13
0
 def random_cosine_direct():
     r1, r2 = random.random(), random.random()
     z = math.sqrt(1 - r2)
     phi = 2 * math.pi * r1
     x = math.cos(phi) * 2 * math.sqrt(r2)
     y = math.sin(phi) * 2 * math.sqrt(r2)
     return Vector3(x, y, z)
示例#14
0
    def get_sphere_uv(self, p: Vector3) -> (float, float):
        """Get the uv coordinates of a point in the sphere"""
        d = Vector3.normalize(p - self._center)

        phi = math.atan2(d.z(), d.x()) / (2 * math.pi)
        u = phi + 0.5

        v = 0.5 - (math.asin(d.y()) / math.pi)
        return u, v
示例#15
0
 def __init__(self, width, height, color_matrix=None):
     self.width = width
     self.height = height
     if color_matrix is None:
         self.color_matrix = list()
         for row in range(self.height):
             self.color_matrix.append([Vector3(0.0, 0.0, 0.0)] * self.width)
     else:
         self.color_matrix = color_matrix
示例#16
0
    def __init__(self, lookfrom:'Vector3', lookat:'Vector3', vectorup:'Vector3', vfov:'float', aspect:'float', aperture:'float', focus_dist:'float', t0:float, t1:float):
        theta =  vfov * math.pi / 180  # vfov to radians
        half_height = math.tan(theta / 2) # h = tan(theta / 2)
        half_width = half_height * aspect

        self._lens_radius = aperture / 2

        self._time0 = t0
        self._time1 = t1

        self._camera_origin = lookfrom
        w = Vector3.normalize(lookfrom - lookat)
        u = Vector3.normalize(Vector3.cross(vectorup, w))
        v = Vector3.cross(w, u)

        self._lower_left_corner = self._camera_origin - half_width * focus_dist * u - half_height * focus_dist * v - focus_dist*w
        self._horizontal = 2 * half_width * focus_dist * u
        self._vertical = 2 * half_height * focus_dist * v
示例#17
0
    def random_to_sphere(self, radius: float,
                         distance_squared: float) -> Vector3:
        r1 = random.random()
        r2 = random.random()
        z = 1 + r2 * (
            math.sqrt(1 - self._radius * self._radius / distance_squared) - 1)
        phi = 2 * math.pi * r1
        x = math.cos(phi) * math.sqrt(1 - z * z)
        y = math.sin(phi) * math.sqrt(1 - z * z)

        return Vector3(x, y, z)
示例#18
0
    def noise(self, p: Vector3):
        u = p.x() - math.floor(p.x())
        v = p.y() - math.floor(p.y())
        w = p.z() - math.floor(p.z())
        i = math.floor(p.x())
        j = math.floor(p.y())
        k = math.floor(p.z())

        # return self._ranvector[self._perm_x[i] ^ self._perm_y[j] ^ self._perm_z[k]]
        # c = [[[self._ranfloat[self._perm_x[(i + ii) & 255] ^ self._perm_y[(j + jj) & 255] ^ self._perm_z[(k + kk) & 255]] for kk in range(2)] for jj in range(2)] for ii in range(2)]
        c = list()
        for ii in range(2):
            for jj in range(2):
                for kk in range(2):
                    c.append(self._ranvector[self._perm_x[(i + ii) & 255]
                                             ^ self._perm_y[(j + jj) & 255]
                                             ^ self._perm_z[(k + kk) & 255]])

        return self.trilineart_interp(c, u, v, w)
示例#19
0
    def schlick(self, incident: 'Vector3', normal: 'Vector3', n1: 'float',
                n2: 'float'):
        r0 = (n1 - n2) / (n1 + n2)
        r0 *= r0
        cosX = Vector3.dot(normal, incident)
        if n1 > n2:
            n = n1 / n2
            sinT2 = n * n * (1.0 - cosX * cosX)
            if sinT2 > 1.0:
                return 1.0
            cosX = math.sqrt(1.0 - sinT2)

        x = 1.0 - cosX
        return r0 + (1.0 - r0) * math.pow(x, 5)
示例#20
0
    def value(self, u: float, v: float, p: Vector3) -> Vector3:
        i = (self.uoffset - u) * (self._width - 1)
        j = (self.voffset + v) * (self._height - 1)
        i = i % self._width
        j = j % self._height
        # i = MathUtils.clamp(i, 0, self._width - 1)
        # j = MathUtils.clamp(j, 0, self._height - 1)

        pix = self._image.load()
        r = int(pix[i, j][0]) / 255.0
        g = int(pix[i, j][1]) / 255.0
        b = int(pix[i, j][2]) / 255.0

        return Vector3(r, g, b)
示例#21
0
    def build_from_w(self, n: Vector3):
        self._w = Vector3.normalize(n)
        if math.fabs(self._w.x()) > 0.9:
            a = Vector3(0, 1, 0)
        else:
            a = Vector3(1, 0, 0)

        self._v = Vector3.normalize(Vector3.cross(self._w, a))
        self._u = Vector3.cross(self._w, self._v)
示例#22
0
    def hit(self, ray: Ray, t_min: float, t_max: float) -> (bool, HitRecord):
        """Checks if the ray 'ray' hits with the sphere between t_min and t_max.
        Returns true if there is a collision, false otherwise.
        Return the hitRecord information if the collision is true.
        """
        current_center = self.get_center(t=ray.time)

        oc = ray.origin - current_center

        a = Vector3.dot(ray.direction, ray.direction)
        b = Vector3.dot(oc, ray.direction)
        c = Vector3.dot(oc, oc) - self._radius * self._radius
        discriminant = b * b - a * c

        if discriminant > 0:
            temp = (-b - math.sqrt(b * b - a * c)) / a
            if t_max > temp > t_min:
                record = HitRecord()
                record.t = temp
                record.hit_point = ray.point_at_parameter(record.t)
                record.hit_point_normal = (record.hit_point -
                                           current_center) / self._radius
                record.u, record.v = self.get_sphere_uv(p=record.hit_point)
                record.material = self._material
                return True, record
            temp = (-b + math.sqrt(b * b - a * c)) / a
            if t_max > temp > t_min:
                record = HitRecord()
                record.t = temp
                record.hit_point = ray.point_at_parameter(record.t)
                record.hit_point_normal = (record.hit_point -
                                           current_center) / self._radius
                record.u, record.v = self.get_sphere_uv(p=record.hit_point)
                record.material = self._material
                return True, record

        return False, None
示例#23
0
    def hit(self, ray: Ray, t_min: float, t_max: float) -> (bool, HitRecord):
        """Checks if the ray 'ray' hits with any of the walls between t_min and t_max."""
        # db = False
        has_hit, rec1 = self._boundary.hit(ray=ray,
                                           t_min=-float('inf'),
                                           t_max=float('inf'))
        if has_hit is True:
            has_hit_again, rec2 = self._boundary.hit(ray=ray,
                                                     t_min=rec1.t + 0.0001,
                                                     t_max=float('inf'))
            if has_hit_again is True:

                if rec1.t < t_min:
                    rec1.t = t_min
                if rec2.t > t_max:
                    rec2.t = t_max

                # if db:
                #     print(f't0 t1 {rec1.t} {rec2.t}')
                if rec1.t >= rec2.t:
                    return False, None
                if rec1.t < 0:
                    rec1.t = 0

                distance_inside_boundary = (rec2.t -
                                            rec1.t) * ray.direction.length()
                hit_distance = -(1 / self._density) * math.log(
                    random.random())  # base 10 ?

                # if db:
                #     print(f'DATA: {hit_distance} {distance_inside_boundary} {hit_distance < distance_inside_boundary}')

                if hit_distance < distance_inside_boundary:
                    # if db:
                    #     print(f'hit distance = {hit_distance}')
                    hit_record = HitRecord()
                    hit_record.t = rec1.t + hit_distance / ray.direction.length(
                    )
                    # if db:
                    #     print(f'rec.t = {hit_record.t}')
                    hit_record.hit_point = ray.point_at_parameter(hit_record.t)
                    # if db:
                    #     print(f'rec.hit_point = {hit_record.hit_point}')
                    hit_record.hit_point_normal = Vector3(1, 0, 0)
                    hit_record.material = self._phase_function

                    return True, hit_record

        return False, None
示例#24
0
    def scatter(self, ray_incident: Ray, hit_record: HitRecord):

        reflected_vector = Vector3.reflect(
            Vector3.normalize(ray_incident.direction),
            hit_record.hit_point_normal)
        attenuation = Vector3(1.0, 1.0, 1.0)

        refracted_vector = Vector3.refract(incident=ray_incident.direction,
                                           normal=hit_record.hit_point_normal,
                                           n1=1.0,
                                           n2=self._refraction_index)
        reflect_probability = self.schlick(incident=ray_incident.direction,
                                           normal=hit_record.hit_point_normal,
                                           n1=1.0,
                                           n2=self._refraction_index)

        if random.random() < reflect_probability:
            scattered = Ray(hit_record.hit_point, reflected_vector,
                            ray_incident.time)
        else:
            scattered = Ray(hit_record.hit_point, refracted_vector,
                            ray_incident.time)

        return scattered, attenuation
示例#25
0
    def one_sphere_noise() -> (list, Camera):
        lookfrom = Vector3(13, 2, 3)
        lookat = Vector3(0, 0, 0)
        camera = Camera(lookfrom=lookfrom, lookat=lookat, vectorup=Vector3(0.0, 1.0, 0.0),
                        vfov=20.0, aspect=16 / 9, aperture=0.0,
                        focus_dist=10.0, t0=0.0, t1=1.0)

        pertext = NoiseTexture(scale=1.0)

        list_of_hitables = [
            Sphere(Vector3(0, -1000, 0), 1000, Lambertian(albedo=Vector3(1.0, 1.0, 1.0), texture=pertext)),
            Sphere(Vector3(0, 2, 0), 2, Lambertian(albedo=Vector3(1.0, 1.0, 1.0), texture=pertext))
        ]

        return list_of_hitables, camera
示例#26
0
    def one_sphere_world():
        lookfrom = Vector3(12, 5, 3)
        lookat = Vector3(0, 2, -1)
        camera = Camera(lookfrom=lookfrom, lookat=lookat, vectorup=Vector3(0.0, 1.0, 0.0),
                        vfov=20.0, aspect=16 / 9, aperture=0.0,
                        focus_dist=10.0, t0=0.0, t1=1.0)

        checker_texture = CheckerTexture(texture0=ConstantTexture(Vector3(0.2, 0.3, 0.1)), texture1=ConstantTexture(Vector3(0.9, 0.9, 0.9)))

        script_dir = os.path.dirname(__file__)
        texture_filepath = os.path.join(script_dir, r'../resources/textures/google-maps.jpg')
        world_texture = ImageTexture(texture_file_path=texture_filepath, uoffset=0.1)
        texture_filepath = os.path.join(script_dir, r'../resources/textures/tiled-background-with-stripes-and-splatter_256x256.jpg')
        colors_texture = ImageTexture(texture_file_path=texture_filepath)

        list_of_hitables = [
            # Sphere(center=Vector3(0.0, -1000, 0.0), radius=1000,
            #        material=Lambertian(albedo=Vector3(1.0, 1.0, 1.0), texture=colors_texture)),
            XZRect(-20, 20, -20, 20, 0, material=Lambertian(albedo=Vector3(1.0, 1.0, 1.0), texture=colors_texture)),
            Sphere(center=Vector3(0, 2, 0), radius=2.0, material=Lambertian(albedo=Vector3(1.0, 1.0, 1.0), texture=world_texture)),
            Sphere(center=Vector3(0, 1.0, -3), radius=1.0,material=Lambertian(albedo=Vector3(1.0, 1.0, 1.0), texture=checker_texture))
        ]
        return list_of_hitables, camera
示例#27
0
    def random_in_unit_disk(self):
        p = Vector3(1.0, 1.0, 0.0)
        while p.length() >= 1.0:
            p = 2.0 * Vector3(random.random(), random.random(), 0) - Vector3(1, 1, 0)

        return p
示例#28
0
 def bounding_box(self, t0: float, t1: float) -> AABB:
     return AABB(min=Vector3(self._x0, self._y0, self._k - 0.0001),
                 max=Vector3(self._x1, self._y1, self._k + 0.0001))
示例#29
0
 def value(self, direction: Vector3) -> float:
     cosine = Vector3.dot(Vector3.normalize(direction), self._uvw.w())
     if cosine > 0:
         return cosine / math.pi
     else:
         return 0
示例#30
0
 def emitted(self, u: float, v: float, p: Vector3) -> Vector3:
     return Vector3(0.0, 0.0, 0.0)