예제 #1
0
def is_shadowed(world, light_position, point):
    """
    >>> w = default_world()
    >>> p = point(0,10,0)
    >>> is_shadowed(w, p)
    False

    >>> w = default_world()
    >>> p = point(10,-10,10)
    >>> is_shadowed(w, p)
    True

    >>> w = default_world()
    >>> p = point(-20,20,-20)
    >>> is_shadowed(w, p)
    False

    >>> w = default_world()
    >>> p = point(-2,2,-2)
    >>> is_shadowed(w, p)
    False

    """

    v = light_position - point
    distance = magnitude(v)
    direction = normalize(v)
    r = shapes.ray(point, direction)
    intersections = intersect_world(world, r)
    intersections = [
        i for i in intersections if i.object.material.casts_shadow
    ]
    h = shapes.hit(intersections)

    return h is not None and h.t < distance
예제 #2
0
 def get_eccentricities(self, start, end):
     """"Returns: an array of the eccentricities of the contained geometry
         Notes: eccentricity is a number representing how close a ray
                is to intersecting the object, 0.0 is the edge of the
                object, more positive numbers indicate we're further
                from intersection, more negative numbers mean the
                intersection is less avoidable
     """
     ray = shapes.ray(start, end-start)
     return [ s.eccentricity(ray) for s in self.collides ]
예제 #3
0
def reflected_color(world, comps, remaining=5):
    """
    >>> w = default_world()
    >>> r = ray(point(0,0,0), vector(0,0,1))
    >>> shape = w.contains[1]
    >>> shape.material.ambient = 1
    >>> i = intersection(1, shape)
    >>> xs = intersections(i)
    >>> comps = prepare_computations(i, r, xs)
    >>> c = reflected_color(w, comps)
    >>> np.isclose(c, color(0,0,0))
    array([ True,  True,  True])

    >>> w = default_world()
    >>> shape = plane()
    >>> shape.material.reflective = 0.5
    >>> shape.transform = translation(0, -1, 0)
    >>> w.contains.append(shape)
    >>> r = ray(point(0,0,-3), vector(0,-np.sqrt(2)/2, np.sqrt(2)/2))
    >>> i = intersection(np.sqrt(2), shape)
    >>> xs = intersections(i)
    >>> comps = prepare_computations(i, r, xs)
    >>> c = reflected_color(w, comps)
    >>> w.contains = w.contains[:-1]
    >>> len(w.contains) == 2
    True
    >>> np.isclose(c, color(0.19033077, 0.23791346, 0.14274808))
    array([ True,  True,  True])

    >>> w = default_world()
    >>> shape = plane()
    >>> shape.material.reflective = 0.5
    >>> shape.transform = translation(0, -1, 0)
    >>> w.contains.append(shape)
    >>> r = ray(point(0,0,-3), vector(0,-np.sqrt(2)/2, np.sqrt(2)/2))
    >>> i = intersection(np.sqrt(2), shape)
    >>> xs = intersections(i)
    >>> comps = prepare_computations(i, r, xs)
    >>> c = reflected_color(w, comps, 0)
    >>> w.contains = w.contains[:-1]
    >>> len(w.contains) == 2
    True
    >>> np.isclose(c, color(0,0,0))
    array([ True,  True,  True])
    """
    if remaining == 0 or comps.object.material.reflective == 0:
        return color(0, 0, 0)

    reflect_ray = shapes.ray(comps.over_point, comps.reflectv)
    c = color_at(world, reflect_ray, remaining - 1)

    return c * comps.object.material.reflective
예제 #4
0
    def is_blocked(self, start, end):
        """Returns: boolean value indicating whether is any geometry between
                    start and end
           Notes: More highly optimized than standard collide
        """
        #be careful with length, ray will normalize its direction
        direct = end - start
        direct_mag2 = direct.mag_squared()

        ray = shapes.ray(start, direct)
        for x in self.collides:
            coll = x.collide(ray)
            if coll and coll.t**2 < direct_mag2:
                return True

        return False
