diff --git a/result_image.png b/result_image.png index ff5b37b..b22a73a 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 7bde971..15865af 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 c49b3d1..2c5b2c9 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 7575737..dee5f57 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 37fae8b..b532aba 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -1,4 +1,4 @@ -use std::ops::{Mul, MulAssign}; +use std::ops::{MulAssign}; use cgmath::{ElementWise, Vector4}; use image::{DynamicImage, GenericImage, Rgba}; @@ -61,7 +61,7 @@ pub fn normalize_colors_global(radiosity_buffer: &mut Vec>>) -> let range = maximum_colors - minimum_colors; //normalize to range for column in &mut *radiosity_buffer { - for mut radiosity_value in column { + for radiosity_value in column { //normalize to range radiosity_value.div_assign_element_wise(range); //map to [0.0..255] @@ -77,12 +77,13 @@ pub fn map_radmap_to_colors(radiosity_buffer: &Vec>>) -> Vec>> = Vec::with_capacity(radiosity_buffer.len()); radiosity_buffer.iter().enumerate().for_each(|(x, col)| { color_buffer.push(Vec::with_capacity(radiosity_buffer[0].len())); - col.iter().enumerate().for_each(|(y, color_value)| { + col.iter().enumerate().for_each(|(_y, color_value)| { //ceil and cast individual colors let casted = color_value.map(|comp| { f32::ceil(comp) as u8 }); - color_buffer[x].push(Rgba::from([casted.x, casted.y, casted.z, casted.w])); + //TODO: no alpha rendering + color_buffer[x].push(Rgba::from([casted.x, casted.y, casted.z, 255])); }); }); diff --git a/src/main.rs b/src/main.rs index a8a52f3..b4a243a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,11 +7,9 @@ mod scene_data; mod colors; use std::string::String; -use std::sync::{Arc, LockResult, Mutex}; use cgmath::Vector4; use clap::{Parser}; use easy_gltf::Scene; -use image::{DynamicImage}; use crate::colors::{map_radmap_to_colors, normalize_colors_global, store_colors_to_image}; use crate::renderer::render; diff --git a/src/renderer.rs b/src/renderer.rs index 3c83d74..8d0ff97 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,15 +1,17 @@ +use std::f32::consts::PI; 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 rayon::iter::{IntoParallelIterator, ParallelIterator}; use easy_gltf::model::{Mode}; -use easy_gltf::{Camera, Material, Projection, Scene}; +use easy_gltf::{Camera, Projection, Scene}; 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>>>> { @@ -36,11 +38,11 @@ pub fn render(scenes: &Vec, // the distance from the camera origin to the view plane let z: f32 = cl_args.height as f32 / (2.0 * fovy.mul(0.5).tan()); - let mut radiosity_buffer: Arc>>>> = + let radiosity_buffer: Arc>>>> = Arc::new(Mutex::new(Vec::with_capacity(cl_args.width))); //prepare the radiosity buffer - (0..cl_args.width).into_iter().for_each(|px| { + (0..cl_args.width).into_iter().for_each(|_px| { let mut empty_vector = Vec::with_capacity(cl_args.height); for _ in 0..cl_args.height { empty_vector.push(Vector4::new(0.0, 0.0, 0.0, 0.0)); @@ -154,24 +156,50 @@ 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(); - let face_normal: Vector3 = (intersected_triangle[0].position - intersected_triangle[1].position) - .cross(intersected_triangle[0].position - intersected_triangle[2].position).normalize(); + let tangent = (intersected_triangle[0].position - + intersected_triangle[1].position).normalize(); + let face_normal: Vector3 = tangent + .cross(intersected_triangle[0].position - + 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; + + let sin_theta = f32::sin(theta); + let direction = tangent * f32::cos(phi) * sin_theta + + bitangent * f32::sin(phi) * sin_theta + + face_normal * f32::cos(theta); let secondary_ray = Ray { //prevent self-intersection source: intersection_data.intersection_point() + RAY_EPSILON * face_normal, - direction: face_normal, + direction: direction.normalize(), }; - let incoming_radiosity = raytrace( + //path trace recursively + let incoming_radiosity: Vector4 = raytrace( &secondary_ray, 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) + ); + //reflected component - pixel_radiosity += incoming_radiosity * 0.2; + pixel_radiosity += (brdf.mul_element_wise(incoming_radiosity) * cos_weighting * 2.0 * PI) ; pixel_radiosity } diff --git a/src/scene_data.rs b/src/scene_data.rs index e7bd1eb..3cb4897 100644 --- a/src/scene_data.rs +++ b/src/scene_data.rs @@ -1,6 +1,6 @@ use std::sync::Arc; use cgmath::Vector3; -use easy_gltf::{Material, Scene}; +use easy_gltf::{Material}; use easy_gltf::model::Triangle; pub(crate) struct IntersectionData {