示例#1
0
 def __init__(self, Pos, W, H, Fov=1, Samples=256):
     """
     Initialises a Camera class.
     Parameters:
         Pos: Vec3. The position of the camera
         W: Float. The camera width
         H: Float. The camera height
     Optional Parameters:
         Fov: Float. The field of view constant of the camera. Default 1.
         Samples: Int. The rendering sample rate. Deafault 256
     """
     # Init variables
     self.fov = Fov
     self.w = W  # Width
     self.h = H  # Height
     self.pos = Pos  # Camera position
     self.samples = Samples  # Sample Rate
     self.normal = Vec3(0, 0, -1)  # Normal (used for rotation)
     self.bgColor = Vec3(1, 1, 1)  # background colour
     self.target = Vec3(0, 0, 0)  # Camera target, used for rotation
     self.barWidth = 50  # progress bar width
     # Array for image data
     self.image_array = [[0] * (self.w * 3) for i in range(self.h)]
     # set rotation matrix
     self.ca = self.setCamera()
示例#2
0
 def loadModel(self, path, mtl):
     """
     Loads a model from a obj and mtl file.
     Parmeters:
         path: String. The path to the obj file
         mtl: String. The path to the mtl file
     """
     mat = None
     if mtl is not None:
         for line in open(mtl):
             if "newmtl" in line:
                 # update current material
                 mat = line[7:].strip()
             elif line[0:2] == "Kd":
                 # add material colour
                 self.materials[mat] = (
                     Vec3(*(map(float, line[3:].strip().split()))))
     verts = []
     for line in open(path):
         # If line is a vertex
         if line[0] == "v":
             # Add vertex
             verts.append(
                 Vec3(*(map(float, line[1:].strip().split(" ")))))
         # If line is a face
         if line[0] == "f":
             # add face
             a = line[1:].strip().split(" ")
             self.addPrimitive(
                 Triangle(*[Copy(verts[int(i) - 1]) for i in a], mat))
         # if line is a material declaration
         if "usemtl" in line:
             mat = line[7:].strip()  # Update material
示例#3
0
 def __init__(self, orig=Vec3(0, 0, 0), dir=Vec3(0, 0, 0)):
     """
     Initializes the ray.
     Optional parameters:
         orig: Vec3. The origin of the ray.
         dir: Vec3. The direction of the ray.
     """
     self.o = orig
     self.d = Normalize(dir)
示例#4
0
def midDay(scene):
    sun = Sun(pos=Vec3(40, 100, 30))
    # orient sun
    sun.lookAt(Vec3(0, 0, 0))
    # Add sun to scene
    scene.addLight(sun)
    # Create sky
    sky = Sky()
    # Add sky to scene
    scene.addLight(sky)
示例#5
0
def dusk(scene):
    sun = Sun(pos=Vec3(40, 20, 0), colour=Vec3(0.4, 0.2, 0.4))
    # orient sun
    sun.lookAt(Vec3(0, 0, 0))
    # Add sun to scene
    scene.addLight(sun)
    # Create sky
    sky = Sky()
    # Add sky to scene
    scene.addLight(sky)
示例#6
0
 def __init__(self, b=AABB(Vec3(0, 0, 0), Vec3(0, 0, 0))):
     """
     Initializes a Braunch Class
     """
     self.leaves = []
     self.braunches = []
     self.materials = {}
     self.bounds = b
     self.lights = []
     self.average = 0
     self.time = 0
示例#7
0
 def __init__(self, size=2, normal=Vec3(0, 0, 1), pos=Vec3(1, 0, 0), colour=Vec3(0.77, 0.77, 0.72)):
     """
     Initializes a sun class
     Optional Parmeters:
         size: Float. The size of the sun. Default 2
         normal: Vec3. The direction the sun's facing. Default Vec3(0, 0, 1)
         pos: Vec3. The position of the sun. Default Vec3(1, 0, 0)
         colour: Vec3. The colour of the sun.
             Default colour=Vec3(0.97, 0.97, 0.72)
     """
     self.size = size
     self.normal = normal
     self.pos = pos
     self.colour = colour
