View difference between Paste ID: U5tsN7L7 and F8f5GHJZ
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")