slight refactor (2)
This commit is contained in:
parent
194ea035e2
commit
6bcbd18fe5
6 changed files with 94 additions and 87 deletions
|
@ -13,8 +13,6 @@ use std::path::PathBuf;
|
|||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use rayon::prelude::{IntoParallelIterator, ParallelIterator};
|
||||
use scene::data::ObjectKind;
|
||||
use scene::Scene;
|
||||
|
||||
use crate::image::Image;
|
||||
|
|
|
@ -5,7 +5,7 @@ use ordered_float::NotNan;
|
|||
use crate::ray::Ray;
|
||||
use crate::utils::compute_rotation_matrix;
|
||||
|
||||
use super::data::{IntersectionContext, ObjectKind};
|
||||
use super::{data::ObjectKind, illumination::IntersectionContext};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cylinder {
|
||||
|
|
|
@ -2,12 +2,12 @@ use std::fmt::Debug;
|
|||
|
||||
use anyhow::Result;
|
||||
use nalgebra::Vector3;
|
||||
use ordered_float::NotNan;
|
||||
|
||||
use crate::image::Color;
|
||||
use crate::ray::Ray;
|
||||
|
||||
use super::Scene;
|
||||
use super::illumination::IntersectionContext;
|
||||
|
||||
pub trait ObjectKind: Debug + Send + Sync {
|
||||
/// Determine where the ray intersects this object, returning the earliest
|
||||
|
@ -74,89 +74,7 @@ pub struct DepthCueing {
|
|||
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 {
|
||||
/// 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
|
||||
pub fn compute_viewing_window(&self, distance: f64) -> Rect {
|
||||
// Compute viewing directions
|
||||
|
|
90
assignment-1b/src/scene/illumination.rs
Normal file
90
assignment-1b/src/scene/illumination.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ pub mod cylinder;
|
|||
pub mod data;
|
||||
pub mod input_file;
|
||||
pub mod sphere;
|
||||
pub mod illumination;
|
||||
|
||||
use nalgebra::Vector3;
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ use ordered_float::NotNan;
|
|||
|
||||
use crate::{ray::Ray, utils::min_f64};
|
||||
|
||||
use super::data::{IntersectionContext, ObjectKind};
|
||||
use super::{data::ObjectKind, illumination::IntersectionContext};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Sphere {
|
||||
|
|
Loading…
Reference in a new issue