'''
------------------------------------------------------------
Name: bt_Raytrace.py (copyright 2013, all rights reserved)
Author: Brent Tyler (tylerART)
Email: brent@tylerart.com
Web: http://tylerart.com
Description:
Renders Spheres
History:
----------------------------------------------------
Date: Version: Notes:
10.12.2013 0.02 It's ALIVE!
----------------------------------------------------
'''
import math
import time
from PIL import Image
def raytrace(scene, frameBuffer):
# Turn on Timer
startTime = time.clock()
# Loop Through Pixels
for y in range(-frameBuffer.imageHeight/2, frameBuffer.imageHeight/2):
for x in range(-frameBuffer.imageWidth/2, frameBuffer.imageWidth/2):
# Create Ray
ray = Ray(Vector(x, y, -1000), scene.cameraDirection)
# Trace Depth
t = 2000.0
# Loop over objects in the scene
for sphere in scene.spheres:
# Calculate Intersection
if sphere.intersects(ray, t):
# Paint Pixel
xPixel = x + frameBuffer.imageWidth/2
yPixel = frameBuffer.imageHeight-1-y-frameBuffer.imageHeight/2
frameBuffer.image.putpixel((xPixel, yPixel), sphere.color)
if y%10 == 0:
percentage = round((float(y + frameBuffer.imageHeight/2)/frameBuffer.imageHeight * 100), 1)
print str(percentage) + '% complete'
# Save Image
frameBuffer.image.save(frameBuffer.imageName)
# Turn Timer Off
endTime = time.clock()
print '100% complete'
print "Render Time: " + str(round(abs(startTime - endTime), 4)) + ' seconds'
class Sphere(object):
def __init__(self, position, radius, color):
self.position = position
self.radius = radius
self.color = color
def intersects(self, ray, t):
# Geometric Eval
L = self.position - ray.origin
tca = L.dot(ray.direction)
if tca < 0: return False
d2 = L.dot(L) - tca * tca
if d2 > self.radius: return False
thc = math.sqrt(self.radius - d2)
t0 = tca - thc
t1 = tca + thc
if t0 > 2000.0: return False
return True
class Vector():
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
def __add__(self, vector):
return Vector(self.x + vector.x, self.y + vector.y, self.z + vector.z)
def __sub__(self, vector):
return Vector(self.x - vector.x, self.y - vector.y, self.z - vector.z)
def dot(self, vector):
return (self.x * vector.x) + (self.y * vector.y) + (self.z * vector.z)
def mag(self):
return math.sqrt(self.dot(self))
def normalize(self):
return self.scale(1.0/self.mag())
def __mul__(self, other):
return (self.x * other.x + self.y * other.y + self.z * other.z)
class Ray(object):
def __init__(self, origin, direction):
self.origin = origin
self.direction = direction
class Scene():
def __init__(self):
self.spheres = []
self.cameraDirection = Vector(0.0, 0.0, 1.0)
self.cameraPosition = Vector(0.0, 0.0, -1000)
def addSphere(self, x, y, z, radius, color):
self.spheres.append(Sphere(Vector(x, y, z), radius, color))
class frameBuffer():
def __init__(self):
self.imageWidth = 320
self.imageHeight = 240
self.imageName = 'image.tif'
self.backgroundColor = (0,0,0)
def setDimentions(self, x, y):
self.imageWidth = x
self.imageHeight = y
def setImageName(self, imageName):
self.imageName = imageName
def setBackgroundColor(self, r, g, b):
self.backgroundColor = (r,g,b)
def create(self):
self.image = Image.new('RGB', (self.imageWidth, self.imageHeight), 'black')
if __name__ == '__main__':
# Create Scene
scene = Scene()
scene.addSphere(0,0,0,250, (255,0,0))
scene.addSphere(250, 100, 0, 10, (0,255,0))
scene.addSphere(-300, -25, 0, 75000, (0,0,255))
scene.addSphere(300, -150, 0, 35000, (255,255,255))
scene.addSphere(50, 350, 0, 7000, (50,50,50))
# Create Frame Buffer
frameBuffer = frameBuffer()
frameBuffer.setDimentions(1280,720)
frameBuffer.create()
# Render
raytrace(scene, frameBuffer)