示例#1
0
 def test_point(self):
     """
     Test if a point with given distance along the
     ray is calculated correctly.
     """
     r = Ray((0, 0, 0), (1, 0, 0))
     nt.assert_almost_equal(r.point(0), r.position)
     nt.assert_almost_equal(r.point(1), r.position + r.direction)
     nt.assert_almost_equal(r.point(2), (2, 0, 0))
示例#2
0
 def test_hypothesis(self, arg):
     """
     Test the ray's invariants with 'random' input from hypothesis
     """
     p, d = arg
     norm_d = np.linalg.norm(d)
     hy.assume(norm_d > 1e-8)
     r = Ray(p, d)
     nt.assert_almost_equal(r.position, p)
     nt.assert_almost_equal(r.direction * norm_d, d)
     nt.assert_almost_equal(np.linalg.norm(r.direction), 1)
     point = r.point(4)
     nt.assert_almost_equal(np.linalg.norm(r.position - point), 4)
示例#3
0
 def test_intersect(self):
     """
     Test if the ray-sphere intersection works as expected.
     """
     s = Sphere()
     nt.assert_almost_equal(s.intersect(Ray()), 1)
     r = Ray(position=(-10, 0, 0))
     nt.assert_almost_equal(s.intersect(r), 9)
     r = Ray(position=(0, -5, 0), direction=(0, 1, 0))
     nt.assert_almost_equal(s.intersect(r), 4)
     r = Ray(position=(10, 0, 0), direction=(1, 0, 0))
     nt.assert_almost_equal(s.intersect(r), np.inf)
     r = Ray(position=(0, 0, 10), direction=(0, 0, -1))
     nt.assert_almost_equal(s.intersect(r), 9)
示例#4
0
 def test_string(self):
     """
     Test if the string representation of the ray is correct. Because testing
     against a concrete string is tough if numpy changes how they print
     arrays - we will just test if the call succedes.
     """
     str(Ray())
示例#5
0
    def setUp(self):
        self.r1 = Ray()
        self.s1 = Sphere(position=(-2, 0, 0)) # intersect at np.inf
        self.s2 = Sphere(position=( 2, 0, 0)) # intersect at 1
        self.p1 = Plane(position=(3, 0, 0))   # intersect at np.inf
        self.p2 = Plane(position=(.5, 0, 0), normal=(1, 0, 0)) # at 0.5

        self.scn = Scene(self.s1, self.s2, self.p1, self.p2)
示例#6
0
    def ray(self, pixel, resolutions, rand=True):
        """
        Given the pixel and the camera resolution, returns a ray that
        originates at the camera position and passes
        through the pixel. If rand is set true, a little random offset (smaller
        than the distance between two pixels is added to the pixel position.
        This will together with multiple samples per pixel mitigate aliasing.

        Parameters
        ----------
        pixel : numpy.ndarray_like of shape (2, )
            (x, y) coordinates of the pixel in the image. Numpy style: aka
            (0, 0) is the upper left hand corner and the x values are iterating
            downwards while y is iterating horizontally.
            x must be in the intervall of [0, dimension[0]]
            and y must be in [0, dimension[1]]
            The pixel [0,0] is the upper lefthand corner and the
            pixel [res_x, rex_y] is the lower righthand corner.
        resolutions : numpy.ndarray_like of shape (2, )
            the resolution of the camera in x and y.
        rand : boolean
            When False, every ray passes through the exact center of
            the pixel. When True a random offset smaller than the distance
            between two pixels is added the the pixel center. The ray then
            passes through the perturbed pixel center.

        Returns
        -------
        ray : Ray
            with the position being the camera position
            and direction being a vector that starts at the position
            and passes through the (potentiall offsetted) given pixel

        Examples
        --------
        >>> camera = PerspectiveCamera()
        >>> camera.ray((50, 50), (100, 100), False)
        Ray(position=[0, 0, 0], direction=[0, 0, 1])
        """
        pixel_x, pixel_y = pixel
        res_x, res_y = resolutions
        max_dim = np.maximum(res_x, res_y)

        # image plane coordinates (x, y) of the pixel
        # x and y are in [-1, 1]
        x = (res_x - 2 * pixel_x) / max_dim
        y = (res_y - 2 * pixel_y) / max_dim

        if rand:
            # add a small perturbation to the pixel coordinate
            delta_x, delta_y = 1 / res_x, 1 / res_y
            sample_x, sample_y = np.random.uniform(-1, 1, size=(2, ))
            x += sample_x * delta_x
            y += sample_y * delta_y

        #        Numpy Style - x is vertical - y is horizontal
        world_coord = self._image_plane_center + x * self._up + y * self._righthand
        return Ray(self._position, world_coord - self._position)
示例#7
0
    def test_intersect(self):
        """
        Test if the ray-plane intersection works as expected.
        """
        r = Ray()
        nt.assert_almost_equal(Plane().intersect(r), np.inf)

        p = Plane(position=(0.5, 0, 0), normal=(1, 0, 0))
        nt.assert_almost_equal(p.intersect(r), 0.5)
示例#8
0
 def test_default_construction(self):
     """
     Test if the ray is constructed with the
     expected default parameters.
     """
     r = Ray()
     nt.assert_almost_equal(r.position, (0, 0, 0))
     nt.assert_almost_equal(r.direction, (1, 0, 0))
     nt.assert_almost_equal(r.position, r._position)
     nt.assert_almost_equal(r.direction, r._direction)
示例#9
0
    def test_intersection(self):
        """
        Test if a scene performs the ray-object intersection with all
        contained renderables correctly.
        """
        (d, o) = self.scn.intersect(self.r1)
        nt.assert_almost_equal(d, 0.5)
        self.assertTrue(o is self.p2)

        r2 = Ray(direction=(0, 1, 0)) # does not intersect scene
        (d, o) = self.scn.intersect(r2)
        nt.assert_almost_equal(d, np.inf)
        self.assertIsNone(o)
示例#10
0
    def _trace(self, ray, path_length):
        """
        Trace a given ray one step through the scene.

        ray : padvinder.ray.Ray
            the ray defining the direction and starting position to continue in

        path_length : number
            the number of intersections this path already had

        Returns
        -------
        color : numpy.ndarray_like of shape (3, )
            returns the light color flowing along the path
        """

        if path_length >= self.path_length:
            return np.zeros(3, )

        (t, obj) = self.scn.intersect(ray)

        if obj is None:
            return self.background_color

        point = ray.point(t)
        normal = obj.normal(point)
        # to avoid numerical inaccuracies and the intersection point
        # ending up within an object
        point += 1e-4 * normal

        out_dir = obj.material.outgoing_direction(normal, ray.direction)

        color = self._trace(Ray(point, out_dir), path_length + 1)

        # path tracing defined 'forward' direction
        # color accumulation, however, is backwards in <-> out
        return obj.material(normal, color, -out_dir, -ray.direction)
示例#11
0
 def test_input(self):
     """
     Test if the input values are checked
     correctly.
     Values that do not validate the checks
     are omitted because they are covered by
     the remaining tests.
     """
     with self.assertRaises(ValueError):
         Ray(position=np.array((np.nan, 0, 0)))
     with self.assertRaises(ValueError):
         Ray(position=np.array((np.inf, 0, 0)))
     with self.assertRaises(ValueError):
         Ray(position=np.array((np.inf, 0, 0)))
     with self.assertRaises(ValueError):
         Ray(direction=np.array((np.nan, 0, 0)))
     with self.assertRaises(ValueError):
         Ray(direction=np.array((np.inf, 0, 0)))
     with self.assertRaises(ValueError):
         Ray(direction=np.array((-np.inf, 0, 0)))