Working realtime tracing

This commit is contained in:
STP
2023-11-16 17:14:24 -05:00
parent a80da71455
commit 9383ef5bbf
7 changed files with 182 additions and 248 deletions

99
Cargo.lock generated
View File

@@ -657,12 +657,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.3"
@@ -755,12 +749,6 @@ dependencies = [
"hashbrown 0.14.2",
]
[[package]]
name = "indoc"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8"
[[package]]
name = "instant"
version = "0.1.12"
@@ -907,15 +895,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "metal"
version = "0.24.0"
@@ -1078,7 +1057,7 @@ dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset 0.6.5",
"memoffset",
]
[[package]]
@@ -1091,7 +1070,7 @@ dependencies = [
"bitflags 1.3.2",
"cfg-if",
"libc",
"memoffset 0.6.5",
"memoffset",
]
[[package]]
@@ -1297,67 +1276,6 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f89dff0959d98c9758c88826cc002e2c3d0b9dfac4139711d1f30de442f1139b"
[[package]]
name = "pyo3"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04e8453b658fe480c3e70c8ed4e3d3ec33eb74988bd186561b0cc66b85c3bc4b"
dependencies = [
"cfg-if",
"indoc",
"libc",
"memoffset 0.9.0",
"parking_lot",
"pyo3-build-config",
"pyo3-ffi",
"pyo3-macros",
"unindent",
]
[[package]]
name = "pyo3-build-config"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96fe70b176a89cff78f2fa7b3c930081e163d5379b4dcdf993e3ae29ca662e5"
dependencies = [
"once_cell",
"target-lexicon",
]
[[package]]
name = "pyo3-ffi"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "214929900fd25e6604661ed9cf349727c8920d47deff196c4e28165a6ef2a96b"
dependencies = [
"libc",
"pyo3-build-config",
]
[[package]]
name = "pyo3-macros"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dac53072f717aa1bfa4db832b39de8c875b7c7af4f4a6fe93cdbf9264cf8383b"
dependencies = [
"proc-macro2",
"pyo3-macros-backend",
"quote",
"syn 2.0.39",
]
[[package]]
name = "pyo3-macros-backend"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7774b5a8282bd4f25f803b1f0d945120be959a36c72e08e7cd031c792fdfd424"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "quote"
version = "1.0.33"
@@ -1459,7 +1377,6 @@ dependencies = [
"nalgebra",
"pixels",
"pollster",
"pyo3",
"roots",
"winit",
"winit_input_helper",
@@ -1670,12 +1587,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "target-lexicon"
version = "0.12.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a"
[[package]]
name = "termcolor"
version = "1.4.0"
@@ -1780,12 +1691,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "unindent"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce"
[[package]]
name = "vec_map"
version = "0.8.2"

View File

@@ -12,7 +12,6 @@ pollster = "0.3"
anyhow = "1.0"
nalgebra = "0.32.3"
roots = "0.0.8"
pyo3 = "0.20.0"
imgui = "0.11"
imgui-wgpu = "0.23"

View File

@@ -1,9 +1,12 @@
use crate::ray::Ray;
use crate::{EPSILON, INFINITY};
use log::error;
use nalgebra as nm;
use nalgebra::Matrix4;
use nalgebra::Perspective3;
use nalgebra::Point3;
use nalgebra::Vector3;
use std::env;
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: Matrix4<f32> = Matrix4::new(
@@ -35,8 +38,8 @@ impl Camera {
) -> Self {
let znear = EPSILON;
let zfar = INFINITY;
let matrix = self.build_view_projection_matrix(eye, target, up, aspect, fovy, znear, zfar);
let inverse = self.build_inverse_view_projection_matrix(eye, target, up, aspect, fovy, znear, zfar);
let (matrix, inverse) =
Camera::build_matrix_and_inverse(&eye, &target, &up, aspect, fovy, znear, zfar);
Camera {
eye,
target,
@@ -45,42 +48,73 @@ impl Camera {
aspect,
znear,
zfar,
matrix,
inverse,
}
}
pub fn build_view_projection_matrix(eye: Point3<f32>, target: Point3<f32>, up: Vector3<f32>, aspect: f32, fovy: f32, znear: f32, zfar: f32) -> Matrix4<f32> {
pub fn build_matrix_and_inverse(
eye: &Point3<f32>,
target: &Point3<f32>,
up: &Vector3<f32>,
aspect: f32,
fovy: f32,
znear: f32,
zfar: f32,
) -> (Matrix4<f32>, Matrix4<f32>) {
let view = Matrix4::look_at_lh(eye, target, up);
let proj = Matrix4::new_perspective(aspect, fovy,znear, zfar);
proj * view
let proj = Perspective3::new(aspect, fovy, znear, zfar);
let matrix = proj.as_matrix() * view;
let inverse = view.try_inverse().expect("No view") * proj.inverse();
(matrix, inverse)
}
pub fn build_inverse_view_projection_matrix(eye: Point3<f32>, target: Point3<f32>, up: Vector3<f32>, aspect: f32, fovy: f32, znear: f32, zfar: f32) -> Matrix4<f32> {
let view_proj = self.build_view_projection_matrix(eye, target, up, aspect, fovy, znear, zfar);
view_proj.try_inverse().expect("Cannot invert!")
}
pub fn cast_rays(&self, width: u32, height: u32) -> Vec<Ray> {
let inverse_matrix = self.build_inverse_view_projection_matrix();
let dx = 2.0 / width as f32;
let dy = 2.0 / height as f32;
pub fn cast_rays(&self, width: i32, height: i32) -> Vec<Ray> {
let aspect = width as f64 / height as f64;
let fovy_radians = (self.fovy as f64).to_radians();
let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan();
let view_direction = (self.target - self.eye).normalize(); // Normalize the view direction vector
let hor = view_direction.cross(&self.up).normalize(); // pointing right
let vert = view_direction.cross(&hor).normalize(); // pointing up
let h_width = 2.0 * (fovh_radians / 2.0).tan();
let v_height = 2.0 * (fovy_radians / 2.0).tan();
let d_hor_vec = hor * (h_width / width as f64) as f32;
let d_vert_vec = vert * (v_height / height as f64) as f32;
let mut rays = Vec::with_capacity(width as usize * height as usize);
for i in 0..width {
for j in 0..height {
let x = -1.0 + i as f32 * dx;
let y = 1.0 - j as f32 * dy;
let a = inverse_matrix.transform_point(&Point3::new(x, y, -1.0));
let b = inverse_matrix.transform_vector(&Vector3::new(0.0, 0.0, 1.0));
let ray = Ray { a, b };
for j in 0..height {
for i in 0..width {
let horizontal = (i as f32 - width as f32 / 2.0) * d_hor_vec;
let vertical = (j as f32 - height as f32 / 2.0) * d_vert_vec;
let direction = view_direction + horizontal + vertical;
let ray = Ray::new(self.eye, direction);
rays.push(ray);
}
}
rays
}
pub fn cast_ray(&self, width: u32, height: u32, x: u32, y: u32) -> Ray {
pub fn cast_ray(&self, width: i32, height: i32, x: i32, y: i32) -> Ray {
let aspect = width as f64 / height as f64;
let fovy_radians = (self.fovy as f64).to_radians();
let fovh_radians = 2.0 * ((fovy_radians / 2.0).tan() * aspect).atan();
let view_direction = (self.target - self.eye).normalize(); // Normalize the view direction vector
let dx = 2.0 / width as f32;
let dy = 2.0 / height as f32;
let hor = view_direction.cross(&self.up).normalize(); // pointing right
let vert = view_direction.cross(&hor).normalize(); // pointing up
let h_width = 2.0 * (fovh_radians / 2.0).tan();
let v_height = 2.0 * (fovy_radians / 2.0).tan();
let d_hor_vec = hor * (h_width / width as f64) as f32;
let d_vert_vec = vert * (v_height / height as f64) as f32;
// Calculate the offsets for the pixel's position on the image plane
let horizontal = ((x as f32 / width as f32) - 0.5) * h_width as f32;
let vertical = ((y as f32 / height as f32) - 0.5) * v_height as f32;
// Calculate the ray direction by summing up the components
let direction = view_direction + (horizontal * d_hor_vec) + (vertical * d_vert_vec);
Ray::new(self.eye, direction)
}
}

View File

@@ -9,6 +9,8 @@ pub(crate) struct Gui {
last_frame: Instant,
last_cursor: Option<imgui::MouseCursor>,
about_open: bool,
pub num_rays: i32,
}
impl Gui {
@@ -58,6 +60,7 @@ impl Gui {
last_frame: Instant::now(),
last_cursor: None,
about_open: true,
num_rays: 8,
}
}
@@ -93,17 +96,11 @@ impl Gui {
// Draw windows and GUI elements here
let mut about_open = false;
ui.main_menu_bar(|| {
ui.menu("Help", || {
ui.menu("Options", || {
about_open = ui.menu_item("About...");
});
});
if about_open {
self.about_open = true;
}
if self.about_open {
ui.show_about_window(&mut self.about_open);
}
ui.slider("Num rays", 1, 100, &mut self.num_rays);
// Render Dear ImGui with WGPU
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {

View File

@@ -5,16 +5,13 @@
//Cameras
use crate::camera::Camera;
use crate::gui::Gui;
use crate::light::Light;
use crate::primitive::*;
use crate::scene::Scene;
use crate::{camera::Camera, gui::Gui, light::Light, primitive::*, ray::Ray, scene::Scene};
use log::error;
use error_iter::ErrorIter as _;
use log::error;
use nalgebra::{Point3, Vector3};
use pixels::{Error, Pixels, SurfaceTexture};
use std::env;
use std::sync::Arc;
use winit::dpi::LogicalSize;
use winit::event::{Event, VirtualKeyCode};
@@ -30,8 +27,8 @@ mod ray;
mod raytracer;
mod scene;
const START_WIDTH: u32 = 640;
const START_HEIGHT: u32 = 480;
const START_WIDTH: i32 = 600;
const START_HEIGHT: i32 = 555;
const BOX_SIZE: i16 = 64;
const EPSILON: f32 = 1e-7;
@@ -39,12 +36,16 @@ const INFINITY: f32 = 1e7;
struct State {
scene: Scene,
width: u32,
height: u32,
camera: Camera,
rays: Vec<Ray>,
index: usize,
width: i32,
height: i32,
}
fn main() -> Result<(), Error> {
env_logger::init();
//Window
let event_loop = EventLoop::new();
let mut input = WinitInputHelper::new();
let window = {
@@ -56,22 +57,56 @@ fn main() -> Result<(), Error> {
.build(&event_loop)
.unwrap()
};
//Pixel surface
let mut pixels = {
let window_size = window.inner_size();
let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
Pixels::new(START_WIDTH, START_HEIGHT, surface_texture)?
Pixels::new(START_WIDTH as u32, START_HEIGHT as u32, surface_texture)?
};
let mut state = State::new(START_WIDTH, START_HEIGHT);
//Camera
let eye = Point3::new(0.0, 0.0, 3.0);
let target = Point3::new(0.0, 0.0, 0.0);
let up = Vector3::new(0.0, 1.0, 0.0);
let arc_camera = Arc::new(Camera::new(
eye,
target,
up,
180.0,
(START_WIDTH as f32 / START_HEIGHT as f32) as f32,
));
let camera = Camera::new(
eye,
target,
up,
180.0,
(START_WIDTH as f32 / START_HEIGHT as f32) as f32,
);
let cameras: Vec<Arc<Camera>> = vec![arc_camera.clone()];
//Primitive
let arc_material = Arc::new(Material::magenta());
let mut primitives: Vec<Arc<dyn Primitive>> = Vec::new();
let arc_sphere = Arc::new(Sphere::unit(arc_material.clone()));
let arc_cone = Arc::new(Cone::unit(arc_material.clone()));
primitives.push(arc_sphere.clone());
primitives.push(arc_cone.clone());
//Lights
let light: Arc<Light>;
let light = Arc::new(Light::white());
let lights = vec![light];
let ambient_light = Arc::new(Vector3::new(1.0, 1.0, 0.0));
//State
let scene = Scene::new(primitives, lights, cameras, ambient_light);
let mut state = State::new(START_WIDTH, START_HEIGHT, scene, camera);
// Set up Dear ImGui
let mut gui = Gui::new(&window, &pixels);
event_loop.run(move |event, _, control_flow| {
// Draw the current frame
if let Event::RedrawRequested(_) = event {
for i in 0..gui.num_rays {
state.draw(pixels.frame_mut());
}
// Draw the world
state.draw(pixels.frame_mut());
// Prepare Dear ImGui
gui.prepare(&window).expect("gui.prepare() failed");
// Render everything together
@@ -80,6 +115,7 @@ fn main() -> Result<(), Error> {
context.scaling_renderer.render(encoder, render_target);
// Render Dear ImGui
gui.render(&window, encoder, render_target, context)?;
// *control_flow = ControlFlow::Exit;
Ok(())
});
// Basic error handling
@@ -98,6 +134,7 @@ fn main() -> Result<(), Error> {
*control_flow = ControlFlow::Exit;
return;
}
if input.key_pressed(VirtualKeyCode::A) {}
// Resize the window
if let Some(size) = input.window_resized() {
if size.width > 0 && size.height > 0 {
@@ -108,13 +145,7 @@ fn main() -> Result<(), Error> {
return;
}
// Resize the world
state.resize(size.width, size.height);
if let Err(err) = pixels.resize_buffer(size.width, size.height) {
log_error("pixels.resize_buffer", err);
*control_flow = ControlFlow::Exit;
return;
}
state.resize(size.width as i32, size.height as i32);
}
}
@@ -134,12 +165,16 @@ fn log_error<E: std::error::Error + 'static>(method_name: &str, err: E) {
impl State {
/// Create a new `World` instance that can draw a moving box.
fn new(width: u32, height: u32) -> Self {
let scene = Scene::empty();
fn new(width: i32, height: i32, scene: Scene, camera: Camera) -> Self {
let index = 0;
let rays = camera.cast_rays(width, height);
Self {
width,
height,
index,
rays,
scene,
camera,
}
}
@@ -147,7 +182,7 @@ impl State {
fn update(&mut self) {}
/// Resize the world
fn resize(&mut self, width: u32, height: u32) {
fn resize(&mut self, width: i32, height: i32) {
self.width = width;
self.height = height;
}
@@ -155,61 +190,23 @@ impl State {
/// Draw the `World` state to the frame buffer.
///
/// Assumes the default texture format: `wgpu::TextureFormat::Rgba8UnormSrgb`
fn draw(&self, frame: &mut [u8]) {
for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
let x = (i % self.width as usize) as i16;
let y = (i / self.width as usize) as i16;
fn draw(&mut self, frame: &mut [u8]) {
let ray = &self.rays[self.index];
let colour = raytracer::shade_ray(&self.scene, &ray);
let pixel = &mut frame[self.index * 4..(self.index + 1) * 4]
.copy_from_slice(&[colour.x, colour.y, colour.z, 255]);
self.index += 1;
}
//Create our scene
let eye = Point3::new(0.0, 0.0, -1.0);
let target = Point3::new(0.0, 0.0, 0.0);
let up = Vector3::new(0.0, 1.0, 0.0);
let arc_camera = Arc::new(Camera::new(
eye,
target,
up,
90.0,
(self.width / self.height) as f32,
));
let cameras: Vec<Arc<Camera>> = vec![arc_camera.clone()];
let arc_material = Arc::new(Material::magenta());
let arc_cone = Arc::new(Cone::unit(arc_material));
let primitives: Vec<Arc<dyn Primitive>> = vec![arc_cone]
.into_iter()
.map(|arc| arc as Arc<dyn Primitive>)
.collect();
let light: Arc<Light>;
let light = Arc::new(Light::white());
let lights = vec![light];
let ambient_light = Arc::new(Vector3::new(1.0, 1.0, 1.0));
let scene = Scene::new(primitives, lights, cameras, ambient_light);
let rays = arc_camera.as_ref().cast_rays(self.width, self.height);
let colours = raytracer::shade_rays(&scene, &rays, self.width, self.height);
println!("{}", colours.len());
//
// let pixels = pixels.frame().chunks_exact_mut(4);
// for (i, colour) in colours.iter().enumerate() {
// let colour = colours[i];
// let pixel = &mut pixels[i];
// pixel[0] = colour.x;
// pixel[1] = colour.y;
// pixel[2] = colour.z;
// }
//
// // Render the frame
// if pixels.render().is_err() {
// eprintln!("Failed to render frame");
// }
let rgba = [0x48, 0xb2, 0xe8, 0xff];
pixel.copy_from_slice(&rgba);
fn draw_all(&mut self, frame: &mut [u8]) {
let rays = self.camera.cast_rays(self.width, self.height);
let colours = raytracer::shade_rays(&self.scene, &rays, self.width, self.height);
for (i, colour) in colours.iter().enumerate() {
let colour = colours[i];
// pixel[0] = colour.x;
// pixel[1] = colour.y;
// pixel[2] = colour.z;
// pixel[3] = 255;
}
}
}

View File

@@ -82,7 +82,7 @@ pub trait Primitive {
}
// SPHERE -----------------------------------------------------------------
struct Sphere {
pub struct Sphere {
position: Point3<f32>,
radius: f32,
bounding_box: BoundingBox,
@@ -103,7 +103,7 @@ impl Sphere {
}
}
fn unit(material: Arc<Material>) -> Self {
pub fn unit(material: Arc<Material>) -> Self {
Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0, material)
}
}
@@ -185,7 +185,7 @@ impl Circle {
}
}
fn unit(material: Arc<Material>) -> Self {
pub fn unit(material: Arc<Material>) -> Self {
let position = Point3::new(0.0, 0.0, 0.0);
let normal = Vector3::new(0.0, 1.0, 0.0);
let radius = 1.0;
@@ -413,7 +413,7 @@ impl Rectangle {
bounding_box: BoundingBox { bln, trf },
}
}
fn unit(material: Arc<Material>) -> Self {
pub fn unit(material: Arc<Material>) -> Self {
Rectangle::new(
Point3::new(0.0, 0.0, 0.0),
Vector3::new(0.0, 1.0, 0.0),
@@ -466,7 +466,7 @@ impl Primitive for Rectangle {
}
// BOX -----------------------------------------------------------------
struct Box {
pub struct Box {
width: f32,
height: f32,
depth: f32,
@@ -486,7 +486,7 @@ impl Box {
bounding_box: BoundingBox { bln, trf },
}
}
fn unit(material: Arc<Material>) -> Self {
pub fn unit(material: Arc<Material>) -> Self {
Box::new(2.0, 2.0, 2.0, material)
}
}
@@ -580,7 +580,7 @@ impl Triangle {
bounding_box,
}
}
fn unit(material: Arc<Material>) -> Self {
pub fn unit(material: Arc<Material>) -> Self {
let u = Point3::new(-1.0, 0.0, -1.0);
let v = Point3::new(0.0, 0.0, 1.0);
let w = Point3::new(1.0, 0.0, -1.0);

View File

@@ -8,57 +8,59 @@ use crate::{
use std::sync::Arc;
use nalgebra::{distance, Matrix4, Point3, Vector3, Vector4};
static ZERO_VECTOR: Vector3<f32> = Vector3::new(0.0, 0.0, 0.0);
static ONE_VECTOR: Vector3<f32> = Vector3::new(1.0, 1.0, 1.0);
pub fn shade_rays(scene: &Scene, rays: &Vec<Ray>, width: u32, height: u32) -> Vec<Vector3<u8>> {
let mut pixel_data = vec![];
pub fn shade_rays(scene: &Scene, rays: &Vec<Ray>, width: i32, height: i32) -> Vec<Vector3<u8>> {
let mut pixel_data = vec![Vector3::new(0, 0, 0); (width * height) as usize];
for ray in rays {
let intersect = get_closest_intersection(scene, ray);
match intersect {
Some(interect) => {
let colour = phong_shade_point(scene, &interect);
pixel_data.push(colour);
}
for (pixel_index, ray) in rays.iter().enumerate() {
let intersect = get_closest_intersection(scene.primitives.clone(), ray);
let colour = match intersect {
Some(intersect) => phong_shade_point(scene, &intersect),
None => {
let colour = Vector3::new(0, 0, 0);
pixel_data.push(colour);
// 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) -> Vector3<u8> {
let intersect = get_closest_intersection(scene.primitives.clone(), ray);
match intersect {
Some(intersect) => phong_shade_point(&scene, &intersect),
None => Vector3::new(0, 0, 0),
}
}
// Find the closest intersection, given a ray in world coordinates
pub fn get_closest_intersection(scene: &Scene, ray: &Ray) -> Option<Intersection> {
pub fn get_closest_intersection(
primitives: Vec<Arc<dyn Primitive>>,
ray: &Ray,
) -> Option<Intersection> {
let mut closest_distance = INFINITY;
let mut closest_intersect: Option<Intersection> = None;
for arc_primitive in &scene.primitives {
for arc_primitive in primitives {
let primitive = arc_primitive.clone();
if primitive.intersect_ray(ray).is_none() {
continue;
};
let intersect = primitive.intersect_ray(ray);
if intersect.is_none() {
continue;
};
let intersect = intersect.unwrap();
if intersect.distance < closest_distance {
closest_distance = intersect.distance;
closest_intersect = Some(intersect);
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<u8> {
//Useful vectors !!!! CHECK IF WE CAN OPTIMISE
let zero_vector = Vector3::new(0.0, 0.0, 0.0);
let one_vector = Vector3::new(1.0, 1.0, 1.0);
//Unpack the intersection data
let Intersection {
point,
@@ -102,7 +104,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
let diffuse = if n_dot_l > 0.0 {
kd * n_dot_l
} else {
zero_vector
ZERO_VECTOR
};
// Compute specular
@@ -111,7 +113,7 @@ pub fn phong_shade_point(scene: &Scene, intersect: &Intersection) -> Vector3<u8>
let specular = if n_dot_h > 0.0 {
ks * n_dot_h.powf(shininess)
} else {
zero_vector
ZERO_VECTOR
};
colour += light_colour.component_mul(&((diffuse + specular) * falloff));