示例#8
0
 def intersectLeaves(self, leaves, ray):
     close = (False, float("inf"), Vec3(0, 0, 0))
     indie = None
     self.average += len(leaves)
     for i in leaves:
         intersection = i.intersect(ray)
         if intersection[0] and 0 < intersection[1] < close[1]:
             close = intersection
             indie = i
     return close, indie
示例#9
0
def diskPoint(normal):
    """
    Returns a random point on a disk with a radius of 1
    normal: Vec3. The normal of the disk to be used
    return: Vec3. The random point
    """
    theta = random() * 2 * pi  # theta in radians (0-2)
    mag = sqrt(random())  # sqrt makes the distribution uniform
    y = sin(theta) * mag  # Generate x and y coords with some trig
    x = cos(theta) * mag
    return orthonormal(Vec3(x, y, 0), normal)  # rotate to match normal
示例#10
0
 def getDir(self, x, y):
     """
     Get direction from pixel coordinates
     Parameters:
         x: The pixel's x coordinates
         y: The pixel's y coordinates
     """
     # calculate direction
     d = Vec3(x / self.w * 2 - 1, y / self.h * 2 - 1, self.fov)
     d = Normalize(d)
     # Rotate to match camera rotation
     return self.multMat(self.ca, d)
示例#11
0
 def fromScene(self, scene):
     """
     Constructs an octree from a scene
     Parameters:
         Scene: Scene. The scene to construct an octree from.
     """
     # Calculate the bounding box of the scene
     minx = miny = minz = +10000
     maxx = maxy = maxz = -10000
     for i in scene.primitives:
         minx = min(min(i.v0.x, i.v1.x, i.v2.x), minx)
         miny = min(min(i.v0.y, i.v1.y, i.v2.y), miny)
         minz = min(min(i.v0.z, i.v1.z, i.v2.z), minz)
         maxx = max(max(i.v0.x, i.v1.x, i.v2.x), maxx)
         maxy = max(max(i.v0.y, i.v1.y, i.v2.y), maxy)
         maxz = max(max(i.v0.z, i.v1.z, i.v2.z), maxz)
     self.bounds = AABB(Vec3(minx, miny, minz), Vec3(maxx, maxy, maxz))
     for t in scene.primitives:
         self.grow((t))
     self.addMaterials(scene.materials)
     self.addLights(scene.lights)
示例#12
0
 def setCamera(self):
     """
     Updates the camera's rotation matrix
     """
     ro = self.pos  # ray origin
     ta = self.target  # ray target
     cr = 0  # Angular rotation (should always be 0)
     # Construct the matix using some fancy linear algebra
     cw = Normalize(ta - ro)
     cp = Vec3(sin(cr), cos(cr), 0.0)
     cu = Normalize(Cross(cw, cp))
     cv = Normalize(Cross(cu, cw))
     return [cu, cv, cw]
示例#13
0
    def rendererCalcColor(self, ray, numBounce, tracer):
        """
        Calculates a pixel colour given a starting ray using Monte Carlo magik!
        Parameters:
            ray: Ray. The ray to be traced
            numBounce: Int. The number of bounces the ray is allowed to do
            tracer: Scene. The scene
        """
        # Variables for colour accumulation
        tCol = Vec3(0, 0, 0)
        gCol = Vec3(1, 1, 1)

        for i in range(numBounce):
            # intersect with seen
            isec = tracer.worldIntersect(ray)
            # intersection information
            sec = isec["t"]
            # if no intersection
            if not sec[0]:
                # stop the accumulation process or return the sky
                if i == 0:
                    return self.bgColor
                else:
                    break
            # Calculate intersection position
            pos = ray.o + (ray.d ^ sec[1])
            # load material
            material = isec["index"].mat
            # Load surface colour and compute direct lighting
            sCol = tracer.materials[material]
            dCol = self.applyDirectLighting(pos, sec[2], tracer)
            # Create new ray
            ray = Ray(orig=pos + (sec[2] ^ 0.1),
                      dir=OrientedHemiDir(sec[2]))
            # accumulate colours
            gCol = sCol * gCol
            tCol += gCol * dCol
        # return the total colour
        return tCol
