from PIL import Image import pygame import math import numpy import sys epsilon = 1.0e-7 class Sphere(object): def __init__(self, center, radius): self.center = numpy.array(center) self.radius = numpy.array(radius) def hit(self, ray): temp = ray.origin - self.center a = numpy.dot(ray.direction, ray.direction) b = 2.0 * numpy.dot(temp, ray.direction) c = numpy.dot(temp, temp) - self.radius * self.radius disc = b * b - 4.0 * a * c if (disc < 0.0): return None else: e = math.sqrt(disc) denom = 2.0 * a t = (-b - e) / denom if (t > epsilon): normal = (temp + t * ray.direction) / self.radius hit_point = ray.origin + t * ray.direction return ShadeRecord(normal=normal, hit_point=hit_point) t = (-b + e) / denom if (t > epsilon): normal = (temp + t * ray.direction) / self.radius hit_point = ray.origin + t * ray.direction return ShadeRecord(normal=normal, hit_point=hit_point) return None class Ray(object): def __init__(self, origin, direction): self.origin = numpy.array(origin) self.direction = numpy.array(direction) class ShadeRecord(object): def __init__(self, hit_point, normal): self.hit_point = hit_point self.normal = normal class Tracer(object): def __init__(self, world): self.world = world def trace_ray(self, ray): if self.world.sphere.hit(ray): return (1.0,0.0,0.0) else: return (0.0,0.0,0.0) class ViewPlane(object): def __init__(self, resolution, pixel_size): self.resolution = resolution self.pixel_size = pixel_size def iter_row(self, row): for column in xrange(self.resolution[0]): origin = numpy.zeros(3) origin[0] = self.pixel_size*(column - self.resolution[0] / 2 + 0.5) origin[1] = self.pixel_size*(row - self.resolution[1] / 2 + 0.5) origin[2] = 100.0 yield ( Ray(origin = origin, direction = (0.0,0.0,-1.0)), (column,row)) def __iter__(self): for row in xrange(self.resolution[1]): yield self.iter_row(row) class World(object): def __init__(self): self.viewplane = ViewPlane(resolution=(320,200), pixel_size=1.0) self.background_color = (0.0,0.0,0.0) self.sphere=Sphere(center=(0.0,0.0,0.0), radius=85.0) def render(self): pygame.init() window = pygame.display.set_mode(self.viewplane.resolution) pxarray = pygame.PixelArray(window) im = Image.new("RGB", self.viewplane.resolution) tracer = Tracer(self) for row in self.viewplane: for ray, pixel in row: color = tracer.trace_ray(ray) im.putpixel(pixel, (int(color[0]*255), int(color[1]*255), int(color[2]*255))) pxarray[pixel[0]][pixel[1]] = (int(color[0]*255), int(color[1]*255), int(color[2]*255)) pygame.display.flip() im.save("render.png", "PNG") while True: for event in pygame.event.get(): if event.type == pygame.QUIT: sys.exit(0) else: print event w=World() w.render()