use std::sync::atomic::Ordering;
use std::sync::{Arc, RwLock};
use crate::blockqueue::BlockQueue;
use crate::core::camera::{Camera, CameraSample};
use crate::core::display::Preview;
use crate::core::film::Pixel;
use crate::core::geometry::{pnt2_inside_exclusivei, vec3_abs_dot_nrmf};
use crate::core::geometry::{Bounds2i, Point2f, Point2i, Ray, Vector2i, Vector3f};
use crate::core::interaction::{Interaction, InteractionCommon, SurfaceInteraction};
use crate::core::light::is_delta_light;
use crate::core::light::{Light, VisibilityTester};
use crate::core::pbrt::{Float, Spectrum};
use crate::core::reflection::BxdfType;
use crate::core::sampler::Sampler;
use crate::core::sampling::power_heuristic;
use crate::core::sampling::Distribution1D;
use crate::core::scene::Scene;
use crate::core::spectrum::xyz_to_rgb;
use crate::integrators::ao::AOIntegrator;
use crate::integrators::bdpt::BDPTIntegrator;
use crate::integrators::directlighting::DirectLightingIntegrator;
use crate::integrators::mlt::MLTIntegrator;
use crate::integrators::path::PathIntegrator;
use crate::integrators::sppm::SPPMIntegrator;
use crate::integrators::volpath::VolPathIntegrator;
use crate::integrators::whitted::WhittedIntegrator;
pub enum Integrator {
BDPT(BDPTIntegrator),
MLT(MLTIntegrator),
SPPM(SPPMIntegrator),
Sampler(SamplerIntegrator),
}
impl Integrator {
pub fn render(&mut self, scene: &Scene, num_threads: u8, display_server: Option<String>) {
match self {
Integrator::BDPT(integrator) => integrator.render(scene, num_threads),
Integrator::MLT(integrator) => integrator.render(scene, num_threads),
Integrator::SPPM(integrator) => integrator.render(scene, num_threads),
Integrator::Sampler(integrator) => {
integrator.render(scene, num_threads, display_server)
}
}
}
}
pub enum SamplerIntegrator {
AO(AOIntegrator),
DirectLighting(DirectLightingIntegrator),
Path(PathIntegrator),
VolPath(VolPathIntegrator),
Whitted(WhittedIntegrator),
}
impl SamplerIntegrator {
pub fn preprocess(&mut self, scene: &Scene) {
match self {
SamplerIntegrator::AO(integrator) => integrator.preprocess(scene),
SamplerIntegrator::DirectLighting(integrator) => integrator.preprocess(scene),
SamplerIntegrator::Path(integrator) => integrator.preprocess(scene),
SamplerIntegrator::VolPath(integrator) => integrator.preprocess(scene),
SamplerIntegrator::Whitted(integrator) => integrator.preprocess(scene),
}
}
pub fn render(&mut self, scene: &Scene, num_threads: u8, display_server: Option<String>) {
let film = self.get_camera().get_film();
let sample_bounds: Bounds2i = film.get_sample_bounds();
self.preprocess(scene);
let sample_extent: Vector2i = sample_bounds.diagonal();
let tile_size: i32 = 16;
let x: i32 = (sample_extent.x + tile_size - 1) / tile_size;
let y: i32 = (sample_extent.y + tile_size - 1) / tile_size;
let n_tiles: Point2i = Point2i { x, y };
let num_cores = if num_threads == 0_u8 {
num_cpus::get()
} else {
num_threads as usize
};
println!("Rendering with {:?} thread(s) ...", num_cores);
{
let block_queue = BlockQueue::new(
(
(n_tiles.x * tile_size) as u32,
(n_tiles.y * tile_size) as u32,
),
(tile_size as u32, tile_size as u32),
(0, 0),
);
let integrator = &self;
let bq = &block_queue;
let sampler = &self.get_sampler();
let camera = &self.get_camera();
let film = &film;
let pixel_bounds = &self.get_pixel_bounds();
let address = display_server.unwrap_or("".to_string());
crossbeam::scope(|scope| {
let (pixel_tx, pixel_rx) = crossbeam_channel::bounded(num_cores);
for _ in 0..num_cores {
let pixel_tx = pixel_tx.clone();
let mut tile_sampler: Box<Sampler> = sampler.clone_with_seed(0_u64);
scope.spawn(move |_| {
while let Some((x, y)) = bq.next() {
let tile: Point2i = Point2i {
x: x as i32,
y: y as i32,
};
let seed: i32 = tile.y * n_tiles.x + tile.x;
tile_sampler.reseed(seed as u64);
let x0: i32 = sample_bounds.p_min.x + tile.x * tile_size;
let x1: i32 = std::cmp::min(x0 + tile_size, sample_bounds.p_max.x);
let y0: i32 = sample_bounds.p_min.y + tile.y * tile_size;
let y1: i32 = std::cmp::min(y0 + tile_size, sample_bounds.p_max.y);
let tile_bounds: Bounds2i =
Bounds2i::new(Point2i { x: x0, y: y0 }, Point2i { x: x1, y: y1 });
let mut film_tile = film.get_film_tile(&tile_bounds);
for pixel in &tile_bounds {
tile_sampler.start_pixel(pixel);
if !pnt2_inside_exclusivei(pixel, &pixel_bounds) {
continue;
}
let mut done: bool = false;
while !done {
let camera_sample: CameraSample =
tile_sampler.get_camera_sample(pixel);
let mut ray: Ray = Ray::default();
let ray_weight: Float =
camera.generate_ray_differential(&camera_sample, &mut ray);
ray.scale_differentials(
1.0 as Float
/ (tile_sampler.get_samples_per_pixel() as Float)
.sqrt(),
);
let mut l: Spectrum = Spectrum::new(0.0 as Float);
let y: Float = l.y();
if ray_weight > 0.0 {
let clipping_start: Float = camera.get_clipping_start();
if clipping_start > 0.0 as Float {
camera
.adjust_to_clipping_start(&camera_sample, &mut ray);
}
l = integrator.li(
&mut ray,
scene,
&mut tile_sampler, 0_i32,
);
}
if l.has_nans() {
println!(
"Not-a-number radiance value returned for pixel \
({:?}, {:?}), sample {:?}. Setting to black.",
pixel.x,
pixel.y,
tile_sampler.get_current_sample_number()
);
l = Spectrum::new(0.0);
} else if y < -10.0e-5 as Float {
println!(
"Negative luminance value, {:?}, returned for pixel \
({:?}, {:?}), sample {:?}. Setting to black.",
y,
pixel.x,
pixel.y,
tile_sampler.get_current_sample_number()
);
l = Spectrum::new(0.0);
} else if y.is_infinite() {
println!(
"Infinite luminance value returned for pixel ({:?}, \
{:?}), sample {:?}. Setting to black.",
pixel.x,
pixel.y,
tile_sampler.get_current_sample_number()
);
l = Spectrum::new(0.0);
}
film_tile.add_sample(camera_sample.p_film, &mut l, ray_weight);
done = !tile_sampler.start_next_sample();
} }
pixel_tx
.send(film_tile)
.unwrap_or_else(|_| panic!("Failed to send tile"));
}
});
}
scope.spawn(move |_| {
crossbeam::scope(|sub_scope| {
let display = Preview::connect_to_display_server(&address);
let connected = display.is_ok();
let exit_thread_clone = if connected {
Some(display.as_ref().unwrap().exit_thread.clone())
} else {
eprintln!("Could not connect to tev");
None
};
if connected {
let display = display.unwrap();
let arc = Arc::new(&film.pixels);
let get_values = move |b: Bounds2i, arc: Arc<&RwLock<Vec<Pixel>>>, width: usize, values: &mut Vec<Vec<Float>>| {
for col in b.p_min.y..b.p_max.y {
for row in b.p_min.x..b.p_max.x {
let v = {
let vec = arc.read().unwrap();
let mut rgb = [0.0; 3];
let pixels = &vec[col as usize * width + row as usize];
xyz_to_rgb(&pixels.xyz, &mut rgb);
let filter_weight_sum = pixels.filter_weight_sum;
if filter_weight_sum != 0.0 as Float {
let inv_wt: Float = 1.0 as Float / filter_weight_sum;
rgb[0] = (rgb[0] * inv_wt).max(0.0 as Float);
rgb[1] = (rgb[1] * inv_wt).max(0.0 as Float);
rgb[2] = (rgb[2] * inv_wt).max(0.0 as Float);
}
rgb
};
for (channel, value) in values.iter_mut().zip(v) {
channel.push(value);
}
}
}
};
display.display_dynamic("rs_pbrt", film.full_resolution,
vec!["R".to_string(), "G".to_string(), "B".to_string()],
sub_scope, arc, get_values);
}
for _ in pbr::PbIter::new(0..bq.len()) {
let film_tile = pixel_rx.recv().unwrap();
film.merge_film_tile(&film_tile);
}
if let Some(exit) = exit_thread_clone {
exit.store(true, Ordering::Relaxed);
}
}).unwrap_or_default();
});
}).expect("What am I even?");
}
film.write_image(1.0 as Float);
}
pub fn li(&self, ray: &mut Ray, scene: &Scene, sampler: &mut Sampler, depth: i32) -> Spectrum {
match self {
SamplerIntegrator::AO(integrator) => integrator.li(ray, scene, sampler, depth),
SamplerIntegrator::DirectLighting(integrator) => {
integrator.li(ray, scene, sampler, depth)
}
SamplerIntegrator::Path(integrator) => integrator.li(ray, scene, sampler, depth),
SamplerIntegrator::VolPath(integrator) => integrator.li(ray, scene, sampler, depth),
SamplerIntegrator::Whitted(integrator) => integrator.li(ray, scene, sampler, depth),
}
}
pub fn get_camera(&self) -> Arc<Camera> {
match self {
SamplerIntegrator::AO(integrator) => integrator.get_camera(),
SamplerIntegrator::DirectLighting(integrator) => integrator.get_camera(),
SamplerIntegrator::Path(integrator) => integrator.get_camera(),
SamplerIntegrator::VolPath(integrator) => integrator.get_camera(),
SamplerIntegrator::Whitted(integrator) => integrator.get_camera(),
}
}
pub fn get_sampler(&self) -> &Sampler {
match self {
SamplerIntegrator::AO(integrator) => integrator.get_sampler(),
SamplerIntegrator::DirectLighting(integrator) => integrator.get_sampler(),
SamplerIntegrator::Path(integrator) => integrator.get_sampler(),
SamplerIntegrator::VolPath(integrator) => integrator.get_sampler(),
SamplerIntegrator::Whitted(integrator) => integrator.get_sampler(),
}
}
pub fn get_pixel_bounds(&self) -> Bounds2i {
match self {
SamplerIntegrator::AO(integrator) => integrator.get_pixel_bounds(),
SamplerIntegrator::DirectLighting(integrator) => integrator.get_pixel_bounds(),
SamplerIntegrator::Path(integrator) => integrator.get_pixel_bounds(),
SamplerIntegrator::VolPath(integrator) => integrator.get_pixel_bounds(),
SamplerIntegrator::Whitted(integrator) => integrator.get_pixel_bounds(),
}
}
pub fn specular_reflect(
&self,
ray: &Ray,
isect: &SurfaceInteraction,
scene: &Scene,
sampler: &mut Sampler,
depth: i32,
) -> Spectrum {
match self {
SamplerIntegrator::DirectLighting(integrator) => {
integrator.specular_reflect(ray, isect, scene, sampler, depth)
}
SamplerIntegrator::Whitted(integrator) => {
integrator.specular_reflect(ray, isect, scene, sampler, depth)
}
_ => Spectrum::default(),
}
}
pub fn specular_transmit(
&self,
ray: &Ray,
isect: &SurfaceInteraction,
scene: &Scene,
sampler: &mut Sampler,
depth: i32,
) -> Spectrum {
match self {
SamplerIntegrator::DirectLighting(integrator) => {
integrator.specular_transmit(ray, isect, scene, sampler, depth)
}
SamplerIntegrator::Whitted(integrator) => {
integrator.specular_transmit(ray, isect, scene, sampler, depth)
}
_ => Spectrum::default(),
}
}
}
pub fn uniform_sample_all_lights(
it: &SurfaceInteraction,
scene: &Scene,
sampler: &mut Sampler,
n_light_samples: &[i32],
handle_media: bool,
) -> Spectrum {
let mut l: Spectrum = Spectrum::new(0.0);
for (j, n_samples) in n_light_samples.iter().enumerate().take(scene.lights.len()) {
let light = &scene.lights[j];
let (u_light_array_is_empty, u_light_array_idx, u_light_array_start) =
sampler.get_2d_array_idxs(*n_samples);
let (u_scattering_array_is_empty, u_scattering_array_idx, u_scattering_array_start) =
sampler.get_2d_array_idxs(*n_samples);
if u_light_array_is_empty || u_scattering_array_is_empty {
let u_light: Point2f = sampler.get_2d();
let u_scattering: Point2f = sampler.get_2d();
l += estimate_direct(
it,
u_scattering,
light,
u_light,
scene,
sampler,
handle_media,
false,
);
} else {
let mut ld: Spectrum = Spectrum::new(0.0);
for k in 0..*n_samples {
let u_scattering_array_sample: Point2f = sampler.get_2d_sample(
u_scattering_array_idx,
u_scattering_array_start + k as usize,
);
let u_light_array_sample: Point2f =
sampler.get_2d_sample(u_light_array_idx, u_light_array_start + k as usize);
ld += estimate_direct(
it,
u_scattering_array_sample,
light,
u_light_array_sample,
scene,
sampler,
handle_media,
false,
);
}
l += ld / *n_samples as Float;
}
}
l
}
pub fn uniform_sample_one_light(
it: &dyn Interaction,
scene: &Scene,
sampler: &mut Sampler,
handle_media: bool,
light_distrib: Option<&Distribution1D>,
) -> Spectrum {
let n_lights: usize = scene.lights.len();
if n_lights == 0_usize {
return Spectrum::default();
}
let light_num: usize;
let mut light_pdf: Option<Float> = Some(0.0 as Float);
let pdf: Float;
if let Some(light_distribution) = light_distrib {
light_num = light_distribution.sample_discrete(sampler.get_1d(), light_pdf.as_mut());
pdf = light_pdf.unwrap();
if pdf == 0.0 as Float {
return Spectrum::default();
}
} else {
light_num = std::cmp::min(
(sampler.get_1d() * n_lights as Float) as usize,
n_lights - 1,
);
pdf = 1.0 as Float / n_lights as Float;
}
let light = &scene.lights[light_num];
let u_light: Point2f = sampler.get_2d();
let u_scattering: Point2f = sampler.get_2d();
estimate_direct(
it,
u_scattering,
light,
u_light,
scene,
sampler,
handle_media,
false,
) / pdf
}
pub fn estimate_direct(
it: &dyn Interaction,
u_scattering: Point2f,
light: &Light,
u_light: Point2f,
scene: &Scene,
sampler: &mut Sampler,
handle_media: bool,
specular: bool,
) -> Spectrum {
let bsdf_flags = if !specular {
BxdfType::BsdfAll as u8 & !(BxdfType::BsdfSpecular as u8)
} else {
BxdfType::BsdfAll as u8
};
let mut ld: Spectrum = Spectrum::new(0.0);
let mut wi: Vector3f = Vector3f::default();
let mut light_pdf: Float = 0.0 as Float;
let mut scattering_pdf: Float = 0.0 as Float;
let mut visibility: VisibilityTester = VisibilityTester::default();
let mut light_intr: InteractionCommon = InteractionCommon::default();
let mut li: Spectrum = light.sample_li(
it.get_common(),
&mut light_intr,
u_light,
&mut wi,
&mut light_pdf,
&mut visibility,
);
if light_pdf > 0.0 as Float && !li.is_black() {
let mut f: Spectrum = Spectrum::new(0.0);
if it.is_surface_interaction() {
if let Some(ref bsdf) = it.get_bsdf() {
if let Some(shading_n) = it.get_shading_n() {
f = bsdf.f(&it.get_wo(), &wi, bsdf_flags)
* Spectrum::new(vec3_abs_dot_nrmf(&wi, &shading_n));
scattering_pdf = bsdf.pdf(&it.get_wo(), &wi, bsdf_flags);
}
}
} else {
if let Some(ref phase) = it.get_phase() {
let p: Float = phase.p(&it.get_wo(), &wi);
f = Spectrum::new(p);
scattering_pdf = p;
}
}
if !f.is_black() {
if handle_media {
li *= visibility.tr(scene, sampler);
} else if !visibility.unoccluded(scene) {
li = Spectrum::new(0.0 as Float);
}
if !li.is_black() {
if is_delta_light(light.get_flags()) {
ld += f * li / light_pdf;
} else {
let weight: Float = power_heuristic(1_u8, light_pdf, 1_u8, scattering_pdf);
ld += f * li * Spectrum::new(weight) / light_pdf;
}
}
}
}
if !is_delta_light(light.get_flags()) {
let mut f: Spectrum = Spectrum::new(0.0);
let mut sampled_specular: bool = false;
if it.is_surface_interaction() {
let mut sampled_type: u8 = 0_u8;
if let Some(ref bsdf) = it.get_bsdf() {
if let Some(shading_n) = it.get_shading_n() {
f = bsdf.sample_f(
&it.get_wo(),
&mut wi,
&u_scattering,
&mut scattering_pdf,
bsdf_flags,
&mut sampled_type,
);
f *= Spectrum::new(vec3_abs_dot_nrmf(&wi, &shading_n));
sampled_specular = (sampled_type & BxdfType::BsdfSpecular as u8) != 0_u8;
}
} else {
println!("TODO: if let Some(ref bsdf) = it.get_bsdf() failed");
}
} else {
if let Some(ref phase) = it.get_phase() {
let p: Float = phase.sample_p(&it.get_wo(), &mut wi, u_scattering);
f = Spectrum::new(p);
scattering_pdf = p;
}
}
if !f.is_black() && scattering_pdf > 0.0 {
let weight = if !sampled_specular {
light_pdf = light.pdf_li(it, &wi);
if light_pdf == 0.0 {
return ld;
}
power_heuristic(1, scattering_pdf, 1, light_pdf)
} else {
1.0
};
let mut ray: Ray = it.spawn_ray(&wi);
let mut tr: Spectrum = Spectrum::new(1.0 as Float);
let mut found_surface_interaction: bool = false;
let mut li: Spectrum = Spectrum::default();
let mut light_isect: SurfaceInteraction = SurfaceInteraction::default();
let mut tr_spectrum: Spectrum = Spectrum::default();
if handle_media {
let hit_surface: bool =
scene.intersect_tr(&mut ray, sampler, &mut light_isect, &mut tr_spectrum);
tr = tr_spectrum; if hit_surface {
found_surface_interaction = true;
if let Some(primitive_raw) = light_isect.primitive {
let primitive = unsafe { &*primitive_raw };
if let Some(area_light) = primitive.get_area_light() {
let pa = &*area_light as *const _ as *const usize;
let pl = &*light as *const _ as *const usize;
if pa == pl {
li = light_isect.le(&-wi);
}
}
}
}
} else if scene.intersect(&mut ray, &mut light_isect) {
found_surface_interaction = true;
if let Some(primitive_raw) = light_isect.primitive {
let primitive = unsafe { &*primitive_raw };
if let Some(area_light) = primitive.get_area_light() {
let pa = &*area_light as *const _ as *const usize;
let pl = &*light as *const _ as *const usize;
if pa == pl {
li = light_isect.le(&-wi);
}
}
}
}
if !found_surface_interaction {
li = light.le(&mut ray);
}
if !li.is_black() {
ld += f * li * tr * weight / scattering_pdf;
}
}
}
ld
}
pub fn compute_light_power_distribution(scene: &Scene) -> Option<Arc<Distribution1D>> {
if scene.lights.is_empty() {
return None;
}
let mut light_power: Vec<Float> = Vec::with_capacity(scene.lights.len());
for li in 0..scene.lights.len() {
let light = &scene.lights[li];
light_power.push(light.power().y());
}
Some(Arc::new(Distribution1D::new(light_power)))
}