Я понимаю, что Питон медленный. Я это знал и собирался как раз изучить swig. Но, мне непонятно другое. Раз уж Вы взялись за беседу со мной
"""
Pure Python ray-tracer :)
taken directly from http://pastebin.com/f8f5ghjz with modifications
another good one alternative at http://www.hxa.name/minilight/
some more equations for getting intersection with other 3d geometries, https://www.cl.cam.ac.uk/teaching/1999/AGraphHCI/SMAG/node2.html#SECTION00023200000000000000
"""
#IMPORTS
from math import sqrt, pow, pi
import time
import PIL,PIL.Image
#GEOMETRIES
class Vector( object ):
def __init__(self,x,y,z):
self.x = x
self.y = y
self.z = z
def dot(self, b):
return self.x*b.x + self.y*b.y + self.z*b.z
def cross(self, b):
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)
def magnitude(self):
return sqrt(self.x**2+self.y**2+self.z**2)
def normal(self):
mag = self.magnitude()
return Vector(self.x/mag,self.y/mag,self.z/mag)
def __add__(self, b):
return Vector(self.x + b.x, self.y+b.y, self.z+b.z)
def __sub__(self, b):
return Vector(self.x-b.x, self.y-b.y, self.z-b.z)
def __mul__(self, b):
assert type(b) == float or type(b) == int
return Vector(self.x*b, self.y*b, self.z*b)
class Sphere( object ):
def __init__(self, center, radius, color):
self.c = center
self.r = radius
self.col = color
def intersection(self, l):
q = l.d.dot(l.o - self.c)**2 - (l.o - self.c).dot(l.o - self.c) + self.r**2
if q < 0:
return Intersection( Vector(0,0,0), -1, Vector(0,0,0), self)
else:
d = -l.d.dot(l.o - self.c)
d1 = d - sqrt(q)
d2 = d + sqrt(q)
if 0 < d1 and ( d1 < d2 or d2 < 0):
return Intersection(l.o+l.d*d1, d1, self.normal(l.o+l.d*d1), self)
elif 0 < d2 and ( d2 < d1 or d1 < 0):
return Intersection(l.o+l.d*d2, d2, self.normal(l.o+l.d*d2), self)
else:
return Intersection( Vector(0,0,0), -1, Vector(0,0,0), self)
def normal(self, b):
return (b - self.c).normal()
class Cylinder( object ):
"just a copy of sphere, needs work. maybe see http://stackoverflow.com/questions/4078401/trying-to-optimize-line-vs-cylinder-intersection"
def __init__(self, startpoint, endpoint, radius, color):
self.s = startpoint
self.e = endpoint
self.r = radius
self.col = color
def intersection(self, l):
q = l.d.dot(l.o - self.c)**2 - (l.o - self.c).dot(l.o - self.c) + self.r**2
if q < 0:
return Intersection( Vector(0,0,0), -1, Vector(0,0,0), self)
else:
d = -l.d.dot(l.o - self.c)
d1 = d - sqrt(q)
d2 = d + sqrt(q)
if 0 < d1 and ( d1 < d2 or d2 < 0):
return Intersection(l.o+l.d*d1, d1, self.normal(l.o+l.d*d1), self)
elif 0 < d2 and ( d2 < d1 or d1 < 0):
return Intersection(l.o+l.d*d2, d2, self.normal(l.o+l.d*d2), self)
else:
return Intersection( Vector(0,0,0), -1, Vector(0,0,0), self)
def normal(self, b):
return (b - self.c).normal()
class LightBulb( Sphere ):
pass
class Plane( object ):
"infinite, no endings"
def __init__(self, point, normal, color):
self.n = normal
self.p = point
self.col = color
def intersection(self, l):
d = l.d.dot(self.n)
if d == 0:
return Intersection( vector(0,0,0), -1, vector(0,0,0), self)
else:
d = (self.p - l.o).dot(self.n) / d
return Intersection(l.o+l.d*d, d, self.n, self)
class Rectangle( object ):
"not done. like a plane, but is limited to the shape of a defined rectangle"
def __init__(self, point, normal, color):
self.n = normal
self.p = point
self.col = color
def intersection(self, ray):
desti = ray.dest.dot(self.n)
if desti == 0:
#??
return Intersection( vector(0,0,0), -1, vector(0,0,0), self)
else:
desti = (self.p - ray.orig).dot(self.n) / desti
return Intersection(ray.orig+ray.desti*desti, desti, self.n, self)
class RectangleBox( object ):
"not done. consists of multiple rectangle objects as its sides"
pass
class AnimatedObject( object ):
def __init__(self, *objs):
self.objs = objs
def __iter__(self):
for obj in self.objs:
yield obj
def __getitem__(self, index):
return self.objs[index]
def reverse(self):
self.objs = [each for each in reversed(self.objs)]
return self
#RAY TRACING INTERNAL COMPONENTS
class Ray( object ):
def __init__(self, origin, direction):
self.o = origin
self.d = direction
class Intersection( object ):
"keeps a record of a known intersection bw ray and obj?"
def __init__(self, point, distance, normal, obj):
self.p = point
self.d = distance
self.n = normal
self.obj = obj
def testRay(ray, objects, ignore=None):
intersect = Intersection( Vector(0,0,0), -1, Vector(0,0,0), None)
for obj in objects:
if obj is not ignore:
currentIntersect = obj.intersection(ray)
if currentIntersect.d > 0 and intersect.d < 0:
intersect = currentIntersect
elif 0 < currentIntersect.d < intersect.d:
intersect = currentIntersect
return intersect
def trace(ray, objects, light, maxRecur):
if maxRecur < 0:
return (0,0,0)
intersect = testRay(ray, objects)
if intersect.d == -1:
col = vector(AMBIENT,AMBIENT,AMBIENT)
elif intersect.n.dot(light - intersect.p) < 0:
col = intersect.obj.col * AMBIENT
else:
lightRay = Ray(intersect.p, (light-intersect.p).normal())
if testRay(lightRay, objects, intersect.obj).d == -1:
lightIntensity = 1000.0/(4*pi*(light-intersect.p).magnitude()**2)
col = intersect.obj.col * max(intersect.n.normal().dot((light - intersect.p).normal()*lightIntensity), AMBIENT)
else:
col = intersect.obj.col * AMBIENT
return col
def gammaCorrection(color,factor):
return (int(pow(color.x/255.0,factor)*255),
int(pow(color.y/255.0,factor)*255),
int(pow(color.z/255.0,factor)*255))
#USER FUNCTIONS
class Camera:
def __init__(self, cameraPos, zoom=50.0, xangle=-5, yangle=-5):
self.pos = cameraPos
self.zoom = zoom
self.xangle = xangle
self.yangle = yangle
def renderScene(camera, lightSource, objs, imagedims, savepath):
imgwidth,imgheight = imagedims
img = PIL.Image.new("RGB",imagedims)
#objs.append( LightBulb(lightSource, 0.2, Vector(*white)) )
print ("rendering 3D scene")
t=time.clock()
for x in range(imgwidth):
#print x
for y in range(imgheight):
ray = Ray( camera.pos, (Vector(x/camera.zoom+camera.xangle,y/camera.zoom+camera.yangle,0)-camera.pos).normal())
col = trace(ray, objs, lightSource, 10)
img.putpixel((x,imgheight-1-y),gammaCorrection(col,GAMMA_CORRECTION))
print ("time taken", time.clock()-t)
img.save(savepath)
def renderAnimation(camera, lightSource, staticobjs, animobjs, imagedims, savepath, saveformat):
"NOTE: savepath should not have file extension, but saveformat should have a dot"
time = 0
while True:
print ("time",time)
timesavepath = savepath+"_"+str(time)+saveformat
objs = []
objs.extend(staticobjs)
objs.extend([animobj[time] for animobj in animobjs])
renderScene(camera, lightSource, objs, imagedims, timesavepath)
time += 1
#SOME LIGHTNING OPTIONS
AMBIENT = 0.05 #daylight/nighttime
GAMMA_CORRECTION = 1/2.2 #lightsource strength?
#COLORS
red = (255,0,0)
yellow = (255,255,0)
green = (0,255,0)
blue = (0,0,255)
grey = (120,120,120)
white = (255,255,255)
purple = (200,0,200)
def origtest():
print ("")
print ("origtest")
#BUILD THE SCENE
imagedims = (1000,1000)
savepath = "3dscene_orig.png"
objs = []
objs.append(Sphere( Vector(-2,0,-10), 2, Vector(*green)))
objs.append(Sphere( Vector(2,0,-10), 3.5, Vector(*red)))
objs.append(Sphere( Vector(0,-4,-10), 3, Vector(*blue)))
objs.append(Plane( Vector(0,0,-12), Vector(0,0,1), Vector(*grey)))
lightSource = Vector(0,10,0)
camera = Camera(Vector(0,0,20))
#RENDER
renderScene(camera, lightSource, objs, imagedims, savepath)
def normaltest():
print ("")
print ("normaltest")
#BUILD THE SCENE
"""
the camera is looking down on the surface with the spheres from above
the surface is like looking down on the xy axis of the xyz coordinate system
the light is down there together with the spheres, except from one of the sides
"""
imagedims = (1200,1200)
savepath = "3dscene.png"
objs = []
objs.append(Sphere( Vector(-4, -2, 1), 1, Vector(*red)))
objs.append(Sphere( Vector(-2, -2, 1), 1, Vector(*blue)))
objs.append(Sphere( Vector(-2, -4, 1), 1, Vector(*green)))
objs.append(Plane( Vector(0,0,0), Vector(0,0,1), Vector(*grey)))
lightSource = Vector(-2.4, -3, 2)
camera = Camera(Vector(-19,-19,2), zoom=2.0, xangle=-30, yangle=-30)
#RENDER
renderScene(camera, lightSource, objs, imagedims, savepath)
def animtest():
print ("")
print ("falling ball test")
#BUILD THE SCENE
imagedims = (200,200)
savepath = "3d_fallball"
saveformat = ".png"
staticobjs = []
staticobjs.append(Sphere( Vector(-4, -2, 1), 1, Vector(*red)))
staticobjs.append(Sphere( Vector(-2, -4, 1), 1, Vector(*green)))
staticobjs.append(Plane( Vector(0,0,0), Vector(0,0,1), Vector(*purple)))
animobjs = []
fallingball = AnimatedObject(Sphere( Vector(-2, -2, 20), 1, Vector(*yellow)),
Sphere( Vector(-2, -2, 15), 1, Vector(*yellow)),
Sphere( Vector(-2, -2, 9), 1, Vector(*yellow)),
Sphere( Vector(-2, -2, 5), 1, Vector(*yellow)),
Sphere( Vector(-2, -2, 1), 1, Vector(*yellow)))
animobjs.append(fallingball)
lightSource = Vector(-4,-4,10)
camera = Camera(Vector(0,0,30))
#RENDER
renderAnimation(camera, lightSource, staticobjs, animobjs, imagedims, savepath, saveformat)
#RUN TESTS
#origtest()
normaltest()
#animtest()
Программа вычисляет цвет пиксела, затем создаёт массив, из которого формируется изображение.
Цикл, понятно, всё посчитает, затем выдаст результат.
Но, как получить результат промежуточного вычисления, который обычно отображают программы рендера?