From 482e6bea3f4c843bfe9ecb6d35b5d7e5168f6e6b Mon Sep 17 00:00:00 2001 From: Bruno BELANYI Date: Mon, 6 Apr 2020 14:57:08 +0200 Subject: [PATCH] library: render: add basics for Pathtracer This is simple ray-tracing, with a binary white-or-black color depending on whether we hit an object or not. --- pathtracer/src/render/pathtrace/pathtracer.rs | 48 ++++++++++++++++++- pathtracer/src/render/progress.rs | 11 ++++- pathtracer/src/render/utils.rs | 21 ++++++++ 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/pathtracer/src/render/pathtrace/pathtracer.rs b/pathtracer/src/render/pathtrace/pathtracer.rs index a73c2db..8c4a8b7 100644 --- a/pathtracer/src/render/pathtrace/pathtracer.rs +++ b/pathtracer/src/render/pathtrace/pathtracer.rs @@ -1,5 +1,8 @@ +use super::super::utils::{buffer_to_image, prepare_buffer}; use super::super::Renderer; -use crate::scene::Scene; +use crate::core::LinearColor; +use crate::scene::{Object, Scene}; +use beevee::ray::Ray; use image::RgbImage; /// Render the [`Scene`] using Pathtracing @@ -23,7 +26,48 @@ impl Pathtracer { /// /// [`Scene`]: ../scene/scene/struct.Scene.html pub fn render(&self) -> RgbImage { - todo!() + let (width, height) = ( + self.scene.camera.film().width(), + self.scene.camera.film().height(), + ); + let total = width * height; + let mut buffer = prepare_buffer(total); + + // (total passes, film) + // FIXME: use MultiProgress because of rendering issues + let (pa, pb) = super::super::progress::get_multiple_progress(total, self.scene.shot_rays); + + // Ensure at least one round of shots + for _ in 0..self.scene.shot_rays.max(1) { + pb.reset(); // We're rendering the whole film again, reset the pixel counter + for y in 0..self.scene.camera.film().height() { + for x in 0..self.scene.camera.film().width() { + let i = x + y * self.scene.camera.film().width(); + buffer[i as usize] += self.pixel_ray(x as f32, y as f32); + pb.inc(1); + } + } + pa.inc(1); // Increment the number of passes + } + + pa.finish(); + pb.finish_and_clear(); + + buffer_to_image(buffer, self.scene.shot_rays, width, height) + } + + fn pixel_ray(&self, x: f32, y: f32) -> LinearColor { + let (x, y) = self.scene.camera.film().pixel_ratio(x, y); + let ray = self.scene.camera.ray_with_ratio(x, y); + if let Some(_) = self.cast_ray(ray) { + LinearColor::new(1., 1., 1.) // FIXME: calculate real color + } else { + LinearColor::black() + } + } + + fn cast_ray(&self, ray: Ray) -> Option<(f32, &Object)> { + self.scene.bvh.walk(&ray, &self.scene.objects) } } diff --git a/pathtracer/src/render/progress.rs b/pathtracer/src/render/progress.rs index 30698ee..15d5851 100644 --- a/pathtracer/src/render/progress.rs +++ b/pathtracer/src/render/progress.rs @@ -2,9 +2,18 @@ use indicatif::ProgressBar; pub fn get_progressbar(total: u64) -> ProgressBar { let pb = ProgressBar::new(total); - pb.set_draw_delta(total / 10000); + pb.set_draw_delta((total / 10000).max(1)); pb.set_style(indicatif::ProgressStyle::default_bar().template( "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {percent:>3}%: {pos}/{len} pixels (ETA: {eta})", )); pb } + +pub fn get_multiple_progress(total: u32, passes: u32) -> (ProgressBar, ProgressBar) { + let pb = ProgressBar::new(passes as u64); + pb.set_draw_delta((passes as u64 / 100).max(1)); + pb.set_style(indicatif::ProgressStyle::default_bar().template( + "{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {percent:>3}%: {pos}/{len} passes (ETA: {eta})", + )); + (pb, get_progressbar(total as u64)) +} diff --git a/pathtracer/src/render/utils.rs b/pathtracer/src/render/utils.rs index 2ff48ce..905f117 100644 --- a/pathtracer/src/render/utils.rs +++ b/pathtracer/src/render/utils.rs @@ -1,4 +1,6 @@ +use crate::core::LinearColor; use crate::Vector; +use image::RgbImage; use nalgebra::Unit; use rand::prelude::thread_rng; use rand::Rng; @@ -103,6 +105,25 @@ pub fn sample_hemisphere(normal: Vector) -> (Vector, f32) { (scattered, 1. / scattered.dot(&normal)) } +pub fn prepare_buffer(total: u32) -> Vec { + let mut ans = Vec::with_capacity(total as usize); + for _ in 0..total { + ans.push(LinearColor::black()); + } + ans +} + +pub fn buffer_to_image(buffer: Vec, passes: u32, width: u32, height: u32) -> RgbImage { + let mut image = RgbImage::new(width, height); + + for (x, y, pixel) in image.enumerate_pixels_mut() { + let i = x as usize + y as usize * width as usize; + *pixel = (buffer[i].clone() / passes as f32).into(); + } + + image +} + #[cfg(test)] mod test { use super::*;