I have written a (very dirty) Python raytracer with a simple scene, for educational purpose.
Not optimized at all, but it generates the image in a few seconds on my 8 core PC.
Now as an extra challenge, I wanted to parallelize the calculation.
Basically:
- Just partition the image in vertical bands, based on a stripe number I can choose.
- Keep the scene and common data declared in the
However, it turns out to be veryyy slow. Like a few minutes.
Indeed, even with l=1, so a single process pool
I tried to put the image array in the local calcul function (without bothering in returning it even), but no improvement.
So I suspect that some item is blocking execution, or forces forth and back between contexts from father process and child.
Any idea on where the problem lies in my code, please ?
Thanks in advance !
Regards
import numpy as np
import matplotlib.pyplot as plt
import time
import multiprocessing as mp
from multiprocessing import Pool
def normer(v):
return v/np.sqrt(np.dot(v,v))
def normale(M,C,r):
return normer(M-C)
def inter_sphere(O,d,C,r):
u=d/np.sqrt(np.dot(d,d))
CO=O-C
a=1
b=2*np.dot(CO,u)
c=np.dot(CO,CO)-r**2
delta=b**2-4*a*c
if delta<0: return np.inf
t1=(-b-np.sqrt(delta))/2
t2=(-b+np.sqrt(delta))/2
if t2>0:
t2=t2
else: t2=np.inf
if t1>0:
t1=t1
else:
t1=np.inf
t=min(t1,t2)
return t
def eclairer(M,L):
ML=L-M
u=normer(ML)
dist=np.sqrt(np.dot(ML,ML))
touche=np.inf
for i,obj in enumerate(scene):
C=scene[i]['position']
r=scene[i]['radius']
t=inter_sphere(M, u, C, r)
if t<touche:
touche=t
if touche>=dist:
color=light
else:
color=[0,0,0]
return color
def calcul(x1,x2,y1,y2):
cas=[(i,j) for i in np.arange(x1,x2) for j in np.arange(y1,y2)]
for (x,y) in cas:
print(x,y)
O=np.array([x,y,0])
u=np.array([0,0,1])
M=O
k=1
parcours=0
color=[0,0,0]
while k<=prof:
dist=np.inf
for i,obj in enumerate(scene):
C=scene[i]['position']
r=scene[i]['radius']
t=inter_sphere(M, u, C, r)
print(x,y,k,t)
if t<dist:
M=M+t*u
n=normale(M, C, r)
M=M+n*0.001
u=n
dist=t
color1=list(scene[i]['color'])
color2=list(eclairer(M,L))
ML=np.sqrt(np.dot(L-M,L-M))
color3=[max(0,np.sqrt(x*y)*np.dot(normer(L-M),u)) for x,y in zip(color1,color2)]
if dist<np.inf:
parcours+=dist
color=[min(255,max(0,x+y/(k**2))) for x,y in zip(color,color3)]
k=k+1
else:
break
image[x,y]=tuple([min(p,255)/255 for p in color])
return
start=time.time()
prof=3
al=1
C=np.array([10,10,5])*al
r=10*al
light=[200,250,250]
L=np.array([20*al,40*al,10])
scene=[dict(type='sphere', position=C, radius=r, color=(100,100,100)),
dict(type='sphere', position=np.array([30,20,30])*al,radius=10*al, color=(50,100,200)),
dict(type='sphere', position=np.array([50,10,50])*al, radius=10*al, color=(50,100,200)),
dict(type='sphere', position=np.array([10,50,80])*al, radius=10*al, color=(255,0,0))]
X,Y = 50*al,50*al
image=np.zeros([X,Y,3])
# DEFINIES THE PARTITION STRIPES
l=3
zones=[(int(X/l*i),int(X/l*(i+1)),0,Y) for i in range(l) ]
for num,zone in enumerate(zones):
print(*zone)
#USED FOR TESTING, IMAGE GENERATED IN 1.2sec
# calcul(*zone)
#Parallel calculation
with Pool(l) as pool:
pool.starmap(calcul,zones)
plt.imsave('fig.png', image)
print(time.time()-start)
I tried to set the image ndarray as local in the
calcul
function, without even bothering about returning the processed data.
Tried using the
with
and putting the code directly.
user3450564 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.