Exemple #1
0
 def testSubtraction(self):
     
     testMatrix1 = Matrix()
     testMatrix2 = Matrix()
     
     testMatrix3 = testMatrix1 - testMatrix2
     
     for row in range(4):
         for col in range(4):
             self.assertTrue(testMatrix3.getValue(row, col) == 0)
             
     testMatrix1.setValue(0, 3, 2.5)
     testMatrix1.setValue(2, 2, 4.2)
     testMatrix1.setValue(3, 0, -301)
     
     testMatrix2.setValue(0, 3, -1)
     testMatrix2.setValue(0, 0, -2)
     testMatrix2.setValue(3, 0, 2)
     
     testMatrix4 = testMatrix1 - testMatrix2
     
     self.assertTrue(testMatrix4.getValue(0, 3) == 3.5)
     self.assertTrue(testMatrix4.getValue(2, 2) == 4.2)
     self.assertTrue(testMatrix4.getValue(3, 0) == -303)
     self.assertTrue(testMatrix4.getValue(0, 0) == 2.0)
     self.assertTrue(testMatrix4.getValue(2, 1) == 0)
Exemple #2
0
    def testCreation(self):

        testMatrix = Matrix()
        
        for row in range(4):
            for col in range(4):
                self.assertTrue(testMatrix.getValue(row, col) == 0)
                
        testMatrix.setValue(0, 0, 1.893)
        testMatrix.setValue(2, 1, -200.1)
        testMatrix.setValue(3, 2, 4)
        
        self.assertTrue(testMatrix.getValue(0, 0) == 1.893)
        self.assertTrue(testMatrix.getValue(2, 1) == -200.1)
        self.assertTrue(testMatrix.getValue(3, 2) == 4)
Exemple #3
0
 def testTransform(self):
     
     testVector1 = Vector3(8.2, 1.524, 1.5)
     
     testMatrix = Matrix()
     testMatrix.setValue(3, 3, 1)
     testMatrix.setValue(2, 2, 3)
     testMatrix.setValue(1, 1, -1)
     testMatrix.setValue(0, 0, 2)
     
     testVector2 = testVector1.transform(testMatrix)
     
     self.assertTrue(testVector2.x == 16.4)
     self.assertTrue(testVector2.y == -1.524)
     self.assertTrue(testVector2.z == 4.5)
Exemple #4
0
from Matrix import Matrix

m = Matrix (3,3)

