diff options
Diffstat (limited to 'lib/hittable.rb')
-rw-r--r-- | lib/hittable.rb | 81 |
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 |