Soft shadows.
authorTim Vaughan <timv@ughan.xyz>
Tue, 21 Feb 2023 11:37:09 +0000 (12:37 +0100)
committerTim Vaughan <timv@ughan.xyz>
Tue, 21 Feb 2023 11:37:09 +0000 (12:37 +0100)
Operations.lua
Primitives.lua
Textures.lua
Vector.lua
raymarch.lua

index d606d39..617916d 100644 (file)
@@ -13,8 +13,20 @@ local function union(s1, s2)
    end
 end
 
+local function diff(s1, s2)
+   return function (location)
+      local p1 = s1(location)
+      local p2 = s2(location)
+
+      return {dist = math.max(p1.dist, -p2.dist),
+              texture = p1.texture}
+   end
+end
+
+
 Operations = {
-   union = union
+   union = union,
+   diff = diff
 }
 
 return Operations
index aafa7cb..c13966b 100644 (file)
@@ -17,9 +17,18 @@ local function make_plane(centre, normal, texture)
    end
 end
 
+local function make_pipe(centre, radius, axis, texture)
+   return function(p)
+      return {dist = V.norm(V.cross(p-centre, axis)) - radius,
+              texture = texture}
+   end
+end
+
+
 Primitives = {
    make_sphere = make_sphere,
-   make_plane = make_plane
+   make_plane = make_plane,
+   make_pipe = make_pipe
 }
 
 return Primitives
index cee3dd9..1e919c7 100644 (file)
@@ -4,19 +4,37 @@ local V = Vector
 
 local function make_phong_texture(lights, pigment, amb, diff, spec, shiny)
    local normalize = V.normalize
-   return function (location, ray_dir, normal, count)
-      local colour = pigment(location[1],location[3])
+
+   local eps = 0.0001
+   local function light_visibility(location, light_dir, pos, dest, res, sdf)
+      local p = sdf(location)
+      if pos + p.dist > dest then
+         return res
+      elseif p.dist < eps then
+         return 0.0
+      else
+         return light_visibility(location + light_dir*p.dist, light_dir, pos+p.dist,
+                                 dest, math.min(res, 30*p.dist/(pos+p.dist)), sdf)
+      end
+   end
+   
+   return function (location, ray_dir, normal, count, sdf)
+      local colour = pigment(location[1],location[2])
       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 light_dist = V.norm(light-location)
+         local light_dir = (light-location)/light_dist
+
+         local vis = light_visibility(location + light_dir*eps, light_dir, eps, light_dist, 1, sdf)
+
          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]*Idiff*diff + Ispec*spec
-         thiscol[2] = thiscol[2] + colour[2]*Idiff*diff + Ispec*spec
-         thiscol[3] = thiscol[3] + colour[3]*Idiff*diff + Ispec*spec
+         thiscol[1] = thiscol[1] + vis*(colour[1]*Idiff*diff + Ispec*spec)
+         thiscol[2] = thiscol[2] + vis*(colour[2]*Idiff*diff + Ispec*spec)
+         thiscol[3] = thiscol[3] + vis*(colour[3]*Idiff*diff + Ispec*spec)
       end
 
       return thiscol
@@ -24,13 +42,13 @@ local function make_phong_texture(lights, pigment, amb, diff, spec, shiny)
 end
 
 local function make_count_texture(scale)
-   return function (location, ray_dir, normal, count)
+   return function (location, ray_dir, normal, count, sdf)
       return {count*scale, count*scale, count*scale}
    end
 end
 
 local function make_flat_texture(pigment)
-   return function (location, ray_dir, normal, count)
+   return function (location, ray_dir, normal, count, sdf)
       return pigment(location[1], location[2])
    end
 end
index 3f35b64..db39ce8 100644 (file)
@@ -101,7 +101,8 @@ Vector = {
    normalize = normalize,
    x = new{1,0,0},
    y = new{0,1,0},
-   z = new{0,0,1}
+   z = new{0,0,1},
+   origin = new{0,0,0}
 }
 
 return Vector
index 5bace40..594645e 100644 (file)
@@ -11,7 +11,7 @@ require "Textures"
 local T = Textures
 
 local eps = 0.01
-local max_dist2 = 20^2
+local max_dist2 = 30^2
 local bg_col = {0.1,0.1,0.1}
 
 local function calculate_normal(sdf, l)
@@ -28,7 +28,9 @@ local function march(start, location, ray_dir, sdf, count)
    
    local p = sdf(location)
    if p.dist < eps then
-      return p.texture(location, ray_dir, calculate_normal(sdf, location-(ray_dir*eps)), count)
+      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)
@@ -76,20 +78,32 @@ local function render(scene, width, height, filename)
 end
 
 
+local lights = {V.new{3,-3,1}}
 
 local scene = {
    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})))),
+         O.diff(
+            O.diff(
+               O.diff(
+                  P.make_sphere(V.new{0,0,0}, 1,
+                                T.make_phong_texture(lights,
+                                                     T.make_solid_pigment({0,1,0}),
+                                                     0.2, 0.7, 1.0, 100)),
+                  P.make_sphere(V.new{0,0,0}, 0.8)),
+               P.make_pipe(V.new{0,0,0}, 0.5, V.new{0,1,0})),
+            P.make_pipe(V.new{0,0,0}, 0.5, V.new{1,0,0})),
+
+         P.make_plane(V.new{0,0,-1.0}, V.new{0,0,1},
+                      T.make_phong_texture(lights,
+                         T.make_checkered_pigment({0.5,0,0.2}, {1,1,1}),
+                         0.2, 1.0, 0, 1))),
+                      -- T.make_flat_texture(T.make_checkered_pigment({0,0,1}, {1,1,1})))),
                                
-   camera = {location = V.new{0,-5,0},
+   camera = {location = V.new{2,-5,1},
              point_at = V.new{0,0,0},
-             right = V.new{1,0,0},
+             right = V.x,
              fov = 1}}
 
+-- render(scene, 320, 200, "test.ppm")
 render(scene, 640, 480, "test.ppm")