class Camera def initialize(width = 100, aspect = 1.0, 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)) @width = width @aliasing = aliasing @depth = depth # Height @height = (@width / aspect).to_i @height = @height < 1 ? 1 : @height # 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 # Camera basis vectors w = (lookfrom - lookat).unit u = vup.cross(w).unit v = w.cross(u) v_u = u * v_width v_v = -v * v_height # 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 - w * 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, @depth) 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