use crate::{
Vec2, Float, TracerCli,
samplers::JitteredSampler, ToneMap
};
use crate::tracer::{
Camera, Film, FilmSample,
Integrator, Scene, Filter, FilmTile
};
use glam::IVec2;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use std::{sync::Mutex, time::Instant};
type PxSampler = JitteredSampler;
const TILE_SIZE: i32 = 16;
const SAMPLES_INCREMENT: i32 = 256;
pub struct Renderer {
scene: Scene,
camera: Camera,
resolution: IVec2,
num_samples: i32,
integrator: Integrator,
tone_map: ToneMap,
filter: Filter,
}
impl Renderer {
pub fn new(scene: Scene, camera: Camera) -> Self {
assert!(scene.num_lights() != 0);
let cli_args: TracerCli = argh::from_env();
cli_args.set_threads();
let resolution = camera.get_resolution();
Self {
scene,
camera,
resolution,
filter: Filter::Box,
num_samples: cli_args.samples,
integrator: cli_args.get_integrator(),
tone_map: ToneMap::NoMap,
}
}
pub fn set_tone_map(&mut self, tone_map: ToneMap) {
self.tone_map = tone_map;
}
pub fn set_filter(&mut self, filter: Filter) {
self.filter = filter;
}
pub fn set_samples(&mut self, samples: i32) {
self.num_samples = samples;
}
pub fn set_integrator(&mut self, integrator: Integrator) {
self.integrator = integrator;
}
pub fn render(&self) -> Film {
println!(
"Rendering scene as a {} x {} image \
with {} thread(s) and {} sample(s) per pixel using {}",
self.resolution.x,
self.resolution.y,
rayon::current_num_threads(),
self.num_samples,
self.integrator,
);
let start = Instant::now();
let mut film = Film::new(
self.resolution.x,
self.resolution.y,
self.num_samples,
);
let mutex = Mutex::new(&mut film);
let tiles_x = (self.resolution.x + TILE_SIZE - 1) / TILE_SIZE;
let tiles_y = (self.resolution.y + TILE_SIZE - 1) / TILE_SIZE;
let mut samples_taken = 0;
while samples_taken < self.num_samples {
let prev = samples_taken;
samples_taken += SAMPLES_INCREMENT;
samples_taken = samples_taken.min(self.num_samples);
let samples = samples_taken - prev;
(0..tiles_y).into_par_iter()
.for_each(|y: i32| {
(0..tiles_x).for_each(|x: i32| {
let px_min = IVec2::new(x, y) * TILE_SIZE;
let px_max = px_min + TILE_SIZE;
let mut tile = self.get_tile(px_min, px_max);
for y in tile.px_min.y..tile.px_max.y {
for x in tile.px_min.x..tile.px_max.x {
self.get_samples(&mut tile, samples, x, y)
}
}
mutex.lock().unwrap().add_tile(tile);
})
});
}
println!("Finished rendering in {:#?}", start.elapsed());
film
}
fn get_tile(&self, px_min: IVec2, px_max: IVec2) -> FilmTile {
FilmTile::new(px_min, px_max.min(self.resolution), self.filter)
}
fn get_samples(&self, tile: &mut FilmTile, num_samples: i32, x: i32, y: i32) {
let xy = Vec2::new(x as Float, y as Float);
PxSampler::new(num_samples)
.flat_map(|rand_sq: Vec2| {
let raster_xy = xy + rand_sq;
self.integrator.integrate(
&self.scene,
&self.camera,
raster_xy,
self.camera.generate_ray(raster_xy),
)
})
.for_each(|mut sample: FilmSample| {
sample.color = self.tone_map.map(sample.color);
tile.add_sample(sample)
})
}
}