From: Tim Vaughan Date: Mon, 20 Feb 2023 14:50:05 +0000 (+0100) Subject: Fixed surface normal calculation bug. X-Git-Url: https://thelambdalab.xyz/gitweb/index.cgi?p=raymarcher.git;a=commitdiff_plain;h=46c1311e546ae5cefc4f596ed13476c649f12f66 Fixed surface normal calculation bug. --- diff --git a/Primitives.lua b/Primitives.lua index 88a3849..aafa7cb 100644 --- a/Primitives.lua +++ b/Primitives.lua @@ -3,14 +3,23 @@ require "Vector" local V = Vector local function make_sphere(centre, radius, texture) - return function(p) - return {dist = V.norm(p-centre) - radius, - texture = texture} - end + local norm = V.norm + return function(p) + return {dist = V.norm(p-centre) - radius, + texture = texture} + end +end + +local function make_plane(centre, normal, texture) + return function(p) + return {dist = normal*(p-centre), + texture = texture} + end end Primitives = { - make_sphere = make_sphere + make_sphere = make_sphere, + make_plane = make_plane } return Primitives diff --git a/Textures.lua b/Textures.lua index a06faa7..cee3dd9 100644 --- a/Textures.lua +++ b/Textures.lua @@ -2,34 +2,61 @@ require "Vector" local V = Vector -local function make_phong(lights, colour, amb, spec, shiny) +local function make_phong_texture(lights, pigment, amb, diff, spec, shiny) + local normalize = V.normalize return function (location, ray_dir, normal, count) - local thiscol = {0, 0, 0} - local reflected_ray = V.normalize(normal*(ray_dir*normal*2) - ray_dir) + local colour = pigment(location[1],location[3]) + local thiscol = {colour[1]*amb, colour[2]*amb, colour[3]*amb} + + local reflected_ray = ray_dir - normal*(ray_dir*normal*2) for _,light in ipairs(lights) do local light_dir = V.normalize(light-location) - local Iamb = math.max(normal*light_dir, 0) + local Idiff = math.max(normal*light_dir, 0) local Ispec = math.pow(math.max(light_dir*reflected_ray,0),shiny) - thiscol[1] = thiscol[1] + colour[1]*Iamb*amb + Ispec*spec - thiscol[2] = thiscol[2] + colour[2]*Iamb*amb + Ispec*spec - thiscol[3] = thiscol[3] + colour[3]*Iamb*amb + Ispec*spec + thiscol[1] = thiscol[1] + colour[1]*Idiff*diff + Ispec*spec + thiscol[2] = thiscol[2] + colour[2]*Idiff*diff + Ispec*spec + thiscol[3] = thiscol[3] + colour[3]*Idiff*diff + Ispec*spec end return thiscol end end -local function make_count_texture() +local function make_count_texture(scale) + return function (location, ray_dir, normal, count) + return {count*scale, count*scale, count*scale} + end +end + +local function make_flat_texture(pigment) return function (location, ray_dir, normal, count) - return {count/10, count/10, count/10} + return pigment(location[1], location[2]) end end +local function make_solid_pigment(colour) + return function (x,y) + return colour + end +end + +local function make_checkered_pigment(colour1, colour2) + return function (x,y) + if (x%1 < 0.5 and y%1 < 0.5) or (x%1 > 0.5 and y%1 > 0.5) then + return colour1 + else + return colour2 + end + end +end Textures = { - make_phong = make_phong, - make_count_texture = make_count_texture + make_phong_texture = make_phong_texture, + make_flat_texture = make_flat_texture, + make_count_texture = make_count_texture, + make_solid_pigment = make_solid_pigment, + make_checkered_pigment = make_checkered_pigment } return Textures diff --git a/raymarch.lua b/raymarch.lua index f1dbc3c..5bace40 100644 --- a/raymarch.lua +++ b/raymarch.lua @@ -11,24 +11,26 @@ require "Textures" local T = Textures local eps = 0.01 -local max_dist = 50 -local bg_col = {1,0,1} +local max_dist2 = 20^2 +local bg_col = {0.1,0.1,0.1} local function calculate_normal(sdf, l) local delta = 1e-3; - 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) + 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(location, ray_dir, sdf, count) +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) - elseif p.dist > max_dist then - return bg_col else - return march(location + ray_dir*p.dist, + return march(start, location + ray_dir*p.dist, ray_dir, sdf, count+1) end end @@ -44,6 +46,8 @@ local function render(scene, width, height, filename) 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 @@ -51,11 +55,11 @@ local function render(scene, width, height, filename) print(y/math.floor(height/10) * 10 .. "%") end - local rayy = cam_dir + up*((y/height - 0.5)*c.fov*aspect) + local rayy = cam_dir + up*((0.5 - y/height)*c.fov*aspect) for x=1,width do - local ray_dir = V.normalize(rayy + right*((x/width - 0.5)*c.fov)) - local col = march(c.location, ray_dir, scene.sdf, 0) + 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.min(col[1]*255, 255), math.min(col[2]*255, 255), math.min(col[3]*255, 255)} @@ -74,13 +78,14 @@ end local scene = { - sdf = O.union(P.make_sphere(V.new{0,0,0}, 1, - -- T.make_count_texture()), - T.make_phong({V.new{2,-1,2}}, {0,1,0}, - 1.0, 0.5, 100)), - P.make_sphere(V.new{1.5,0,1.0}, 0.5, - T.make_phong({V.new{2,-1,2}}, {0,0,1}, - 1.0, 0.5, 100))), + sdf = + O.union( + P.make_sphere(V.new{0,0,0}, 1, + T.make_phong_texture({V.new{2,-3,1}}, + T.make_solid_pigment({0,1,0}), + 0.2, 0.7, 1.0, 100)), + P.make_plane(V.new{0,0,-3.0}, V.new{0,0,1}, + T.make_flat_texture(T.make_checkered_pigment({0,0,1}, {1,1,1})))), camera = {location = V.new{0,-5,0}, point_at = V.new{0,0,0},