def refracted_color(self, comps: Precomputed, remaining: int = 5) -> Color:
        if remaining <= 0:
            return Color(0, 0, 0)

        if comps.object.material.transparency == 0:
            return Color(0, 0, 0)

        # Find the ratio of the first index of refraction to the second.
        # (Yup, this is inverted from the definition of Snell's Law.)
        n_ratio = comps.n1 / comps.n2

        # cos(theta_i) is the same as the dot product of the two vectors
        cos_i = Vec3.dot(comps.eyev, comps.normalv)

        # Find sin(theta_t)^2 via trigonometric identity
        sin2_t = n_ratio**2 * (1 - cos_i**2)

        if sin2_t > 1:
            return Color(0, 0, 0)

        # Find cos(theta_t) via trigonometric identity
        cos_t = math.sqrt(1.0 - sin2_t)

        # Compute the direction of the refracted ray
        direction = comps.normalv * (n_ratio * cos_i -
                                     cos_t) - comps.eyev * n_ratio

        # Create the refracted ray
        refract_ray = Ray(comps.under_point, direction)

        # Find the color of the refracted ray, making sure to multiply
        # by the transparency value to account for any opacity
        result = self.color_at(refract_ray, remaining - 1) *\
               comps.object.material.transparency
        return result
Beispiel #2
0
def ch7():
    # Step 1
    floor = Sphere()
    floor.transform = Matrix.scaling(10, 0.01, 10)
    floor.material = Material()
    floor.material.color = Color(1, 0.9, 0.9)
    floor.material.specular = 0

    # Step 2
    left_wall = Sphere()
    left_wall.transform = Matrix.translation(0, 0, 5) * Matrix.rotation_y(-45) * \
                          Matrix.rotation_x(90) * Matrix.scaling(10, 0.01, 10)
    left_wall.material = floor.material

    # Step 3
    right_wall = Sphere()
    right_wall.transform = Matrix.translation(0, 0, 5) * Matrix.rotation_y(45) * \
                           Matrix.rotation_x(90) * Matrix.scaling(10, 0.01, 10)
    right_wall.material = floor.material

    # Step 4
    middle = Sphere()
    middle.transform = Matrix.translation(-0.5, 1, 0.5)
    middle.material = Material()
    middle.material.color = Color(0.1, 1, 0.5)
    middle.material.diffuse = 0.7
    middle.material.specular = 0.3

    # Step 5
    right = Sphere()
    right.transform = Matrix.translation(1.5, 0.5, -0.5) * Matrix.scaling(
        0.5, 0.5, 0.5)
    right.material = Material()
    right.material.color = Color(0.5, 1, 0.1)
    right.material.diffuse = 0.7
    right.material.specular = 0.3

    # Step 6
    left = Sphere()
    left.transform = Matrix.translation(-1.5, 0.33, -0.75) * Matrix.scaling(
        0.33, 0.33, 0.33)
    left.material = Material()
    left.material.color = Color(1, 0.8, 0.1)
    left.material.diffuse = 0.7
    left.material.specular = 0.3

    world = World()
    world.light = PointLight(point(-10, 10, -10), Color(1, 1, 1))
    world.objects.extend([floor, left_wall, right_wall, middle, right, left])

    camera = Camera(100, 50, 60)
    # camera = Camera(500, 250, 60)
    camera.transform = view_transform(point(0, 1.5, -5), point(0, 1, 0),
                                      vector(0, 1, 0))

    canvas = camera.render(world)

    with open('ch8.ppm', 'w') as fp:
        fp.write(canvas.to_ppm())
    def reflected_color(self, comps: Precomputed, remaining: int = 5) -> Color:
        if remaining <= 0:
            return Color(0, 0, 0)
        if comps.object.material.reflective == 0.0:
            return Color(0, 0, 0)

        reflect_ray = Ray(comps.over_point, comps.reflectv)
        color = self.color_at(reflect_ray, remaining - 1)

        return color * comps.object.material.reflective
    def lighting(self, obj, light: PointLight, point: Point, eyev: Vector,
                 normalv: Vector, in_shadow: bool) -> Color:
        """Calculates the color shaded on the surface of the object using various
        properties from the material.

        :param obj: The Shape object that will be used if a pattern is set.
        :param light: A light source that potentially intersects with the surface.
        :param point: A point in the space.
        :param eyev: A vector representing the direction from the point on the surface to the eye of the view
        :param normalv: The normal of the surface.
        :param in_shadow: Boolean indicating if the point is in shadow between an object and the light source.
        :return:
        """
        color = self.pattern.pattern_at_shape(
            obj, point) if self.pattern is not None else self.color

        # combine the surface color with the light's color/intensity
        effective_color = color * light.intensity

        # find the direction to the light source
        lightv = (light.position - point).normalize()

        # compute the ambient contribution
        ambient = effective_color * self.ambient

        # if the point is in shadow, ambient should be the only color factored into
        if in_shadow:
            return ambient

        # light_dot_normal represent the cosine of the angle between the
        # light vector and the normal vector. A negative number means the
        # light is on the other side of the surface.
        light_dot_normal = Vec3.dot(lightv, normalv)
        if light_dot_normal < 0:
            diffuse = Color(0, 0, 0)
            specular = Color(0, 0, 0)
        else:
            # compute the diffuse contribution
            diffuse = effective_color * self.diffuse * light_dot_normal

            # reflect_dot_eye represents the cosine of the angle between the
            # reflection vector and the eye vector. A negative number means the
            # light reflects away from the eye.
            reflectv = reflect(-lightv, normalv)
            reflect_dot_eye = Vec3.dot(reflectv, eyev)

            if reflect_dot_eye <= 0:
                specular = Color(0, 0, 0)
            else:
                # compute the specular contribution
                factor = reflect_dot_eye**self.shininess
                specular = light.intensity * self.specular * factor
        return ambient + diffuse + specular
