From b976ce98be7275bf92e01f6c86bf2b694530481e Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Thu, 13 Jun 2024 13:24:53 +0100 Subject: Done C10, Materials --- lib/hittable.rb | 16 +++++++--------- lib/material.rb | 38 ++++++++++++++++++++++++++++++++++++++ lib/ray.rb | 7 +++++-- lib/vec3.rb | 8 ++++++++ rtiaw | 14 +++++++++++--- 5 files changed, 69 insertions(+), 14 deletions(-) create mode 100644 lib/material.rb 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 diff --git a/rtiaw b/rtiaw index 82e4303..71b4fbd 100755 --- a/rtiaw +++ b/rtiaw @@ -1,15 +1,23 @@ #!/usr/bin/env ruby -$LOAD_PATH.unshift File.dirname(__FILE__) + '/lib' +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib')) require 'interval' require 'vec3' require 'ray' require 'hittable' require 'camera' +require 'material' + +mat_ground = Lambertian.new(0.8, 0.8, 0.0) +mat_centre = Lambertian.new(0.1, 0.2, 0.5) +mat_left = Metal.new(0.8, 0.8, 0.8, 0.3) +mat_right = Metal.new(0.8, 0.6, 0.2, 1.0) world = Hittables.new -world << Sphere.new(0,0,-1, 0.5) -world << Sphere.new(0,-100.5,-1, 100) +world << Sphere.new(0, -100.5, -1, 100, mat_ground) +world << Sphere.new(0, 0, -1, 0.5, mat_centre) +world << Sphere.new(-1, 0, -1, 0.5, mat_left) +world << Sphere.new(1, 0, -1, 0.5, mat_right) camera = Camera.new(400, 16.0 / 9) camera.render(world) -- cgit v1.2.1