From 3549acb5dbd2f6fe43be6cce9828785ccead7e77 Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Fri, 14 Jun 2024 12:23:49 +0100 Subject: Finished! Good god performance nope. --- lib/camera.rb | 25 +++++++++++++++++++------ lib/hittable.rb | 4 ++-- lib/material.rb | 17 +++++++---------- lib/vec3.rb | 27 +++++++++++++++------------ 4 files changed, 43 insertions(+), 30 deletions(-) (limited to 'lib') diff --git a/lib/camera.rb b/lib/camera.rb index 365ebcd..3ddf769 100644 --- a/lib/camera.rb +++ b/lib/camera.rb @@ -3,7 +3,8 @@ class Camera aliasing: 10, depth: 10, vfov: 90.0, lookfrom: Point.new(0, 0, 0), lookat: Point.new(0, 0, -1), - vup: Vec3.new(0, 1, 0)) + vup: Vec3.new(0, 1, 0), + defocus_angle: 0, focus_dist: 10) @width = width @aliasing = aliasing @depth = depth @@ -14,13 +15,12 @@ class Camera # Camera @c_centre = lookfrom - c_focallength = (lookfrom - lookat).mag # Viewport theta = vfov * Maths::PI / 180 h = Maths.tan(theta / 2) - v_height = 2.0 * h * c_focallength - v_width = v_height * @width.to_f / @height + v_height = 2.0 * h * focus_dist + v_width = (v_height * @width) / @height # Camera basis vectors w = (lookfrom - lookat).unit @@ -36,8 +36,14 @@ class Camera # Upper left pixel v_upperleft = @c_centre - v_u / 2 - v_v / 2 - - w * c_focallength + w * focus_dist @p00_loc = v_upperleft + (@pd_u + @pd_v) / 2 + + @defocus_angle = defocus_angle + defocus_radius = focus_dist * Maths.tan((@defocus_angle / 2) * + Maths::PI / 180) + @defocus_disk_u = u * defocus_radius + @defocus_disk_v = v * defocus_radius end def render(world) @@ -70,6 +76,13 @@ class Camera @pd_u * (col + rand - 0.5) + @pd_v * (row + rand - 0.5) - Ray.new(@c_centre, p_sample - @c_centre) + ray_origin = @defocus_angle <= 0 ? @c_centre : defocus_disk_sample + ray_dir = p_sample - ray_origin + Ray.new(ray_origin, ray_dir) + end + + def defocus_disk_sample + p = Vec3.random_in_unit(dimensions: 2) + @c_centre + @defocus_disk_u * p.x + @defocus_disk_v * p.y end end diff --git a/lib/hittable.rb b/lib/hittable.rb index 2a433d8..1e22613 100644 --- a/lib/hittable.rb +++ b/lib/hittable.rb @@ -45,8 +45,8 @@ class Hittables < Hittable end class Sphere < Hittable - def initialize(ox, oy, oz, radius = 1, material) - @centre = Point.new(ox, oy, oz) + def initialize(centre, radius = 1, material) + @centre = centre @radius = radius @material = material end diff --git a/lib/material.rb b/lib/material.rb index 752b85f..14eb5f9 100644 --- a/lib/material.rb +++ b/lib/material.rb @@ -1,6 +1,6 @@ class Material - def initialize(r, g, b) - @albedo = Colour.new(r, g, b) + def initialize(albedo) + @albedo = albedo end def attenuation @@ -14,21 +14,21 @@ end class Lambertian < Material def scatter(ray, record) - scat = record.normal + Vec3.random_unit + scat = record.normal + Vec3.random_in_unit scat = rec.normal if scat.near_zero? Ray.new(record.point, scat) end end class Metal < Material - def initialize(r, g, b, fuzz) + def initialize(albedo, fuzz) @fuzz = fuzz < 1 ? fuzz : 1 - super(r, g, b) + super(albedo) end def scatter(ray, record) refl = ray.direction.reflect(record.normal) - refl = refl.unit + (Vec3.random_unit * @fuzz) + refl = refl.unit + (Vec3.random_in_unit * @fuzz) if refl.dot(record.normal) > 0 Ray.new(record.point, refl) else @@ -40,10 +40,7 @@ end class Dielectric < Material def initialize(ref_index) @ref_index = ref_index - end - - def attenuation - Colour.new(1.0, 1.0, 1.0) + super(Colour.new(1.0, 1.0, 1.0)) end def scatter(ray, record) diff --git a/lib/vec3.rb b/lib/vec3.rb index 478fe4c..46d8ee1 100644 --- a/lib/vec3.rb +++ b/lib/vec3.rb @@ -1,8 +1,8 @@ class Vec3 - def initialize(x = 0, y = 0, z = 0) - @x = x.to_f - @y = y.to_f - @z = z.to_f + def initialize(x = 0.0, y = 0.0, z = 0.0) + @x = x + @y = y + @z = z end attr_reader :x, :y, :z @@ -67,7 +67,7 @@ class Vec3 rout_perp + rout_parr end - def in_unit_sphere? + def in_unit? mag_sqr < 1 end @@ -79,18 +79,21 @@ class Vec3 "{#{@x}, #{@y}, #{@z}}" end - def self.random(min = 0, max = 1) - interval = Interval.new(min.to_f, max.to_f) - Vec3.new(interval.sample, interval.sample, interval.sample) + def self.random(min: -1.0, max: 1.0, dimensions: 3) + interval = Interval.new(min, max) + self.new( + dimensions >= 1 ? interval.sample : 0, + dimensions >= 2 ? interval.sample : 0, + dimensions >= 3 ? interval.sample : 0 + ) end - def self.random_unit(normal = nil) + def self.random_in_unit(normal: nil, dimensions: 3) p = nil while p.nil? - c = Vec3.random - p = c.unit if c.in_unit_sphere? + c = self.random(dimensions: dimensions) + p = c.unit if c.in_unit? end - normal.nil? || p.dot(normal) > 0 ? p : -p end end -- cgit v1.2.1