RapydRay is a basic raytracer in RapydScript.
You can play with it in real time here RapydRay
#
# Based on:
#http://viola.informatik.uni-bremen.de/typo/html/qingteng/index.html
# Documentation :
# http://www.scratchapixel.com
# Thanks to Alexander Tsepkov the creator of RapydScript, and
# Charles Law for allowing testing RapydScript online
# Post comments and suggestions on RapydScript Google Group
# You can modify the script and see the modifications
# in direct live (quite)
# When modifying you can reduce WIDTH and HEIGHT to avoid lag
# Authored by Salvatore DI DIO (email : salvatore dot didio at gmail)
# Best performance with Firefox
# On my mac book pro : 1280 x 800 rendered in less than a minute
WIDTH = 320
HEIGHT = 240
class Vector:
def __init__(self, x=0, y=0, z=0):
self.x = x
self.y = y
self.z = z
def copy(self):
return Vector(self.x, self.y, self.z)
def length(self):
return Math.sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
def sqrLength(self):
return (self.x*self.x + self.y*self.y + self.z*self.z)
def normalize(self):
inv = 1.0/self.length()
return Vector(self.x*inv, self.y*inv,self.z*inv)
def negate(self):
return Vector(-self.x, -self.y, -self.z)
def add(self,v):
return Vector(self.x + v.x, self.y + v.y, self.z + v.z)
def sub(self,v):
return Vector(self.x - v.x, self.y - v.y, self.z - v.z)
def times(self, k):
return Vector(k*self.x, k*self.y, k*self.z)
def dot(self,t):
return self.x*t.x + self.y*t.y+self.z*t.z
def cross(self,w):
v = Vector()
v.x = self.y * w.z - self.z * w.y
v.y = -self.x * w.z + self.z * w.x
v.z = self.x * w.y - self.y * w.x
return v
@staticmethod
def zero():
return Vector()
class Ray:
def __init__(self, vectOrigin, vectDest):
self.origin = vectOrigin
self.direction = vectDest
def getPoint(self, t):
# p = self.origin + t * self.direction
return self.origin.add(self.direction.times(t))
class IntersectionResult:
def __init__(self):
self.sceneobject = null
self.distance = 0
self.position = Vector.zero()
self.normal = Vector.zero()
@staticmethod
def nohit():
return IntersectionResult()
class Sphere:
def __init__(self, center = Vector(0,2,-1), radius = 1):
self.center = center
self.radius = radius
self.name = "Sphere"
def initialize(self):
self.sqrRadius = self.radius * self.radius
def double(self):
return 100
def intersect(self, ray):
v = ray.origin.sub(self.center)
a0 = v.sqrLength() - self.sqrRadius
DdotV = ray.direction.dot(v)
if DdotV <= 0:
discr = DdotV * DdotV - a0
if discr >= 0:
r = IntersectionResult()
r.sceneobject = self
r.distance = -DdotV - Math.sqrt(discr)
r.position = ray.getPoint(r.distance)
r.normal = r.position.sub(self.center)
return r
return IntersectionResult.nohit()
class Camera:
def __init__(self,eye, front, up, fov,aspectratio):
self.eye = eye
self.front = front
self.RefUp = up
self.fov = fov
self.aspectratio = aspectratio
def initialize(self):
self.right = self.front.cross(self.RefUp)
self.up = self.right.cross(self.front)
self.fovScale = Math.tan(self.fov * 0.5 * Math.PI/180)*2
def generateRay(self,x,y):
r = self.right.times((x - 0.5) * self.fovScale*self.aspectratio)
u = self.up.times((y - 0.5) * self.fovScale)
ray = Ray(self.eye, self.front.add(r).add(u).normalize())
return ray
class Union:
def __init__(self,geometries):
self.geometries = geometries
def initialize(self):
for geo in self.geometries:
geo.initialize()
def intersect(self, ray):
minDistance = Infinity
minResult = IntersectionResult()
for geo in self.geometries:
result = geo.intersect(ray)
if ((result.sceneobject != null) and (result.distance < minDistance)):
minDistance = result.distance
minResult = result
return minResult
class Plane:
def __init__(self, normal, distance):
self.normal = normal
self.d = distance
self.name = "Plane"
def initialize(self):
self.position = self.normal.times(self.d)
def intersect(self, ray):
a = ray.direction.dot(self.normal)
if a >= 0:
return IntersectionResult.nohit()
b = self.normal.dot(ray.origin.sub(self.position))
result = IntersectionResult()
result.sceneobject = self
result.distance = -b/a
result.position = ray.getPoint(result.distance)
result.normal = self.normal
return result
class Color:
def __init__(self, r=0, g=0, b=0):
self.r = r
self.g = g
self.b = b
def add(self,c):
return Color(self.r + c.r, self.g + c.g, self.b + c.b)
def times(self, k):
return Color(k*self.r, k*self.g, k*self.b)
def modulate(self, c):
return Color(self.r * c.r, self.g * c.g, self.b * c.b)
Color.black = Color()
Color.red = Color(1,0,0)
Color.white = Color(1,1,1)
Color.green = Color(0,1,0)
Color.blue = Color(0,0,1)
Color.gold = Color(1,0.8746,0)
Color.cyan = Color(0,1,1)
class CheckerMaterial:
def __init__(self, scale, reflectiveness):
self.scale = scale
self.reflectiveness = reflectiveness
def sample(self,ray,position, normal):
v = Math.abs((Math.floor(position.x * 0.1) \
+ Math.floor(position.z * self.scale)) % 2)
if v < 1:
return Color.black
else:
return Color.white
lightDir = Vector(6, 8.5, 15).normalize()
lightColor = Color(1,1,1)
class PhongMaterial:
def __init__(self,diffuse, specular, shininess, reflectiveness):
self.diffuse = diffuse
self.specular = specular
self.shininess = shininess
self.reflectiveness = reflectiveness
def sample(self,ray,position,normal):
NdotL = normal.dot(lightDir)
H = (lightDir.sub(ray.direction)).normalize()
NdotH = normal.dot(H)
diffuseTerm = self.diffuse.times(Math.max(NdotL, 0))
specularTerm = self.specular.times(Math.pow(Math.max(NdotH, 0), self.shininess))
return lightColor.modulate(diffuseTerm.add(specularTerm))
def rayTraceRecursive(scene, ray, maxReflect):
result = scene.intersect(ray)
if result.sceneobject:
reflectiveness = result.sceneobject.material.reflectiveness
mat = result.sceneobject.material
color = mat.sample(ray, result.position,result.normal)
color.times(1 - reflectiveness)
if (reflectiveness > 0 and maxReflect > 0):
r = result.normal.times(-2 * result.normal.dot(ray.direction)).add(ray.direction)
ray = Ray(result.position, r)
reflectedColor = rayTraceRecursive(scene, ray, maxReflect - 1)
color = color.add(reflectedColor.times(reflectiveness))
return color
return Color(0,0,0)
def main():
canvas = document.getElementById("canvas")
ctx = canvas.getContext("2d")
canvas.width = WIDTH
canvas.height = HEIGHT
w = canvas.width
h = canvas.height
ctx.fillStyle = "rgb(255,255,255)"
ctx.fillRect(0, 0, w, h)
aspectratio = w/h
camera = Camera(Vector(0,0.56,8),Vector(0,0,-1),Vector(0,1,0),30,aspectratio)
camera.initialize()
sphere = Sphere(Vector(1.5,0,-1),1)
sphere.material = PhongMaterial(Color.red, Color.white, 500,0.25)
sphere2 = Sphere(Vector(-1.5,0,-1),1)
sphere2.material = PhongMaterial(Color.gold, Color.white, 500,0.25)
sphere3 = Sphere(Vector(0,0,-2),1)
sphere3.material = PhongMaterial(Color.blue, Color.white, 500,0.25)
plane = Plane(Vector(0,1,0),-1)
#plane.material = new CheckerMaterial(0.1,0.9);
plane.material = PhongMaterial(Color.cyan, Color.white, 500,1.0)
scene = Union([plane,sphere,sphere2,sphere3])
scene.initialize()
maxReflection = 2
imgdata = ctx.createImageData(w,h)
#start
for y in range(h):
for x in range(w):
sy = 1- y/h
sx = x/w
index = (x + y * w) * 4
ray = camera.generateRay(sx, sy)
result = scene.intersect(ray)
if (result.sceneobject != null):
col = rayTraceRecursive(scene, ray, maxReflection)
imgdata.data[index + 0] = col.r * 255
imgdata.data[index + 1] = col.g * 255
imgdata.data[index + 2] = col.b * 255
imgdata.data[index + 3] = 255
else:
imgdata.data[index + 0] = 0
imgdata.data[index + 1] = 0
imgdata.data[index + 2] = 0
imgdata.data[index + 3] = 255
ctx.putImageData(imgdata,0,0)
main()
Top comments (0)