Refactor Vector3<f64> -> Point

This commit is contained in:
Michael Zhang 2023-02-20 17:40:19 -06:00
parent 00000520f9
commit 0000053097
17 changed files with 128 additions and 68 deletions

View file

@ -0,0 +1,10 @@
eye 0 0 0
viewdir 0 -0.3 -1
updir 0 1 0
hfov 60
imsize 256 512
bkgcolor 0.1 0.1 0.1
light 0 -1 0 0 1 1 1
mtlcolor 1 1 0 1 1 1 0.2 0.6 0.2 20
sphere 0 -2 -3 1
sphere 0 0 -3 0.5

View file

@ -18,7 +18,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "assignment-1b" name = "assignment-1c"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",

View file

@ -1,5 +1,5 @@
[package] [package]
name = "assignment-1b" name = "assignment-1c"
authors = ["Michael Zhang <zhan4854@umn.edu>"] authors = ["Michael Zhang <zhan4854@umn.edu>"]
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
@ -15,7 +15,7 @@ strip = true
lto = true lto = true
[[bin]] [[bin]]
name = "raytracer1b" name = "raytracer1c"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]

View file

@ -8,8 +8,8 @@ ZIP := zip
PANDOC := pandoc PANDOC := pandoc
CONVERT := convert CONVERT := convert
HANDIN := ./hw1b.michael.zhang.zip HANDIN := ./hw1c.michael.zhang.zip
BINARY := ./raytracer1b BINARY := ./raytracer1c
WRITEUP := ./writeup.pdf WRITEUP := ./writeup.pdf
SHOWCASE := ./showcase.png SHOWCASE := ./showcase.png
SOURCES := Cargo.toml $(shell find -name "*.rs") SOURCES := Cargo.toml $(shell find -name "*.rs")
@ -31,7 +31,7 @@ $(BINARY): $(SOURCES)
-e CARGO_TARGET_DIR=/usr/src/myapp/target/docker \ -e CARGO_TARGET_DIR=/usr/src/myapp/target/docker \
rust \ rust \
cargo build --profile release-handin cargo build --profile release-handin
mv target/docker/release-handin/raytracer1b $@ mv target/docker/release-handin/raytracer1c $@
$(HANDIN): $(BINARY) $(WRITEUP) Makefile Cargo.toml Cargo.lock README.md $(EXAMPLES_PNG) $(EXAMPLES_PPM) $(SHOWCASE) $(HANDIN): $(BINARY) $(WRITEUP) Makefile Cargo.toml Cargo.lock README.md $(EXAMPLES_PNG) $(EXAMPLES_PPM) $(SHOWCASE)
$(ZIP) -r $@ src examples $^ $(ZIP) -r $@ src examples $^

View file

@ -0,0 +1,17 @@
eye 0 0 0
viewdir 0 0 -1
updir 0 1 0
hfov 60
imsize 512 256
bkgcolor 0.1 0.1 0.1
light -2 1 0 1 1 1 1
mtlcolor 1 0 1 1 1 1 0.1 0.4 0.4 20
v -2 1 -5
v -1 -1 -4
v 1 -1 -4
v 2 1 -6
f 1 2 3
f 1 3 4

View file

@ -0,0 +1,21 @@
eye 0 0 0
viewdir 0 0 -1
updir 0 1 0
hfov 60
imsize 512 256
bkgcolor 0.1 0.1 0.1
light -2 1 0 1 1 1 1
mtlcolor 1 0 1 1 1 1 0.1 0.4 0.4 20
v -2 1 -5
v -1 -1 -4
v 1 -1 -4
v 2 1 -6
vn -2 1 1
vn -1 -1 1
vn 2 1 1
f 1//1 2//2 3//3
f 1 3 4

View file

@ -0,0 +1,10 @@
eye 2 -6 1
viewdir -1 3 -0.5
updir 0 0 1
hfov 50
imsize 512 512
bkgcolor 0.5 0.7 0.9
light 0 1 -1 0 1 1 1
mtlcolor 0 1 0 1 1 1 0.2 0.8 0.1 20
texture earthtexture.ppm
sphere 0 0 0 2

