From d76d0878c5dadc757dcf3c7b73cf7694b9f4c96b Mon Sep 17 00:00:00 2001 From: Emil Mikulic Date: Mon, 5 Sep 2016 16:17:18 +1000 Subject: [PATCH] cp raytracer.py tf.py --- tf.py | 143 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) create mode 100755 tf.py diff --git a/tf.py b/tf.py new file mode 100755 index 0000000..2bc8365 --- /dev/null +++ b/tf.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python +""" +The worst raytracer. +Emil Mikulic was here 2012. +""" +import numpy as np +from accidental_complexity import show +# this file contains mostly essential complexity + +def vec(x, y, z): + return np.array((x, y, z), dtype=np.float) + +def color(r, g, b): + return np.reshape(vec(r,g,b), (1,1,3)) + +def dot(a, b): + return (a * b).sum(axis=2) + +def mag(a): + return np.sqrt(np.square(a).sum(axis=2)) + +def depthwise(a): + return np.expand_dims(a, axis=2) + +def norm(a): + return a / depthwise(mag(a)) + +def reflect(i, n): + c = n * depthwise(dot(n, -i)) + return 2*c + i + +def diffuse(n, l): + return depthwise(dot(n, l).clip(0, 1)) + +def specular(r, l, power): + return pow(diffuse(r, l), power) + +def lerp(ar, a, b, u, v): + "[u,v] is mapped to [a,b]" + return a + (b-a) * (ar.clip(u,v) - u) / (v-u) + +class Scene: + def __init__(self): + self.light1 = vec(20,10,-10) + self.light1_col = color(1,.5,.3) + self.light2 = vec(-5,5,5) + self.light2_col = color(1,1,1) - self.light1_col + self.sky_col = color(.2, .3, .6) + self.sph_center = vec(-.5,0,0) + self.sph_rad = .5 + self.sph_col = color(1,1,1) + self.camera = vec(0,0,-5) + self.ground_col1 = color(.4,.4,.4) + self.ground_col2 = color(.8,.8,.8) + self.plane_n = vec(0,1,0) + self.plane_amb = 0.02 + self.plane_dif = 0.9 + self.plane_height = -0.5 + self.fog_near = 0 + self.fog_far = 40 + + def trace_bg(self, camera, ray): + # background + plane_depth = (self.plane_height - camera[:,:,1]) / ray[:,:,1] + plane_p = camera + depthwise(plane_depth) * ray + plane_l1 = norm(self.light1 - plane_p) + plane_l2 = norm(self.light2 - plane_p) + plane_xpos = plane_p[:,:,0] - np.floor(plane_p[:,:,0]) + plane_zpos = plane_p[:,:,2] - np.floor(plane_p[:,:,2]) + checkers = depthwise(np.logical_xor(plane_xpos < 0.5, plane_zpos < 0.5)) + + # trace shadows (light 1) + ec = plane_p - self.sph_center + b = 2 * dot(plane_l1, ec) + c = dot(ec, ec) - np.square(self.sph_rad) + det = b*b - 4*c + depth = (-b - np.sqrt(det)) / 2 + illum_l1 = np.where(depthwise(depth > 0), 0, self.light1_col) + + # light 2 + b = 2 * dot(plane_l2, ec) + det = b*b - 4*c + depth = (-b - np.sqrt(det)) / 2 + illum_l2 = np.where(depthwise(depth > 0), 0, self.light2_col) + + # shade plane + plane_col = np.where(checkers, self.ground_col1, self.ground_col2) * ( + self.plane_amb + self.plane_dif * ( + diffuse(self.plane_n, plane_l1) * illum_l1 + + diffuse(self.plane_n, plane_l2) * illum_l2)) + + fog_dist = mag(plane_p - vec(0, self.plane_height, 0)) + fog = lerp(fog_dist, 1, 0, self.fog_near, self.fog_far) + plane_col *= depthwise(np.square(fog)) + + sky = self.sky_col * depthwise(ray[:,:,1]) + return np.where(depthwise(plane_depth > 0), plane_col, sky) + + def render(self, w, h): + # set up scene + sph_amb = 0.02 + sph_diff = 0.05 + sph_spec = 1.1 + sph_refl = 0.5 + sph_shine = 20 + + # points on the screen plane (z = 0) + aspect = float(w) / float(h) + scr_x = np.linspace(-1 + 1./w, 1 - 1./w, w) * aspect + scr_y = np.linspace(-1 + 1./h, 1 - 1./h, h) * -1 + scr = np.zeros((h,w,3), dtype=np.float) + scr[:,:,0] = np.reshape(scr_x, (1, w)) + scr[:,:,1] = np.reshape(scr_y, (h, 1)) + + ray = norm(scr - self.camera) + + # sphere + ec = self.camera - self.sph_center + b = 2 * dot(ray, ec) + c = ec.dot(ec) - np.square(self.sph_rad) + det = b*b - 4*c + sph_depth = (-b - np.sqrt(det)) / 2 + + # sphere color + sph_p = self.camera + depthwise(sph_depth) * ray + sph_l1 = norm(self.light1 - sph_p) + sph_l2 = norm(self.light2 - sph_p) + sph_n = (sph_p - self.sph_center) / self.sph_rad + sph_r = norm(reflect(ray, sph_n)) + r_col = self.trace_bg(sph_p, sph_r) + + sph = self.sph_col * (sph_amb + + sph_diff * diffuse(sph_n, sph_l1) * self.light1_col + + sph_diff * diffuse(sph_n, sph_l2) * self.light2_col + + sph_spec * specular(sph_r, sph_l1, sph_shine) * self.light1_col + + sph_spec * specular(sph_r, sph_l2, sph_shine) * self.light2_col + + r_col * sph_refl) + + bg = self.trace_bg(np.reshape(self.camera, (1,1,3)), ray) + return np.where(depthwise(sph_depth > 0), sph, bg) + +show(Scene().render(800, 600)) +# vim:set ts=2 sw=2 et: -- 2.17.1