Integrated raytracing into ray class

This commit is contained in:
STP
2023-11-28 13:23:13 -05:00
parent 62e62df2cc
commit 2bc964cd2d
2 changed files with 107 additions and 85 deletions

View File

@@ -1,8 +1,27 @@
use crate::{primitive::Intersection, raytracer::phong_shade_point, scene::Scene}; use crate::{node::Node, scene::Scene};
use nalgebra::{Matrix4, Point3, Vector3}; use nalgebra::{Matrix4, Point3, Vector3};
#[derive(Clone)] // INTERSECTION -----------------------------------------------------------------
pub struct Intersection {
// Information about an intersection
pub point: Point3<f64>,
pub normal: Vector3<f64>,
pub distance: f64,
}
impl Intersection {
pub fn transform(&self, trans: &Matrix4<f64>, inv_trans: &Matrix4<f64>) -> Intersection {
let point = trans.transform_point(&self.point);
let normal = inv_trans.transpose().transform_vector(&self.normal);
Intersection {
point,
normal,
distance: self.distance,
}
}
}
// Ray struct represents a ray in 3D space with a starting point 'a' and a direction 'b' // Ray struct represents a ray in 3D space with a starting point 'a' and a direction 'b'
#[derive(Clone)]
pub struct Ray { pub struct Ray {
pub a: Point3<f64>, pub a: Point3<f64>,
pub b: Vector3<f64>, pub b: Vector3<f64>,
@@ -35,6 +54,9 @@ impl Ray {
let mut closest_node = None; let mut closest_node = None;
for (_, node) in &scene.nodes { for (_, node) in &scene.nodes {
if !node.active {
continue;
}
// Transform ray into local model cordinates // Transform ray into local model cordinates
let ray = self.transform(&node.inv_model); let ray = self.transform(&node.inv_model);
// Check bounding box intersection // Check bounding box intersection
@@ -57,12 +79,94 @@ impl Ray {
//Inverse transform back to world coords //Inverse transform back to world coords
let node = closest_node.unwrap(); let node = closest_node.unwrap();
let intersect = intersect.transform(&node.model, &node.inv_model); let intersect = intersect.transform(&node.model, &node.inv_model);
Some(phong_shade_point(&scene, &intersect)) // If there is an intersection, shade it Some(Ray::phong_shade_point(&scene, &self, &node, &intersect)) // If there is an intersection, shade it
} }
None => None, // If there is no intersection, return None None => None, // If there is no intersection, return None
} }
} }
// Function to shade a point in the scene using Phong shading model
pub fn phong_shade_point(
scene: &Scene,
ray: &Ray,
node: &Node,
intersect: &Intersection,
) -> Vector3<u8> {
let point = &intersect.point;
let normal = &intersect.normal;
let incidence = &ray.b;
let material = &node.material;
let kd = &material.kd;
let ks = &material.ks;
let shininess = material.shininess;
// Point to camera
let to_camera = -incidence;
// Compute the ambient light component and set it as base colour
let mut colour = Vector3::zeros();
for (_, light) in &scene.lights {
if !light.active {
continue;
}
if light.ambient {
colour += light.colour;
continue;
}
// Point to light
let to_light = light.position - point;
let light_distance = to_light.norm() as f32;
let to_light = to_light.normalize();
// let to_light_ray = Ray::new(point.clone() + 0.0001 * normal, to_light);
// if to_light_ray.light_blocked(scene) {
// continue;
// }
// Diffuse component
let n_dot_l = normal.dot(&to_light).max(0.0) as f32;
let diffuse = n_dot_l * kd;
// Specular component
let mut specular = Vector3::zeros();
if n_dot_l > 0.0 {
// Halfway vector.
let h = to_camera + to_light.normalize();
let n_dot_h = normal.dot(&h).max(0.0) as f32;
specular = ks * n_dot_h.powf(shininess);
}
// Compute light falloff
let falloff = 1.0
/ (1.0
+ light.falloff[0]
+ light.falloff[1] * light_distance
+ light.falloff[2] * light_distance.powi(2));
let light_intensity = light.colour.component_mul(&(diffuse + specular)) * falloff;
colour += &light_intensity;
}
colour *= 255.0;
let (r, g, b) = (colour.x as u8, colour.y as u8, colour.z as u8);
Vector3::new(r, g, b)
}
pub fn light_blocked(&mut self, scene: &Scene) -> bool {
for (_, node) in &scene.nodes {
if !node.active {
continue;
}
self.transform(&node.inv_model);
if node.primitive.intersect_bounding_box(&self) {
if node.primitive.intersect_ray(&self).is_some() {
return true;
}
}
}
false
}
// Return a transformed version of the ray // Return a transformed version of the ray
pub fn transform(&self, trans: &Matrix4<f64>) -> Ray { pub fn transform(&self, trans: &Matrix4<f64>) -> Ray {
Ray { Ray {

View File

@@ -1,82 +0,0 @@
use crate::{light::Light, primitive::Intersection, ray::Ray, scene::*};
use nalgebra::Vector3;
// Function to shade a point in the scene using Phong shading model
pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8> {
let point = &intersect.point;
let material = &intersect.material;
let normal = &intersect.normal;
let incidence = &intersect.incidence;
let kd = &material.kd;
let ks = &material.ks;
let shininess = material.shininess;
// Point to camera
let to_camera = -incidence;
// Compute the ambient light component and set it as base colour
let mut colour = Vector3::zeros();
for (_, light) in &scene.lights {
let Light {
position: light_position,
colour: light_colour,
falloff: light_falloff,
ambient: light_ambient,
} = light;
if *light_ambient {
colour += light_colour;
continue;
}
// Point to light
let to_light = light_position - point;
let light_distance = to_light.norm() as f32;
let to_light = to_light;
//let to_light_ray = Ray::new(point.clone() + normal * EPSILON, to_light);
// if light_blocked(scene, to_light_ray) {
// continue;
// }
// Diffuse component
let n_dot_l = normal.dot(&to_light).max(0.0) as f32;
let diffuse = n_dot_l * kd;
// Specular component
let mut specular = Vector3::zeros();
if n_dot_l > 0.0 {
// Halfway vector.
let h = to_camera + to_light.normalize();
let n_dot_h = normal.dot(&h).max(0.0) as f32;
specular = ks * n_dot_h.powf(shininess);
}
// Compute light falloff
let falloff = 1.0
/ (1.0
+ light_falloff[0]
+ light_falloff[1] * light_distance
+ light_falloff[2] * light_distance.powi(2));
let light_intensity = light_colour.component_mul(&(diffuse + specular)) * falloff;
colour += &light_intensity;
}
colour *= 255.0;
let (r, g, b) = (colour.x as u8, colour.y as u8, colour.z as u8);
Vector3::new(r, g, b)
}
fn light_blocked(scene: &Scene, ray: Ray) -> bool {
for (_, node) in &scene.nodes {
let ray = ray.transform(&node.inv_model);
if node.primitive.intersect_bounding_box(&ray) {
if node.primitive.intersect_ray(&ray).is_some() {
return true;
}
}
}
false
}