aboutsummaryrefslogtreecommitdiff
path: root/lib/hittable.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hittable.rb')
-rw-r--r--lib/hittable.rb81
1 files changed, 81 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