use bxdf::prelude::*;
use sample::prelude::*;
use filming::prelude::*;
use filming::film::FilmTile;
use super::Renderer;
use std::sync::Arc;
use super::scene::Scene;
use spectrum::{RGBSpectrumf, Spectrum};
use rayon::prelude::*;
use aren_alloc::Allocator;
use geometry::prelude::*;
use std::path::{PathBuf, Path};
profile_use!();
pub struct PTRenderer<S> {
sampler: S,
camera: Arc<Camera>,
filename: PathBuf,
max_depth: usize,
multithreaded: bool,
rr_threshold: Float,
min_depth: usize,
}
impl<S: Sampler> PTRenderer<S> {
pub fn new<P: AsRef<Path> + ?Sized>(
sampler: S, camera: Arc<Camera>,
filename: &P, max_depth: usize, multithreaded: bool
) -> PTRenderer<S> {
PTRenderer{
sampler: sampler,
camera: camera,
filename: filename.as_ref().to_path_buf(),
max_depth: max_depth,
multithreaded: multithreaded,
rr_threshold: 0.05 as Float,
min_depth: max_depth/2,
}
}
}
fn calculate_lighting<S: Sampler>(
mut ray: RayDifferential,
scene: &Scene,
sampler: &mut S,
alloc: &Allocator,
depth: usize,
max_depth: usize,
min_depth: usize,
rr_threshold: Float
) -> RGBSpectrumf {
let mut ret = RGBSpectrumf::black();
if depth > max_depth { return ret; }
let mut beta = RGBSpectrumf::new(1. as Float, 1. as Float, 1. as Float);
let mut specular_bounce = false;
let mut bounces = 0;
loop {
if let Some(mut si) = scene.aggregate.intersect_ray(&mut ray.ray) {
if bounces == 0 || specular_bounce {
let term = si.le(-ray.ray.direction());
if !term.valid() {
warn!("invalid le {:?} from {:p}, vray: {:p}", term, &si, &ray);
}
ret += beta * term;
}
if let Some(primitive) = si.primitive_hit {
let dxy = si.compute_dxy(&ray);
let bsdf = primitive.get_material().compute_scattering(
&mut si, &dxy, alloc
);
let mut tags = BXDF_ALL;
tags.remove(BXDF_SPECULAR);
if bsdf.have_n(tags) > 0 {
let term = scene.uniform_sample_one_light(&si, sampler, &bsdf);
ret += beta * term;
}
let wo = -(ray.ray.direction());
let (f, wi, pdf, bt) = bsdf.evaluate_sampled(wo, sampler.next_2d(), BXDF_ALL);
specular_bounce = bt.intersects(BXDF_SPECULAR);
if f.is_black() || pdf == 0. as Float { break; }
beta *= f * (wi.dot(si.shading_norm).abs() / pdf);
if !beta.valid() {
warn!("invalid beta {:?} encountered from {:?} dot {:?} with pdf {}, breaking current bouncing", beta, wi, si.shading_norm, pdf);
break;
}
debug_assert!(beta.inner.y >= 0. as Float);
ray = si.spawn_ray_differential(wi, Some(&dxy));
} else {
break;
}
} else {
break;
}
bounces += 1;
if bounces >= max_depth { break; }
if beta.to_xyz().y < rr_threshold && bounces >= min_depth {
let q = rr_threshold.max(0.05 as Float);
if sampler.next() < q { break; }
beta /= 1.0 as Float - q;
}
}
ret
}
impl<S: Sampler> Renderer for PTRenderer<S> {
fn render(&mut self, scene: &Scene) {
profile_start!("pt rendering");
info!("Path tracing rendering process started");
let mut tiles: Vec<FilmTile<RGBSpectrumf>> = self.camera.get_film().spawn_tiles(16, 16);
let render_tile = |tile: &mut FilmTile<_>| {
let mut sampler = self.sampler.clone();
let tile_bound = tile.bounding();
let allocator = Allocator::new();
for p in tile_bound {
let p: Point2<u32> = p.cast();
sampler.start_pixel(p);
loop {
let camera_sample_info = sampler.get_camera_sample(p);
let mut ray_differential = self.camera.generate_path_differential(camera_sample_info);
ray_differential.scale_differentials(1.0 as Float / sampler.sample_per_pixel() as Float);
profile_start!("pt light calculation");
let total_randiance = calculate_lighting(
ray_differential, scene, &mut sampler,
&allocator, 0, self.max_depth,
self.min_depth, self.rr_threshold
);
profile_end!("pt light calculation");
profile_start!("pt add sample");
if total_randiance.valid() {
tile.add_sample(camera_sample_info.pfilm, &total_randiance);
} else {
tile.add_sample(camera_sample_info.pfilm, &RGBSpectrumf::black());
}
profile_end!("pt add sample");
if !sampler.next_sample() { break; }
}
}
};
if self.multithreaded {
tiles.par_iter_mut().for_each(|tile| render_tile(tile));
} else {
for tile in &mut tiles { render_tile(tile); }
}
let render_result = self.camera.get_film().collect_into(tiles);
profile_end!("pt rendering");
info!("Path tracing rendering process ended");
if let Ok(_) = render_result.save(&self.filename) {
info!("Path tracing result saved at {:?}", self.filename);
} else {
warn!("Path tracing result saving at {:?} failed", self.filename);
}
profile_dump!("pt rendering results.html");
}
}