예제 #5
0
    def render_ray(self, xfactor, yfactor):
        "Renders a single ray at the fraction of the screen specified"
        
        #generate ray for this pixel
        d = ( self.look + self.right * xfactor * self.tanfov +
              self.up * -yfactor * self.tanfov )
        r = shapes.ray(self.pos, d)

        intersect = self.scene.collide( r )

        #see if we managed to hit anything
        if not intersect:
            self.intersect_info = ()
            return vector3()

        color = intersect.surf.m.color( self.scene, intersect, self.pos )
        self.intersect_info = intersect.surf.m.info
        return color
예제 #6
0
def ray_for_pixel(cam, px, py):
    """
    >>> c = camera(201, 101, np.pi/2)
    >>> r = ray_for_pixel(c, 100, 50)
    >>> r.origin.compare(point(0,0,0))
    True

    >>> r.direction.compare(vector(0,0,-1))
    True

    >>> c = camera(201, 101, np.pi/2)
    >>> r = ray_for_pixel(c, 0, 0)
    >>> r.origin.compare(point(0,0,0))
    True

    >>> r.direction.compare(vector(0.66519, 0.33259, -0.66851))
    True

    >>> c = camera(201, 101, np.pi/2)
    >>> c.transform = matrix_multiply(rotation_y(np.pi/4), translation(0,-2,5))
    >>> r = ray_for_pixel(c, 100, 50)
    >>> r.origin.compare(point(0,2,-5))
    True

    >>> r.direction.compare(vector(np.sqrt(2)/2, 0, -np.sqrt(2)/2))
    True
    """
    xoffset = (px + 0.5) * cam.pixel_size
    yoffset = (py + 0.5) * cam.pixel_size
    world_x = cam.half_width - xoffset
    world_y = cam.half_height - yoffset

    pixel = inverse(cam.transform) * point(world_x, world_y, -1)
    origin = inverse(cam.transform) * point(0, 0, 0)
    direction = normalize(pixel - origin)

    return shapes.ray(origin, direction)
예제 #7
0
 def test_one(self):
     p = vector3()
     d = vector3(0.0, 0.0, -1.0)
     self.scene.collide( shapes.ray(p, d) )
     return
예제 #8
0
 def get_augmented_eccentricities(self, start, end):
     """Notes: see documentation for get_eccentricities
               adds the relevant shape to a tuple with the eccentricity
     """
     ray = shapes.ray(start,end-start)
     return [ ( s.eccentricity(ray), s ) for s in self.collides ]
예제 #9
0
def refracted_color(world, comps, remaining=5):
    """
    >>> w = default_world()
    >>> shape = w.contains[0]
    >>> r = ray(point(0,0,-5), vector(0,0,1))
    >>> xs = intersections(intersection(4,shape), intersection(6,shape))
    >>> comps = prepare_computations(xs[0], r, xs)
    >>> c = refracted_color(w, comps, 5)
    >>> c == color(0,0,0)
    array([ True,  True,  True])

    >>> w = default_world()
    >>> shape = w.contains[0]
    >>> shape.material.transparency = 1.0
    >>> shape.material.refractive_index = 1.5
    >>> r = ray(point(0,0,-5), vector(0,0,1))
    >>> xs = intersections(intersection(4,shape), intersection(6,shape))
    >>> comps = prepare_computations(xs[0], r, xs)
    >>> c = refracted_color(w, comps, 0)
    >>> c == color(0,0,0)
    array([ True,  True,  True])

    >>> w = default_world()
    >>> shape = w.contains[0]
    >>> shape.material.transparency = 1.0
    >>> shape.material.refractive_index = 1.5
    >>> r = ray(point(0,0,np.sqrt(2)/2), vector(0,1,0))
    >>> xs = intersections(intersection(-np.sqrt(2),shape), intersection(np.sqrt(2),shape))
    >>> comps = prepare_computations(xs[1], r, xs)
    >>> c = refracted_color(w, comps, 5)
    >>> c == color(0,0,0)
    array([ True,  True,  True])

    >>> w = default_world()
    >>> A = w.contains[0]
    >>> A.material.ambient = 1.0
    >>> A.material.pattern = test_pattern()
    >>> B = w.contains[1]
    >>> B.material.transparency = 1.0
    >>> B.material.refractive_index = 1.5
    >>> r = ray(point(0,0,0.1), vector(0,1,0))
    >>> xs = intersections(intersection(-0.9899, A), intersection(-0.4899, B), intersection(0.4899, B), intersection(0.9899, A))
    >>> comps = prepare_computations(xs[2], r, xs)
    >>> c = refracted_color(w, comps, 5)
    >>> c
    array([0.        , 0.99888367, 0.04721668])

    Below is how the above test is written in the book
    np.isclose(c, color(0, 0.99888, 0.04725))
    array([ True,  True,  True])
    """
    if comps.object.material.transparency == 0 or remaining == 0:
        return color(0, 0, 0)

    n_ratio = comps.n1 / comps.n2
    cos_i = dot(comps.eyev, comps.normalv)
    sin2_t = n_ratio**2 * (1 - cos_i**2)

    if sin2_t > 1.0:
        return color(0, 0, 0)

    cos_t = np.sqrt(1.0 - sin2_t)
    direction = comps.normalv * (n_ratio * cos_i - cos_t) - \
                comps.eyev * n_ratio

    refracted_ray = shapes.ray(comps.under_point, direction)
    c = color_at(world, refracted_ray, remaining - 1) * \
        comps.object.material.transparency
    return c