示例#14
0
 def applyDirectLighting(self, pos, nor, scene):
     """
     Applies Direct lighting
     Parameters:
         pos: Vec3. The point to apply the direct lighting
         nor: Vec3. The surface normal.
         scene: Scene. The scene.
     """
     # start the accumulation
     dCol = Vec3(0, 0, 0)
     # iterate over lights and accumulate colors
     for i in scene.lights:
         dCol += i.calcDirect(pos, nor, scene)
     return dCol
示例#15
0
 def multMat(self, mat, vec):
     """
     Multiplies a Vec3 by a matrix.
     Parameters:
         mat: List<Vec3>. The matrix to be multiplied
         vec: Vec3. The vector to be multiplied
     """
     # Initialise the output vector
     out = Vec3(0, 0, 0)
     # iterate through the dimensions
     dimensions = ["x", "y", "z"]
     for i in range(3):
         out = out + (mat[i] ^ getattr(vec, dimensions[i]))
     return out
示例#16
0
    def intersect(self, ray):
        """Intersects a ray with a Triangle
        ray: Ray the ray to be intersected
        return: Tuple (Bool hit, Float distance, Vec3 normal)
        """
        # Miss tuple
        miss = (False, 0, Vec3(0, 0, 0))
        normal = Vec3(0, 0, 0)
        v0 = self.v0
        v1 = self.v1
        v2 = self.v2
        # edges of the triangle
        v0v1 = v1 - v0
        v0v2 = v2 - v0
        # Determinant of a 3*3 matrix using triple scaler product
        pvec = Cross(ray.d, v0v2)
        det = Dot(v0v1, pvec)

        if det < 0.000001:
            return miss

        invDet = 1.0 / det
        tvec = ray.o - v0
        u = Dot(tvec, pvec) * invDet
        # make sure the barycentric coordinates are in the triangle
        if u < 0 or u > 1:
            return miss

        qvec = Cross(tvec, v0v1)
        v = Dot(ray.d, qvec) * invDet

        # make sure the barycentric coordinates are in the triangle
        if v < 0 or u + v > 1:
            return miss

        return (True, Dot(v0v2, qvec) * invDet, self.normal)
示例#17
0
def orthonormal(p, normal):
    """
    Translates a normalized vector to have its z axis aligned along
    a specific normal.

    p: Vec3 the vector to be translated
    normal: the normal for the vector to be translated around
    """
    # create orthonormal basis around normal
    w = normal
    v = Cross(Vec3(0.00319, 0.0078, 1.0), w)  # jittered up
    v = Normalize(v)  # normalize
    u = Cross(v, w)

    hemi_dir = (u ^ p.x) + (v ^ p.y) + (w ^ p.z)  # linear projection
    return Normalize(hemi_dir)  # make direction
示例#18
0
def sampleHemisphere(u1, u2):
    """Return a point on the hemisphere from polar coordinates
    u1: Float. the radius of the polar coordinats
    u2: Float. theta

    return: Vec3. point on hemisphere
    """
    # make r uniform over the sphere using the inverse itegral
    # of the distribution density
    r = sqrt(u1)
    # scale theta to radians
    theta = 2 * pi * u2
    # calculate x and y
    x = r * cos(theta)
    y = r * sin(theta)

    return Vec3(x, y, sqrt(max(0, 1 - u1)))
示例#19
0
 def worldIntersect(self, ray):
     """
     intersects the ray with the octree.
     Parameters:
         r: Ray. The ray to intersect
     return: Tuple (Bool hit, Float distance, Vec3 normal)
     """
     # the closest intersection so far
     close = (False, float("inf"), Vec3(0, 0, 0))
     index = -1
     for i in range(len(self.primitives)):
         intersection = self.primitives[i].intersect(ray)
         self.average += 1
         # if there's a new closest intersection update index and close
         if intersection[0] and 0 < intersection[1] < close[1]:
             close = intersection
             index = i
     return {"t": close, "index": self.primitives[index]}
示例#20
0
 def initRay(self, x):
     for y in range(0, self.h):
         col = Vec3(0, 0, 0)
         # Aspect correction in the case the output image is not square
         # This took me super long to figure out
         mx = ((x + (self.h - self.w) / 2) * self.w/self.h)
         # average the samples
         for i in range(self.samples):
             # calculate direction
             # Adds the random to get anti-aliasing
             a = self.getDir(mx + random(), y + random())
             # create ray for rendering
             ray = Ray(orig=self.pos, dir=a)
             # render!
             col = col + self.rendererCalcColor(ray, 4, self.tracer)
         col = col ^ (1 / self.samples)  # average the samples
         self.savePixel(col, x, y)  # save pixel
     return "complete"
