1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
use std::io::{stderr, Write};

use hittables::HittableList;
use math::{Color, Ray};
use rand::{rngs::ThreadRng, thread_rng, Rng};

use crate::{camera::Camera, utils::write_color};

pub mod camera;
pub mod hittables;
pub mod materials;
pub mod math;
pub mod utils;

fn ray_color(ray: Ray, world: &HittableList, rng: &mut ThreadRng, depth: usize) -> Color {
    if depth <= 0 {
        return Color::new(0.0, 0.0, 0.0);
    }

    if let Some(rec) = world.hit(ray, 0.001, f64::INFINITY) {
        return if let Some((attenuation, scattered)) = rec.material.scatter(ray, rec, rng) {
            attenuation * ray_color(scattered, world, rng, depth - 1)
        } else {
            Color::new(0.0, 0.0, 0.0)
        };
    }

    let unit_direction = ray.direction.unit_vector();
    let t = 0.5 * (unit_direction.y() + 1.0);

    (1.0 - t) * Color::new(1.0, 1.0, 1.0) + Color::new(0.5, 0.7, 1.0) * t
}

pub fn run(image_width: i32, samples_per_pixel: i32, max_depth: usize, world: &HittableList) {
    // Image
    let aspect_ratio = 16.0 / 9.0;
    let image_height = (image_width as f64 / aspect_ratio) as i32;

    // Camera
    let camera = Camera::new();

    // Render
    println!("P3\n{image_width} {image_height}\n255");

    let mut rng = thread_rng();

    for j in (0..image_height).rev() {
        eprint!("\rScanlines remaining: {j}  ");
        stderr().flush().unwrap_or_default();

        for i in 0..image_width {
            let mut pixel_color = Color::new(0.0, 0.0, 0.0);

            for _ in 0..samples_per_pixel {
                let u = (i as f64 + rng.gen::<f64>()) / (image_width - 1) as f64;
                let v = (j as f64 + rng.gen::<f64>()) / (image_height - 1) as f64;

                pixel_color += ray_color(camera.ray(u, v), &world, &mut rng, max_depth);
            }

            println!("{}", write_color(pixel_color, samples_per_pixel));
        }
    }

    eprintln!("\nDone");
}