aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNat Lasseter <user@4574.co.uk>2024-06-13 13:24:53 +0100
committerNat Lasseter <user@4574.co.uk>2024-06-13 13:24:53 +0100
commitb976ce98be7275bf92e01f6c86bf2b694530481e (patch)
tree0b405ce8ecb2ef3d01e17c2bf089adc7788d084e /lib
parent523f9439ed0d2e4c2d51edc623b6c4d62885cfdd (diff)
Done C10, Materials
Diffstat (limited to 'lib')
-rw-r--r--lib/hittable.rb16
-rw-r--r--lib/material.rb38
-rw-r--r--lib/ray.rb7
-rw-r--r--lib/vec3.rb8
4 files changed, 58 insertions, 11 deletions
diff --git a/lib/hittable.rb b/lib/hittable.rb
index c84764f..facdd14 100644
--- a/lib/hittable.rb
+++ b/lib/hittable.rb
@@ -1,16 +1,13 @@
class HitRecord
- def initialize(point, t, ray, out_normal)
+ def initialize(point, t, ray, out_normal, material)
@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
+ @material = material
end
+
+ attr_accessor :point, :normal, :t, :material
end
class Hittable
@@ -48,9 +45,10 @@ class Hittables < Hittable
end
class Sphere < Hittable
- def initialize(ox, oy, oz, radius = 1)
+ def initialize(ox, oy, oz, radius = 1, material)
@centre = Point.new(ox, oy, oz)
@radius = radius
+ @material = material
end
attr_reader :centre, :radius
@@ -76,6 +74,6 @@ class Sphere < Hittable
t = root
p = ray.at(t)
o_n = (p - @centre) / @radius
- HitRecord.new(p, t, ray, o_n)
+ HitRecord.new(p, t, ray, o_n, @material)
end
end
diff --git a/lib/material.rb b/lib/material.rb
new file mode 100644
index 0000000..c935147
--- /dev/null
+++ b/lib/material.rb
@@ -0,0 +1,38 @@
+class Material
+ def initialize(r, g, b)
+ @albedo = Colour.new(r, g, b)
+ end
+
+ def attenuation
+ @albedo
+ end
+
+ def scatter(ray, record)
+ nil
+ end
+end
+
+class Lambertian < Material
+ def scatter(ray, record)
+ scat = record.normal + Vec3.random_unit
+ scat = rec.normal if scat.near_zero?
+ Ray.new(record.point, scat)
+ end
+end
+
+class Metal < Material
+ def initialize(r, g, b, fuzz)
+ @fuzz = fuzz < 1 ? fuzz : 1
+ super(r, g, b)
+ end
+
+ def scatter(ray, record)
+ refl = ray.direction.reflect(record.normal)
+ refl = refl.unit + (Vec3.random_unit * @fuzz)
+ if refl.dot(record.normal) > 0
+ Ray.new(record.point, refl)
+ else
+ nil
+ end
+ end
+end
diff --git a/lib/ray.rb b/lib/ray.rb
index eb6038c..1285158 100644
--- a/lib/ray.rb
+++ b/lib/ray.rb
@@ -14,8 +14,11 @@ class Ray
return Colour.new if depth <= 0
if rec = world.hit(self, Interval.new(0.0001, Float::INFINITY))
- dir = rec.normal + Vec3.random_unit
- return Ray.new(rec.point, dir).colour(world, depth - 1) * 0.5
+ if scat = rec.material.scatter(self, rec)
+ return scat.colour(world, depth - 1) * rec.material.attenuation
+ else
+ return Colour.new(0,0,0)
+ end
end
unit_dir = direction.unit
diff --git a/lib/vec3.rb b/lib/vec3.rb
index 08cbf69..3069b65 100644
--- a/lib/vec3.rb
+++ b/lib/vec3.rb
@@ -56,10 +56,18 @@ class Vec3
self / mag
end
+ def reflect(normal)
+ self - normal * dot(normal) * 2
+ end
+
def in_unit_sphere?
mag_sqr < 1
end
+ def near_zero?(s = 1e-8)
+ @x.abs < s && @y.abs < s && @z.abs < s
+ end
+
def to_s
"{#{@x}, #{@y}, #{@z}}"
end