示例#21
0
 def worldIntersect(self, r):
     """
     intersects the ray with the octree.
     Parameters:
         r: Ray. The ray to intersect
     return: Tuple (Bool hit, Float distance, Vec3 normal)
     """
     miss = (False, float("inf"), Vec3(0, 0, 0))
     # Breadth first search
     queue = [self]
     index = None
     for i in queue:
         if i.bounds.intersect(r) < 1000:
             # Check leaves
             intersect, indet = self.intersectLeaves(i.leaves, r)
             if intersect[0] and 0 < intersect[1] < miss[1]:
                 miss = intersect
                 index = indet
             # Check braunches
             queue += i.braunches
     # print(total)
     return {"t": miss, "index": index}
示例#22
0
 def render(self, tracer, imgOut):
     """
     Renders a scene to an image
     Parameters:
         tracer: Scene. The scene to be rendered.
         imgOut: String. The name of the output file
     """
     # loop through all the pixels in the image
     for x in range(self.w):
         for y in range(self.h):
             col = Vec3(0, 0, 0)
             # Aspect correction in the case the output image is not square
             # This took me super long to figure out
             mx = ((x + (self.h - self.w) / 2) * self.w / self.h)
             # average the samples
             for i in range(self.samples):
                 # calculate direction
                 # Adds the random to get anti-aliasing
                 a = self.getDir(mx + random(), y + random())
                 # create ray for rendering
                 ray = Ray(orig=self.pos, dir=a)
                 # render!
                 col = col + self.rendererCalcColor(ray, 4, tracer)
             col = col ^ (1 / self.samples)  # average the samples
             self.savePixel(col, x, y)  # save pixel
         # ===Update progress bar===
         # Clear terminal
         os.system('cls' if os.name == 'nt' else 'clear')
         # Calculate progress
         prog = int(round((x) / self.w * self.barWidth))
         # Print progress bar
         print("[" + "=" * prog + ">" + " " * (self.barWidth - prog) +
               "] " + str(round(prog *
                                (100 / self.barWidth))) + "% completed")
     # ===Save Image===
     self.saveImage(imgOut)  # save image
示例#23
0
 def subDivide(self):
     """
     Subdivides into 8 smaller AABB's
     returns: List. 8 smaller AABB's
     """
     # A-H represent the 8 corners of the box
     # M is the midpoint of the box
     a = self.info[0]
     g = self.info[1]
     b = Vec3(g.x, a.y, a.z)
     c = Vec3(g.x, a.y, g.z)
     d = Vec3(a.x, a.y, g.z)
     e = Vec3(a.x, g.y, a.z)
     f = Vec3(b.x, g.y, b.z)
     h = Vec3(d.x, g.y, g.z)
     # Calculate midpoint
     m = self.info[0] + ((self.info[1] - self.info[0]) ^ 0.5)
     boxes = []
     # Construct all 8 boxes
     for i in [a, b, c, d, e, f, g, h]:
         boxes.append(AABB(i, m))
     return boxes
示例#24
0
from Vector3 import Vec3, Cross, Normalize
from random import random
from math import pi, sin, cos

normal = Normalize(Vec3(1, 0, 0))
f = open("data.dat", "w")


def diskPoint():
    theta = random() * pi * 2
    mag = random()
    x = cos(theta) * mag
    y = sin(theta) * mag
    return Vec3(x, y, 0)


for i in range(1000):
    p = diskPoint()
    f.write("{0}  {1}\n".format(p.x, p.y))
    w = normal
    v = Cross(Vec3(0.00319, 0.0078, 1.0), w)  # jittered up
    v = Normalize(v)  # normalize
    u = Cross(v, w)

    hemi_dir = (u ^ p.x) + (v ^ p.y) + (w ^ p.z)
    hemi_dir = Normalize(hemi_dir)
    # f.write("{0}   {1}   {2}\n".format(hemi_dir.x, hemi_dir.y, hemi_dir.z))
    print(Normalize(hemi_dir))
