-- Main rendering functions require "Vector" local V = Vector require "Primitives" local P = Primitives require "Operations" local O = Operations require "Textures" local T = Textures local eps = 0.001 local max_dist2 = 30^2 local bg_col = {0.1,0.1,0.1} local function calculate_normal(sdf, l) local delta = eps/10 return V.new{sdf(l+V.x*delta).dist-sdf(l-V.x*delta).dist, sdf(l+V.y*delta).dist-sdf(l-V.y*delta).dist, sdf(l+V.z*delta).dist-sdf(l-V.z*delta).dist}/(2*delta) end local function march(start, location, ray_dir, sdf, count) if V.norm2(location-start) > max_dist2 then return bg_col end local p = sdf(location) if p.dist < eps then return p.texture(location, ray_dir, calculate_normal(sdf, location-(ray_dir*eps)), count, sdf) else return march(start, location + ray_dir*p.dist, ray_dir, sdf, count+1) end end local function render(scene, width, height, filename) print("Rendering to file " .. filename .. "...") local f = io.open(filename, "w") f:write("P6 ", width, " ", height, " 255\n") local c = scene.camera local cam_dir = V.normalize(c.point_at - c.location) local right = V.normalize(c.right) local up = V.cross(right,cam_dir) local aspect = height/width; local normalize = V.normalize -- optimization for y=1,height do if y % math.floor(height/10) == 0 then print(y/math.floor(height/10) * 10 .. "%") end local rayy = cam_dir + up*((0.5 - y/height)*c.fov*aspect) for x=1,width do local ray_dir = normalize(rayy + right*((x/width - 0.5)*c.fov)) local col = march(c.location, c.location, ray_dir, scene.sdf, 0) col = {math.floor(math.min(col[1]*255, 255)), math.floor(math.min(col[2]*255, 255)), math.floor(math.min(col[3]*255, 255))} f:write(string.char(col[1]), string.char(col[2]), string.char(col[3])) end end f:close() print("done") end Render = { eps = eps, max_dist2 = max_dist2, bg_col = bg_col, render = render } return Render