Skip to content

Instantly share code, notes, and snippets.

@seanjensengrey
Created April 9, 2014 23:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seanjensengrey/8fc69d6f60629dda6e76 to your computer and use it in GitHub Desktop.
Save seanjensengrey/8fc69d6f60629dda6e76 to your computer and use it in GitHub Desktop.
simplistic lua raytracer, a single sphere
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