m.setValue (3, 5.5)
print m.getValue(3)
m.printMatrix()
Exemple #5
0
    def testMultiplication(self):
        
        testMatrix1 = Matrix()
        testMatrix2 = Matrix()
        
        testMatrix3 = testMatrix1 * testMatrix2
        
        for row in range(4):
            for col in range(4):
                self.assertTrue(testMatrix3.getValue(row, col) == 0)
                
        testMatrix1.setValue(0, 3, 2.5)
        testMatrix1.setValue(2, 2, 4.2)
        testMatrix1.setValue(3, 0, -301)
        
        testMatrix2.setValue(0, 3, -1)
        testMatrix2.setValue(0, 0, -2)
        testMatrix2.setValue(3, 0, 2)
        
        testMatrix4 = testMatrix1 * testMatrix2
        
        self.assertTrue(testMatrix4.getValue(0, 3) == 0)
        self.assertTrue(testMatrix4.getValue(2, 2) == 0)
        self.assertTrue(testMatrix4.getValue(3, 0) == 602)
        self.assertTrue(testMatrix4.getValue(0, 0) == 5)
        self.assertTrue(testMatrix4.getValue(2, 1) == 0)
        self.assertTrue(testMatrix4.getValue(3, 3) == 301)


        testMatrix5 = Matrix()
        
        testMatrix5.setValue(0, 0, .5)
        testMatrix5.setValue(0, 1, 7)
        testMatrix5.setValue(0, 2, 2.9)
        testMatrix5.setValue(0, 3, -1.0)

        testMatrix5.setValue(1, 0, 9)
        testMatrix5.setValue(1, 1, 1.1)
        testMatrix5.setValue(1, 2, -302.01)
        testMatrix5.setValue(1, 3, 21.0)

        testMatrix5.setValue(2, 0, 0)
        testMatrix5.setValue(2, 1, 1.8)
        testMatrix5.setValue(2, 2, 9.9)
        testMatrix5.setValue(2, 3, 10)

        testMatrix5.setValue(3, 0, -4)
        testMatrix5.setValue(3, 1, 2)
        testMatrix5.setValue(3, 2, 1)
        testMatrix5.setValue(3, 3, 8.9)


        testMatrix6 = Matrix()

        testMatrix6.setValue(0, 0, 18.2)
        testMatrix6.setValue(0, 1, 2)
        testMatrix6.setValue(0, 2, 4)
        testMatrix6.setValue(0, 3, 6)

        testMatrix6.setValue(1, 0, 1)
        testMatrix6.setValue(1, 1, 2)
        testMatrix6.setValue(1, 2, 4)
        testMatrix6.setValue(1, 3, 3)

        testMatrix6.setValue(2, 0, 9)
        testMatrix6.setValue(2, 1, 8)
        testMatrix6.setValue(2, 2, 7)
        testMatrix6.setValue(2, 3, 5)

        testMatrix6.setValue(3, 0, .1)
        testMatrix6.setValue(3, 1, .8)
        testMatrix6.setValue(3, 2, -.5)
        testMatrix6.setValue(3, 3, -12)

        
        testMatrix7 = testMatrix5 * testMatrix6

        self.assertTrue(round(testMatrix7.getValue(0, 0), 2) == 42.1)
        self.assertTrue(round(testMatrix7.getValue(0, 1), 2) == 37.4)
        self.assertTrue(round(testMatrix7.getValue(0, 2), 2) == 50.8)
        self.assertTrue(round(testMatrix7.getValue(0, 3), 2) == 50.5)

        self.assertTrue(round(testMatrix7.getValue(1, 0), 2) == -2551.09)
        self.assertTrue(round(testMatrix7.getValue(1, 1), 2) == -2379.08)
        self.assertTrue(round(testMatrix7.getValue(1, 2), 2) == -2084.17)
        self.assertTrue(round(testMatrix7.getValue(1, 3), 2) == -1704.75)

        self.assertTrue(round(testMatrix7.getValue(2, 0), 2) == 91.9)
        self.assertTrue(round(testMatrix7.getValue(2, 1), 2) == 90.8)
        self.assertTrue(round(testMatrix7.getValue(2, 2), 2) == 71.5)
        self.assertTrue(round(testMatrix7.getValue(2, 3), 2) == -65.1)

        self.assertTrue(round(testMatrix7.getValue(3, 0), 2) == -60.91)
        self.assertTrue(round(testMatrix7.getValue(3, 1), 2) == 11.12)
        self.assertTrue(round(testMatrix7.getValue(3, 2), 2) == -5.45)
        self.assertTrue(round(testMatrix7.getValue(3, 3), 2) == -119.8)
