コード例 #1
0
ファイル: material.py プロジェクト: skytreader/praytracing
 def scatter(self, incident_ray: Ray,
             record: "HitRecord") -> ReflectionRecord:
     reflected: Vec3 = reflect(incident_ray.direction.unit_vector(),
                               record.normal)
     scattered: Ray = Ray(
         record.p, reflected + self.fuzz * random_unit_sphere_point())
     return ReflectionRecord(self.albedo, scattered)
コード例 #2
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_ray_position():
    r = Ray(Point(2, 3, 4), Vector(1, 0, 0))

    assert r.position(0) == Point(2, 3, 4)
    assert r.position(1) == Point(3, 3, 4)
    assert r.position(-1) == Point(1, 3, 4)
    assert r.position(2.5) == Point(4.5, 3, 4)
コード例 #3
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_ray_init():
    origin = Point(1, 2, 3)
    direct = Vector(4, 5, 6)

    r = Ray(origin, direct)
    assert r.origin == origin
    assert r.direction == direct
コード例 #4
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_ray_intersect_sphere_tangent():
    r = Ray(Point(0, 1, -5), Vector(0, 0, 1))
    s = Sphere()
    x = r.intersects(s)

    assert len(x) == 2
    assert x[0].t == 5
    assert x[1].t == 5
コード例 #5
0
ファイル: test_primitives.py プロジェクト: qTipTip/RayTracer
def test_sphere_intersection_scaled():
    r = Ray(Point(0, 0, -5), Vector(0, 0, 1))
    s = Sphere()
    s.set_transform(scaling(2, 2, 2))
    x = r.intersects(s)
    assert len(x) == 2
    assert x[0].t == 3
    assert x[1].t == 7
コード例 #6
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_ray_intersects_sphere_object():
    r = Ray(Point(0, 0, -5), Vector(0, 0, 1))
    s = Sphere()
    x = r.intersects(s)

    assert len(x) == 2
    assert x[0].object == s
    assert x[1].object == s
コード例 #7
0
ファイル: material.py プロジェクト: skytreader/praytracing
 def scatter(self, incident_ray: Ray,
             record: "HitRecord") -> ReflectionRecord:
     # Lots of Physics I don't understand :\
     target: Vec3 = record.p + record.normal + random_unit_sphere_point()
     scattered: Ray = Ray(record.p, target - record.p)
     attenuation: Vec3 = self.albedo
     reflecord: ReflectionRecord = ReflectionRecord(attenuation, scattered)
     return reflecord
コード例 #8
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_ray_intersect_from_middle():
    r = Ray(Point(0, 0, 0), Vector(0, 0, 1))
    s = Sphere()
    x = r.intersects(s)

    assert len(x) == 2
    assert x[0].t == -1
    assert x[1].t == 1
コード例 #9
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_ray_intersect_from_behind():
    r = Ray(Point(0, 0, 5), Vector(0, 0, 1))
    s = Sphere()
    x = r.intersects(s)

    assert len(x) == 2
    assert x[0].t == -6
    assert x[1].t == -4
コード例 #10
0
ファイル: camera.py プロジェクト: skytreader/praytracing
 def get_ray(self, s: float, t: float) -> Ray:
     random_point_in_disc: Vec3 = random_in_unit_disk() * self.lens_radius
     offset: Vec3 = (
         self.__u * random_point_in_disc.x +
         self.__v * random_point_in_disc.y
     )
     return Ray(
         self.origin + offset,
         self.lower_left_corner + (self.h_movement * s) +
         (self.v_movement * t) - self.origin - offset
     )
コード例 #11
0
ファイル: material.py プロジェクト: skytreader/praytracing
    def scatter(self, incident_ray: Ray,
                record: "HitRecord") -> ReflectionRecord:
        reflected: Vec3 = reflect(incident_ray.direction, record.normal)
        attenuation: Vec3 = Vec3(1, 1, 1)

        # These are just placeholders; the following conditional block is their
        # actual "initial values".
        outward_normal: Vec3 = Vec3(1, 1, 1)
        nint: float = 0
        cosine: float = 0

        if incident_ray.direction.dot(record.normal) > 0:
            outward_normal = -1 * record.normal
            nint = self.refractive_index
            cosine = (self.refractive_index *
                      incident_ray.direction.dot(record.normal) /
                      incident_ray.direction.length())
        else:
            outward_normal = record.normal
            nint = 1 / self.refractive_index
            cosine = -(incident_ray.direction.dot(record.normal) /
                       incident_ray.direction.length())

        # All this convoluted mix-up with _refracted and refracted is just
        # for type consistency.
        refracted: Vec3 = Vec3(0, 0, 0)
        _refracted: Optional[Vec3] = refract(incident_ray.direction,
                                             outward_normal, nint)
        reflection_probability: float = 1
        if _refracted is not None:
            reflection_probability = self.__schlick_approximation(cosine)
            refracted = _refracted

        if reflection_probability == 1:
            return ReflectionRecord(attenuation, Ray(record.p, reflected))
        else:
            return ReflectionRecord(attenuation, Ray(record.p, refracted))