示例#25
0
 def __init__(self, colour=Vec3(0.6, 0.6, 0.55)):
     """initializes a sky class
     Optional parameters:
         colour: Vec3. The colour of the sun. Default Vec3(0.4, 0.4, 0.45)
     """
     self.colour = colour
示例#26
0
from Vector3 import Vec3
from Camera import Camera
from Scene import Scene
from Sun import Sun
from Sky import Sky
from timeit import default_timer as timer
from Octree import Braunch

# turn on tree
useTree = False
# Create new scene
scene = Scene()
# Load Model and materials
scene.loadModel("models/pyramid.obj", "models/pyramid.mtl")
# Create Sun
sun = Sun(pos=Vec3(80, 50, 50))
sun.lookAt(Vec3(0, 0, 0))
scene.addLight(sun)
# Create sky
sky = Sky()
scene.addLight(sky)
# Create Camera
cam = Camera(Vec3(8, 4, 8), int(200), int(200), Fov=1.4, Samples=2)
cam.lookAt(Vec3(0, 3, 0))
# Render scene
ts = timer()
if useTree:
    tree = Braunch()
    tree.fromScene(scene)
    cam.render(tree, "pyramid.png")
else:
示例#27
0
                   message="File does not exist")
material = validInput("Material File (.obj): ",
                      path.isfile,
                      message="File does not exist")
width = inputInt("Output Width: ")
height = inputInt("Output Height: ")
while True:
    proto = input("lighting (" + str([i for i in lighting.presets]) + "): ")
    if proto in lighting.presets:
        break
preset = proto
output = input("output image name: ")
# add the extension png if necessary
output += "" if output[-4:] == ".png" else ".png"

# Create new scene
scene = Scene()
# Load Model and materials
scene.loadModel(model, material)
# Add lighting
lighting.presets[preset](scene)
# Create Camera
cam = Camera(Vec3(-8, 5, 8), width, height, Fov=1.4, Samples=samples)
cam.lookAt(Vec3(0, 3, 0))
# Render scene
ts = timer()
cam.render(scene, output)

print("Render time: {}".format(timer() - ts))
print("Intersections: ", scene.average)
示例#28
0
            tmin, tmax = tmax, tmin
        for a in ["y", "z"]:
            b = getattr(ray.d, a)
            if b == 0:
                b += 0.000001
            tymin = (getattr(self.min, a) - getattr(ray.o, a)) / \
                b
            tymax = (getattr(self.max, a) - getattr(ray.o, a)) / \
                b
            # Swap if necessary
            if tymin > tymax:
                tymin, tymax = tymax, tymin
            # Check collision
            if (tmin > tymax) or (tymin > tmax):
                return False
            # Update tmin and tmax
            tmin = min(tmin, tymin)
            tmax = max(tmax, tymax)
            t = tmin
        if (t < 0):
            t = tmax
            if t < 0:
                return False
        return True


aabb = AABB(Vec3(-1, -1, -1), Vec3(1, 1, 1))
ray = Ray(orig=Vec3(0, 0, -3), dir=Vec3(0, 0, -2))
z = aabb.intersect(ray)
print(z)
示例#29
0
from Octree import Braunch
from Scene import Scene
from Vector3 import Vec3
from timeit import default_timer as timer
from Sun import Sun
from Sky import Sky
from Camera import Camera

# Create new scene
scene = Scene()
# Load Model and materials
scene.loadModel("untitled.obj", "untitled.mtl")

sun = Sun(pos=Vec3(-20, 30, 30))
sun.lookAt(Vec3(0, 0, 0))
scene.addLight(sun)
sun.size = 3
# Create sky
sky = Sky(colour=Vec3(0.3, 0.2, 0.3))
scene.addLight(sky)
# Create Camera
cam = Camera(Vec3(-4, 3, -5), 256, 256, Fov=1, Samples=7)
cam.lookAt(Vec3(0, 0, 0))
tree = Braunch()
tree.fromScene(scene)
tree.display()
# Render scene
ts = timer()
cam.render(tree)
treeTime = timer() - ts
# print("Render time: {}".format(timer()-ts))
示例#30
0
def diskPoint():
    theta = random() * pi * 2
    mag = random()
    x = cos(theta) * mag
    y = sin(theta) * mag
    return Vec3(x, y, 0)