Created
April 9, 2014 23:20
-
-
Save seanjensengrey/8fc69d6f60629dda6e76 to your computer and use it in GitHub Desktop.
simplistic lua raytracer, a single sphere
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
DELTA = 1.5e-8 | |
_INFINITY = math.huge | |
__tostring_vec = function(vec) | |
return string.format("Vec(%f,%f,%f)",vec.x,vec.y,vec.z) | |
end | |
vec_mt = {} | |
vec_mt.__tostring = __tostring_vec | |
Vec = function(x,y,z) | |
local v = {} | |
v.x = x | |
v.y = y | |
v.z = z | |
setmetatable(v,vec_mt) | |
return v | |
end | |
add = function(a,b) | |
return Vec(a.x+b.x, a.y+b.y, a.z+b.z) | |
end | |
sub = function(a,b) | |
return Vec(a.x-b.x, a.y-b.y, a.z-b.z) | |
end | |
scale = function(s,a) | |
return Vec(s * a.x, s * a.y, s * a.z) | |
end | |
dot = function(a,b) | |
return a.x*b.x + a.y*b.y + a.z*b.z | |
end | |
unitize = function(a) | |
return scale( 1.0 / ( math.sqrt( dot( a, a) ) ), a) | |
end | |
__tostring_ray = function(ray) | |
return string.format("Ray(%s,%s)",tostring(ray.orig),tostring(ray.dir)) | |
end | |
ray_mt = {} | |
ray_mt.__tostring = __tostring_ray | |
Ray = function(orig, dir) | |
local r = {} | |
r.orig = orig | |
r.dir = dir | |
setmetatable(r,ray_mt) | |
return r | |
end | |
__tostring_hit = function(hit) | |
return string.format("Hit(%d,%s",hit.lamb,pv(hit.normal)) | |
end | |
hit_mt = {} | |
hit_mt.__tostring = __tostring_hit | |
Hit = function(lamb, normal) | |
local h = {} | |
h.lamb = lamb | |
h.normal = normal | |
setmetatable(h,hit_mt) | |
return h | |
end | |
Sphere = function(center, radius) | |
s = {} | |
s.center = center or Vec(0,0,0) | |
s.radius = radius or 1.0 | |
return s | |
end | |
function ray_sphere(sphere, hit, ray) | |
local function rs(sphere, ray) | |
local v = sub(sphere.center, ray.orig) | |
local b = dot(v, ray.dir) | |
local disc = b*b - dot(v,v) + sphere.radius*sphere.radius | |
if disc < 0.0 then return _INFINITY end | |
local d = math.sqrt(disc) | |
local t2 = b + d | |
if t2 < 0.0 then return _INFINITY end | |
local t1 = b - d | |
if t1 > 0.0 then | |
return t1 | |
else | |
return t2 | |
end | |
end | |
local l = rs(sphere,ray) | |
if l >= hit.lamb then | |
return hit | |
end | |
local n = add(ray.orig, sub( scale(l, ray.dir), sphere.center)) | |
local h = Hit(l, unitize(n)) | |
return h | |
end | |
scene = Sphere(Vec(0,-1.0,0),1.0) | |
function ray_trace(light, ray) | |
-- hard coded scene | |
local _ = Hit(_INFINITY, Vec(0,0,0)) | |
local hit = ray_sphere(scene, _, ray) | |
if hit.lamb == _INFINITY then | |
return 0.0 | |
end | |
local o = add(ray.orig, add(scale(hit.lamb,ray.dir), scale(DELTA,hit.normal))) | |
local g = dot(hit.normal,light) | |
if g >= 0.0 then return 0.0 end | |
local sray = Ray(o,scale(-1,light)) | |
local si = ray_sphere(scene, Hit(_INFINITY, Vec(0,0,0)), sray) | |
if si.lamb == _INFINITY then return -g else return 0.0 end | |
end | |
local light_vec = unitize(Vec(-1, -3, 2)) | |
local eye_vec = Vec(0, 0, -10) | |
function run(dim) | |
local n = dim | |
local fo = io.open("ltest.pgm","wb") | |
fo:write(string.format("P5\n%d %d\n255\n",dim,dim)) | |
for y=n-1,0,-1 do | |
for x=0,n-1,1 do | |
local d = Vec( x - (n/2.0), y - (n/2.0), n) | |
local ray = Ray(eye_vec, unitize(d)) | |
local g = ray_trace(light_vec, ray) | |
fo:write(string.char(0.5 + (255.0 * g))) | |
end | |
end | |
fo:close() | |
end | |
if #arg == 0 then | |
run(256) | |
else | |
run(tonumber(arg[1])) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment