2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/examples/PyGmy/ppygmy.py Sun Oct 01 00:57:02 2006 +0000
2.3 @@ -0,0 +1,398 @@
2.4 +#!/usr/bin/env python
2.5 +
2.6 +"""
2.7 +An adaptation of pygmy.py ("a rubbish raytracer") employing pprocess
2.8 +functionality in order to take advantage of multiprocessing environments.
2.9 +
2.10 +--------
2.11 +
2.12 +Copyright (C) 2005 Dave Griffiths
2.13 +Copyright (C) 2006 Paul Boddie <paul@boddie.org.uk>
2.14 +
2.15 +This program is free software; you can redistribute it and/or
2.16 +modify it under the terms of the GNU General Public License
2.17 +as published by the Free Software Foundation; either version 2
2.18 +of the License, or (at your option) any later version.
2.19 +
2.20 +This program is distributed in the hope that it will be useful,
2.21 +but WITHOUT ANY WARRANTY; without even the implied warranty of
2.22 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2.23 +GNU General Public License for more details.
2.24 +
2.25 +You should have received a copy of the GNU General Public License
2.26 +along with this program; if not, write to the Free Software
2.27 +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2.28 +"""
2.29 +
2.30 +import Image, ImageDraw, random, copy
2.31 +from math import *
2.32 +import pprocess
2.33 +
2.34 +def sq(a):
2.35 + return a*a
2.36 +
2.37 +class vec:
2.38 + def __init__(self, x, y, z):
2.39 + self.x=float(x)
2.40 + self.y=float(y)
2.41 + self.z=float(z)
2.42 +
2.43 + def __add__(self,other):
2.44 + return vec(self.x+other.x,self.y+other.y,self.z+other.z)
2.45 +
2.46 + def __sub__(self,other):
2.47 + return vec(self.x-other.x,self.y-other.y,self.z-other.z)
2.48 +
2.49 + def __mul__(self,amount):
2.50 + return vec(self.x*amount,self.y*amount,self.z*amount)
2.51 +
2.52 + def __div__(self,amount):
2.53 + return vec(self.x/amount,self.y/amount,self.z/amount)
2.54 +
2.55 + def __neg__(self):
2.56 + return vec(-self.x,-self.y,-self.z)
2.57 +
2.58 + def dot(self,other):
2.59 + return (self.x*other.x)+(self.y*other.y)+(self.z*other.z)
2.60 +
2.61 + def cross(self,other):
2.62 + return vec(self.y*other.z - self.z*other.y,
2.63 + self.z*other.x - self.x*other.z,
2.64 + self.x*other.y - self.y*other.x)
2.65 +
2.66 + def dist(self,other):
2.67 + return sqrt((other.x-self.x)*(other.x-self.x)+
2.68 + (other.y-self.y)*(other.y-self.y)+
2.69 + (other.z-self.z)*(other.z-self.z))
2.70 +
2.71 + def sq(self):
2.72 + return sq(self.x)+sq(self.y)+sq(self.z)
2.73 +
2.74 + def mag(self):
2.75 + return self.dist(vec(0,0,0))
2.76 +
2.77 + def norm(self):
2.78 + mag=self.mag()
2.79 + if mag!=0:
2.80 + self.x=self.x/mag
2.81 + self.y=self.y/mag
2.82 + self.z=self.z/mag
2.83 +
2.84 + def reflect(self,normal):
2.85 + vdn=self.dot(normal)*2
2.86 + return self-normal*vdn
2.87 +
2.88 +class line:
2.89 + def __init__(self, start, end):
2.90 + self.start=start
2.91 + self.end=end
2.92 +
2.93 + def vec(self):
2.94 + return self.end-self.start
2.95 +
2.96 + def closestpoint(self, point):
2.97 + l=self.end-self.start
2.98 + l2=point-self.start
2.99 + t=l.dot(l2)
2.100 + if t<=0: return self.start
2.101 + if t>l.mag(): return self.end
2.102 + return self.start+l*t
2.103 +
2.104 +class renderobject:
2.105 + def __init__(self, shader):
2.106 + self.shader=shader
2.107 +
2.108 + def intersect(self,l):
2.109 + return "none",vec(0,0,0),vec(0,0,0) # type, position, normal
2.110 +
2.111 +class plane(renderobject):
2.112 + def __init__(self,plane,dist,shader):
2.113 + renderobject.__init__(self,shader)
2.114 + self.plane=plane
2.115 + self.dist=dist
2.116 +
2.117 + def intersect(self,l):
2.118 + vd=self.plane.dot(l.vec())
2.119 + if vd==0: return "none",vec(0,0,0),vec(0,0,0)
2.120 + v0 = -(self.plane.dot(l.start)+self.dist)
2.121 + t = v0/vd
2.122 + if t<0 or t>1: return "none",vec(0,0,0),vec(0,0,0)
2.123 + return "one",l.start+(l.vec()*t),self.plane
2.124 +
2.125 +
2.126 +class sphere(renderobject):
2.127 + def __init__(self, pos, radius, shader):
2.128 + renderobject.__init__(self,shader)
2.129 + self.pos=pos
2.130 + self.radius=radius
2.131 +
2.132 + def disttoline(self,l):
2.133 + return self.pos.dist(l.closestpoint(self.pos))
2.134 +
2.135 + def intersect(self,l):
2.136 + lvec=l.vec()
2.137 + a = sq(lvec.x)+sq(lvec.y)+sq(lvec.z)
2.138 +
2.139 + b = 2*(lvec.x*(l.start.x-self.pos.x)+ \
2.140 + lvec.y*(l.start.y-self.pos.y)+ \
2.141 + lvec.z*(l.start.z-self.pos.z))
2.142 +
2.143 + c = self.pos.sq()+l.start.sq() - \
2.144 + 2*(self.pos.x*l.start.x+self.pos.y*l.start.y+self.pos.z*l.start.z)-sq(self.radius)
2.145 +
2.146 + i = b*b-4*a*c
2.147 +
2.148 + intersectiontype="none"
2.149 + pos=vec(0,0,0)
2.150 + norm=vec(0,0,0)
2.151 + t=0
2.152 +
2.153 + if i>0 :
2.154 + if i==0:
2.155 + intersectiontype="one"
2.156 + t = -b/(2*a);
2.157 + else:
2.158 + intersectiontype="two"
2.159 + t = (-b - sqrt( b*b - 4*a*c )) / (2*a)
2.160 + # just bother with one for the moment
2.161 + # t2= (-b + sqrt( b*b - 4*a*c )) / (2*a)
2.162 +
2.163 + if t>0 and t<1:
2.164 + pos = l.start+lvec*t
2.165 + norm=pos-self.pos
2.166 + norm.norm()
2.167 + else:
2.168 + intersectiontype="none"
2.169 +
2.170 + return intersectiontype,pos,norm
2.171 +
2.172 + def intersects(self,l):
2.173 + return self.disttoline(l)<self.radius
2.174 +
2.175 +class light:
2.176 + def __init__(self):
2.177 + pass
2.178 +
2.179 + def checkshadow(self, obj, objects,l):
2.180 + # shadowing built into the lights (is this right?)
2.181 + for ob in objects:
2.182 + if ob is not obj:
2.183 + intersects,pos,norm = ob.intersect(l)
2.184 + if intersects is not "none":
2.185 + return 1
2.186 + return 0
2.187 +
2.188 + def light(self, obj, objects, pos, normal):
2.189 + pass
2.190 +
2.191 +class parallellight(light):
2.192 + def __init__(self, direction, col):
2.193 + direction.norm()
2.194 + self.direction=direction
2.195 + self.col=col
2.196 +
2.197 + def inshadow(self, obj, objects, pos):
2.198 + # create a longish line towards the light
2.199 + l = line(pos,pos+self.direction*1000)
2.200 + return self.checkshadow(obj,objects,l)
2.201 +
2.202 + def light(self, shaderinfo):
2.203 + if self.inshadow(shaderinfo["thisobj"],shaderinfo["objects"],shaderinfo["position"]): return vec(0,0,0)
2.204 + return self.col*self.direction.dot(shaderinfo["normal"])
2.205 +
2.206 +class pointlight(light):
2.207 + def __init__(self, position, col):
2.208 + self.position=position
2.209 + self.col=col
2.210 +
2.211 + def inshadow(self, obj, objects, pos):
2.212 + l = line(pos,self.position)
2.213 + return self.checkshadow(obj,objects,l)
2.214 +
2.215 + def light(self, shaderinfo):
2.216 + if self.inshadow(shaderinfo["thisobj"],shaderinfo["objects"],shaderinfo["position"]): return vec(0,0,0)
2.217 + direction = shaderinfo["position"]-self.position;
2.218 + direction.norm()
2.219 + direction=-direction
2.220 + return self.col*direction.dot(shaderinfo["normal"])
2.221 +
2.222 +class shader:
2.223 + def __init__(self):
2.224 + pass
2.225 +
2.226 + # a load of helper functions for shaders, need much improvement
2.227 +
2.228 + def getreflected(self,shaderinfo):
2.229 + depth=shaderinfo["depth"]
2.230 + col=vec(0,0,0)
2.231 + if depth>0:
2.232 + lray=copy.copy(shaderinfo["ray"])
2.233 + ray=lray.vec()
2.234 + normal=copy.copy(shaderinfo["normal"])
2.235 + ray=ray.reflect(normal)
2.236 + reflected=line(shaderinfo["position"],shaderinfo["position"]+ray)
2.237 + obj=shaderinfo["thisobj"]
2.238 + objects=shaderinfo["objects"]
2.239 + newshaderinfo = copy.copy(shaderinfo)
2.240 + newshaderinfo["ray"]=reflected
2.241 + newshaderinfo["depth"]=depth-1
2.242 + # todo - depth test
2.243 + for ob in objects:
2.244 + if ob is not obj:
2.245 + intersects,position,normal = ob.intersect(reflected)
2.246 + if intersects is not "none":
2.247 + newshaderinfo["thisobj"]=ob
2.248 + newshaderinfo["position"]=position
2.249 + newshaderinfo["normal"]=normal
2.250 + col=col+ob.shader.shade(newshaderinfo)
2.251 + return col
2.252 +
2.253 + def isoccluded(self,ray,shaderinfo):
2.254 + dist=ray.mag()
2.255 + test=line(shaderinfo["position"],shaderinfo["position"]+ray)
2.256 + obj=shaderinfo["thisobj"]
2.257 + objects=shaderinfo["objects"]
2.258 + # todo - depth test
2.259 + for ob in objects:
2.260 + if ob is not obj:
2.261 + intersects,position,normal = ob.intersect(test)
2.262 + if intersects is not "none":
2.263 + return 1
2.264 + return 0
2.265 +
2.266 + def doocclusion(self,samples,shaderinfo):
2.267 + # not really very scientific, or good in any way...
2.268 + oc=0.0
2.269 + for i in range(0,samples):
2.270 + ray=vec(random.randrange(-100,100),random.randrange(-100,100),random.randrange(-100,100))
2.271 + ray.norm()
2.272 + ray=ray*2.5
2.273 + if self.isoccluded(ray,shaderinfo):
2.274 + oc=oc+1
2.275 + oc=oc/float(samples)
2.276 + return 1-oc
2.277 +
2.278 + def getcolour(self,ray,shaderinfo):
2.279 + depth=shaderinfo["depth"]
2.280 + col=vec(0,0,0)
2.281 + if depth>0:
2.282 + test=line(shaderinfo["position"],shaderinfo["position"]+ray)
2.283 + obj=shaderinfo["thisobj"]
2.284 + objects=shaderinfo["objects"]
2.285 + newshaderinfo = copy.copy(shaderinfo)
2.286 + newshaderinfo["ray"]=test
2.287 + newshaderinfo["depth"]=depth-1
2.288 + # todo - depth test
2.289 + for ob in objects:
2.290 + if ob is not obj:
2.291 + intersects,position,normal = ob.intersect(test)
2.292 + if intersects is not "none":
2.293 + newshaderinfo["thisobj"]=ob
2.294 + newshaderinfo["position"]=position
2.295 + newshaderinfo["normal"]=normal
2.296 + col=col+ob.shader.shade(newshaderinfo)
2.297 + return col
2.298 +
2.299 + def docolourbleed(self,samples,shaderinfo):
2.300 + # not really very scientific, or good in any way...
2.301 + col=vec(0,0,0)
2.302 + for i in range(0,samples):
2.303 + ray=vec(random.randrange(-100,100),random.randrange(-100,100),random.randrange(-100,100))
2.304 + ray.norm()
2.305 + ray=ray*5
2.306 + col=col+self.getcolour(ray,shaderinfo)
2.307 + col=col/float(samples)
2.308 + return col
2.309 +
2.310 + def shade(self,shaderinfo):
2.311 + col=vec(0,0,0)
2.312 + for lite in shaderinfo["lights"]:
2.313 + col=col+lite.light(shaderinfo)
2.314 + return col
2.315 +
2.316 +class world:
2.317 + def __init__(self,width,height):
2.318 + self.lights=[]
2.319 + self.objects=[]
2.320 + self.cameratype="persp"
2.321 + self.width=width
2.322 + self.height=height
2.323 + self.backplane=2000.0
2.324 + self.imageplane=5.0
2.325 + self.aspect=self.width/float(self.height)
2.326 +
2.327 + def render_row(self, channel, sy):
2.328 +
2.329 + """
2.330 + Render the given row, using the 'channel' provided to communicate
2.331 + result data back to the coordinating process, and using 'sy' as the row
2.332 + position. A tuple containing 'sy' and a list of result numbers is
2.333 + returned by this function via the given 'channel'.
2.334 + """
2.335 +
2.336 + row = []
2.337 + for sx in range(0,self.width):
2.338 + x=2*(0.5-sx/float(self.width))*self.aspect
2.339 + y=2*(0.5-sy/float(self.height))
2.340 + if self.cameratype=="ortho":
2.341 + ray = line(vec(x,y,0),vec(x,y,self.backplane))
2.342 + else:
2.343 + ray = line(vec(0,0,0),vec(x,y,self.imageplane))
2.344 + ray.end=ray.end*self.backplane
2.345 +
2.346 + col=vec(0,0,0)
2.347 + depth=self.backplane
2.348 + shaderinfo={"ray":ray,"lights":self.lights,"objects":self.objects,"depth":2}
2.349 +
2.350 + for obj in self.objects:
2.351 + intersects,position,normal = obj.intersect(ray)
2.352 + if intersects is not "none":
2.353 + if position.z<depth and position.z>0:
2.354 + depth=position.z
2.355 + shaderinfo["thisobj"]=obj
2.356 + shaderinfo["position"]=position
2.357 + shaderinfo["normal"]=normal
2.358 + col=obj.shader.shade(shaderinfo)
2.359 + row.append(col)
2.360 +
2.361 + channel.send((sy, row))
2.362 +
2.363 + def render(self, filename, limit):
2.364 +
2.365 + """
2.366 + Render the image with many processes, saving it to 'filename', using the
2.367 + given process 'limit' to constrain the number of processes used.
2.368 + """
2.369 +
2.370 + image = Image.new("RGB", (self.width,self.height))
2.371 + draw = ImageDraw.Draw(image)
2.372 + total = self.width*self.height
2.373 + count = 0
2.374 + nproc = 0
2.375 +
2.376 + exchange = pprocess.Exchange()
2.377 + y = 0
2.378 + while y < self.height or nproc > 0:
2.379 + if nproc < limit:
2.380 + channel = pprocess.start(self.render_row, y)
2.381 + exchange.add(channel)
2.382 + y += 1
2.383 + nproc += 1
2.384 +
2.385 + for channel in exchange.ready():
2.386 + nproc -= 1
2.387 + sy, row = channel.receive()
2.388 + sx = 0
2.389 + for col in row:
2.390 + draw.point((sx,sy),fill=(col.x*255,col.y*255,col.z*255))
2.391 + #depth=depth/self.backplane
2.392 + #draw.point((sx,sy),fill=(depth*255,depth*255,depth*255))
2.393 + count=count+1
2.394 + sx += 1
2.395 +
2.396 + percentstr = str(int((count/float(total))*100))+"%"
2.397 + print "\b\b\b"+percentstr
2.398 +
2.399 + image.save(filename)
2.400 +
2.401 +# vim: tabstop=4 expandtab shiftwidth=4
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/examples/PyGmy/scene.py Sun Oct 01 00:57:02 2006 +0000
3.3 @@ -0,0 +1,66 @@
3.4 +#!/usr/bin/env python
3.5 +
3.6 +"""
3.7 +An example scene from...
3.8 +
3.9 +http://www.pawfal.org/index.php?page=PyGmy
3.10 +"""
3.11 +
3.12 +import math
3.13 +from ppygmy import *
3.14 +import sys
3.15 +
3.16 +class everythingshader(shader):
3.17 + def __init__(self):
3.18 + pass
3.19 +
3.20 + def shade(self,shaderinfo):
3.21 + col = shader.shade(self,shaderinfo)
3.22 + ref = self.getreflected(shaderinfo)
3.23 + col = col*0.5+ref*0.5
3.24 + return col*self.doocclusion(10,shaderinfo)
3.25 +
3.26 +class spotshader(shader):
3.27 + def __init__(self):
3.28 + pass
3.29 +
3.30 + def shade(self,shaderinfo):
3.31 + col = shader.shade(self,shaderinfo)
3.32 + position=shaderinfo["position"]
3.33 + jitter=(math.sin(position.x)+math.cos(position.z))
3.34 + if jitter>0.5: col=col/2
3.35 + ref = self.getreflected(shaderinfo)
3.36 + return ref*0.5+col*0.5*self.doocclusion(10,shaderinfo)
3.37 +
3.38 +if __name__ == "__main__":
3.39 + w = world(300,200)
3.40 + numballs=10.0
3.41 + offset = vec(0,-5,55)
3.42 + rad=12.0
3.43 + radperball=(2*3.141)/numballs
3.44 +
3.45 + for i in range(0,numballs):
3.46 + x=sin(0.3+radperball*float(i))*rad
3.47 + y=cos(0.3+radperball*float(i))*rad
3.48 + w.objects.append(sphere(vec(x,0,y)+offset,2,everythingshader()))
3.49 +
3.50 + w.objects.append(sphere(vec(3,3,0)+offset,5,everythingshader()))
3.51 + w.objects.append(plane(vec(0,1,0),7,spotshader()))
3.52 + w.lights.append(parallellight(vec(1,1,-1),vec(0.3,0.9,0.1)))
3.53 + w.lights.append(pointlight(vec(5,100,-5),vec(0.5,0.5,1)))
3.54 +
3.55 + if len(sys.argv) > 1:
3.56 + if "--help" in sys.argv:
3.57 + print "Specify a limit to the number of processes."
3.58 + print "For example:"
3.59 + print "python", sys.argv[0], "4"
3.60 + sys.exit(1)
3.61 + else:
3.62 + limit = int(sys.argv[1])
3.63 + else:
3.64 + limit = 1
3.65 +
3.66 + print "Number of processes:", limit
3.67 + w.render("test.tif", limit)
3.68 +
3.69 +# vim: tabstop=4 expandtab shiftwidth=4