コード例 #12
0
ファイル: 6_matterial.py プロジェクト: skytreader/praytracing
def color(ray: Ray, world: HittableList) -> Vec3:
    # Some reflected rays hit not at zero but at some near-zero value due to
    # floating point shennanigans. So we try to compensate for that.
    hit_attempt: Optional[HitRecord] = world.hit(ray, 0.001,
                                                 sys.float_info.max)
    if hit_attempt is not None:
        target: Vec3 = hit_attempt.p + hit_attempt.normal + random_unit_sphere_point(
        )
        # FIXME mmm recursion
        # reflector_rate * reflected_color
        # So in this case, the matterial is a 50% reflector.
        return 0.5 * color(Ray(hit_attempt.p, target - hit_attempt.p), world)
    else:
        unit_direction: Vec3 = ray.direction.unit_vector()
        t: float = 0.5 * (unit_direction.y + 1)
        return ((1.0 - t) * UNIT_VEC3) + (t * Vec3(0.5, 0.7, 1.0))
コード例 #13
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_scale():
    r = Ray(Point(1, 2, 3), Vector(0, 1, 0))
    m = scaling(2, 3, 4)
    r2 = r.transform(m)
    assert r2.origin == Point(2, 6, 12)
    assert r2.direction == Vector(0, 3, 0)
コード例 #14
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_translate():
    r = Ray(Point(1, 2, 3), Vector(0, 1, 0))
    m = translation(3, 4, 5)
    r2 = r.transform(m)
    assert r2.origin == Point(4, 6, 8)
    assert r2.direction == Vector(0, 1, 0)
コード例 #15
0
    vertical = Vec3(data=(0., VIEWPORT_HEIGHT, 0.))
    lower_left_corner = origin - horizontal / 2 - vertical / 2

    x = np.tile(
        np.linspace(lower_left_corner.x(),
                    lower_left_corner.x() + viewport_width, IMAGE_WIDTH),
        image_height)
    y = np.repeat(
        np.linspace(lower_left_corner.y(),
                    lower_left_corner.y() + VIEWPORT_HEIGHT, image_height),
        IMAGE_WIDTH)

    colors = []
    for i in range(NUM_SAMPLES):
        x_fidget = np.random.rand(
            IMAGE_WIDTH * image_height) / (IMAGE_WIDTH - 1)
        y_fidget = np.random.rand(
            IMAGE_WIDTH * image_height) / (image_height - 1)
        uv = Vec3(data=(x + x_fidget, y + y_fidget, -focal_length))
        r = Ray(origin, uv - origin)
        colors.append(ray_color(r, world))

    for y in range(image_height):
        for x in range(IMAGE_WIDTH):
            u = x / IMAGE_WIDTH
            v = (image_height - y) / image_height
            write_color(data, y, x, colors, NUM_SAMPLES)

    matplotlib.image.imsave('out.png', data)
    print(f"Finished in {time.time() - start} seconds")
コード例 #16
0
ファイル: test_primitives.py プロジェクト: qTipTip/RayTracer
def test_sphere_intersection_translate():
    r = Ray(Point(0, 0, -5), Vector(0, 0, 1))
    s = Sphere()
    s.set_transform(translation(5, 0, 0))
    x = r.intersects(s)
    assert len(x) == 0
コード例 #17
0
from src.canvas import Canvas
from src.color import Color
from src.primitives import Sphere
from src.ray import Ray
from src.transformations import scaling, rotation_z
from src.tupl import Point

s = Sphere()
t = rotation_z(pi / 4) @ scaling(0.5, 1, 1)
s.set_transform(t)
r_origin = Point(0, 0, -5)
wall_z = 10
wall_size = 7

N = 100
c = Canvas(N, N)
pixel_size = wall_size / N
half = wall_size / 2
red = Color(255, 0, 0)

for y in range(c.height):
    world_y = half - pixel_size * y
    for x in range(c.width):
        world_x = -half + pixel_size * x
        position = Point(world_x, world_y, wall_z)
        r = Ray(r_origin, (position - r_origin).normalize())
        X = r.intersects(s)
        if X.hit is not None:
            c.write_pixel(x, y, red)
c.to_ppm('circle.ppm')
コード例 #18
0
ファイル: camera.py プロジェクト: skytreader/praytracing
 def get_ray(self, u: float, v: float) -> Ray:
     return Ray(
         self.origin,
         self.lower_left_corner + (self.h_movement * u) + (self.v_movement * v) - self.origin
     )
コード例 #19
0
    width = 400
    height = 200
    ppm: PPM = PPM(width, height)
    lower_left_corner: Vec3 = Vec3(-2, -1, -1)
    h_movement: Vec3 = Vec3(4, 0, 0)
    v_movement: Vec3 = Vec3(0, 2, 0)
    origin: Vec3 = Vec3(0, 0, 0)

    for j in range(height - 1, -1, -1):
        for i in range(width):
            # Get the ratio of how far are we from the "edges".
            u = i / width
            v = j / height
            # And use those ratios to "move" the ray away from the origin. Note:
            #  - (h_ * u) + (v_ * v) is scaled movement
            #  - the movement scales differently depending on dimension:
            #    horizontal movement is increasing towards the vector (4, 0, 0)
            #    while vertical movement is decreasing from the vector (0, 2, 0)
            r: Ray = Ray(
                origin,
                lower_left_corner + (h_movement * u) + (v_movement * v))
            _color: Vec3 = color(r)
            _color *= 255.9
            _color.map(int)

            # Note the translation for the row: we want the white part of the
            # gradient at the bottom so we do this.
            ppm.set_pixel((height - 1) - j, i, _color)

    ppm.write(_derive_ppm_filename())
コード例 #20
0
ファイル: test_ray.py プロジェクト: qTipTip/RayTracer
def test_ray_intersect_sphere_no_intersection():
    r = Ray(Point(0, 2, -5), Vector(0, 0, 1))
    s = Sphere()
    x = r.intersects(s)

    assert len(x) == 0