Welcome, guest | Sign In | My Account | Store | Cart
# Voxel-based Ray Tracing
# No refraction for simplicity!
# Single point light source for simplicity!
# FB36 - 20130829
import math
from PIL import Image
imgx
= 400; imgy = 400 ; imgz = 400 # voxel-box size
image
= Image.new("RGB", (imgx, imgy))
pixels
= image.load()

print "Creating voxels..."

# each voxel can have RGB color
voxelRGB
= [[[(0, 0, 0) for x in range(imgx)] for y in range(imgy)] for z in range(imgz)]

# each voxel can have an opacity coefficient 0 or 1 (for simplicity)
opacity
= [[[0 for x in range(imgx)] for y in range(imgy)] for z in range(imgz)]

# each voxel can have a 3d normal unit vector (for reflections/refractions)
normal
= [[[(0.0, 0.0, 0.0) for x in range(imgx)] for y in range(imgy)] for z in range(imgz)]

# each voxel can have a reflectivity coefficient between 0 and 1
reflectivity
= [[[0.0 for x in range(imgx)] for y in range(imgy)] for z in range(imgz)]

eye
= (imgx / 2.0, 250.0, -100.0)
light
= (- 10.0, -10.0, -10.0)
ambientBrightness
= 0.3 # between 0 and 1

# cx, cy, cz: center; r: radius (in voxels)
# rc: reflectivity coefficient between 0 and 1
def CreateMirrorSphere(cx, cy, cz, r, colorRGB, rc):
   
print "Creating mirror sphere..."
   
# sphere is set of voxels which have distance = r to center
   
for z in range(imgz):
       
for y in range(imgy):
           
for x in range(imgx):
                dx
= x - cx
                dy
= y - cy
                dz
= z - cz
                d
= math.sqrt(dx * dx + dy * dy + dz * dz)
               
if abs(d - r) < 1.0:
                    voxelRGB
[z][y][x] = colorRGB
                    opacity
[z][y][x] = 1
                    normal
[z][y][x] = (dx / d, dy / d, dz / d)
                    reflectivity
[z][y][x] = rc
   
def CreateCheckerboardFloor(sqrX, sqrZ, colorRGB0, colorRGB1):
   
print "Creating checkerboard floor..."
   
for z in range(imgz):
       
for x in range(imgx):
           
if int(x / sqrX) % 2 != int(z / sqrZ) % 2: # xor
                voxelRGB
[z][imgy - 1][x] = colorRGB0
           
else:
                voxelRGB
[z][imgy - 1][x] = colorRGB1
            opacity
[z][imgy - 1][x] = 1
            normal
[z][imgy - 1][x] = (0.0, -1.0, 0.0)

def CombineColors(c0, c1, w):
   
return (int(round(c0[0] + (c1[0] - c0[0]) * w)),
           
int(round(c0[1] + (c1[1] - c0[1]) * w)),
           
int(round(c0[2] + (c1[2] - c0[2]) * w)))
   
# 3D Reflection (all vectors are unit vectors)
def Reflection(normalVector, incidentRayVector):
   
(nx, ny, nz) = normalVector
   
(ix, iy, iz) = incidentRayVector
    dotProduct
= nx * ix + ny * iy + nz * iz
    rx
= ix - 2 * dotProduct * nx
    ry
= iy - 2 * dotProduct * ny
    rz
= iz - 2 * dotProduct * nz    
   
return (rx, ry, rz)

def LightIntensity(surfaceVoxelCoordinates, surfaceNormalVector):
   
(svx, svy, svz) = surfaceVoxelCoordinates
   
(nx, ny, nz) = surfaceNormalVector
   
(Lx, Ly, Lz) = light
    dx
= Lx - svx
    dy
= Ly - svy
    dz
= Lz - svz
    d
= math.sqrt(dx * dx + dy * dy + dz * dz)
    dx
= dx / d; dy = dy / d; dz = dz / d # unit vector towards light
    cosT
= nx * dx + ny * dy + nz * dz
   
if cosT < 0.0: cosT = 0.0 # the surface faces away from the light source
   
return cosT

def ShadowIntensity(rayX, rayY, rayZ):
   
(Lx, Ly, Lz) = light
    dx
= Lx - rayX
    dy
= Ly - rayY
    dz
= Lz - rayZ
    d
= math.sqrt(dx * dx + dy * dy + dz * dz)
    dx
