basic path tracing

This commit is contained in:
CDaut 2023-11-27 22:26:16 +01:00
parent 7f9af4ceaa
commit 507ee2011c
8 changed files with 43 additions and 16 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 686 KiB

Before After
Before After

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +1,4 @@
use std::ops::{Mul, MulAssign}; use std::ops::{MulAssign};
use cgmath::{ElementWise, Vector4}; use cgmath::{ElementWise, Vector4};
use image::{DynamicImage, GenericImage, Rgba}; use image::{DynamicImage, GenericImage, Rgba};
@ -61,7 +61,7 @@ pub fn normalize_colors_global(radiosity_buffer: &mut Vec<Vec<Vector4<f32>>>) ->
let range = maximum_colors - minimum_colors; let range = maximum_colors - minimum_colors;
//normalize to range //normalize to range
for column in &mut *radiosity_buffer { for column in &mut *radiosity_buffer {
for mut radiosity_value in column { for radiosity_value in column {
//normalize to range //normalize to range
radiosity_value.div_assign_element_wise(range); radiosity_value.div_assign_element_wise(range);
//map to [0.0..255] //map to [0.0..255]
@ -77,12 +77,13 @@ pub fn map_radmap_to_colors(radiosity_buffer: &Vec<Vec<Vector4<f32>>>) -> Vec<Ve
let mut color_buffer: Vec<Vec<Rgba<u8>>> = Vec::with_capacity(radiosity_buffer.len()); let mut color_buffer: Vec<Vec<Rgba<u8>>> = Vec::with_capacity(radiosity_buffer.len());
radiosity_buffer.iter().enumerate().for_each(|(x, col)| { radiosity_buffer.iter().enumerate().for_each(|(x, col)| {
color_buffer.push(Vec::with_capacity(radiosity_buffer[0].len())); 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 //ceil and cast individual colors
let casted = color_value.map(|comp| { let casted = color_value.map(|comp| {
f32::ceil(comp) as u8 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]));
}); });
}); });

View file

@ -7,11 +7,9 @@ mod scene_data;
mod colors; mod colors;
use std::string::String; use std::string::String;
use std::sync::{Arc, LockResult, Mutex};
use cgmath::Vector4; use cgmath::Vector4;
use clap::{Parser}; use clap::{Parser};
use easy_gltf::Scene; use easy_gltf::Scene;
use image::{DynamicImage};
use crate::colors::{map_radmap_to_colors, normalize_colors_global, store_colors_to_image}; use crate::colors::{map_radmap_to_colors, normalize_colors_global, store_colors_to_image};
use crate::renderer::render; use crate::renderer::render;

View file

@ -1,15 +1,17 @@
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 rayon::iter::{IntoParallelIterator, ParallelIterator}; use rayon::iter::{IntoParallelIterator, ParallelIterator};
use easy_gltf::model::{Mode}; use easy_gltf::model::{Mode};
use easy_gltf::{Camera, Material, Projection, Scene}; use easy_gltf::{Camera, Projection, Scene};
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};
use crate::scene_data::IntersectionData; use crate::scene_data::IntersectionData;
const RAY_EPSILON: f32 = 0.0007; const RAY_EPSILON: f32 = 0.0007;
const EMISSION_MULTIPLIER: f32 = 10.0;
pub fn render(scenes: &Vec<Scene>, pub fn render(scenes: &Vec<Scene>,
cl_args: &Args) -> Arc<Mutex<Vec<Vec<Vector4<f32>>>>> { cl_args: &Args) -> Arc<Mutex<Vec<Vec<Vector4<f32>>>>> {
@ -36,11 +38,11 @@ pub fn render(scenes: &Vec<Scene>,
// the distance from the camera origin to the view plane // 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 z: f32 = cl_args.height as f32 / (2.0 * fovy.mul(0.5).tan());
let mut radiosity_buffer: Arc<Mutex<Vec<Vec<Vector4<f32>>>>> = let radiosity_buffer: Arc<Mutex<Vec<Vec<Vector4<f32>>>>> =
Arc::new(Mutex::new(Vec::with_capacity(cl_args.width))); Arc::new(Mutex::new(Vec::with_capacity(cl_args.width)));
//prepare the radiosity buffer //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); let mut empty_vector = Vec::with_capacity(cl_args.height);
for _ in 0..cl_args.height { for _ in 0..cl_args.height {
empty_vector.push(Vector4::new(0.0, 0.0, 0.0, 0.0)); 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)) .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 //get the intersected triangle and calculate the face normals
let intersected_triangle = intersection_data.intersected_triangle(); let intersected_triangle = intersection_data.intersected_triangle();
let face_normal: Vector3<f32> = (intersected_triangle[0].position - intersected_triangle[1].position) let tangent = (intersected_triangle[0].position -
.cross(intersected_triangle[0].position - intersected_triangle[2].position).normalize(); intersected_triangle[1].position).normalize();
let face_normal: Vector3<f32> = 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::<f32>() * 2.0 * PI;
//allow arbitrary angles
let theta = rand::random::<f32>() * 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 { 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: face_normal, direction: direction.normalize(),
}; };
let incoming_radiosity = raytrace( //path trace recursively
let incoming_radiosity: Vector4<f32> = raytrace(
&secondary_ray, &secondary_ray,
global_scene, global_scene,
recursion_depth_left - 1); 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 //reflected component
pixel_radiosity += incoming_radiosity * 0.2; pixel_radiosity += (brdf.mul_element_wise(incoming_radiosity) * cos_weighting * 2.0 * PI) ;
pixel_radiosity pixel_radiosity
} }

View file

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use cgmath::Vector3; use cgmath::Vector3;
use easy_gltf::{Material, Scene}; use easy_gltf::{Material};
use easy_gltf::model::Triangle; use easy_gltf::model::Triangle;
pub(crate) struct IntersectionData { pub(crate) struct IntersectionData {