def default_world():
    world = World()
    world.light = PointLight(point(-10, 10, -10), Color(1, 1, 1))

    s1 = Sphere()
    s1.material.color = Color(0.8, 1.0, 0.6)
    s1.material.diffuse = 0.7
    s1.material.specular = 0.2
    world.objects.append(s1)

    s2 = Sphere()
    s2.transform = Matrix.scaling(0.5, 0.5, 0.5)
    world.objects.append(s2)

    return world
 def color_at(self, ray: Ray, remaining: int = 5) -> Color:
     intersections = self.intersect_world(ray)
     intersect = intersections.hit()
     if intersect is None:
         return Color(0, 0, 0)
     precomputed = intersect.prepare_computations(ray)
     return self.shade_hit(precomputed, remaining)
Beispiel #7
0
def ch9():
    # Step 1
    floor = Plane()
    floor.transform = Matrix.scaling(10, 0.01, 10)
    floor.material = Material()
    floor.material.color = Color(1, 0.9, 0.9)
    floor.material.specular = 0
    floor.material.pattern = StripePattern(Color(1, 0, 0), Color(0, 0, 1))

    # Middle biggest sphere
    middle = Sphere()
    middle.transform = Matrix.translation(-0.5, 1, 0.5)
    middle.material = Material()
    middle.material.color = Color(0.1, 1, 0.5)
    middle.material.diffuse = 0.7
    middle.material.specular = 0.3
    middle.material.pattern = RingPattern(Color(1, 0, 1), Color(1, 1, 1))
    middle.material.pattern.transform = Matrix.scaling(0.25, 0.5, 0.25)

    # Smaller right sphere
    right = Sphere()
    right.transform = Matrix.translation(1.5, 0.5, -0.5) * Matrix.scaling(0.5, 0.5, 0.5)
    right.material = Material()
    right.material.color = Color(0.5, 1, 0.1)
    right.material.diffuse = 0.7
    right.material.specular = 0.3
    right.material.reflective = 1.0

    # Left yellow sphere
    left = Sphere()
    left.transform = Matrix.translation(-1.5, 0.33, -0.75) * Matrix.scaling(0.33, 0.33, 0.33)
    left.material = Material()
    left.material.color = Color(1, 0.8, 0.1)
    left.material.diffuse = 0.7
    left.material.specular = 0.3

    world = World()
    world.light = PointLight(point(-10, 10, -10), Color(1, 1, 1))
    world.objects.extend([floor, middle, right, left])

    camera = Camera(100, 50, 60)
    # camera = Camera(500, 250, 60)
    camera.transform = view_transform(point(0, 1.5, -5),
                                      point(0, 1, 0),
                                      vector(0, 1, 0))

    canvas = camera.render(world)

    with open('ch9.ppm', 'w') as fp:
        fp.write(canvas.to_ppm())
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.pixels = []

        # Set each pixel to be black by default
        for row in range(self.height):
            pixel_rows = []
            for column in range(self.width):
                pixel_rows.append(Color(0, 0, 0))
            self.pixels.append(pixel_rows)