= dx / d; dy = dy / d; dz = dz / d # unit vector towards light

   
while True: # shadow ray tracing

        rayX
+= dx; rayY += dy; rayZ += dz # move the ray by 1 voxel

        rayXint
= int(round(rayX))
        rayYint
= int(round(rayY))
        rayZint
= int(round(rayZ))

       
# if ray goes outside of the voxel-box
       
if rayXint < 0 or rayXint > imgx - 1 \
           
or rayYint < 0 or rayYint > imgy - 1 \
           
or rayZint < 0 or rayZint > imgz - 1:
           
return 1.0

       
# if ray hits an object
       
if opacity[rayZint][rayYint][rayXint] == 1:
           
# stop tracing here for simplicity
           
return 0.0

       
# if ray hits the light source
        vx
= Lx - rayX
        vy
= Ly - rayY
        vz
= Lz - rayZ
        d
= math.sqrt(vx * vx + vy * vy + vz * vz)
       
if d < 1.0:
           
return 1.0

# Ray Tracer (traces the ray and returns an RGB color)
def RayTrace(rayX, rayY, rayZ, dx, dy, dz):

    brightness
= 1.0 # the ray has full brightness at the beginning
    color
= (0, 0, 0)

   
while True:

        rayX
+= dx; rayY += dy; rayZ += dz # move the ray by 1 voxel

        rayXint
= int(round(rayX))
        rayYint
= int(round(rayY))
        rayZint
= int(round(rayZ))

       
# if ray goes outside of the voxel-box
       
if rayXint < 0 or rayXint > imgx - 1 \
           
or rayYint < 0 or rayYint > imgy - 1 \
           
or rayZint < 0 or rayZint > imgz - 1:
           
return color

       
# if ray hits an object
       
if opacity[rayZint][rayYint][rayXint] == 1:

           
# if ray hits a non-reflective object
           
if reflectivity[rayZint][rayYint][rayXint] == 0.0:
                brightness
*= ShadowIntensity(rayX, rayY, rayZ)
                brightness
*= LightIntensity((rayX, rayY, rayZ), \
                                             normal
[rayZint][rayYint][rayXint])
                brightness
= brightness * (1.0 - ambientBrightness) + ambientBrightness
                color
= CombineColors(color, voxelRGB[rayZint][rayYint][rayXint], brightness)                
               
return color

           
# if ray hits a reflective object
           
if reflectivity[rayZint][rayYint][rayXint] > 0.0:
                brightness
*= reflectivity[rayZint][rayYint][rayXint]

                brightnessTemp
= ShadowIntensity(rayX, rayY, rayZ)
                brightnessTemp
*= LightIntensity((rayX, rayY, rayZ), \
                                                 normal
[rayZint][rayYint][rayXint])
                brightnessTemp
= brightnessTemp * (1.0 - ambientBrightness) + ambientBrightness
                colorTemp
= voxelRGB[rayZint][rayYint][rayXint]
                colorTemp
= (int(colorTemp[0] * brightnessTemp), \
                             
int(colorTemp[1] * brightnessTemp), \
                             
int(colorTemp[2] * brightnessTemp))

                color
= CombineColors(color, colorTemp, \
                                     
1.0 - reflectivity[rayZint][rayYint][rayXint])
               
(dx, dy, dz) = Reflection(normal[rayZint][rayYint][rayXint], (dx, dy, dz))

def CreateScene():
   
print "Creating scene..."
   
CreateCheckerboardFloor(20, 20, (255, 255, 0), (0, 0, 255))
   
CreateMirrorSphere(imgx / 2.0, imgy / 2.0, imgz / 2, 150.0, (0, 255, 0), 0.8)

def RenderScene():
   
print "Rendering scene..."
   
for ky in range(imgy):
       
print str(100 * ky / (imgy - 1)).zfill(3) + "%"
       
for kx in range(imgx):
            dx
= kx - eye[0]
            dy
= ky - eye[1]
            dz
= 0.0 - eye[2]
            d
= math.sqrt(dx * dx + dy * dy + dz * dz)
            dx
= dx / d; dy = dy / d; dz = dz / d # ray unit vector
            pixels
[kx, ky] = RayTrace(kx, ky, 0, dx, dy, dz)

# MAIN
CreateScene()
RenderScene()
image
.save("Voxel_Based_Ray_Tracing.png", "PNG")

History