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.
This commit is contained in:
parent
6066fe00dc
commit
482e6bea3f
|
@ -1,5 +1,8 @@
|
||||||
|
use super::super::utils::{buffer_to_image, prepare_buffer};
|
||||||
use super::super::Renderer;
|
use super::super::Renderer;
|
||||||
use crate::scene::Scene;
|
use crate::core::LinearColor;
|
||||||
|
use crate::scene::{Object, Scene};
|
||||||
|
use beevee::ray::Ray;
|
||||||
use image::RgbImage;
|
use image::RgbImage;
|
||||||
|
|
||||||
/// Render the [`Scene`] using Pathtracing
|
/// Render the [`Scene`] using Pathtracing
|
||||||
|
@ -23,7 +26,48 @@ impl Pathtracer {
|
||||||
///
|
///
|
||||||
/// [`Scene`]: ../scene/scene/struct.Scene.html
|
/// [`Scene`]: ../scene/scene/struct.Scene.html
|
||||||
pub fn render(&self) -> RgbImage {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,18 @@ use indicatif::ProgressBar;
|
||||||
|
|
||||||
pub fn get_progressbar(total: u64) -> ProgressBar {
|
pub fn get_progressbar(total: u64) -> ProgressBar {
|
||||||
let pb = ProgressBar::new(total);
|
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(
|
pb.set_style(indicatif::ProgressStyle::default_bar().template(
|
||||||
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {percent:>3}%: {pos}/{len} pixels (ETA: {eta})",
|
"{spinner:.green} [{elapsed_precise}] [{wide_bar:.cyan/blue}] {percent:>3}%: {pos}/{len} pixels (ETA: {eta})",
|
||||||
));
|
));
|
||||||
pb
|
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))
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use crate::core::LinearColor;
|
||||||
use crate::Vector;
|
use crate::Vector;
|
||||||
|
use image::RgbImage;
|
||||||
use nalgebra::Unit;
|
use nalgebra::Unit;
|
||||||
use rand::prelude::thread_rng;
|
use rand::prelude::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
@ -103,6 +105,25 @@ pub fn sample_hemisphere(normal: Vector) -> (Vector, f32) {
|
||||||
(scattered, 1. / scattered.dot(&normal))
|
(scattered, 1. / scattered.dot(&normal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn prepare_buffer(total: u32) -> Vec<LinearColor> {
|
||||||
|
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<LinearColor>, 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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Reference in a new issue