Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

tiny tracer no vector class

By: a guest on Jan 9th, 2013  |  syntax: Python  |  size: 4.29 KB  |  views: 101  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #tiny-tracer-no-vector-class. 2x speedup.
  2.  
  3. #original pastebin: http://pastebin.com/F8f5GHJZ
  4. #original thread:   http://www.reddit.com/r/tinycode/comments/169ri9/ray_tracer_in_140_sloc_of_python_with_picture/
  5.  
  6. from math import sqrt, pow, pi
  7. import Image, sys
  8.  
  9. def vMul(a,b):  return [ a[0]*b[0], a[1]*b[1], a[2]*b[2] ]
  10. def vMulD(a,b): return [ a[0]*b,    a[1]*b,    a[2]*b    ]
  11. def vAdd(a,b):  return [ a[0]+b[0], a[1]+b[1], a[2]+b[2] ]
  12. def vAddD(a,b): return [ a[0]+b,    a[1]+b,    a[2]+b    ]
  13. def vSub(a,b):  return [ a[0]-b[0], a[1]-b[1], a[2]-b[2] ]
  14. def vSubD(a,b): return [ a[0]-b,    a[1]-b,    a[2]-b    ]
  15. def vNeg(a):    return [-a[0],     -a[1],     -a[2]      ]
  16. def vPow(a,b):  return [ a[0]**b,   a[1]**b,   a[2]**b   ]
  17.  
  18. def vDot(a,b):  return a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
  19. def vSqr(a):    return vDot(a,a)
  20. def vLen(a):    return sqrt(vSqr(a))
  21. def vNorm(a):   return vMulD(a, 1.0/vLen(a))
  22.  
  23. class Sphere( object ):
  24.        
  25.         def __init__(self, center, radius, color):
  26.                 self.c = center
  27.                 self.r = radius
  28.                 self.col = color
  29.                
  30.         def intersection(self, l):
  31.                 q = vDot(l.d, vSub(l.o, self.c))**2 - vSqr(vSub(l.o, self.c)) + self.r**2
  32.                 if q < 0:
  33.                         return Intersection( [0,0,0], -1, [0,0,0], self)
  34.                 else:
  35.                         d = -vDot(l.d, vSub(l.o, self.c))
  36.                         d1 = d - sqrt(q)
  37.                         d2 = d + sqrt(q)
  38.                         if 0 < d1 and ( d1 < d2 or d2 < 0):
  39.                                 return Intersection(vAdd(l.o,vMulD(l.d,d1)), d1, self.normal(vAdd(l.o,vMulD(l.d,d1))), self)
  40.                         elif 0 < d2 and ( d2 < d1 or d1 < 0):
  41.                                 return Intersection(vAdd(l.o,vMulD(l.d,d2)), d2, self.normal(vAdd(l.o,vMulD(l.d,d2))), self)
  42.                         else:
  43.                                 return Intersection( [0,0,0], -1, [0,0,0], self)
  44.        
  45.         def normal(self, b):
  46.                 return vNorm(vSub(b,self.c))
  47.        
  48. class Plane( object ):
  49.        
  50.         def __init__(self, point, normal, color):
  51.                 self.n = normal
  52.                 self.p = point
  53.                 self.col = color
  54.                
  55.         def intersection(self, l):
  56.                 d = vDot(l.d, self.n)
  57.                 if d == 0:
  58.                         return Intersection( [0,0,0], -1, vector[0,0,0], self)
  59.                 else:
  60.                         d = vDot(vSub(self.p,l.o),self.n) / d
  61.                         return Intersection(vAdd(l.o,vMulD(l.d,d)), d, self.n, self)
  62.        
  63. class Ray( object ):
  64.        
  65.         def __init__(self, origin, direction):
  66.                 self.o = origin
  67.                 self.d = direction
  68.                
  69. class Intersection( object ):
  70.        
  71.         def __init__(self, point, distance, normal, obj):
  72.                 self.p = point
  73.                 self.d = distance
  74.                 self.n = normal
  75.                 self.obj = obj
  76.                
  77. def testRay(ray, objects, ignore=None):
  78.         intersect = Intersection( [0,0,0], -1, [0,0,0], None)
  79.        
  80.         for obj in objects:
  81.                 if obj is not ignore:
  82.                         currentIntersect = obj.intersection(ray)
  83.                         if currentIntersect.d > 0 and intersect.d < 0:
  84.                                 intersect = currentIntersect
  85.                         elif 0 < currentIntersect.d < intersect.d:
  86.                                 intersect = currentIntersect
  87.         return intersect
  88.        
  89. def trace(ray, objects, light, maxRecur):
  90.         if maxRecur < 0:
  91.                 return [0,0,0]
  92.         intersect = testRay(ray, objects)
  93.         if intersect.d == -1:
  94.                 col = [AMBIENT,AMBIENT,AMBIENT]
  95.         elif vDot(intersect.n, vSub(light,intersect.p)) < 0:
  96.                 col = vMulD(intersect.obj.col, AMBIENT)
  97.         else:
  98.                 lightRay = Ray(intersect.p, vNorm(vSub(light,intersect.p)))
  99.                 if testRay(lightRay, objects, intersect.obj).d == -1:
  100.                         lightIntensity = 1000.0/(4*pi*vLen(vSub(light,intersect.p))**2)
  101.                         col = vMulD(intersect.obj.col, max(vDot(vNorm(intersect.n),vMulD(vNorm(vSub(light,intersect.p)),lightIntensity)), AMBIENT))
  102.                 else:
  103.                         col = vMulD(intersect.obj.col, AMBIENT)
  104.         return col
  105.        
  106. def gammaCorrection(color,factor):
  107.         return (int(pow(color[0]/255.0,factor)*255),
  108.                         int(pow(color[1]/255.0,factor)*255),
  109.                         int(pow(color[2]/255.0,factor)*255))
  110.  
  111. AMBIENT = 0.1
  112. GAMMA_CORRECTION = 1/2.2
  113. MAX_RECURSION = 10
  114.  
  115. #these are left global so we don't have to pass them to apply_async each pixel
  116. objs = [ Sphere([-2, 0,-10], 2,   [0,255,0]),
  117.          Sphere([ 2, 0,-10], 3.5, [255,0,0]),
  118.          Sphere([ 0,-4,-10], 3,   [0,0,255]),
  119.          Plane([0,0,-12], [0,0,1], [255,255,255]) ]
  120.  
  121. lightSource = [0,10,0]
  122. cameraPos = [0,0,20]
  123.  
  124. def main():
  125.         img = Image.new("RGB",(500,500))
  126.         for x in range(500):
  127.                 results = []
  128.                 for y in range(500):
  129.                         ray =  Ray(cameraPos, vNorm(vSub([x/50.0-5, y/50.0-5, 0], cameraPos)))
  130.                         col = trace(ray, objs, lightSource, MAX_RECURSION)
  131.                         img.putpixel((x,499-y),gammaCorrection(col,GAMMA_CORRECTION))
  132.                 print x
  133.                 sys.stdout.flush()
  134.         img.save("trace.bmp","BMP")
  135.  
  136. if __name__ == '__main__':
  137.         main()