diffusion lighting
This commit is contained in:
172
src/ray.rs
172
src/ray.rs
@@ -1,5 +1,24 @@
|
|||||||
use crate::{node::Node, scene::Scene};
|
use crate::{node::Node, scene::Scene};
|
||||||
use nalgebra::{Matrix4, Point3, Vector3};
|
use nalgebra::{Matrix4, Point3, Vector3};
|
||||||
|
use rand;
|
||||||
|
|
||||||
|
const MAX_DEPTH: u8 = 5;
|
||||||
|
const DIFFUSE_RAYS: i8 = 5;
|
||||||
|
const DIFFUSE_COEFFICIENT: f32 = 0.5;
|
||||||
|
|
||||||
|
fn random_vec() -> Vector3<f64> {
|
||||||
|
Vector3::new(rand::random(), rand::random(), rand::random())
|
||||||
|
}
|
||||||
|
fn random_unit_vec() -> Vector3<f64> {
|
||||||
|
random_vec().normalize()
|
||||||
|
}
|
||||||
|
fn random_on_hemisphere(normal: &Vector3<f64>) -> Vector3<f64> {
|
||||||
|
let dir = random_unit_vec();
|
||||||
|
match dir.dot(normal) > 0.0 {
|
||||||
|
true => dir,
|
||||||
|
false => -dir,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// INTERSECTION -----------------------------------------------------------------
|
// INTERSECTION -----------------------------------------------------------------
|
||||||
pub struct Intersection {
|
pub struct Intersection {
|
||||||
@@ -8,6 +27,7 @@ pub struct Intersection {
|
|||||||
pub normal: Vector3<f64>,
|
pub normal: Vector3<f64>,
|
||||||
pub distance: f64,
|
pub distance: f64,
|
||||||
}
|
}
|
||||||
|
//Intersection point including point and normal
|
||||||
impl Intersection {
|
impl Intersection {
|
||||||
pub fn transform(&self, trans: &Matrix4<f64>, inv_trans: &Matrix4<f64>) -> Intersection {
|
pub fn transform(&self, trans: &Matrix4<f64>, inv_trans: &Matrix4<f64>) -> Intersection {
|
||||||
let point = trans.transform_point(&self.point);
|
let point = trans.transform_point(&self.point);
|
||||||
@@ -46,13 +66,35 @@ impl Ray {
|
|||||||
pub fn at_t(&self, t: f64) -> Point3<f64> {
|
pub fn at_t(&self, t: f64) -> Point3<f64> {
|
||||||
self.a + self.b * t
|
self.a + self.b * t
|
||||||
}
|
}
|
||||||
// This function takes a scene and returns the color of the point where the ray intersects the scene
|
// Return a transformed version of the ray
|
||||||
pub fn shade_ray(&self, scene: &Scene) -> Option<Vector3<u8>> {
|
pub fn transform(&self, trans: &Matrix4<f64>) -> Ray {
|
||||||
//Get the closest intersection of the ray with the scene
|
Ray {
|
||||||
|
a: trans.transform_point(&self.a),
|
||||||
|
b: trans.transform_vector(&self.b),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//This function will determine if the ray hits an object in the scene
|
||||||
|
pub fn hit_scene(&self, scene: &Scene) -> bool {
|
||||||
|
for (_, node) in &scene.nodes {
|
||||||
|
if !node.active {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Transform ray into local model cordinates
|
||||||
|
let ray = self.transform(&node.inv_model);
|
||||||
|
// Check bounding box intersection
|
||||||
|
if node.primitive.intersect_bounding_box(&ray) {
|
||||||
|
// Check primitive intersection
|
||||||
|
if node.primitive.intersect_ray(&ray).is_some() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
//This function find the closest intersection point of a ray with an object in the scene
|
||||||
|
pub fn closest_intersect<'a>(&'a self, scene: &'a Scene) -> Option<(&Node, Intersection)> {
|
||||||
let mut closest_distance = f64::MAX;
|
let mut closest_distance = f64::MAX;
|
||||||
let mut closest_intersect: Option<Intersection> = None;
|
let mut closest_intersect: Option<(&Node, Intersection)> = None;
|
||||||
let mut closest_node = None;
|
|
||||||
|
|
||||||
for (_, node) in &scene.nodes {
|
for (_, node) in &scene.nodes {
|
||||||
if !node.active {
|
if !node.active {
|
||||||
continue;
|
continue;
|
||||||
@@ -66,20 +108,28 @@ impl Ray {
|
|||||||
// Check for closest distance
|
// Check for closest distance
|
||||||
if intersect.distance < closest_distance {
|
if intersect.distance < closest_distance {
|
||||||
closest_distance = intersect.distance;
|
closest_distance = intersect.distance;
|
||||||
closest_intersect = Some(intersect);
|
closest_intersect = Some((node, intersect));
|
||||||
closest_node = Some(node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Shade the intersection point if there is one
|
|
||||||
match closest_intersect {
|
match closest_intersect {
|
||||||
Some(intersect) => {
|
Some((node, intersect)) => {
|
||||||
//Inverse transform back to world coords
|
Some((node, intersect.transform(&node.model, &node.inv_model)))
|
||||||
let node = closest_node.unwrap();
|
}
|
||||||
let intersect = intersect.transform(&node.model, &node.inv_model);
|
None => None,
|
||||||
Some(Ray::phong_shade_point(&scene, &self, &node, &intersect)) // If there is an intersection, shade it
|
}
|
||||||
|
}
|
||||||
|
// This function takes a scene and returns the color of the point where the ray intersects the scene
|
||||||
|
pub fn shade_ray(&self, scene: &Scene, depth: u8) -> Option<Vector3<f32>> {
|
||||||
|
if depth == MAX_DEPTH {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match self.closest_intersect(scene) {
|
||||||
|
Some((node, intersect)) => {
|
||||||
|
Some(Ray::phong_shade_point(
|
||||||
|
&scene, &self, &node, &intersect, depth,
|
||||||
|
)) // 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
|
||||||
}
|
}
|
||||||
@@ -91,18 +141,13 @@ impl Ray {
|
|||||||
ray: &Ray,
|
ray: &Ray,
|
||||||
node: &Node,
|
node: &Node,
|
||||||
intersect: &Intersection,
|
intersect: &Intersection,
|
||||||
) -> Vector3<u8> {
|
depth: u8,
|
||||||
|
) -> Vector3<f32> {
|
||||||
let point = &intersect.point;
|
let point = &intersect.point;
|
||||||
let normal = &intersect.normal;
|
let normal = &intersect.normal;
|
||||||
let incidence = &ray.b;
|
let incidence = &ray.b;
|
||||||
|
|
||||||
let material = &node.material;
|
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
|
// Compute the ambient light component and set it as base colour
|
||||||
let mut colour = Vector3::zeros();
|
let mut colour = Vector3::zeros();
|
||||||
@@ -121,59 +166,62 @@ impl Ray {
|
|||||||
let light_distance = to_light.norm() as f32;
|
let light_distance = to_light.norm() as f32;
|
||||||
let to_light = to_light.normalize();
|
let to_light = to_light.normalize();
|
||||||
|
|
||||||
// let to_light_ray = Ray::new(point.clone() + 0.0001 * normal, to_light);
|
let to_light_ray = Ray::new(point.clone() + 0.001 * normal, to_light);
|
||||||
// if to_light_ray.light_blocked(scene) {
|
if to_light_ray.light_blocked(scene, node) {
|
||||||
// continue;
|
continue;
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
let n_dot_l = normal.dot(&to_light).max(0.0) as f32;
|
||||||
|
|
||||||
//Diffuse component
|
//Diffuse component
|
||||||
let n_dot_l = normal.dot(&to_light).max(0.0) as f32;
|
let mut diffuse = Vector3::zeros();
|
||||||
let diffuse = n_dot_l * kd;
|
// diffuse = material.kd * n_dot_l;
|
||||||
|
for _ in 0..DIFFUSE_RAYS {
|
||||||
|
let diffuse_dir = random_on_hemisphere(normal);
|
||||||
|
let ray = Ray::new(point.clone() + normal, diffuse_dir);
|
||||||
|
if let Some(col) = ray.shade_ray(scene, depth + 1) {
|
||||||
|
diffuse += col * DIFFUSE_COEFFICIENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//Specular component
|
//Specular component
|
||||||
let mut specular = Vector3::zeros();
|
let mut specular = Vector3::zeros();
|
||||||
if n_dot_l > 0.0 {
|
if n_dot_l > 0.0 {
|
||||||
// Halfway vector.
|
let h = (to_light - incidence).normalize();
|
||||||
let h = to_camera + to_light.normalize();
|
|
||||||
let n_dot_h = normal.dot(&h).max(0.0) as f32;
|
let n_dot_h = normal.dot(&h).max(0.0) as f32;
|
||||||
specular = ks * n_dot_h.powf(shininess);
|
specular = material.ks * n_dot_h.powf(material.shininess);
|
||||||
}
|
}
|
||||||
// Compute light falloff
|
|
||||||
|
//Falloff
|
||||||
let falloff = 1.0
|
let falloff = 1.0
|
||||||
/ (1.0
|
/ (1.0
|
||||||
+ light.falloff[0]
|
+ light.falloff[0]
|
||||||
+ light.falloff[1] * light_distance
|
+ light.falloff[1] * light_distance
|
||||||
+ light.falloff[2] * light_distance.powi(2));
|
+ light.falloff[2] * light_distance * light_distance);
|
||||||
|
|
||||||
let light_intensity = light.colour.component_mul(&(diffuse + specular)) * falloff;
|
let intensity = light
|
||||||
colour += &light_intensity;
|
.colour
|
||||||
|
.component_mul(&((diffuse + specular) * falloff));
|
||||||
|
colour += &intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
colour *= 255.0;
|
colour
|
||||||
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 {
|
pub fn light_blocked(&self, scene: &Scene, _node: &Node) -> bool {
|
||||||
for (_, node) in &scene.nodes {
|
for (_, node) in &scene.nodes {
|
||||||
if !node.active {
|
if !node.active {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
self.transform(&node.inv_model);
|
let ray = self.transform(&node.inv_model);
|
||||||
if node.primitive.intersect_bounding_box(&self) {
|
if node.primitive.intersect_bounding_box(&ray) {
|
||||||
if node.primitive.intersect_ray(&self).is_some() {
|
if node.primitive.intersect_ray(&ray).is_some() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
// Return a transformed version of the ray
|
|
||||||
pub fn transform(&self, trans: &Matrix4<f64>) -> Ray {
|
|
||||||
Ray {
|
|
||||||
a: trans.transform_point(&self.a),
|
|
||||||
b: trans.transform_vector(&self.b),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//Cast a set of rays
|
//Cast a set of rays
|
||||||
pub fn cast_rays(
|
pub fn cast_rays(
|
||||||
eye: &Point3<f64>,
|
eye: &Point3<f64>,
|
||||||
@@ -190,29 +238,31 @@ impl Ray {
|
|||||||
let fovy_radians = fovy.to_radians();
|
let fovy_radians = fovy.to_radians();
|
||||||
let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan();
|
let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan();
|
||||||
// Vectors pointing forward, right and up
|
// Vectors pointing forward, right and up
|
||||||
let forward = (target - eye).normalize();
|
let zv = (target - eye).normalize();
|
||||||
let right = forward.cross(&up).normalize();
|
let xv = zv.cross(&up).normalize();
|
||||||
let up = right.cross(&forward).normalize();
|
let yv = xv.cross(&zv).normalize();
|
||||||
// ☐ height and width of projection
|
// ☐ height and width of projection
|
||||||
let vheight = 2.0 * (fovy_radians / 2.0).tan();
|
let vheight = 2.0 * (fovy_radians / 2.0).tan();
|
||||||
let vwidth = 2.0 * (fovh_radians / 2.0).tan();
|
let vwidth = 2.0 * (fovh_radians / 2.0).tan();
|
||||||
// Increment of right and up per pixel
|
// Increment of right and up per pixel
|
||||||
let d_hor_vec = right * (vwidth / width);
|
let dy = vheight / height;
|
||||||
let d_vert_vec = up * (vheight / height);
|
let dx = vwidth / width;
|
||||||
|
let dxv = dx * xv;
|
||||||
|
let dyv = dy * yv;
|
||||||
// Half the width for later calculation
|
// Half the width for later calculation
|
||||||
let half_width = width / 2.0;
|
let half_width = width / 2.0;
|
||||||
let half_height = height / 2.0;
|
let half_height = height / 2.0;
|
||||||
// Array of rays
|
// Array of rays
|
||||||
let mut rays = Vec::with_capacity(width as usize * height as usize);
|
let mut rays = Vec::with_capacity(width as usize * height as usize);
|
||||||
// Iterate column by row
|
// Iterate column by row
|
||||||
for row in 0..height as u32 {
|
for y in 0..height as u32 {
|
||||||
for column in 0..width as u32 {
|
for x in 0..width as u32 {
|
||||||
let x = (column as f64) - half_width;
|
let x = (x as f64) - half_width;
|
||||||
let y = half_height - (row as f64);
|
let y = half_height - (y as f64);
|
||||||
|
|
||||||
let horizontal = x * &d_hor_vec;
|
let horizontal = x * &dxv;
|
||||||
let vertical = y * &d_vert_vec;
|
let vertical = y * &dyv;
|
||||||
let direction = (forward + horizontal + vertical).normalize();
|
let direction = (zv + horizontal + vertical).normalize();
|
||||||
let ray = Ray::new(eye.clone(), direction);
|
let ray = Ray::new(eye.clone(), direction);
|
||||||
rays.push(ray);
|
rays.push(ray);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user