View file

@ -1,5 +1,7 @@
#![doc = include_str!("../README.md")] #![doc = include_str!("../README.md")]
use nalgebra::Vector3;
#[macro_use] #[macro_use]
extern crate anyhow; extern crate anyhow;
#[macro_use] #[macro_use]
@ -11,3 +13,5 @@ pub mod image;
pub mod ray; pub mod ray;
pub mod scene; pub mod scene;
pub mod utils; pub mod utils;
pub type Point = Vector3<f64>;

View file

@ -5,9 +5,9 @@ use std::fs::File;
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use assignment_1b::image::Image; use assignment_1c::image::Image;
use assignment_1b::ray::Ray; use assignment_1c::ray::Ray;
use assignment_1b::scene::Scene; use assignment_1c::scene::Scene;
use clap::Parser; use clap::Parser;

View file

@ -1,4 +1,4 @@
use nalgebra::Vector3; use crate::Point;
/// A normalized parametric Ray of the form (origin + direction * time) /// A normalized parametric Ray of the form (origin + direction * time)
/// ///
@ -6,13 +6,13 @@ use nalgebra::Vector3;
/// time occurs on the ray. /// time occurs on the ray.
#[derive(Debug)] #[derive(Debug)]
pub struct Ray { pub struct Ray {
pub origin: Vector3<f64>, pub origin: Point,
pub direction: Vector3<f64>, pub direction: Point,
} }
impl Ray { impl Ray {
/// Construct a ray from endpoints /// Construct a ray from endpoints
pub fn from_endpoints(start: Vector3<f64>, end: Vector3<f64>) -> Self { pub fn from_endpoints(start: Point, end: Point) -> Self {
let delta = (end - start).normalize(); let delta = (end - start).normalize();
Ray { Ray {
origin: start, origin: start,
@ -21,7 +21,7 @@ impl Ray {
} }
/// Evaluate the ray at a certain point in time, yielding a point /// Evaluate the ray at a certain point in time, yielding a point
pub fn eval(&self, time: f64) -> Vector3<f64> { pub fn eval(&self, time: f64) -> Point {
self.origin + self.direction * time self.origin + self.direction * time
} }
} }

View file

@ -1,16 +1,16 @@
use anyhow::Result; use anyhow::Result;
use nalgebra::Vector3;
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::ray::Ray;
use crate::utils::compute_rotation_matrix; use crate::utils::compute_rotation_matrix;
use crate::{ray::Ray, Point};
use super::{illumination::IntersectionContext}; use super::illumination::IntersectionContext;
#[derive(Debug)] #[derive(Debug)]
pub struct Cylinder { pub struct Cylinder {
pub center: Vector3<f64>, pub center: Point,
pub direction: Vector3<f64>, pub direction: Point,
pub radius: f64, pub radius: f64,
pub length: f64, pub length: f64,
} }
@ -26,7 +26,7 @@ impl Cylinder {
) -> Result<Option<IntersectionContext>> { ) -> Result<Option<IntersectionContext>> {
// Determine rotation matrix for turning the cylinder upright along the // Determine rotation matrix for turning the cylinder upright along the
// Z-axis // Z-axis
let target_direction = Vector3::new(0.0, 0.0, 1.0); let target_direction = Point::new(0.0, 0.0, 1.0);
let rotation_matrix = let rotation_matrix =
compute_rotation_matrix(self.direction, target_direction)?; compute_rotation_matrix(self.direction, target_direction)?;
let inverse_rotation_matrix = let inverse_rotation_matrix =
@ -153,7 +153,7 @@ impl Cylinder {
} }
let normal_rotated = let normal_rotated =
Vector3::new(0.0, 0.0, rotated_point.z - rotated_cylinder_center.z) Point::new(0.0, 0.0, rotated_point.z - rotated_cylinder_center.z)
.normalize(); .normalize();
let normal = inverse_rotation_matrix * normal_rotated; let normal = inverse_rotation_matrix * normal_rotated;
@ -179,23 +179,21 @@ impl Cylinder {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use nalgebra::Vector3; use crate::{ray::Ray, Point};
use crate::{ray::Ray};
use super::Cylinder; use super::Cylinder;
#[test] #[test]
fn test_cylinder() { fn test_cylinder() {
let cylinder = Cylinder { let cylinder = Cylinder {
center: Vector3::new(0.0, 0.0, 0.0), center: Point::new(0.0, 0.0, 0.0),
direction: Vector3::new(0.0, 1.0, 0.0), direction: Point::new(0.0, 1.0, 0.0),
radius: 3.0, radius: 3.0,
length: 4.0, length: 4.0,
}; };
let eye = Vector3::new(0.0, 3.0, 3.0); let eye = Point::new(0.0, 3.0, 3.0);
let end = Vector3::new(0.0, 2.0, 2.0); let end = Point::new(0.0, 2.0, 2.0);
let ray = Ray::from_endpoints(eye, end); let ray = Ray::from_endpoints(eye, end);
let res = cylinder.intersects_ray_at(&ray); let res = cylinder.intersects_ray_at(&ray);

View file

@ -1,11 +1,11 @@
use std::fmt::Debug; use std::fmt::Debug;
use anyhow::Result; use anyhow::Result;
use nalgebra::Vector3;
use crate::image::Color; use crate::image::Color;
use crate::ray::Ray; use crate::ray::Ray;
use crate::utils::cross; use crate::utils::cross;
use crate::Point;
use super::cylinder::Cylinder; use super::cylinder::Cylinder;
use super::illumination::IntersectionContext; use super::illumination::IntersectionContext;
@ -46,16 +46,16 @@ pub struct Object {
#[derive(Debug)] #[derive(Debug)]
pub struct Rect { pub struct Rect {
pub upper_left: Vector3<f64>, pub upper_left: Point,
pub upper_right: Vector3<f64>, pub upper_right: Point,
pub lower_left: Vector3<f64>, pub lower_left: Point,
pub lower_right: Vector3<f64>, pub lower_right: Point,
} }
#[derive(Debug)] #[derive(Debug)]
pub struct Material { pub struct Material {
pub diffuse_color: Vector3<f64>, pub diffuse_color: Point,
pub specular_color: Vector3<f64>, pub specular_color: Point,
pub k_a: f64, pub k_a: f64,
pub k_d: f64, pub k_d: f64,
@ -67,7 +67,7 @@ pub struct Material {
pub enum LightKind { pub enum LightKind {
/// A point light source exists at a point and emits light in all directions /// A point light source exists at a point and emits light in all directions
Point { Point {
location: Vector3<f64>, location: Point,
/// Whether light attenuation is enabled for this light /// Whether light attenuation is enabled for this light
attenuation: Option<Attenuation>, attenuation: Option<Attenuation>,
@ -75,7 +75,7 @@ pub enum LightKind {
/// A directional light source exists at an infinitely far location but emits /// A directional light source exists at an infinitely far location but emits
/// light in a specific direction /// light in a specific direction
Directional { direction: Vector3<f64> }, Directional { direction: Point },
} }
#[derive(Debug)] #[derive(Debug)]
@ -84,13 +84,13 @@ pub struct Light {
pub kind: LightKind, pub kind: LightKind,
/// The color, or intensity, of the light source /// The color, or intensity, of the light source
pub color: Vector3<f64>, pub color: Point,
} }
impl Light { impl Light {
/// Get the unit directional vector pointing from the given point to this /// Get the unit directional vector pointing from the given point to this
/// light source /// light source
pub fn direction_from(&self, point: Vector3<f64>) -> Vector3<f64> { pub fn direction_from(&self, point: Point) -> Point {
match self.kind { match self.kind {
LightKind::Point { location, .. } => location - point, LightKind::Point { location, .. } => location - point,
LightKind::Directional { direction } => -direction, LightKind::Directional { direction } => -direction,
@ -200,7 +200,7 @@ impl Scene {
pub fn pixel_translation_function( pub fn pixel_translation_function(
&self, &self,
distance: f64, distance: f64,
) -> impl Fn(usize, usize) -> Vector3<f64> { ) -> impl Fn(usize, usize) -> Point {
let view_window = self.compute_viewing_window(distance); let view_window = self.compute_viewing_window(distance);
let dx = view_window.upper_right - view_window.upper_left; let dx = view_window.upper_right - view_window.upper_left;

View file

@ -1,6 +1,5 @@
use std::iter; use std::iter;
use nalgebra::Vector3;
use ordered_float::NotNan; use ordered_float::NotNan;
use rand::Rng; use rand::Rng;
use rayon::prelude::{ use rayon::prelude::{
@ -8,7 +7,7 @@ use rayon::prelude::{
ParallelIterator, ParallelIterator,
}; };
use crate::{image::Color, ray::Ray, utils::dot}; use crate::{image::Color, ray::Ray, utils::dot, Point};
use super::{ use super::{
data::{DepthCueing, Light, LightKind, Object}, data::{DepthCueing, Light, LightKind, Object},
@ -39,7 +38,7 @@ impl Scene {
let ambient_component = material.k_a * material.diffuse_color; let ambient_component = material.k_a * material.diffuse_color;
// Diffuse and specular lighting for each separate light // Diffuse and specular lighting for each separate light
let diffuse_and_specular: Vector3<f64> = self let diffuse_and_specular: Point = self
.lights .lights
.par_iter() .par_iter()
.map(|light| { .map(|light| {
@ -131,7 +130,7 @@ impl Scene {
pub fn compute_shadow_coefficient( pub fn compute_shadow_coefficient(
&self, &self,
obj_idx: usize, obj_idx: usize,
point: Vector3<f64>, point: Point,
light: &Light, light: &Light,
) -> f64 { ) -> f64 {
let light_direction = light.direction_from(point); let light_direction = light.direction_from(point);
@ -200,8 +199,8 @@ impl Scene {
fn compute_soft_shadow_coefficient( fn compute_soft_shadow_coefficient(
&self, &self,
light_location: Vector3<f64>, light_location: Point,
original_intersection_point: Vector3<f64>, original_intersection_point: Point,
object: &Object, object: &Object,
) -> f64 { ) -> f64 {
// Soft shadows: jitter some rays here to somewhere close to the // Soft shadows: jitter some rays here to somewhere close to the
@ -215,7 +214,7 @@ impl Scene {
let x = rng.gen_range(0.0..JITTER_RADIUS); let x = rng.gen_range(0.0..JITTER_RADIUS);
let y = rng.gen_range(0.0..JITTER_RADIUS); let y = rng.gen_range(0.0..JITTER_RADIUS);
let z = rng.gen_range(0.0..JITTER_RADIUS); let z = rng.gen_range(0.0..JITTER_RADIUS);
let delta = Vector3::new(x, y, z); let delta = Point::new(x, y, z);
light_location + delta light_location + delta
}) })
.take(JITTER_RAYS) .take(JITTER_RAYS)
@ -263,12 +262,12 @@ pub struct IntersectionContext {
/// The intersection point. /// The intersection point.
#[derivative(PartialEq = "ignore", Ord = "ignore")] #[derivative(PartialEq = "ignore", Ord = "ignore")]
pub point: Vector3<f64>, pub point: Point,
/// The normal vector protruding from the surface of the object at the /// The normal vector protruding from the surface of the object at the
/// intersection point /// intersection point
#[derivative(PartialEq = "ignore", Ord = "ignore")] #[derivative(PartialEq = "ignore", Ord = "ignore")]
pub normal: Vector3<f64>, pub normal: Point,
} }
impl Eq for IntersectionContext {} impl Eq for IntersectionContext {}

View file

@ -1,13 +1,15 @@
use std::{fs::File, io::Read, path::Path}; use std::{fs::File, io::Read, path::Path};
use anyhow::Result; use anyhow::Result;
use nalgebra::Vector3;
use crate::scene::{ use crate::{
cylinder::Cylinder, scene::{
data::{Attenuation, Light, LightKind, Material, Object}, cylinder::Cylinder,
sphere::Sphere, data::{Attenuation, Light, LightKind, Material, Object},
Scene, sphere::Sphere,
Scene,
},
Point,
}; };
use super::data::{DepthCueing, ObjectKind}; use super::data::{DepthCueing, ObjectKind};
@ -56,11 +58,7 @@ impl Scene {
let read_vec3 = |start: usize| { let read_vec3 = |start: usize| {
ensure!(parts.len() >= start + 3, "Vec3 requires 3 components."); ensure!(parts.len() >= start + 3, "Vec3 requires 3 components.");
Ok(Vector3::new( Ok(Point::new(parts[start], parts[start + 1], parts[start + 2]))
parts[start],
parts[start + 1],
parts[start + 2],
))
}; };
match keyword { match keyword {

View file

@ -6,9 +6,9 @@ pub mod sphere;
use nalgebra::Vector3; use nalgebra::Vector3;
use crate::image::Color; use crate::{image::Color, Point};
use self::data::{DepthCueing, Light, Material, Object, Attenuation}; use self::data::{Attenuation, DepthCueing, Light, Material, Object};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Scene { pub struct Scene {
@ -31,4 +31,6 @@ pub struct Scene {
pub materials: Vec<Material>, pub materials: Vec<Material>,
pub lights: Vec<Light>, pub lights: Vec<Light>,
pub objects: Vec<Object>, pub objects: Vec<Object>,
pub vertices: Vec<Point>,
} }

View file

@ -1,14 +1,13 @@
use anyhow::Result; use anyhow::Result;
use nalgebra::Vector3;
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::{ray::Ray, utils::min_f64}; use crate::{ray::Ray, utils::min_f64, Point};
use super::illumination::IntersectionContext; use super::illumination::IntersectionContext;
#[derive(Debug)] #[derive(Debug)]
pub struct Sphere { pub struct Sphere {
pub center: Vector3<f64>, pub center: Point,
pub radius: f64, pub radius: f64,
} }

View file

@ -2,6 +2,8 @@ use anyhow::Result;
use nalgebra::{Matrix3, Vector3}; use nalgebra::{Matrix3, Vector3};
use ordered_float::NotNan; use ordered_float::NotNan;
use crate::Point;
/// Finds the minimum of an iterator of f64s, ignoring any NaN values /// Finds the minimum of an iterator of f64s, ignoring any NaN values
#[inline] #[inline]
pub fn min_f64<I>(i: I) -> Option<f64> pub fn min_f64<I>(i: I) -> Option<f64>
@ -28,17 +30,17 @@ where
/// Dot-product between two 3D vectors. /// Dot-product between two 3D vectors.
#[inline] #[inline]
pub fn dot(a: Vector3<f64>, b: Vector3<f64>) -> f64 { pub fn dot(a: Point, b: Point) -> f64 {
a.x * b.x + a.y * b.y + a.z * b.z a.x * b.x + a.y * b.y + a.z * b.z
} }
/// Cross-product between two 3D vectors. /// Cross-product between two 3D vectors.
#[inline] #[inline]
pub fn cross(a: Vector3<f64>, b: Vector3<f64>) -> Vector3<f64> { pub fn cross(a: Point, b: Point) -> Point {
let x = a.y * b.z - a.z * b.y; let x = a.y * b.z - a.z * b.y;
let y = a.z * b.x - a.x * b.z; let y = a.z * b.x - a.x * b.z;
let z = a.x * b.y - a.y * b.x; let z = a.x * b.y - a.y * b.x;
Vector3::new(x, y, z) Point::new(x, y, z)
} }
/// Calculate the rotation matrix between the 2 given vectors /// Calculate the rotation matrix between the 2 given vectors