From the book Raytracing in a weekend
code adapted to python
emacs configuration emacs
(org-babel-do-load-languages
'org-babel-load-languages
'((python . t)))
(setq org-src-preserve-indentation t)
helper functions
from utilities import point3,color,ray,vec3
import math
def dot(vec1, vec2):
return (vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z )
import math
def unit_vector(v):
length = math.sqrt(v.x**2+ v.y**2+ v.z**2)
return vec3(v.x/length, v.y/length, v.z/length)
def ray_color(r):
t = hit_sphere(point3(0,0, -1), 0.5, r)
if( t > 0.0):
N = unit_vector(r.at(t) - vec3(0,0,-1))
res = vec3(N.x + 1, N.y + 1, N.z + 1) * 0.5
return res
unit_direction = unit_vector(r.dir)
t = 0.5* (unit_direction.y + 1.0)
return color(1.0,1.0,1.0)* (1.0-t) + color(0.5,0.7,1.0) * t
def write_color(ppm,pixel_color):
#translate 0,255
ir = int(255* pixel_color.x)
ig = int(255* pixel_color.y)
ib = int(255* pixel_color.z)
ppm.write(str(ir) + " " + str(ig) + " " + str(ib)+ "\n")
def hit_sphere(center, radius, r):
oc = r.orig - center
a = dot(r.dir, r.dir)
b = 2.0 * dot(oc, r.dir)
c = dot(oc, oc) - (radius * radius)
discrim = b*b - 4*a*c
if(discrim < 0):
return -1.0
else:
return (-b - math.sqrt(discrim)) / (2.0 *a)
main function
#P3 : colors are in ascii
#width and height
# max value
aspect_ratio = 16.0/9.0
image_width = 400
image_height= int(image_width / aspect_ratio)
#camera
viewport_height = 2.0
viewport_width = aspect_ratio * viewport_height
focal_length = 1.0
origin = point3(0,0,0)
horizontal = vec3(viewport_width, 0,0)
vertical = vec3(0, viewport_height, 0)
lower_left_corner = origin - (horizontal/2) - (vertical/2) - vec3(0,0,focal_length)
header = "P3\n"+ str(image_width)+ " "+str(image_height)+"\n255\n"
with open('result.ppm', 'w') as ppm:
ppm.write(header)
for j in range(image_height,-1 ,-1):
print("scanlines remaining "+ str(j) + "\n")
for i in range(image_width):
u = float(i)/(image_width - 1)
v = float(j)/(image_height- 1)
vecu = vec3(horizontal.x * u , horizontal.y * u , horizontal.z * u)
vecv = vec3(vertical.x * v ,vertical.y * v ,vertical.z * v)
r = ray(origin, lower_left_corner + vecu+ vecv- origin)
pixel_color = ray_color(r)
write_color(ppm, pixel_color)
write the utility classes
import vector
class vec3:
def __init__(self,x,y,z):
self.x =x
self.y = y
self.z = z
def __add__(self, vec2):
return vec3(self.x + vec2.x, self.y + vec2.y ,self.z + vec2.z)
def __sub__(self, vec2):
return vec3(self.x - vec2.x, self.y - vec2.y ,self.z - vec2.z)
def __mul__(self, scalar):
return vec3(scalar*self.x,scalar*self.y,scalar*self.z)
def __truediv__(self, scalar ):
return vec3(self.x/scalar,self.y/scalar,self.z/scalar)
def __floordiv__(self, scalar):
return vec3(self.x/scalar,self.y/scalar,self.z/scalar)
class ray:
def __init__ (self, origin, direction):
self.orig = vec3(origin.x, origin.y , origin.z)
self.dir = vec3(direction.x,direction.y , direction.z)
def at(self,t):
return self.orig + (self.dir * t)
point3 = vec3
color = vec3
render blue to white gradient
start from sending rays into the scene
not really sure how I did that, all I did was write utility methods
understood python’s operator overloading a little bit

render a sphere
- only 2 methods need to be changed \(hit\_sphere\ and\ ray\_color\)
DONE fix bug there are only red scan lines

Shading with surface normals
- how are the normals computed \(hitpoint - center\)

TODO materials
diffuse materials
has something to do with reflection and refraction and how diffuse materials behave wrt above