diff --git a/result_image.png b/result_image.png index b22a73a..a3e14a5 100644 Binary files a/result_image.png and b/result_image.png differ diff --git a/scenes/emissive_cube.blend b/scenes/emissive_cube.blend index 15865af..b65055c 100644 Binary files a/scenes/emissive_cube.blend and b/scenes/emissive_cube.blend differ diff --git a/scenes/emissive_cube.blend1 b/scenes/emissive_cube.blend1 index 2c5b2c9..1c50254 100644 Binary files a/scenes/emissive_cube.blend1 and b/scenes/emissive_cube.blend1 differ diff --git a/scenes/emissive_cube.glb b/scenes/emissive_cube.glb index dee5f57..89f2b7d 100644 Binary files a/scenes/emissive_cube.glb and b/scenes/emissive_cube.glb differ diff --git a/src/colors.rs b/src/colors.rs index b532aba..0dd7b3b 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -2,11 +2,10 @@ use std::ops::{MulAssign}; use cgmath::{ElementWise, Vector4}; use image::{DynamicImage, GenericImage, Rgba}; +const GAMMA: f32 = 2.0; + /// normalizes the color for k rays on a single pixel -pub fn normalize_color_single_pixel(radiosity_vector: Vector4, rays_per_pixel: usize) -> Rgba { - radiosity_vector.map(|component| - f32::ceil(component / rays_per_pixel as f32) as u8 - ); +pub fn normalize_color_single_pixel(radiosity_vector: Vector4) -> Rgba { let radiosity_as_arr = [ radiosity_vector.x, @@ -31,41 +30,40 @@ pub fn normalize_color_single_pixel(radiosity_vector: Vector4, rays_per_pix pub fn normalize_colors_global(radiosity_buffer: &mut Vec>>) -> &Vec>> { //largest radiosity found yet - let mut maximum_colors = Vector4::new( - f32::NEG_INFINITY, - f32::NEG_INFINITY, - f32::NEG_INFINITY, - f32::NEG_INFINITY); + let mut maximum_radiosity = f32::NEG_INFINITY; //smallest radiosity found yet - let mut minimum_colors = Vector4::new( - f32::INFINITY, - f32::INFINITY, - f32::INFINITY, - f32::INFINITY); + let mut minimum_radiosity = f32::INFINITY; + //find maximum and minimum radiosity radiosity_buffer.iter().for_each(|col| { col.iter().for_each(|color| { - maximum_colors.x = f32::max(maximum_colors.x, color.x); - maximum_colors.y = f32::max(maximum_colors.y, color.y); - maximum_colors.z = f32::max(maximum_colors.z, color.z); - maximum_colors.w = f32::max(maximum_colors.w, color.w); + maximum_radiosity = f32::max(maximum_radiosity, color.x); + maximum_radiosity = f32::max(maximum_radiosity, color.y); + maximum_radiosity = f32::max(maximum_radiosity, color.z); - minimum_colors.x = f32::min(minimum_colors.x, color.x); - minimum_colors.y = f32::min(minimum_colors.y, color.y); - minimum_colors.z = f32::min(minimum_colors.z, color.z); - minimum_colors.w = f32::min(minimum_colors.w, color.w); - }) + minimum_radiosity = f32::min(minimum_radiosity, color.x); + minimum_radiosity = f32::min(minimum_radiosity, color.y); + minimum_radiosity = f32::min(minimum_radiosity, color.z); + }); }); //calculate difference between min and max pixel radiosity - let range = maximum_colors - minimum_colors; + //TODO: drop upper 20 percent of radiosity to illiminate outliers + let range = (maximum_radiosity * 0.8) - minimum_radiosity; //normalize to range for column in &mut *radiosity_buffer { for radiosity_value in column { + //Gamma correct + radiosity_value.map(|single_channel_radiosity| + f32::powf(single_channel_radiosity, 1.0/GAMMA) + ); //normalize to range radiosity_value.div_assign_element_wise(range); //map to [0.0..255] radiosity_value.mul_assign(255.0); + radiosity_value.map(|single_ch_radiosity| + f32::max(single_ch_radiosity, 255.0) + ); } } diff --git a/src/renderer.rs b/src/renderer.rs index 8d0ff97..bd5b041 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,17 +1,17 @@ use std::f32::consts::PI; use std::ops::{Add, Mul}; -use std::sync::{Arc, Mutex}; +use std::sync::{Arc, LockResult, Mutex}; use cgmath::{Angle, ElementWise, InnerSpace, Matrix4, Vector2, Vector3, Vector4}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use easy_gltf::model::{Mode}; use easy_gltf::{Camera, Projection, Scene}; +use rayon::prelude::IntoParallelRefIterator; use crate::Args; use crate::geometry::{Intersectable}; use crate::ray::{construct_primary_rays, Ray}; use crate::scene_data::IntersectionData; const RAY_EPSILON: f32 = 0.0007; -const EMISSION_MULTIPLIER: f32 = 10.0; pub fn render(scenes: &Vec, cl_args: &Args) -> Arc>>>> { @@ -68,7 +68,7 @@ pub fn render(scenes: &Vec, //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) + raytrace(&ray, render_scene, cl_args.recurse) ); }); @@ -156,8 +156,6 @@ fn accumulate_colors(intersection_data: &IntersectionData, .get_base_color_alpha(Vector2::new(0.0, 0.0)) ); - //TODO: hack, because light sources are always too dim - pixel_radiosity *= EMISSION_MULTIPLIER; //get the intersected triangle and calculate the face normals let intersected_triangle = intersection_data.intersected_triangle(); @@ -168,9 +166,9 @@ fn accumulate_colors(intersection_data: &IntersectionData, intersected_triangle[2].position).normalize(); let bitangent = tangent.cross(face_normal); - /** + /* generate random direction on hemisphere - **/ + */ let phi = rand::random::() * 2.0 * PI; //allow arbitrary angles let theta = rand::random::() * 2.0 * PI; @@ -192,14 +190,16 @@ fn accumulate_colors(intersection_data: &IntersectionData, global_scene, recursion_depth_left - 1); + let cos_weighting = direction.dot(face_normal); - let brdf = 0.5 * intersection_data.material().get_base_color_alpha( - Vector2::new(0.0,0.0) - ); + //make ray contribution decay exponentially + let brdf = intersection_data.material() + .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 - 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; pixel_radiosity }