SHOW:
|
|
- or go back to the newest paste.
| 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 | import timeit | |
| 7 | ||
| 8 | from math import sqrt, pow, pi | |
| 9 | - | def vMul(a,b): return [ a[0]*b[0], a[1]*b[1], a[2]*b[2] ] |
| 9 | + | |
| 10 | - | def vMulD(a,b): return [ a[0]*b, a[1]*b, a[2]*b ] |
| 10 | + | |
| 11 | - | def vAdd(a,b): return [ a[0]+b[0], a[1]+b[1], a[2]+b[2] ] |
| 11 | + | def vMul(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 ] |
| 12 | + | def vMulD(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] ] |
| 13 | + | def vAdd(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 ] |
| 14 | + | def vAddD(a,b): return ( a[0]+b, a[1]+b, a[2]+b ) |
| 15 | - | def vNeg(a): return [-a[0], -a[1], -a[2] ] |
| 15 | + | def vSub(a,b): return ( a[0]-b[0], a[1]-b[1], a[2]-b[2] ) |
| 16 | - | def vPow(a,b): return [ a[0]**b, a[1]**b, a[2]**b ] |
| 16 | + | def vSubD(a,b): return ( a[0]-b, a[1]-b, a[2]-b ) |
| 17 | def vNeg(a): return (-a[0], -a[1], -a[2] ) | |
| 18 | def vPow(a,b): return ( a[0]**b, a[1]**b, a[2]**b ) | |
| 19 | ||
| 20 | def vDot(a,b): return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] | |
| 21 | def vSqr(a): return vDot(a,a) | |
| 22 | def vLen(a): return sqrt(vSqr(a)) | |
| 23 | def vNorm(a): return vMulD(a, 1.0/vLen(a)) | |
| 24 | - | |
| 24 | + | |
| 25 | - | def __init__(self, center, radius, color): |
| 25 | + | |
| 26 | - | self.c = center |
| 26 | + | |
| 27 | - | self.r = radius |
| 27 | + | def __init__(self, center, radius, color): |
| 28 | - | self.col = color |
| 28 | + | self.c = center |
| 29 | - | |
| 29 | + | self.r = radius |
| 30 | - | def intersection(self, l): |
| 30 | + | self.col = color |
| 31 | - | q = vDot(l.d, vSub(l.o, self.c))**2 - vSqr(vSub(l.o, self.c)) + self.r**2 |
| 31 | + | |
| 32 | - | if q < 0: |
| 32 | + | def intersection(self, l): |
| 33 | - | return Intersection( [0,0,0], -1, [0,0,0], self) |
| 33 | + | q = vDot(l.d, vSub(l.o, self.c))**2 - vSqr(vSub(l.o, self.c)) + self.r**2 |
| 34 | - | else: |
| 34 | + | if q < 0: |
| 35 | - | d = -vDot(l.d, vSub(l.o, self.c)) |
| 35 | + | return Intersection( (0,0,0), -1, (0,0,0), self) |
| 36 | - | d1 = d - sqrt(q) |
| 36 | + | else: |
| 37 | - | d2 = d + sqrt(q) |
| 37 | + | d = -vDot(l.d, vSub(l.o, self.c)) |
| 38 | - | if 0 < d1 and ( d1 < d2 or d2 < 0): |
| 38 | + | d1 = d - sqrt(q) |
| 39 | - | return Intersection(vAdd(l.o,vMulD(l.d,d1)), d1, self.normal(vAdd(l.o,vMulD(l.d,d1))), self) |
| 39 | + | d2 = d + sqrt(q) |
| 40 | - | elif 0 < d2 and ( d2 < d1 or d1 < 0): |
| 40 | + | if 0 < d1 and ( d1 < d2 or d2 < 0): |
| 41 | - | return Intersection(vAdd(l.o,vMulD(l.d,d2)), d2, self.normal(vAdd(l.o,vMulD(l.d,d2))), self) |
| 41 | + | return Intersection(vAdd(l.o,vMulD(l.d,d1)), d1, self.normal(vAdd(l.o,vMulD(l.d,d1))), self) |
| 42 | - | else: |
| 42 | + | elif 0 < d2 and ( d2 < d1 or d1 < 0): |
| 43 | - | return Intersection( [0,0,0], -1, [0,0,0], self) |
| 43 | + | return Intersection(vAdd(l.o,vMulD(l.d,d2)), d2, self.normal(vAdd(l.o,vMulD(l.d,d2))), self) |
| 44 | - | |
| 44 | + | else: |
| 45 | - | def normal(self, b): |
| 45 | + | return Intersection( (0,0,0), -1, (0,0,0), self) |
| 46 | - | return vNorm(vSub(b,self.c)) |
| 46 | + | |
| 47 | - | |
| 47 | + | def normal(self, b): |
| 48 | return vNorm(vSub(b,self.c)) | |
| 49 | - | |
| 49 | + | |
| 50 | - | def __init__(self, point, normal, color): |
| 50 | + | |
| 51 | - | self.n = normal |
| 51 | + | |
| 52 | - | self.p = point |
| 52 | + | def __init__(self, point, normal, color): |
| 53 | - | self.col = color |
| 53 | + | self.n = normal |
| 54 | - | |
| 54 | + | self.p = point |
| 55 | - | def intersection(self, l): |
| 55 | + | self.col = color |
| 56 | - | d = vDot(l.d, self.n) |
| 56 | + | |
| 57 | - | if d == 0: |
| 57 | + | def intersection(self, l): |
| 58 | - | return Intersection( [0,0,0], -1, vector[0,0,0], self) |
| 58 | + | d = vDot(l.d, self.n) |
| 59 | - | else: |
| 59 | + | if d == 0: |
| 60 | - | d = vDot(vSub(self.p,l.o),self.n) / d |
| 60 | + | return Intersection( (0,0,0), -1, vector(0,0,0), self) |
| 61 | - | return Intersection(vAdd(l.o,vMulD(l.d,d)), d, self.n, self) |
| 61 | + | else: |
| 62 | - | |
| 62 | + | d = vDot(vSub(self.p,l.o),self.n) / d |
| 63 | return Intersection(vAdd(l.o,vMulD(l.d,d)), d, self.n, self) | |
| 64 | - | |
| 64 | + | |
| 65 | - | def __init__(self, origin, direction): |
| 65 | + | |
| 66 | - | self.o = origin |
| 66 | + | |
| 67 | - | self.d = direction |
| 67 | + | def __init__(self, origin, direction): |
| 68 | - | |
| 68 | + | self.o = origin |
| 69 | self.d = direction | |
| 70 | - | |
| 70 | + | |
| 71 | - | def __init__(self, point, distance, normal, obj): |
| 71 | + | |
| 72 | - | self.p = point |
| 72 | + | |
| 73 | - | self.d = distance |
| 73 | + | def __init__(self, point, distance, normal, obj): |
| 74 | - | self.n = normal |
| 74 | + | self.p = point |
| 75 | - | self.obj = obj |
| 75 | + | self.d = distance |
| 76 | - | |
| 76 | + | self.n = normal |
| 77 | self.obj = obj | |
| 78 | - | intersect = Intersection( [0,0,0], -1, [0,0,0], None) |
| 78 | + | |
| 79 | - | |
| 79 | + | |
| 80 | - | for obj in objects: |
| 80 | + | intersect = Intersection( (0,0,0), -1, (0,0,0), None) |
| 81 | - | if obj is not ignore: |
| 81 | + | |
| 82 | - | currentIntersect = obj.intersection(ray) |
| 82 | + | for obj in objects: |
| 83 | - | if currentIntersect.d > 0 and intersect.d < 0: |
| 83 | + | if obj is not ignore: |
| 84 | - | intersect = currentIntersect |
| 84 | + | currentIntersect = obj.intersection(ray) |
| 85 | - | elif 0 < currentIntersect.d < intersect.d: |
| 85 | + | if currentIntersect.d > 0 and intersect.d < 0: |
| 86 | - | intersect = currentIntersect |
| 86 | + | intersect = currentIntersect |
| 87 | - | return intersect |
| 87 | + | elif 0 < currentIntersect.d < intersect.d: |
| 88 | - | |
| 88 | + | intersect = currentIntersect |
| 89 | return intersect | |
| 90 | - | if maxRecur < 0: |
| 90 | + | |
| 91 | - | return [0,0,0] |
| 91 | + | |
| 92 | - | intersect = testRay(ray, objects) |
| 92 | + | if maxRecur < 0: |
| 93 | - | if intersect.d == -1: |
| 93 | + | return (0,0,0) |
| 94 | - | col = [AMBIENT,AMBIENT,AMBIENT] |
| 94 | + | intersect = testRay(ray, objects) |
| 95 | - | elif vDot(intersect.n, vSub(light,intersect.p)) < 0: |
| 95 | + | if intersect.d == -1: |
| 96 | - | col = vMulD(intersect.obj.col, AMBIENT) |
| 96 | + | col = (AMBIENT,AMBIENT,AMBIENT) |
| 97 | - | else: |
| 97 | + | elif vDot(intersect.n, vSub(light,intersect.p)) < 0: |
| 98 | - | lightRay = Ray(intersect.p, vNorm(vSub(light,intersect.p))) |
| 98 | + | col = vMulD(intersect.obj.col, AMBIENT) |
| 99 | - | if testRay(lightRay, objects, intersect.obj).d == -1: |
| 99 | + | else: |
| 100 | - | lightIntensity = 1000.0/(4*pi*vLen(vSub(light,intersect.p))**2) |
| 100 | + | lightRay = Ray(intersect.p, vNorm(vSub(light,intersect.p))) |
| 101 | - | col = vMulD(intersect.obj.col, max(vDot(vNorm(intersect.n),vMulD(vNorm(vSub(light,intersect.p)),lightIntensity)), AMBIENT)) |
| 101 | + | if testRay(lightRay, objects, intersect.obj).d == -1: |
| 102 | - | else: |
| 102 | + | lightIntensity = 1000.0/(4*pi*vLen(vSub(light,intersect.p))**2) |
| 103 | - | col = vMulD(intersect.obj.col, AMBIENT) |
| 103 | + | col = vMulD(intersect.obj.col, max(vDot(vNorm(intersect.n),vMulD(vNorm(vSub(light,intersect.p)),lightIntensity)), AMBIENT)) |
| 104 | - | return col |
| 104 | + | else: |
| 105 | - | |
| 105 | + | col = vMulD(intersect.obj.col, AMBIENT) |
| 106 | return col | |
| 107 | - | return (int(pow(color[0]/255.0,factor)*255), |
| 107 | + | |
| 108 | - | int(pow(color[1]/255.0,factor)*255), |
| 108 | + | |
| 109 | - | int(pow(color[2]/255.0,factor)*255)) |
| 109 | + | return (int(pow(color[0]/255.0,factor)*255), |
| 110 | int(pow(color[1]/255.0,factor)*255), | |
| 111 | int(pow(color[2]/255.0,factor)*255)) | |
| 112 | ||
| 113 | AMBIENT = 0.1 | |
| 114 | GAMMA_CORRECTION = 1/2.2 | |
| 115 | MAX_RECURSION = 10 | |
| 116 | - | objs = [ Sphere([-2, 0,-10], 2, [0,255,0]), |
| 116 | + | |
| 117 | - | Sphere([ 2, 0,-10], 3.5, [255,0,0]), |
| 117 | + | |
| 118 | - | Sphere([ 0,-4,-10], 3, [0,0,255]), |
| 118 | + | objs = ( Sphere((-2, 0,-10), 2, (0,255,0)), |
| 119 | - | Plane([0,0,-12], [0,0,1], [255,255,255]) ] |
| 119 | + | Sphere(( 2, 0,-10), 3.5, (255,0,0)), |
| 120 | Sphere(( 0,-4,-10), 3, (0,0,255)), | |
| 121 | - | lightSource = [0,10,0] |
| 121 | + | Plane((0,0,-12), (0,0,1), (255,255,255)) ) |
| 122 | - | cameraPos = [0,0,20] |
| 122 | + | |
| 123 | lightSource = (0,10,0) | |
| 124 | cameraPos = (0,0,20) | |
| 125 | - | img = Image.new("RGB",(500,500))
|
| 125 | + | |
| 126 | - | for x in range(500): |
| 126 | + | |
| 127 | - | results = [] |
| 127 | + | img = Image.new("RGB",(500,500))
|
| 128 | - | for y in range(500): |
| 128 | + | for x in range(500): |
| 129 | - | ray = Ray(cameraPos, vNorm(vSub([x/50.0-5, y/50.0-5, 0], cameraPos))) |
| 129 | + | results = [] |
| 130 | - | col = trace(ray, objs, lightSource, MAX_RECURSION) |
| 130 | + | for y in range(500): |
| 131 | - | img.putpixel((x,499-y),gammaCorrection(col,GAMMA_CORRECTION)) |
| 131 | + | ray = Ray(cameraPos, vNorm(vSub((x/50.0-5, y/50.0-5, 0), cameraPos))) |
| 132 | - | print x |
| 132 | + | col = trace(ray, objs, lightSource, MAX_RECURSION) |
| 133 | - | sys.stdout.flush() |
| 133 | + | img.putpixel((x,499-y),gammaCorrection(col,GAMMA_CORRECTION)) |
| 134 | - | img.save("trace.bmp","BMP")
|
| 134 | + | img.save("trace.bmp","BMP")
|
| 135 | ||
| 136 | if __name__ == '__main__': | |
| 137 | - | main() |
| 137 | + | timer = timeit.Timer(stmt='main()',setup="from __main__ import main") |
| 138 | t = timer.timeit(number=1) | |
| 139 | print "t = %f"%t |