slight refactor (2)

This commit is contained in:
Michael Zhang 2023-02-15 01:22:42 -06:00
parent 00000330fe
commit 0000034060
6 changed files with 94 additions and 87 deletions

View file

@ -13,8 +13,6 @@ use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use clap::Parser; use clap::Parser;
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
use scene::data::ObjectKind;
use scene::Scene; use scene::Scene;
use crate::image::Image; use crate::image::Image;

View file

@ -5,7 +5,7 @@ use ordered_float::NotNan;
use crate::ray::Ray; use crate::ray::Ray;
use crate::utils::compute_rotation_matrix; use crate::utils::compute_rotation_matrix;
use super::data::{IntersectionContext, ObjectKind}; use super::{data::ObjectKind, illumination::IntersectionContext};
#[derive(Debug)] #[derive(Debug)]
pub struct Cylinder { pub struct Cylinder {

View file

@ -2,12 +2,12 @@ use std::fmt::Debug;
use anyhow::Result; use anyhow::Result;
use nalgebra::Vector3; use nalgebra::Vector3;
use ordered_float::NotNan;
use crate::image::Color; use crate::image::Color;
use crate::ray::Ray; use crate::ray::Ray;
use super::Scene; use super::Scene;
use super::illumination::IntersectionContext;
pub trait ObjectKind: Debug + Send + Sync { pub trait ObjectKind: Debug + Send + Sync {
/// Determine where the ray intersects this object, returning the earliest /// Determine where the ray intersects this object, returning the earliest
@ -74,89 +74,7 @@ pub struct DepthCueing {
dist_min: f64, dist_min: f64,
} }
/// Information about an intersection
#[derive(Derivative)]
#[derivative(Debug, PartialEq, PartialOrd, Ord)]
pub struct IntersectionContext {
/// The time of the intersection in the parametric ray
///
/// Unfortunately, IEEE floats in Rust don't have total ordering, because
/// NaNs violate ordering properties. The way to remedy this is to ensure we
/// don't have NaNs by wrapping it into this type, which then implements
/// total ordering.
pub time: NotNan<f64>,
/// The intersection point.
#[derivative(PartialEq = "ignore", Ord = "ignore")]
pub point: Vector3<f64>,
/// The normal vector protruding from the surface of the object at the
/// intersection point
#[derivative(PartialEq = "ignore", Ord = "ignore")]
pub normal: Vector3<f64>,
}
impl Eq for IntersectionContext {}
impl Scene { impl Scene {
/// Determine the color that should be used to fill this pixel.
///
/// - material_idx is the index into the materials list.
/// - intersection_context contains information on vectors where the
/// intersection occurred
///
/// Also known as Shade_Ray in the slides.
pub fn compute_pixel_color(
&self,
material_idx: usize,
intersection_context: IntersectionContext,
) -> Color {
// TODO: Does it make sense to make this function fallible from an API
// design standpoint?
let material = match self.materials.get(material_idx) {
Some(v) => v,
None => return self.bkg_color,
};
let ambient_component = material.k_a * material.diffuse_color;
// Diffuse and specular lighting for each separate light
let diffuse_and_specular: Vector3<f64> = self
.lights
.iter()
.map(|light| {
// The vector pointing in the direction of the light
let light_direction = match light.kind {
LightKind::Point { location } => {
location - intersection_context.point
}
LightKind::Directional { direction } => direction,
}
.normalize();
let normal = intersection_context.normal.normalize();
let viewer_direction = self.eye_pos - intersection_context.point;
let halfway_direction =
((light_direction + viewer_direction) / 2.0).normalize();
let diffuse_component = material.k_d
* material.diffuse_color
* normal.dot(&light_direction).max(0.0);
let specular_component = material.k_s
* material.specular_color
* normal
.dot(&halfway_direction)
.max(0.0)
.powf(material.exponent);
diffuse_component + specular_component
})
.sum();
ambient_component + diffuse_and_specular
}
/// Determine the boundaries of the viewing window in world coordinates /// Determine the boundaries of the viewing window in world coordinates
pub fn compute_viewing_window(&self, distance: f64) -> Rect { pub fn compute_viewing_window(&self, distance: f64) -> Rect {
// Compute viewing directions // Compute viewing directions

View file

@ -0,0 +1,90 @@
use nalgebra::Vector3;
use ordered_float::NotNan;
use crate::image::Color;
use super::{Scene, data::LightKind};
/// Information about an intersection
#[derive(Derivative)]
#[derivative(Debug, PartialEq, PartialOrd, Ord)]
pub struct IntersectionContext {
/// The time of the intersection in the parametric ray
///
/// Unfortunately, IEEE floats in Rust don't have total ordering, because
/// NaNs violate ordering properties. The way to remedy this is to ensure we
/// don't have NaNs by wrapping it into this type, which then implements
/// total ordering.
pub time: NotNan<f64>,
/// The intersection point.
#[derivative(PartialEq = "ignore", Ord = "ignore")]
pub point: Vector3<f64>,
/// The normal vector protruding from the surface of the object at the
/// intersection point
#[derivative(PartialEq = "ignore", Ord = "ignore")]
pub normal: Vector3<f64>,
}
impl Eq for IntersectionContext {}
impl Scene {
/// Determine the color that should be used to fill this pixel.
///
/// - material_idx is the index into the materials list.
/// - intersection_context contains information on vectors where the
/// intersection occurred
///
/// Also known as Shade_Ray in the slides.
pub fn compute_pixel_color(
&self,
material_idx: usize,
intersection_context: IntersectionContext,
) -> Color {
// TODO: Does it make sense to make this function fallible from an API
// design standpoint?
let material = match self.materials.get(material_idx) {
Some(v) => v,
None => return self.bkg_color,
};
let ambient_component = material.k_a * material.diffuse_color;
// Diffuse and specular lighting for each separate light
let diffuse_and_specular: Vector3<f64> = self
.lights
.iter()
.map(|light| {
// The vector pointing in the direction of the light
let light_direction = match light.kind {
LightKind::Point { location } => {
location - intersection_context.point
}
LightKind::Directional { direction } => direction,
}
.normalize();
let normal = intersection_context.normal.normalize();
let viewer_direction = self.eye_pos - intersection_context.point;
let halfway_direction =
((light_direction + viewer_direction) / 2.0).normalize();
let diffuse_component = material.k_d
* material.diffuse_color
* normal.dot(&light_direction).max(0.0);
let specular_component = material.k_s
* material.specular_color
* normal
.dot(&halfway_direction)
.max(0.0)
.powf(material.exponent);
diffuse_component + specular_component
})
.sum();
ambient_component + diffuse_and_specular
}
}

View file

@ -2,6 +2,7 @@ pub mod cylinder;
pub mod data; pub mod data;
pub mod input_file; pub mod input_file;
pub mod sphere; pub mod sphere;
pub mod illumination;
use nalgebra::Vector3; use nalgebra::Vector3;

View file

@ -4,7 +4,7 @@ use ordered_float::NotNan;
use crate::{ray::Ray, utils::min_f64}; use crate::{ray::Ray, utils::min_f64};
use super::data::{IntersectionContext, ObjectKind}; use super::{data::ObjectKind, illumination::IntersectionContext};
#[derive(Debug)] #[derive(Debug)]
pub struct Sphere { pub struct Sphere {