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 |