Beispiel #9
0
def ch14():
    world = World()
    world.light = PointLight(point(-10, 10, -10), Color(1, 1, 1))

    hex = hexagon()
    world.objects.append(hex)

    camera = Camera(100, 50, 60)
    camera.transform = view_transform(point(0, 2, -1), point(0, 0, 0),
                                      vector(0, 1, 0))
    canvas = camera.render(world)

    with open('ch14.ppm', 'w') as fp:
        fp.write(canvas.to_ppm())
 def __init__(self,
              color=None,
              ambient=0.1,
              diffuse=0.9,
              specular=0.9,
              shininess=200.0):
     self.color = Color(1, 1, 1) if color is None else color
     self.ambient = ambient
     self.diffuse = diffuse
     self.specular = specular
     self.shininess = shininess
     self.pattern = None
     self.reflective = 0.0
     self.transparency = 0.0
     self.refractive_index = 1.0
Beispiel #11
0
    def clean(self, filter_func):
        new_img_data = []
        color_black = (0, 0, 0)
        color_white = (255, 255, 255)

        for i, colordata in enumerate(self.im.convert("HSV").getdata()):
            color = Color(colordata)
            if filter_func(color):
                new_img_data.append(color_black)
                continue
            new_img_data.append(color_white)

        self.im = Image.new(self.im.mode, self.im.size)
        self.im.putdata(new_img_data)
        return
# Can probably have this done internally in the Canvas class, but left
# it here for simplicity and it doesn't hide how the conversion is done.
def convert_position_to_canvas(canvas: Canvas, world_position: Vec3):
    """Converts the position from world to canvas. This is done by subtracting
    the z world position from the canvas height, the other positions are left alone."""
    return point(
        round(world_position.x + (canvas.width / 2)), round(world_position.y),
        round((canvas.height - (canvas.height / 2) - world_position.z)))


if __name__ == '__main__':
    width = 400
    height = 400
    canvas = Canvas(width, height)

    hour_rotation = 360 / 12
    hour_rotation_matrix = Matrix.rotation_y(hour_rotation)

    radius = (3 / 8) * canvas.width
    print(f'Radius: {radius}')
    position = point(0, 0, 1) * radius

    for i in range(12):
        canvas_point = convert_position_to_canvas(canvas, position)
        print(f'Position:{position}\nCanvas:{canvas_point}')
        canvas.write_pixel(canvas_point.x, canvas_point.z, Color(1, 1, 1))
        position = hour_rotation_matrix * position

    with open('clock2.ppm', 'w') as fp:
        fp.write(canvas.to_ppm())
def step_impl(context, attribute, r, g, b):
    expected = Color(r, g, b)
    result = context
    for prop in attribute.split('.'):
        result = getattr(result, prop)
    assert expected == result, f'{result} != {expected}'
def step_impl(context, attribute, r, g, b):
    setattr(context, attribute, Color(r, g, b))
                  0)  # The starting position can be set anywhere different
    velocity = vector(1, 1.8, 0).normalize(
    ) * 11.25  # This can be changed to any value to change the trajectory

    p = Projectile(start, velocity)

    gravity = vector(
        0, -0.1,
        0)  # This can be changed to force the projectile down faster/slower
    wind = vector(
        -0.01, 0,
        0)  # This can changed to force the projectile forward less or more
    e = Environment(gravity, wind)

    screen_width = 900  # You can play around with the width of the canvas
    screen_height = 550  # You can play around with the height of the canvas
    c = Canvas(screen_width, screen_height)

    # It's ok if the y position is above the canvas as it can come down into it still.
    # But with the x-axis, it's not worth it since, once it's off screen, the wind is
    # is too strong in the beginning or the it will not return on-screen on the right end
    while p.position.y > 0 and 0 <= p.position.x < c.width:
        canvas_position = convert_position_to_canvas(c, p.position)
        c.write_pixel(canvas_position.x, canvas_position.y, Color(1, 0, 0))
        p = tick(e, p)

    # The final process of storing the canvas to a .ppm file.
    # You can change the name of the file to whatever you like
    with open('ch1and2.ppm', 'w') as fp:
        fp.write(c.to_ppm())
