diff --git a/src/material/mod.rs b/src/material/mod.rs index 3aa6bb7..f946891 100644 --- a/src/material/mod.rs +++ b/src/material/mod.rs @@ -2,6 +2,17 @@ use super::core::color::LinearColor; use super::Point2D; use serde::Deserialize; +/// A structure holding all the physical proprerties relating to light at a point. +#[derive(Debug, PartialEq, Clone)] +pub struct LightProperties { + /// The diffuse component. + pub diffuse: LinearColor, + /// The specular component, + pub specular: LinearColor, + /// The reflectivity coefficient, + pub reflectivity: f32, +} + /// All the existing `Material` implementation. #[serde(tag = "type")] #[serde(rename_all = "lowercase")] @@ -15,12 +26,8 @@ pub enum MaterialEnum { /// Represent the physical light properties of an object in the scene; #[enum_dispatch::enum_dispatch(MaterialEnum)] pub trait Material: std::fmt::Debug { - /// The diffuse component on a texel point. - fn diffuse(&self, point: Point2D) -> LinearColor; - /// The specular component on a texel point. - fn specular(&self, point: Point2D) -> LinearColor; - /// The reflectivity coefficient on a texel point. - fn reflectivity(&self, point: Point2D) -> f32; + /// Get the physical properties at a point. + fn properties(&self, point: Point2D) -> LightProperties; } pub mod uniform; diff --git a/src/material/uniform.rs b/src/material/uniform.rs index bb73c11..85584d1 100644 --- a/src/material/uniform.rs +++ b/src/material/uniform.rs @@ -1,3 +1,4 @@ +use super::LightProperties; use super::Material; use crate::core::color::LinearColor; use crate::Point2D; @@ -22,16 +23,12 @@ impl UniformMaterial { } impl Material for UniformMaterial { - fn diffuse(&self, _: Point2D) -> LinearColor { - self.diffuse.clone() - } - - fn specular(&self, _: Point2D) -> LinearColor { - self.specular.clone() - } - - fn reflectivity(&self, _: Point2D) -> f32 { - self.reflectivity + fn properties(&self, _: Point2D) -> LightProperties { + LightProperties { + diffuse: self.diffuse.clone(), + specular: self.specular.clone(), + reflectivity: self.reflectivity, + } } } @@ -67,7 +64,7 @@ mod test { fn diffuse_works() { let mat = simple_material(); assert_eq!( - mat.diffuse(Point2D::origin()), + mat.properties(Point2D::origin()).diffuse, LinearColor::new(0.5, 0.5, 0.5) ) } @@ -76,7 +73,7 @@ mod test { fn specular_works() { let mat = simple_material(); assert_eq!( - mat.specular(Point2D::origin()), + mat.properties(Point2D::origin()).specular, LinearColor::new(1., 1., 1.) ) } @@ -84,7 +81,7 @@ mod test { #[test] fn reflectivity_works() { let mat = simple_material(); - assert!(mat.reflectivity(Point2D::origin()) - 0.5 < std::f32::EPSILON) + assert!(mat.properties(Point2D::origin()).reflectivity - 0.5 < std::f32::EPSILON) } #[test] diff --git a/src/render/scene.rs b/src/render/scene.rs index 14fc81e..70af479 100644 --- a/src/render/scene.rs +++ b/src/render/scene.rs @@ -1,10 +1,10 @@ use super::{light_aggregate::LightAggregate, object::Object}; use crate::{ core::{Camera, LinearColor}, - material::Material, + material::{LightProperties, Material}, shape::Shape, texture::Texture, - {Point, Point2D, Vector}, + {Point, Vector}, }; use bvh::{bvh::BVH, ray::Ray}; use image::RgbImage; @@ -114,24 +114,28 @@ impl Scene { let normal = object.shape.normal(&point); let reflected = reflected(incident_ray, normal); let texel = object.shape.project_texel(&point); - self.illuminate(point, object, texel, normal, reflected) - + self.reflection(point, object, texel, reflected, reflection_limit) + + let properties = object.material.properties(texel); + let object_color = object.texture.texel_color(texel); + + self.illuminate(point, object_color, &properties, normal, reflected) + + self.reflection(point, &properties, reflected, reflection_limit) } fn reflection( &self, point: Point, - object: &Object, - texel: Point2D, + properties: &LightProperties, reflected: Vector, reflection_limit: u32, ) -> LinearColor { - let reflectivity = object.material.reflectivity(texel); + let reflectivity = properties.reflectivity; if reflectivity > 1e-5 && reflection_limit > 0 { let reflection_start = point + reflected * 0.001; if let Some((t, obj)) = self.cast_ray(Ray::new(reflection_start, reflected)) { let resulting_position = reflection_start + reflected * t; - return self.color_at(resulting_position, obj, reflected, reflection_limit - 1); + let color = self.color_at(resulting_position, obj, reflected, reflection_limit - 1); + return color * reflectivity; } }; LinearColor::black() @@ -140,13 +144,13 @@ impl Scene { fn illuminate( &self, point: Point, - object: &Object, - texel: Point2D, + object_color: LinearColor, + properties: &LightProperties, normal: Vector, reflected: Vector, ) -> LinearColor { - let ambient = self.illuminate_ambient(object.texture.texel_color(texel)); - let spatial = self.illuminate_spatial(point, object, texel, normal, reflected); + let ambient = self.illuminate_ambient(object_color); + let spatial = self.illuminate_spatial(point, properties, normal, reflected); ambient + spatial } @@ -160,13 +164,10 @@ impl Scene { fn illuminate_spatial( &self, point: Point, - object: &Object, - texel: Point2D, + properties: &LightProperties, normal: Vector, reflected: Vector, ) -> LinearColor { - let k_d = object.material.diffuse(texel); - let k_s = object.material.specular(texel); self.lights .spatial_lights_iter() .map(|light| { @@ -178,8 +179,8 @@ impl Scene { _ => {} } let lum = light.illumination(&point); - let diffused = k_d.clone() * normal.dot(&direction); - let specular = k_s.clone() * reflected.dot(&direction); + let diffused = properties.diffuse.clone() * normal.dot(&direction); + let specular = properties.specular.clone() * reflected.dot(&direction); lum * (diffused + specular) }) .sum()