aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/camera.rb25
-rw-r--r--lib/hittable.rb4
-rw-r--r--lib/material.rb17
-rw-r--r--lib/vec3.rb27
-rwxr-xr-xrtiaw65
5 files changed, 96 insertions, 42 deletions
diff --git a/lib/camera.rb b/lib/camera.rb
index 365ebcd..3ddf769 100644
--- a/lib/camera.rb
+++ b/lib/camera.rb
@@ -3,7 +3,8 @@ class Camera
aliasing: 10, depth: 10, vfov: 90.0,
lookfrom: Point.new(0, 0, 0),
lookat: Point.new(0, 0, -1),
- vup: Vec3.new(0, 1, 0))
+ vup: Vec3.new(0, 1, 0),
+ defocus_angle: 0, focus_dist: 10)
@width = width
@aliasing = aliasing
@depth = depth
@@ -14,13 +15,12 @@ class Camera
# Camera
@c_centre = lookfrom
- c_focallength = (lookfrom - lookat).mag
# Viewport
theta = vfov * Maths::PI / 180
h = Maths.tan(theta / 2)
- v_height = 2.0 * h * c_focallength
- v_width = v_height * @width.to_f / @height
+ v_height = 2.0 * h * focus_dist
+ v_width = (v_height * @width) / @height
# Camera basis vectors
w = (lookfrom - lookat).unit
@@ -36,8 +36,14 @@ class Camera
# Upper left pixel
v_upperleft = @c_centre - v_u / 2 - v_v / 2 -
- w * c_focallength
+ w * focus_dist
@p00_loc = v_upperleft + (@pd_u + @pd_v) / 2
+
+ @defocus_angle = defocus_angle
+ defocus_radius = focus_dist * Maths.tan((@defocus_angle / 2) *
+ Maths::PI / 180)
+ @defocus_disk_u = u * defocus_radius
+ @defocus_disk_v = v * defocus_radius
end
def render(world)
@@ -70,6 +76,13 @@ class Camera
@pd_u * (col + rand - 0.5) +
@pd_v * (row + rand - 0.5)
- Ray.new(@c_centre, p_sample - @c_centre)
+ ray_origin = @defocus_angle <= 0 ? @c_centre : defocus_disk_sample
+ ray_dir = p_sample - ray_origin
+ Ray.new(ray_origin, ray_dir)
+ end
+
+ def defocus_disk_sample
+ p = Vec3.random_in_unit(dimensions: 2)
+ @c_centre + @defocus_disk_u * p.x + @defocus_disk_v * p.y
end
end
diff --git a/lib/hittable.rb b/lib/hittable.rb
index 2a433d8..1e22613 100644
--- a/lib/hittable.rb
+++ b/lib/hittable.rb
@@ -45,8 +45,8 @@ class Hittables < Hittable
end
class Sphere < Hittable
- def initialize(ox, oy, oz, radius = 1, material)
- @centre = Point.new(ox, oy, oz)
+ def initialize(centre, radius = 1, material)
+ @centre = centre
@radius = radius
@material = material
end
diff --git a/lib/material.rb b/lib/material.rb
index 752b85f..14eb5f9 100644
--- a/lib/material.rb
+++ b/lib/material.rb
@@ -1,6 +1,6 @@
class Material
- def initialize(r, g, b)
- @albedo = Colour.new(r, g, b)
+ def initialize(albedo)
+ @albedo = albedo
end
def attenuation
@@ -14,21 +14,21 @@ end
class Lambertian < Material
def scatter(ray, record)
- scat = record.normal + Vec3.random_unit
+ scat = record.normal + Vec3.random_in_unit
scat = rec.normal if scat.near_zero?
Ray.new(record.point, scat)
end
end
class Metal < Material
- def initialize(r, g, b, fuzz)
+ def initialize(albedo, fuzz)
@fuzz = fuzz < 1 ? fuzz : 1
- super(r, g, b)
+ super(albedo)
end
def scatter(ray, record)
refl = ray.direction.reflect(record.normal)
- refl = refl.unit + (Vec3.random_unit * @fuzz)
+ refl = refl.unit + (Vec3.random_in_unit * @fuzz)
if refl.dot(record.normal) > 0
Ray.new(record.point, refl)
else
@@ -40,10 +40,7 @@ end
class Dielectric < Material
def initialize(ref_index)
@ref_index = ref_index
- end
-
- def attenuation
- Colour.new(1.0, 1.0, 1.0)
+ super(Colour.new(1.0, 1.0, 1.0))
end
def scatter(ray, record)
diff --git a/lib/vec3.rb b/lib/vec3.rb
index 478fe4c..46d8ee1 100644
--- a/lib/vec3.rb
+++ b/lib/vec3.rb
@@ -1,8 +1,8 @@
class Vec3
- def initialize(x = 0, y = 0, z = 0)
- @x = x.to_f
- @y = y.to_f
- @z = z.to_f
+ def initialize(x = 0.0, y = 0.0, z = 0.0)
+ @x = x
+ @y = y
+ @z = z
end
attr_reader :x, :y, :z
@@ -67,7 +67,7 @@ class Vec3
rout_perp + rout_parr
end
- def in_unit_sphere?
+ def in_unit?
mag_sqr < 1
end
@@ -79,18 +79,21 @@ class Vec3
"{#{@x}, #{@y}, #{@z}}"
end
- def self.random(min = 0, max = 1)
- interval = Interval.new(min.to_f, max.to_f)
- Vec3.new(interval.sample, interval.sample, interval.sample)
+ def self.random(min: -1.0, max: 1.0, dimensions: 3)
+ interval = Interval.new(min, max)
+ self.new(
+ dimensions >= 1 ? interval.sample : 0,
+ dimensions >= 2 ? interval.sample : 0,
+ dimensions >= 3 ? interval.sample : 0
+ )
end
- def self.random_unit(normal = nil)
+ def self.random_in_unit(normal: nil, dimensions: 3)
p = nil
while p.nil?
- c = Vec3.random
- p = c.unit if c.in_unit_sphere?
+ c = self.random(dimensions: dimensions)
+ p = c.unit if c.in_unit?
end
-
normal.nil? || p.dot(normal) > 0 ? p : -p
end
end
diff --git a/rtiaw b/rtiaw
index 234aa85..f8a6700 100755
--- a/rtiaw
+++ b/rtiaw
@@ -10,20 +10,61 @@ 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 = Dielectric.new(1.5)
-mat_bubble = Dielectric.new(1 / 1.5)
-mat_right = Metal.new(0.8, 0.6, 0.2, 1.0)
+#mat_ground = Lambertian.new(Colour.new(0.8, 0.8, 0.0))
+#mat_centre = Lambertian.new(Colour.new(0.1, 0.2, 0.5))
+#mat_left = Dielectric.new(1.5)
+#mat_bubble = Dielectric.new(1 / 1.5)
+#mat_right = Metal.new(Colour.new(0.8, 0.6, 0.2), 1.0)
+#
+#world = Hittables.new
+#world << Sphere.new(Point.new( 0, -100.5, -1), 100, mat_ground)
+#world << Sphere.new(Point.new( 0, 0, -1.2), 0.5, mat_centre)
+#world << Sphere.new(Point.new(-1, 0, -1), 0.5, mat_left)
+#world << Sphere.new(Point.new(-1, 0, -1), 0.4, mat_bubble)
+#world << Sphere.new(Point.new( 1, 0, -1), 0.5, mat_right)
+#
+#camera = Camera.new(400, 16.0 / 9,
+# vfov: 20,
+# lookfrom: Point.new(-2,2,1),
+# defocus_angle: 10, focus_dist: 3.4)
+#camera.render(world)
world = Hittables.new
-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)
+
+mat_ground = Lambertian.new(Colour.new(0.5, 0.5, 0.5))
+world << Sphere.new(Point.new(0.0, -1000.0, 0.0), 1000.0, mat_ground)
+
+(-4...4).each do |a|
+ (-4...4).each do |b|
+ centre = Point.new(a + 0.9 * rand, 0.2, b + 0.9 * rand)
+ if (centre - Point.new(4, 0.2, 0)).mag > 0.9
+ s_mat = case rand
+ when 0...0.8
+ albedo = Colour.random * Colour.random
+ Lambertian.new(albedo)
+ when 0.8...0.95
+ albedo = Colour.random(min: 0.5, max: 1)
+ fuzz = rand(0...0.5)
+ Metal.new(albedo, fuzz)
+ else
+ Dielectric.new(1.5)
+ end
+ world << Sphere.new(centre, 0.2, s_mat)
+ end
+ end
+end
+
+mat1 = Dielectric.new(1.5)
+world << Sphere.new(Point.new(0.0, 1.0, 0.0), 1.0, mat1)
+mat2 = Lambertian.new(Colour.new(0.4, 0.2, 0.1))
+world << Sphere.new(Point.new(-4.0, 1.0, 0.0), 1.0, mat2)
+mat3 = Metal.new(Colour.new(0.7, 0.6, 0.5), 0.0)
+world << Sphere.new(Point.new(4.0, 1.0, 0.0), 1.0, mat3)
camera = Camera.new(400, 16.0 / 9,
- vfov: 20,
- lookfrom: Point.new(-2,2,1))
+ aliasing: 4, depth: 4,
+ vfov: 20.0,
+ lookfrom: Point.new(13.0, 2.0, 3.0),
+ lookat: Point.new,
+ defocus_angle: 0.6, focus_dist: 10.0)
camera.render(world)