From 51416f5dfac39b6ede8340b57b9bed4eb3edc646 Mon Sep 17 00:00:00 2001 From: Nat Lasseter Date: Wed, 12 Jun 2024 21:26:19 +0100 Subject: Done Chapter 8 --- Makefile | 2 +- lib/camera.rb | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/interval.rb | 14 ++++++++++-- lib/ray.rb | 10 +++++++++ lib/vec3.rb | 7 +++++- rtiaw | 70 +++------------------------------------------------------ 6 files changed, 95 insertions(+), 71 deletions(-) create mode 100644 lib/camera.rb diff --git a/Makefile b/Makefile index 3536afe..fd3f4e6 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ out.ppm: rtiaw $(wildcard lib/*.rb) .phony: view clean view: out.ppm - eog out.ppm & + xv out.ppm clean: rm -f out.ppm diff --git a/lib/camera.rb b/lib/camera.rb new file mode 100644 index 0000000..f8721bb --- /dev/null +++ b/lib/camera.rb @@ -0,0 +1,63 @@ +class Camera + def initialize(width = 100, aspect = 1.0, aliasing = 10) + @width = width + @aspect = aspect + @aliasing = aliasing + + # Height + @height = (@width / @aspect).to_i + @height = @height < 1 ? 1 : @height + + # Camera + c_focallength = 1.0 + @c_centre = Point.new(0,0,0) + + # Viewport + @v_height = 2.0 + @v_width = @v_height * @width.to_f / @height + v_u = Vec3.new(@v_width, 0, 0) + v_v = Vec3.new(0, -@v_height, 0) + + # Pixel deltas + @pd_u = v_u / @width + @pd_v = v_v / @height + + # Upper left pixel + v_upperleft = @c_centre - v_u / 2 - v_v / 2 - + Vec3.new(0, 0, c_focallength) + @p00_loc = v_upperleft + (@pd_u + @pd_v) / 2 + end + + def render(world) + start = Time.now.to_f + + puts "P3" + puts "#{@width} #{@height}" + puts "255" + + @height.times do |row| + $stderr.print "Scanlines remaining: %4d" % (@height - row) + @width.times do |col| + p_colour = Colour.new + @aliasing.times do + p_colour += get_ray(row, col).colour(world) + end + puts (p_colour / @aliasing).to_ppm + end + puts + end + + finish = Time.now.to_f + took = finish - start + + $stderr.puts "Done. Took %4.2f seconds. " % took + end + + def get_ray(row, col) + p_sample = @p00_loc + + @pd_u * (col + rand - 0.5) + + @pd_v * (row + rand - 0.5) + + Ray.new(@c_centre, p_sample - @c_centre) + end +end diff --git a/lib/interval.rb b/lib/interval.rb index 0aa787f..3ba7e09 100644 --- a/lib/interval.rb +++ b/lib/interval.rb @@ -1,7 +1,7 @@ class Interval def initialize(min = Float::INFINITY, max = -Float::INFINITY) - @min = min - @max = max + @min = min.to_f + @max = max.to_f end attr_reader :min, :max @@ -17,4 +17,14 @@ class Interval def surround?(x) min < x && x < max end + + def sample + rand(@min...@max) + end + + def clamp(x) + return @min if x < @min + return @max if x > @max + x + end end diff --git a/lib/ray.rb b/lib/ray.rb index 4d282cb..47f2b26 100644 --- a/lib/ray.rb +++ b/lib/ray.rb @@ -9,4 +9,14 @@ class Ray def at(time) @origin + (@direction * time) end + + def colour(world) + if rec = world.hit(self, Interval.new(0, Float::INFINITY)) + return (Colour.new(1,1,1) + rec.normal) * 0.5 + end + + unit_dir = direction.unit + a = (unit_dir.y + 1) / 2 + Colour.new(1.0, 1.0, 1.0) * (1-a) + Colour.new(0.5, 0.7, 1.0) * a + end end diff --git a/lib/vec3.rb b/lib/vec3.rb index 6287813..b9a5a73 100644 --- a/lib/vec3.rb +++ b/lib/vec3.rb @@ -69,6 +69,11 @@ class Colour < Vec3 def b; @z; end def to_ppm - "%3d %3d %3d" % [(r * 255.999).to_i, (g * 255.999).to_i, (b * 255.999).to_i] + intensity = Interval.new(0.0, 0.999) + "%3d %3d %3d" % [ + (intensity.clamp(r) * 256).to_i, + (intensity.clamp(g) * 256).to_i, + (intensity.clamp(b) * 256).to_i + ] end end diff --git a/rtiaw b/rtiaw index 5fb7e5a..82e4303 100755 --- a/rtiaw +++ b/rtiaw @@ -1,79 +1,15 @@ #!/usr/bin/env ruby -start = Time.now.to_f - $LOAD_PATH.unshift File.dirname(__FILE__) + '/lib' require 'interval' require 'vec3' require 'ray' require 'hittable' +require 'camera' -def ray_colour(ray, world) - if rec = world.hit(ray, Interval.new(0, Float::INFINITY)) - return (Colour.new(1,1,1) + rec.normal) * 0.5 - end - - unit_dir = ray.direction.unit - a = (unit_dir.y + 1) / 2 - Colour.new(1.0, 1.0, 1.0) * (1-a) + Colour.new(0.5, 0.7, 1.0) * a -end - -# Define image -width = 400 -aspect = 16.0 / 9 -max = 255 - -# Calculate height -height = (width / aspect).to_i -height = height < 1 ? 1 : height - -# World world = Hittables.new world << Sphere.new(0,0,-1, 0.5) world << Sphere.new(0,-100.5,-1, 100) -# Camera -c_focallength = 1.0 -c_centre = Point.new(0,0,0) - -# Viewport -v_height = 2.0 -v_width = v_height * width.to_f / height -v_u = Vec3.new(v_width, 0, 0) -v_v = Vec3.new(0, -v_height, 0) - -# Pixel deltas -pd_u = v_u / width -pd_v = v_v / height - -# Upper left pixel -v_upperleft = c_centre - - Vec3.new(0, 0, c_focallength) - - v_u / 2 - - v_v / 2 -p_00loc = v_upperleft + - (pd_u + pd_v) / 2 - -puts <<-EOH -P3 -#{width} #{height} -#{max} -EOH - -height.times do |row| - $stderr.print "Scanlines remaining: %4d" % (height - row) - width.times do |col| - p_centre = p_00loc + (pd_u * col) + (pd_v * row) - ray_dir = p_centre - c_centre - ray = Ray.new(c_centre, ray_dir) - - p_colour = ray_colour(ray, world) - puts p_colour.to_ppm - end - puts -end - -finish = Time.now.to_f -took = finish - start - -$stderr.puts "Done. Took %4.2f seconds. " % took +camera = Camera.new(400, 16.0 / 9) +camera.render(world) -- cgit v1.2.1