Exemple #6
0
class Raytracer(object):
    """Raytracer defines a renderer that accepts a Scene and uses the basic ray tracing algorithm to render it"""

    def __init__(self, scene, maxRayDepth=3):
        """
        Create a Raytracer object
        
        Keyword arguments:
            scene (Scene) -- the scene to render
            maxRayDepth (int) -- the maximum recursive steps for the ray tracing
            
        """
        self.scene = scene
        self.maxRayDepth = maxRayDepth

        #initialize private variables to be used at render time
        self._curRayDepth = 1
        self._toWorldTrans = Matrix()
        self._viewWidth = 0
        self._viewHeight = 0 
        
    def _initWorldTransformations(self):
        """
        Initiliaze transformations and viewport variables to allow straigtforward world-to-view transformations
        at render time.
        
        """
        
        #initialize the transformation matrix using the scene's camera
        self._toWorldTrans.setValue(0, 0, self.scene.camera.u.x)
        self._toWorldTrans.setValue(1, 0, self.scene.camera.u.y)
        self._toWorldTrans.setValue(2, 0, self.scene.camera.u.z)

        self._toWorldTrans.setValue(0, 1, self.scene.camera.v.x)
        self._toWorldTrans.setValue(1, 1, self.scene.camera.v.y)
        self._toWorldTrans.setValue(2, 1, self.scene.camera.v.z)

        self._toWorldTrans.setValue(0, 2, self.scene.camera.n.x)
        self._toWorldTrans.setValue(1, 2, self.scene.camera.n.y)
        self._toWorldTrans.setValue(2, 2, self.scene.camera.n.z)

        self._toWorldTrans.setValue(0, 3, self.scene.camera.lookAt.x)
        self._toWorldTrans.setValue(1, 3, self.scene.camera.lookAt.y)
        self._toWorldTrans.setValue(2, 3, self.scene.camera.lookAt.z)
    
        self._toWorldTrans.setValue(3, 3, 1.0)

        #compute the dimensions of the viewport using the scene's camera
        self._viewWidth = math.tan(math.radians(self.scene.camera.fov)) * abs(self.scene.camera.lookFrom.z - \
                self.scene.camera.lookAt.z) * 2        
        self._viewHeight = self._viewWidth

    def renderScene(self, imageWidth, imageHeight, outputPath):
        """
        Render the scene with the specified parameters
        
        Keyword arguments:
            imageWidth (int) -- width in pixels of the image to render, this value must be the same as imageHeight
            imageHeight (int) -- height in pixels of the image to render, this value must be the same as imageWidth
            outputPath (str) -- path to write the rendered image
            
        Note -- the current impelementation is limited to square images, so imageWidth must equal imageHeight
        
        """
        
        if not imageWidth == imageHeight:
            print "Error -- imageWidth must equal imageHeight"
            return
            
        #initialize world-to-view transformations and image
        self._initWorldTransformations()
        outputImg = Image.new("RGB", (imageWidth, imageHeight))

        #iterate over each pixel
        for i in range(imageWidth):
            for j in range(imageHeight):
                
                #get the direction from the camera to the current pixel
                direction = self._rayDirection(j, i, imageWidth, imageHeight, self.scene.camera)

                self._curRayDepth = 1
                
                #recursively trace a ray into the scene
                color = self._shootRay(self.scene.camera.lookFrom, direction)

                #set the pixel in the image
                outputImg.putpixel((j, i), (int(color.x * 255), int(color.y * 255), int(color.z * 255)))

        #save the output image
        outputImg.save(outputPath)

    def _rayDirection(self, pixelX, pixelY, imageWidth, imageHeight, camera):
        """
        Compute the direction of the ray with the given parameters
        
        Keyword arguments:
            pixelX (int) -- the horizontal pixel index of the image, (0, 0) is the top left of the image
            pixelY (int) -- the vertical pixel index of the image, (0, 0) is the top left of the image
            imageWidth (int) -- the total number of pixels horizontally in the image
            imageHeight (int) -- the total number of pixels vertically in the image
            camera (Camera) -- the camera that defines the view into the scene
        
        Returns a Vector3 representing the direction of the ray given the parameters
        
        """
            
        #since (0, 0) is the top left of the image, flip the y coordinate
        newPixelY = imageHeight - pixelY
        
        #compute the x and y values given the view dimensions
        newX = (pixelX  / float(imageWidth - 1)) * self._viewWidth
        newY = (newPixelY  / float(imageHeight - 1)) * self._viewHeight
        newX -= self._viewWidth / 2.0
        newY -= self._viewHeight / 2.0

        #compute the world space position of the pixel
        pixelPos = Vector3(newX, newY, 0.0)
        pixelPos = pixelPos.transform(self._toWorldTrans)
        pixelPos.normalize()

        direction = Vector3(pixelPos.x - camera.lookFrom.x, pixelPos.y - camera.lookFrom.y, pixelPos.z - camera.lookFrom.z)   

        return direction

    def _shootRay(self, lookFrom, direction):
        """
        Shoot a ray into the scene recursively
        
        Keyword arguments:
            lookFrom (Point3) -- the initial position of the ray
            direction (Vector3) -- the direction of the ray
        
        Returns an RGB color value represented as a Point3
        
        """
        
        #find an intersection with an object in the scene
        intersection, object = self._findIntersection(lookFrom, direction)
        
        #if there was no intersection return the background color
        if intersection.x == float('inf'):
            return self.scene.bgColor

        #evaluate the shader of the intersected object
        curColor = self._calculateColor(intersection, direction, object, self.scene.light)

        if self._curRayDepth < self.maxRayDepth and object.shader.isReflective():
        
            reflection = object.getReflection(intersection, direction)
            
            #move the intersection along the direction a small amount to avoid repeated self collision
            movedIntersection = object.moveIntersection(intersection, reflection)

            self._curRayDepth += 1
            
            #recursive
            return curColor + self._shootRay(movedIntersection, reflection)

        return curColor
            
    def _findIntersection(self, lookFrom, direction):
        """
        Find the nearest intersection given the parameters
        
        Keyword arguments:
            lookFrom (Point3) -- the initial position of the ray
            direction (Vector3) -- the direction of the ray

        Returns an intersection and object if there was an intersection, returns a Point3 at infinity if there was
        no intersection.
        
        """
        
        shortestDist = float('inf')
        dist = shortestDist
        
        nearestIntersection = Point3(float('inf'), float('inf'), float('inf'))
        intersectedObject = 0
        
        for object in self.scene.objects:
            intersection = object.findIntersection(lookFrom, direction)
            
            if not intersection.x == float('inf'):
                
                dist = lookFrom.distSquared(intersection)
                
                if dist < shortestDist:
                    shortestDist = dist
                    nearestIntersection = intersection
                    intersectedObject = object

        return nearestIntersection, intersectedObject

    def _calculateColor(self, intersection, rayDirection, object, light):
        """
        Compute the color value with the given parameters
        
        Keyword arguments:
            intersection (Point3) -- the intersection point where the color is being evaluated
            rayDirection (Vector3) -- the incoming direction of the ray
            object (Primitive) -- the object that has been intersected
            light (Light) -- the light illuminating the scene
            
        Returns an RGB color value represented as a Point3
        
        """
        
        #get the direction to the light
        lightDirection = light.getDirection(intersection)
        dirToLight = Vector3(lightDirection.x * -1, lightDirection.y * -1, lightDirection.z * -1)
        
        #move the intersection toward the light a small amount to prevent self intersection
        movedIntersection = object.moveIntersection(intersection, dirToLight)
        
        #check if the current intersection is in shadow
        if object.shader.isReflective() == False and self._inShadow(movedIntersection, dirToLight, light):
        
            inShadowColor = Point3(self.scene.ambientColor.x  * object.shader.diffuse.x,
                            self.scene.ambientColor.x  * object.shader.diffuse.y,
                            self.scene.ambientColor.x  * object.shader.diffuse.z)

            return inShadowColor
        else:
            
            #the intersection is not in shadow, so evaluate the full shader
            objectColor = object.getColor(intersection, dirToLight, rayDirection, self.scene.ambientColor, 
                                        self.scene.light.color, self.scene.bgColor)

            return objectColor

    def _inShadow(self, intersection, direction, light):
        """
        Check whether the intersection is in shadow given the direction and light
        
        Keyword arguments:
            intersection (Point3) -- the intersection point to check
            direction (Vector3) -- the direction towards the light source
            light (Light) -- the light illuminating the scene
            
        Returns True if the intersection is in shadow, and False if it is not
        
        """

        for object in self.scene.objects:
            newIntersection = object.findIntersection(intersection, direction)
            dist = intersection.distSquared(newIntersection)
            distToLight = intersection.distSquared(light.position)

            if newIntersection.x != float('inf') and dist < distToLight:
                return True

        return False