diff options
author | Nat Lasseter <user@4574.co.uk> | 2024-06-12 17:37:40 +0100 |
---|---|---|
committer | Nat Lasseter <user@4574.co.uk> | 2024-06-12 17:37:40 +0100 |
commit | df7c4a808d691e1d927047bb86aa9738780e694d (patch) | |
tree | da197b87ebf2c48fe27b9aabf2db1fff5786c7f4 /lib |
Initial Commit (Up to Chapter 7)
Diffstat (limited to 'lib')
-rw-r--r-- | lib/hittable.rb | 81 | ||||
-rw-r--r-- | lib/interval.rb | 20 | ||||
-rw-r--r-- | lib/ray.rb | 12 | ||||
-rw-r--r-- | lib/vec3.rb | 74 |
4 files changed, 187 insertions, 0 deletions
diff --git a/lib/hittable.rb b/lib/hittable.rb new file mode 100644 index 0000000..80254d1 --- /dev/null +++ b/lib/hittable.rb @@ -0,0 +1,81 @@ +class HitRecord + def initialize(point, t, ray, out_normal) + @point = point + @t = t + set_face_normal(ray, out_normal) + end + + attr_accessor :point, :normal, :t + + def set_face_normal(ray, out_normal) + @front_face = ray.direction.dot(out_normal) < 0 + @normal = @front_face ? out_normal : -out_normal + end +end + +class Hittable + def hit(ray, trange) + nil + end +end + +class Hittables < Hittable + def initialize + clear + end + + def clear + @objects = [] + end + + def <<(object) + @objects << object + end + + def hit(ray, trange) + rec = nil + closest = trange.max + + @objects.each do |object| + if trec = object.hit(ray, Interval.new(trange.min, closest)) + rec = trec + closest = trec.t + end + end + + rec + end +end + +class Sphere < Hittable + def initialize(ox, oy, oz, radius = 1) + @centre = Point.new(ox, oy, oz) + @radius = radius + end + + attr_reader :centre, :radius + + def hit(ray, trange) + oc = @centre - ray.origin + a = ray.direction.mag_sqr + h = ray.direction.dot(oc) + c = oc.mag_sqr - @radius ** 2 + disc = h ** 2 - a * c + + return nil if disc < 0 + + sqrtd = disc ** 0.5 + root = (h - sqrtd) / a + if !trange.surround?(root) + root = (h + sqrtd) / a + if !trange.surround?(root) + return nil + end + end + + t = root + p = ray.at(t) + o_n = (p - @centre) / @radius + HitRecord.new(p, t, ray, o_n) + end +end diff --git a/lib/interval.rb b/lib/interval.rb new file mode 100644 index 0000000..0aa787f --- /dev/null +++ b/lib/interval.rb @@ -0,0 +1,20 @@ +class Interval + def initialize(min = Float::INFINITY, max = -Float::INFINITY) + @min = min + @max = max + end + + attr_reader :min, :max + + def size + @max - @min + end + + def include?(x) + min <= x && x <= max + end + + def surround?(x) + min < x && x < max + end +end diff --git a/lib/ray.rb b/lib/ray.rb new file mode 100644 index 0000000..4d282cb --- /dev/null +++ b/lib/ray.rb @@ -0,0 +1,12 @@ +class Ray + def initialize(origin = Point.new, direction = Vec3.new) + @origin = origin + @direction = direction + end + + attr_reader :origin, :direction + + def at(time) + @origin + (@direction * time) + end +end diff --git a/lib/vec3.rb b/lib/vec3.rb new file mode 100644 index 0000000..6287813 --- /dev/null +++ b/lib/vec3.rb @@ -0,0 +1,74 @@ +class Vec3 + def initialize(x = 0, y = 0, z = 0) + @x = x.to_f + @y = y.to_f + @z = z.to_f + end + + attr_reader :x, :y, :z + + def +(oth) + self.class.new(@x + oth.x, @y + oth.y, @z + oth.z) + end + + def -@ + self.class.new(-@x, -@y, -@z) + end + + def -(oth) + self + -oth + end + + def *(oth) + if oth.is_a?(Vec3) + self.class.new(@x * oth.x, @y * oth.y, @z * oth.z) + else + self.class.new(@x * oth, @y * oth, @z * oth) + end + end + + def /(oth) + self * (1.0 / oth) + end + + def dot(oth) + v = self * oth + v.x + v.y + v.z + end + + def cross(oth) + self.class.new( + @y * oth.z - @z * oth.y, + @z * oth.x - @x * oth.z, + @x * oth.y - @y * oth.x + ) + end + + def mag_sqr + @x ** 2 + @y ** 2 + @z ** 2 + end + + def mag + mag_sqr ** 0.5 + end + + def unit + self / mag + end + + def to_s + "{#{@x}, #{@y}, #{@z}}" + end +end + +class Point < Vec3; end + +class Colour < Vec3 + def r; @x; end + def g; @y; end + def b; @z; end + + def to_ppm + "%3d %3d %3d" % [(r * 255.999).to_i, (g * 255.999).to_i, (b * 255.999).to_i] + end +end |