From 69e0357135a49157ed7a8c2e57c331e960422e15 Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Thu, 13 Jun 2024 14:38:50 +0100 Subject: Chapter 11 --- lib/hittable.rb | 6 +++--- lib/material.rb | 31 +++++++++++++++++++++++++++++++ lib/vec3.rb | 7 +++++++ rtiaw | 12 +++++++----- 4 files changed, 48 insertions(+), 8 deletions(-) diff --git a/lib/hittable.rb b/lib/hittable.rb index facdd14..2a433d8 100644 --- a/lib/hittable.rb +++ b/lib/hittable.rb @@ -2,12 +2,12 @@ class HitRecord def initialize(point, t, ray, out_normal, material) @point = point @t = t - front_face = ray.direction.dot(out_normal) < 0 - @normal = front_face ? out_normal : -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 + attr_reader :point, :normal, :t, :front_face, :material end class Hittable diff --git a/lib/material.rb b/lib/material.rb index c935147..752b85f 100644 --- a/lib/material.rb +++ b/lib/material.rb @@ -36,3 +36,34 @@ class Metal < Material end end end + +class Dielectric < Material + def initialize(ref_index) + @ref_index = ref_index + end + + def attenuation + Colour.new(1.0, 1.0, 1.0) + end + + def scatter(ray, record) + ri = record.front_face ? (1.0 / @ref_index) : @ref_index + unit_dir = ray.direction.unit + costheta = [(-unit_dir).dot(record.normal), 1.0].min + sintheta = (1.0 - costheta ** 2) ** 0.5 + + cannot_refract = ri * sintheta > 1.0 + maybe_reflect_anyway = Dielectric.reflectance(costheta, ri) > rand + + refr = cannot_refract || maybe_reflect_anyway ? + unit_dir.reflect(record.normal) : + unit_dir.refract(record.normal, ri) + + Ray.new(record.point, refr) + end + + def self.reflectance(costheta, ri) + r0 = ((1.0 - ri) / (1.0 + ri)) ** 2 + r0 + (1.0 - r0) * (1.0 - costheta) ** 5 + end +end diff --git a/lib/vec3.rb b/lib/vec3.rb index 3069b65..478fe4c 100644 --- a/lib/vec3.rb +++ b/lib/vec3.rb @@ -60,6 +60,13 @@ class Vec3 self - normal * dot(normal) * 2 end + def refract(normal, etaratio) + costheta = [(-self).dot(normal), 1.0].min + rout_perp = (self + normal * costheta) * etaratio + rout_parr = normal * -((1.0 - rout_perp.mag_sqr).abs ** 0.5) + rout_perp + rout_parr + end + def in_unit_sphere? mag_sqr < 1 end diff --git a/rtiaw b/rtiaw index 71b4fbd..45487b9 100755 --- a/rtiaw +++ b/rtiaw @@ -10,14 +10,16 @@ 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_left = Dielectric.new(1.5) +mat_bubble = Dielectric.new(1 / 1.5) mat_right = Metal.new(0.8, 0.6, 0.2, 1.0) world = Hittables.new -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) +world << Sphere.new( 0, -100.5, -1, 100, mat_ground) +world << Sphere.new( 0, 0, -1.2, 0.5, mat_centre) +world << Sphere.new(-1, 0, -1, 0.5, mat_left) +world << Sphere.new(-1, 0, -1, 0.4, mat_bubble) +world << Sphere.new( 1, 0, -1, 0.5, mat_right) camera = Camera.new(400, 16.0 / 9) camera.render(world) -- cgit v1.2.1