use crate::{ light::Light, primitive::{Intersection, Primitive}, ray::Ray, scene::Scene, INFINITY, }; use std::sync::Arc; use nalgebra::{distance, Matrix4, Point3, Unit, Vector3, Vector4}; static ZERO_VECTOR: Vector3 = Vector3::new(0.0, 0.0, 0.0); static ONE_VECTOR: Vector3 = Vector3::new(1.0, 1.0, 1.0); pub fn shade_rays(scene: &Scene, rays: &Vec, width: i32, height: i32) -> Vec> { let mut pixel_data = vec![Vector3::new(0, 0, 0); (width * height) as usize]; for (pixel_index, ray) in rays.iter().enumerate() { let intersect = get_closest_intersection(&scene.primitives, ray); let colour = match intersect { Some(intersect) => phong_shade_point(scene, &intersect), None => { // Handle rays that miss objects (e.g., use a background color or environment map) Vector3::new(0, 0, 0) } }; pixel_data[pixel_index] = colour; } pixel_data } //Shade a single ray pub fn shade_ray(scene: &Scene, ray: &Ray) -> Option> { let intersect = get_closest_intersection(&scene.primitives, ray); match intersect { Some(intersect) => Some(phong_shade_point(&scene, &intersect)), None => None, } } // Find the closest intersection, given a ray in world coordinates pub fn get_closest_intersection( primitives: &Vec>, ray: &Ray, ) -> Option { let mut closest_distance = INFINITY; let mut closest_intersect: Option = None; for arc_primitive in primitives { let primitive = arc_primitive.clone(); if let Some(intersect) = primitive.intersect_ray(ray) { if intersect.distance < closest_distance { closest_distance = intersect.distance; closest_intersect = Some(intersect); } } } closest_intersect } // We want to shade a point placed in our scene pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3 { //Useful vectors !!!! CHECK IF WE CAN OPTIMISE //Unpack the intersection data let Intersection { point, normal, incidence, material, .. } = intersect; let binding = scene.ambient_light.clone(); let ambient_light = binding.as_ref(); let kd = material.kd; let ks = material.ks; let shininess = material.shininess; // We should now have all the information for our ray-tracer // Let us first compute the ambient light component and set it as out base colour let mut colour = kd.component_mul(ambient_light); for arc_light in scene.lights.as_ref() { let light = arc_light.clone(); let Light { position: light_position, colour: light_colour, falloff: light_falloff, } = light; // Get light incidence vector let to_light = light_position - point; let light_distance = to_light.norm(); let light_incidence = Unit::new_normalize(to_light); // Compute light falloff let falloff = 1.0 / (light_falloff[0] + light_falloff[1] * light_distance + light_falloff[2] * light_distance * light_distance); // Compute diffuse let n_dot_l = normal.dot(&light_incidence); let diffuse = if n_dot_l > 0.0 { kd * n_dot_l } else { ZERO_VECTOR }; // Compute specular let h = (&light_incidence.into_inner() + incidence.into_inner()).normalize(); let n_dot_h = normal.dot(&h); let specular = if n_dot_h > 0.0 { ks * n_dot_h.powf(shininess) } else { ZERO_VECTOR }; colour += light_colour.component_mul(&((diffuse + specular) * falloff)); } let r = nalgebra::clamp(colour.x * 255.0, 0.0, 255.0) as u8; let g = nalgebra::clamp(colour.y * 255.0, 0.0, 255.0) as u8; let b = nalgebra::clamp(colour.z * 255.0, 0.0, 255.0) as u8; Vector3::new(r, g, b) }