예제 #10
0
    def color(self, scene, collision, camera):
        """Arguments: scene containing all geometry and lighting,
                      collision object containing information
                          about the impact on this surface
        """
        if not collision:
            return (0.0, 0.0, 0.0)     

        #info used to constuct final stored info struct
        #info struct can be check to see if pixels are on a boundary
        #to use supersampling anti-aliasing only when necessary
        num_lights = 0
        refl_info = None

        #base color is the emissive
        color = self.emis

        #add ambient, no falloff or anything
        color += self.ambient.component_mult( self.ambi )

        #need to pull out a little to avoid colliding
        #with surface that this point is on
        surface_norm = collision.surf.normal( collision.point )
        collision.point += surface_norm * 0.0001

        to_camera = (camera - collision.point).norm()

        #other light components must be done independently for each light
        for light in scene.lights:
            #check the angles because that's the cheapest
            #nothing's going to happen if we're on the wrong side of surface
            to_light = (light.p - collision.point).norm()
            dp = to_light.dp(surface_norm)

            if dp < 0.0:
                continue
            
            #check to see if this light is visible from current point
            #back out if have collision betweeen source and light
            shade = light.get_shade( scene, collision.point )
            if shade < 0.01:
                continue

            #num_lights is used in the info struct that deals
            #with anti-aliasing, if the light doesn't affect this ignore it
            if light.alias_contributor:
                num_lights += 1

            #add diffuse, falloff on angle from light and distance
            diff_factor = max( 0.0, to_light.dp(surface_norm) ) * shade
            color += light.diff.component_mult(self.diff) * diff_factor

            #specular factor will be computed based on dot product
            #between normal and vector halfway between
            #vector to light and vector to camera

            #this means the closers the object is to directly reflecting
            #the light into the camera the higher the specular factor
            #which is exactly what we want
            ideal_reflect = (to_camera + to_light).norm()

            spec_factor = max( 0.0, ideal_reflect.dp(surface_norm) )
            spec_factor = (spec_factor ** self.shiny) * shade
            color += light.spec.component_mult(self.spec) * spec_factor

        if self.refl > 0.0:
            #reflect ray by subtracting twice the ray projected onto
            #the normal, this essentially reverses its direction relative
            #to the normal while leaving the rest intact
            #which is what we want
            
            ray_onto_norm = surface_norm * collision.ray.d.dp(surface_norm)
            reflected = collision.ray.d - (ray_onto_norm * 2.0)
            reflect_ray = shapes.ray( collision.point, reflected )

            ref_collide = scene.collide( reflect_ray )
            ref_color = ref_collide.surf.m.color(scene, ref_collide, camera )
            ref_color = ref_color / 255.0

            refl_info = ref_collide.surf.m.info

            color *= (1.0-self.refl)
            color += ref_color * self.refl

        # set our info struct so it can be read if desired
        self.info = (collision.surf, num_lights, refl_info)
        color.cut_max(1.0)
        color = color * 255.0
        return color