use crate::camera::Camera; use crate::hittable::Hittable; use crate::ray::Ray; use crate::scene::Scene; use crate::utils::print_colour; use cgmath::prelude::*; use cgmath::{vec3, Vector3}; use rand::prelude::*; use rayon::prelude::*; use std::f64::INFINITY; fn ray_colour(rng: &mut ThreadRng, ray: &Ray, scene: &Scene, depth: i32) -> Vector3 { if depth <= 0 { return vec3(0.0, 0.0, 0.0); } if let Some(t) = scene.is_hit(ray, 0.001, INFINITY) { if let Some(nray) = t.material.scatter(rng, ray, &t) { ray_colour(rng, &nray.ray, scene, depth - 1).mul_element_wise(nray.attenuation) } else { vec3(0.0, 0.0, 0.0) } } else { let t = 0.5 * (ray.dir.normalize().y + 1.0); (1.0 - t) * vec3(1.0, 1.0, 1.0) + t * vec3(0.5, 0.7, 1.0) } } pub fn thread_render( world: &Scene, camera: &Camera, rng: &mut ThreadRng, image_height: i32, image_width: i32, samples: i32, max_depth: i32, i: i32, j: i32, ) -> Vector3 { let mut colour = vec3(0.0, 0.0, 0.0); for _ in 0..samples { let ru: f64 = rng.gen(); let rv: f64 = rng.gen(); let u = (i as f64 + ru) / image_width as f64; let v = ((image_height - 1 - j) as f64 + rv) / image_height as f64; let ray = camera.get_ray(rng, u, v); colour += ray_colour(rng, &ray, &world, max_depth); } colour } pub fn render( world: &Scene, camera: &Camera, image_height: i32, image_width: i32, samples: i32, max_depth: i32, threads: i32, ) { for j in 0..image_height { eprint!("\rScanlines: {:3} / {:3}", j + 1, image_height); for i in 0..image_width { let colours = (0..threads).into_par_iter().map(|_| { let mut trng = thread_rng(); thread_render( world, camera, &mut trng, image_height, image_width, samples / threads, max_depth, i, j, ) }); let colour = colours.sum::>(); print_colour(&colour, samples); } } }