Beispiel #16
0
def ch5and6():
    # start the ray at z = -5
    ray_origin = point(0, 0, -5)

    # put the wall at z = 10
    wall_z = 10
    wall_size = 7

    canvas_pixels = 100
    pixel_size = wall_size / canvas_pixels
    half = wall_size / 2

    canvas = Canvas(canvas_pixels, canvas_pixels)
    shape = Sphere()

    # Optional sphere transformations - uncomment to see the sphere changes
    # shape.transform = Matrix.rotation_z(45) * Matrix.scaling(0.5, 1, 1)
    # shape.transform = Matrix.scaling(1, 0.5, 1)
    # shape.transform = Matrix.shearing(1, 0, 0, 0, 0, 0) * Matrix.scaling(0.5, 1, 1)

    # CH 6
    # CH 6.1
    shape.material.color = Color(1, 0.2, 1)

    # CH 6.2
    light_position = point(-10, 10, -10)
    light_color = Color(1, 1, 1)
    light = PointLight(light_position, light_color)

    # for each row of pixels in the canvas
    for y in range(canvas_pixels):

        # compute the world y coordinate (top = +half, bottom = -half)
        # canvas -> world coordinate conversion
        # (0, 0) -> (-3.5, 3.5)
        # (99, 99) -> (3.43, -3.43)
        world_y = half - pixel_size * y

        # for each pixel in the row
        for x in range(canvas_pixels):

            # compute the world x coordinate (left = -half, right = half)
            world_x = -half + pixel_size * x

            # describe the point on the wall that the ray will target
            position = point(world_x, world_y, wall_z)

            r = Ray(ray_origin, (position - ray_origin).normalize())
            xs = shape.intersect(r)

            intersection = xs.hit()
            if intersection is not None:
                # World position of the intersection
                intersect_point = r.position(intersection.t)
                # World position of the normal at the point
                intersect_normal = intersection.obj.normal_at(intersect_point)
                # Reverse ray direction to get vector from intersection to eye
                eye = -r.direction

                # Color is calculate by the material of the sphere, ambient, diffuse, specular, and the light sources
                color = intersection.obj.material.lighting(
                    light, intersect_point, eye, intersect_normal)
                canvas.write_pixel(x, y, color)

    with open('ch6.ppm', 'w') as fp:
        fp.write(canvas.to_ppm())
Beispiel #17
0
def step_impl(context, x, y, r, g, b):
    expected = Color(r, g, b)
    result = context.image.pixel_at(x, y)
    assert expected == result, f'{result} != {expected}'
Beispiel #18
0
 def pattern_at(self, point: Vec3) -> Color:
     return Color(point.x, point.y, point.z)
Beispiel #19
0
def step_impl(context, x, y, z, r, g, b):
    context.w.light = PointLight(point(x, y, z), Color(r, g, b))
Beispiel #20
0
def step_impl(context, r1, g1, b1, r2, g2, b2):
    context.m.pattern = StripePattern(Color(r1, g1, b1), Color(r2, g2, b2))
Beispiel #21
0
def step_impl(context, x, y, z):
    expected = Color(0, 0, 0)
    result = context.pattern.pattern_at(point(x, y, z))
    assert expected == result, f'{result} != {expected}'
Beispiel #22
0
def step_impl(context, r, g, b):
    expected = Color(r, g, b)
    result = context.result
    assert expected == result, f'{result} != {expected}'
Beispiel #23
0
# Copyright(c) Eric Steinberger 2018

from src.Color import Color
from src.config import RobotConfig

BLACK = Color(name="black",
              rgb=[25, 25, 25],
              position=RobotConfig.POS_COLOR_BLACK,
              color_id=0,
              code=[1, 1, 1, 0])
GREEN = Color(name="green",
              rgb=[0, 148, 84],
              position=RobotConfig.POS_COLOR_GREEN,
              color_id=1,
              code=[1, 0, 0, 0])
BLUE = Color(name="blue",
             rgb=[1, 51, 236],
             position=RobotConfig.POS_COLOR_BLUE,
             color_id=2,
             code=[1, 0, 1, 0])
YELLOW = Color(name="yellow",
               rgb=[255, 219, 0],
               position=RobotConfig.POS_COLOR_YELLOW,
               color_id=3,
               code=[0, 1, 0, 0])
RED = Color(name="red",
            rgb=[240, 14, 17],
            position=RobotConfig.POS_COLOR_RED,
            color_id=4,
            code=[0, 1, 1, 0])
WHITE = Color(name="white",
Beispiel #24
0
def step_impl(context, r, g, b):
    context.intensity = Color(r, g, b)