SHOW:
|
|
- or go back to the newest paste.
| 1 | from PIL import Image | |
| 2 | - | import Image |
| 2 | + | |
| 3 | ||
| 4 | class Vector( object ): | |
| 5 | - | |
| 5 | + | |
| 6 | def __init__(self,x,y,z): | |
| 7 | self.x = x | |
| 8 | self.y = y | |
| 9 | self.z = z | |
| 10 | - | |
| 10 | + | |
| 11 | def dot(self, b): | |
| 12 | return self.x*b.x + self.y*b.y + self.z*b.z | |
| 13 | - | |
| 13 | + | |
| 14 | def cross(self, b): | |
| 15 | return (self.y*b.z-self.z*b.y, self.z*b.x-self.x*b.z, self.x*b.y-self.y*b.x) | |
| 16 | - | |
| 16 | + | |
| 17 | def magnitude(self): | |
| 18 | return sqrt(self.x**2+self.y**2+self.z**2) | |
| 19 | - | |
| 19 | + | |
| 20 | def normal(self): | |
| 21 | mag = self.magnitude() | |
| 22 | return Vector(self.x/mag,self.y/mag,self.z/mag) | |
| 23 | - | |
| 23 | + | |
| 24 | def __add__(self, b): | |
| 25 | return Vector(self.x + b.x, self.y+b.y, self.z+b.z) | |
| 26 | - | |
| 26 | + | |
| 27 | def __sub__(self, b): | |
| 28 | return Vector(self.x-b.x, self.y-b.y, self.z-b.z) | |
| 29 | - | |
| 29 | + | |
| 30 | def __mul__(self, b): | |
| 31 | assert type(b) == float or type(b) == int | |
| 32 | - | return Vector(self.x*b, self.y*b, self.z*b) |
| 32 | + | return Vector(self.x*b, self.y*b, self.z*b) |
| 33 | - | |
| 33 | + | |
| 34 | class Sphere( object ): | |
| 35 | - | |
| 35 | + | |
| 36 | def __init__(self, center, radius, color): | |
| 37 | self.c = center | |
| 38 | self.r = radius | |
| 39 | self.col = color | |
| 40 | - | |
| 40 | + | |
| 41 | def intersection(self, l): | |
| 42 | q = l.d.dot(l.o - self.c)**2 - (l.o - self.c).dot(l.o - self.c) + self.r**2 | |
| 43 | if q < 0: | |
| 44 | return Intersection( Vector(0,0,0), -1, Vector(0,0,0), self) | |
| 45 | else: | |
| 46 | d = -l.d.dot(l.o - self.c) | |
| 47 | d1 = d - sqrt(q) | |
| 48 | d2 = d + sqrt(q) | |
| 49 | if 0 < d1 and ( d1 < d2 or d2 < 0): | |
| 50 | return Intersection(l.o+l.d*d1, d1, self.normal(l.o+l.d*d1), self) | |
| 51 | elif 0 < d2 and ( d2 < d1 or d1 < 0): | |
| 52 | return Intersection(l.o+l.d*d2, d2, self.normal(l.o+l.d*d2), self) | |
| 53 | else: | |
| 54 | - | return Intersection( Vector(0,0,0), -1, Vector(0,0,0), self) |
| 54 | + | return Intersection( Vector(0,0,0), -1, Vector(0,0,0), self) |
| 55 | - | |
| 55 | + | |
| 56 | def normal(self, b): | |
| 57 | return (b - self.c).normal() | |
| 58 | - | |
| 58 | + | |
| 59 | class Plane( object ): | |
| 60 | - | |
| 60 | + | |
| 61 | def __init__(self, point, normal, color): | |
| 62 | self.n = normal | |
| 63 | self.p = point | |
| 64 | self.col = color | |
| 65 | - | |
| 65 | + | |
| 66 | def intersection(self, l): | |
| 67 | d = l.d.dot(self.n) | |
| 68 | if d == 0: | |
| 69 | return Intersection( vector(0,0,0), -1, vector(0,0,0), self) | |
| 70 | else: | |
| 71 | d = (self.p - l.o).dot(self.n) / d | |
| 72 | return Intersection(l.o+l.d*d, d, self.n, self) | |
| 73 | - | |
| 73 | + | |
| 74 | class Ray( object ): | |
| 75 | - | |
| 75 | + | |
| 76 | def __init__(self, origin, direction): | |
| 77 | self.o = origin | |
| 78 | self.d = direction | |
| 79 | - | |
| 79 | + | |
| 80 | class Intersection( object ): | |
| 81 | - | |
| 81 | + | |
| 82 | def __init__(self, point, distance, normal, obj): | |
| 83 | self.p = point | |
| 84 | self.d = distance | |
| 85 | self.n = normal | |
| 86 | self.obj = obj | |
| 87 | - | |
| 87 | + | |
| 88 | def testRay(ray, objects, ignore=None): | |
| 89 | intersect = Intersection( Vector(0,0,0), -1, Vector(0,0,0), None) | |
| 90 | - | |
| 90 | + | |
| 91 | for obj in objects: | |
| 92 | if obj is not ignore: | |
| 93 | currentIntersect = obj.intersection(ray) | |
| 94 | if currentIntersect.d > 0 and intersect.d < 0: | |
| 95 | intersect = currentIntersect | |
| 96 | elif 0 < currentIntersect.d < intersect.d: | |
| 97 | intersect = currentIntersect | |
| 98 | return intersect | |
| 99 | - | |
| 99 | + | |
| 100 | def trace(ray, objects, light, maxRecur): | |
| 101 | if maxRecur < 0: | |
| 102 | return (0,0,0) | |
| 103 | - | intersect = testRay(ray, objects) |
| 103 | + | intersect = testRay(ray, objects) |
| 104 | if intersect.d == -1: | |
| 105 | col = vector(AMBIENT,AMBIENT,AMBIENT) | |
| 106 | elif intersect.n.dot(light - intersect.p) < 0: | |
| 107 | col = intersect.obj.col * AMBIENT | |
| 108 | else: | |
| 109 | lightRay = Ray(intersect.p, (light-intersect.p).normal()) | |
| 110 | if testRay(lightRay, objects, intersect.obj).d == -1: | |
| 111 | lightIntensity = 1000.0/(4*pi*(light-intersect.p).magnitude()**2) | |
| 112 | col = intersect.obj.col * max(intersect.n.normal().dot((light - intersect.p).normal()*lightIntensity), AMBIENT) | |
| 113 | else: | |
| 114 | col = intersect.obj.col * AMBIENT | |
| 115 | return col | |
| 116 | - | |
| 116 | + | |
| 117 | def gammaCorrection(color,factor): | |
| 118 | return (int(pow(color.x/255.0,factor)*255), | |
| 119 | int(pow(color.y/255.0,factor)*255), | |
| 120 | int(pow(color.z/255.0,factor)*255)) | |
| 121 | - | |
| 121 | + | |
| 122 | ||
| 123 | AMBIENT = 0.1 | |
| 124 | GAMMA_CORRECTION = 1/2.2 | |
| 125 | - | |
| 125 | + | |
| 126 | objs = [] | |
| 127 | objs.append(Sphere( Vector(-2,0,-10), 2, Vector(0,255,0))) | |
| 128 | objs.append(Sphere( Vector(2,0,-10), 3.5, Vector(255,0,0))) | |
| 129 | objs.append(Sphere( Vector(0,-4,-10), 3, Vector(0,0,255))) | |
| 130 | objs.append(Plane( Vector(0,0,-12), Vector(0,0,1), Vector(255,255,255))) | |
| 131 | - | lightSource = Vector(0,10,0) |
| 131 | + | lightSource = Vector(5,5,2) |
| 132 | - | img = Image.new("RGB",(500,500))
|
| 132 | + | realWidth = 200 |
| 133 | antialias = True | |
| 134 | - | for x in range(500): |
| 134 | + | |
| 135 | width = realWidth if not(antialias) else 2*realWidth | |
| 136 | - | for y in range(500): |
| 136 | + | img = Image.new("RGB",(width,width))
|
| 137 | - | ray = Ray( cameraPos, (Vector(x/50.0-5,y/50.0-5,0)-cameraPos).normal()) |
| 137 | + | |
| 138 | cameraPos = Vector(0,0,20) | |
| 139 | - | img.putpixel((x,499-y),gammaCorrection(col,GAMMA_CORRECTION)) |
| 139 | + | for x in xrange(width): |
| 140 | - | img.save("trace.bmp","BMP") |
| 140 | + | |
| 141 | for y in xrange(width): | |
| 142 | ray = Ray( cameraPos, (Vector(((10.0*x)/width-5),((10.0*y)/width-5),0)-cameraPos).normal()) | |
| 143 | col = trace(ray, objs, lightSource, 10) | |
| 144 | img.putpixel((x,width-1-y),gammaCorrection(col,GAMMA_CORRECTION)) | |
| 145 | ||
| 146 | if not(antialias): | |
| 147 | img.save("trace.bmp","BMP")
| |
| 148 | else: | |
| 149 | img_antialias = Image.new("RGB", (realWidth, realWidth))
| |
| 150 | for x in xrange(realWidth): | |
| 151 | for y in xrange(realWidth): | |
| 152 | col1 = img.getpixel((2*x, 2*y)) | |
| 153 | col2 = img.getpixel((2*x + 1, 2*y)) | |
| 154 | col3 = img.getpixel((2*x + 1, 2*y + 1)) | |
| 155 | col4 = img.getpixel((2*x, 2*y + 1)) | |
| 156 | blended = (int((col1[0]+col2[0]+col3[0]+col4[0])/4.0), int((col1[1]+col2[1]+col3[1]+col4[1])/4.0), int((col1[2]+col2[2]+col3[2]+col4[2])/4.0)) | |
| 157 | img_antialias.putpixel((x, y), blended) | |
| 158 | img_antialias.save("trace.bmp", "BMP") |