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) {
let aspect_ratio = 16.0 / 9.0;
let image_height = (image_width as f64 / aspect_ratio) as i32;
let camera = Camera::new();
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");
}