diffuse path tracing

This commit is contained in:
CDaut 2023-11-28 01:02:12 +01:00
parent 8cb5476588
commit 44514117cd
10 changed files with 62 additions and 46 deletions

View file

@ -12,3 +12,4 @@ cgmath = "0.18.0"
rayon = "1.8.0" rayon = "1.8.0"
image = "0.24.7" image = "0.24.7"
rand = "0.8.5" rand = "0.8.5"
indicatif = { version = "0.17.7", features = ["rayon"] }

BIN
first_really_good_image.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -48,8 +48,7 @@ pub fn normalize_colors_global(radiosity_buffer: &mut Vec<Vec<Vector4<f32>>>) ->
}); });
//calculate difference between min and max pixel radiosity //calculate difference between min and max pixel radiosity
//TODO: drop upper 20 percent of radiosity to illiminate outliers let range = maximum_radiosity - minimum_radiosity;
let range = (maximum_radiosity * 0.8) - minimum_radiosity;
//normalize to range //normalize to range
for column in &mut *radiosity_buffer { for column in &mut *radiosity_buffer {
for radiosity_value in column { for radiosity_value in column {

View file

@ -49,6 +49,7 @@ fn main() {
&args.gltf_file_path) &args.gltf_file_path)
.expect(&*format!("Failed to load glTF file {}", &args.gltf_file_path)); .expect(&*format!("Failed to load glTF file {}", &args.gltf_file_path));
println!("Path tracing image. Columns finished:");
let mut radiosity_buffer: Vec<Vec<Vector4<f32>>> = match render(scenes, &args).lock() { let mut radiosity_buffer: Vec<Vec<Vector4<f32>>> = match render(scenes, &args).lock() {
Ok(buffer) => {buffer.to_vec()} Ok(buffer) => {buffer.to_vec()}
Err(_) => {panic!("Unable to lock radiosity buffer!")} Err(_) => {panic!("Unable to lock radiosity buffer!")}
@ -64,6 +65,6 @@ fn main() {
let output_image = let output_image =
store_colors_to_image(as_colors); store_colors_to_image(as_colors);
output_image.save("result_image.png").expect("Unable to save image!"); output_image.save("../output_image.png").expect("Unable to save image!");
} }

View file

@ -1,11 +1,13 @@
use indicatif::ProgressIterator;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::ops::{Add, Mul}; use std::ops::{Add, Mul};
use std::sync::{Arc, LockResult, Mutex}; use std::sync::{Arc, Mutex};
use cgmath::{Angle, ElementWise, InnerSpace, Matrix4, Vector2, Vector3, Vector4}; use cgmath::{Angle, ElementWise, InnerSpace, Matrix4, Vector2, Vector3, Vector4};
use cgmath::num_traits::abs;
use rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
use easy_gltf::model::{Mode}; use easy_gltf::model::{Mode};
use easy_gltf::{Camera, Projection, Scene}; use easy_gltf::{Camera, Projection, Scene};
use rayon::prelude::IntoParallelRefIterator; use indicatif::ParallelProgressIterator;
use crate::Args; use crate::Args;
use crate::geometry::{Intersectable}; use crate::geometry::{Intersectable};
use crate::ray::{construct_primary_rays, Ray}; use crate::ray::{construct_primary_rays, Ray};
@ -51,36 +53,38 @@ pub fn render(scenes: &Vec<Scene>,
}); });
//iterate over all pixels in the image //iterate over all pixels in the image
(0..cl_args.width).into_par_iter().for_each(|px| { (0..cl_args.width).into_par_iter()
(0..cl_args.height).into_par_iter().for_each(|py| { .progress_count(cl_args.width as u64)
//construct all rays .for_each(|px| {
let rays: Vec<Ray> = construct_primary_rays( (0..cl_args.height).into_par_iter().for_each(|py| {
(cl_args.width, cl_args.height), //construct all rays
(px, py), let rays: Vec<Ray> = construct_primary_rays(
&transform_matrix, (cl_args.width, cl_args.height),
z, (px, py),
cl_args.multiplier, &transform_matrix,
); z,
cl_args.multiplier,
//let the initial pixel color be black and opaque
let mut pixel_radiosity: Vector4<f32> = Vector4::new(0.0, 0.0, 0.0, 1.0);
//cast each ray and get the output luminosity and sum them up
rays.iter().for_each(|ray| {
pixel_radiosity = pixel_radiosity.add(
raytrace(&ray, render_scene, cl_args.recurse)
); );
});
//store radiosity into the buffer //let the initial pixel color be black and opaque
match radiosity_buffer.clone().lock() { let mut pixel_radiosity: Vector4<f32> = Vector4::new(0.0, 0.0, 0.0, 1.0);
Ok(mut buffer) => {
buffer[px][py] = pixel_radiosity; //cast each ray and get the output luminosity and sum them up
rays.iter().for_each(|ray| {
pixel_radiosity = pixel_radiosity.add(
raytrace(&ray, render_scene, cl_args.recurse)
);
});
//store radiosity into the buffer
match radiosity_buffer.clone().lock() {
Ok(mut buffer) => {
buffer[px][py] = pixel_radiosity;
}
Err(_) => { panic!("Unable to lock pixel buffer!") }
} }
Err(_) => { panic!("Unable to lock pixel buffer!") } });
}
}); });
});
radiosity_buffer radiosity_buffer
} }
@ -121,6 +125,7 @@ fn raytrace(ray: &Ray, scene: &Scene, recursion_depth_left: usize) -> Vector4<f3
isec, isec,
model.material(), model.material(),
triangle.clone(), triangle.clone(),
ray,
) )
); );
} }
@ -161,27 +166,31 @@ fn accumulate_colors(intersection_data: &IntersectionData,
let intersected_triangle = intersection_data.intersected_triangle(); let intersected_triangle = intersection_data.intersected_triangle();
let tangent = (intersected_triangle[0].position - let tangent = (intersected_triangle[0].position -
intersected_triangle[1].position).normalize(); intersected_triangle[1].position).normalize();
let face_normal: Vector3<f32> = tangent let mut face_normal: Vector3<f32> = tangent
.cross(intersected_triangle[0].position - .cross(intersected_triangle[0].position -
intersected_triangle[2].position).normalize(); intersected_triangle[2].position).normalize();
let bitangent = tangent.cross(face_normal); //flip normal if face is hit from behind
if face_normal.dot(-intersection_data.ray().direction) < 0.0 {
face_normal *= -1.0;
}
let bitangent = tangent.cross(face_normal).normalize();
/* /*
generate random direction on hemisphere generate random direction on hemisphere
*/ */
let phi = rand::random::<f32>() * 2.0 * PI; let phi = rand::random::<f32>() * 2.0 * PI;
//allow arbitrary angles //allow arbitrary angles
let theta = rand::random::<f32>() * 2.0 * PI; let radius = rand::random::<f32>();
let sqrt_radius = f32::sqrt(rand::random::<f32>());
let sin_theta = f32::sin(theta); let direction = (tangent * f32::cos(phi) * sqrt_radius +
let direction = tangent * f32::cos(phi) * sin_theta + bitangent * f32::sin(phi) * sqrt_radius +
bitangent * f32::sin(phi) * sin_theta + face_normal * f32::sqrt(1.0 - radius)).normalize();
face_normal * f32::cos(theta);
let secondary_ray = Ray { let secondary_ray = Ray {
//prevent self-intersection //prevent self-intersection
source: intersection_data.intersection_point() + RAY_EPSILON * face_normal, source: intersection_data.intersection_point() + RAY_EPSILON * face_normal,
direction: direction.normalize(), direction,
}; };
//path trace recursively //path trace recursively
@ -193,10 +202,8 @@ fn accumulate_colors(intersection_data: &IntersectionData,
let cos_weighting = direction.dot(face_normal); let cos_weighting = direction.dot(face_normal);
//make ray contribution decay exponentially
let brdf = intersection_data.material() let brdf = intersection_data.material()
.get_base_color_alpha(Vector2::new(0.0, 0.0)) .get_base_color_alpha(Vector2::new(0.0, 0.0));
* (1.0 - f32::powf(6.0, -0.5 * recursion_depth_left as f32));
//reflected component //reflected component
pixel_radiosity += brdf.mul_element_wise(incoming_radiosity) * cos_weighting * 2.0 * PI; pixel_radiosity += brdf.mul_element_wise(incoming_radiosity) * cos_weighting * 2.0 * PI;

View file

@ -2,22 +2,26 @@ use std::sync::Arc;
use cgmath::Vector3; use cgmath::Vector3;
use easy_gltf::{Material}; use easy_gltf::{Material};
use easy_gltf::model::Triangle; use easy_gltf::model::Triangle;
use crate::ray::Ray;
pub(crate) struct IntersectionData { pub(crate) struct IntersectionData<'a> {
intersection_point: Vector3<f32>, intersection_point: Vector3<f32>,
material: Arc<Material>, material: Arc<Material>,
intersected_triangle: Triangle, intersected_triangle: Triangle,
ray: &'a Ray,
} }
impl IntersectionData { impl IntersectionData<'_>{
pub fn new( pub fn new(
intersection_point: Vector3<f32>, intersection_point: Vector3<f32>,
material: Arc<Material>, material: Arc<Material>,
intersected_triangle: Triangle) -> IntersectionData { intersected_triangle: Triangle,
ray: &Ray) -> IntersectionData {
IntersectionData { IntersectionData {
intersection_point, intersection_point,
material, material,
intersected_triangle, intersected_triangle,
ray,
} }
} }
@ -32,4 +36,8 @@ impl IntersectionData {
pub fn intersected_triangle(&self) -> Triangle { pub fn intersected_triangle(&self) -> Triangle {
self.intersected_triangle self.intersected_triangle
} }
pub fn ray(&self) -> &Ray {
self.ray
}
} }