Minor refactor

This commit is contained in:
Michael Zhang 2023-02-25 15:50:13 -06:00
parent 05462fde28
commit 8ab0a9d139
11 changed files with 130 additions and 81 deletions

View file

@ -1,12 +1,12 @@
use generator::{done, Gn};
use itertools::Itertools;
use std::{
fs::File,
io::{BufRead, BufReader, Read, Write},
path::Path,
};
use anyhow::Result;
use anyhow::{Context, Result};
use generator::{done, Gn};
use itertools::Itertools;
use nalgebra::Vector3;
/// A pixel color represented by a red, green, and blue value in the range 0-1.
@ -38,20 +38,28 @@ impl Image {
let mut line_reader = BufReader::new(r);
let mut header = String::new();
line_reader.read_line(&mut header)?;
let parts = header.split(" ").collect::<Vec<_>>();
line_reader
.read_line(&mut header)
.context("Could not read line")?;
let parts = header.trim().split(" ").collect::<Vec<_>>();
let width = parts[1].parse::<usize>()?;
let height = parts[2].parse::<usize>()?;
let max_value = parts[3].parse::<usize>()?;
let width = parts[1].parse::<usize>().context("Could not read width")?;
let height = parts[2].parse::<usize>().context("Could not read height")?;
let max_value = parts[3]
.parse::<usize>()
.context("Could not read max value")?;
// Generator for reading numbers
let numbers = Gn::<()>::new_scoped(move |mut s| {
macro_rules! gen_try {
($expr:expr) => {
($expr:expr, $str:expr $(, $($arg:expr),* $(,)?)?) => {
match $expr {
Ok(v) => v,
Err(e) => {
s.yield_(Err(anyhow::Error::from(e)));
s.yield_(
Err(anyhow::Error::from(e))
.with_context(|| format!($str $(, $($arg,)*)?)),
);
done!();
}
}
@ -59,11 +67,12 @@ impl Image {
}
for line in line_reader.lines() {
let line = gen_try!(line);
let parts = line.split_whitespace();
let line = gen_try!(line, "Could not read line");
let parts = line.trim().split_whitespace();
for part in parts {
let int = gen_try!(part.parse::<f64>());
let int =
gen_try!(part.parse::<u64>(), "Could not read int from: {}", part);
s.yield_(Ok(int));
}
}
@ -78,9 +87,9 @@ impl Image {
None => bail!("Not enough elements"),
};
let r = r? / max_value as f64;
let g = g? / max_value as f64;
let b = b? / max_value as f64;
let r = r? as f64 / max_value as f64;
let g = g? as f64 / max_value as f64;
let b = b? as f64 / max_value as f64;
let color = Color::new(r, g, b);
data.push(color);

View file

@ -112,8 +112,9 @@ fn main() -> Result<()> {
Ok(match earliest_intersection {
// Take the object's material color
Some((obj_idx, intersection_context, object)) => scene
.compute_pixel_color(obj_idx, object.material, intersection_context),
Some((obj_idx, intersection_context, object)) => {
scene.compute_pixel_color(obj_idx, object, intersection_context)
}
// There was no intersection, so this should default to the scene's
// background color

View file

@ -200,7 +200,7 @@ mod tests {
let ray = Ray::from_endpoints(eye, end);
let scene = Scene::default();
let res = cylinder.intersects_ray_at(&scene, &ray);
let _res = cylinder.intersects_ray_at(&scene, &ray);
// panic!("Result: {res:?}");
}
}

View file

@ -1,53 +1,11 @@
use std::fmt::Debug;
use anyhow::Result;
use crate::image::Color;
use crate::ray::Ray;
use crate::utils::cross;
use crate::Point;
use super::cylinder::Cylinder;
use super::illumination::IntersectionContext;
use super::sphere::Sphere;
use super::triangle::Triangle;
use super::Scene;
#[derive(Debug)]
pub enum ObjectKind {
Sphere(Sphere),
Cylinder(Cylinder),
Triangle(Triangle),
}
impl ObjectKind {
/// Determine where the ray intersects this object, returning the earliest
/// time this happens. Returns None if no intersection occurs.
///
/// Also known as Trace_Ray in the slides, except not the part where it calls
/// Shade_Ray.
pub fn intersects_ray_at(
&self,
scene: &Scene,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
match self {
ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(scene, ray),
ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(scene, ray),
ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray),
}
}
}
/// An object in the scene
#[derive(Debug)]
pub struct Object {
pub kind: ObjectKind,
/// Index into the scene's material color list
pub material: usize,
}
#[derive(Debug)]
pub struct Rect {
pub upper_left: Point,

View file

@ -25,17 +25,27 @@ impl Scene {
pub fn compute_pixel_color(
&self,
obj_idx: usize,
material_idx: usize,
object: &Object,
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) {
let material = match self.materials.get(object.material_idx) {
Some(v) => v,
None => return self.bkg_color,
};
let ambient_component = material.k_a * material.diffuse_color;
let diffuse_color = match object.texture_idx {
Some(texture_idx) => {
let texture = &self.textures[texture_idx];
// TODO: need to implement
material.diffuse_color
}
None => material.diffuse_color,
};
let ambient_component = material.k_a * diffuse_color;
// Diffuse and specular lighting for each separate light
let diffuse_and_specular: Color = self
@ -51,7 +61,7 @@ impl Scene {
((light_direction + viewer_direction) / 2.0).normalize();
let diffuse_component = material.k_d
* material.diffuse_color
* diffuse_color
* dot(normal, light_direction).max(0.0);
let specular_component = material.k_s

View file

@ -1,8 +1,6 @@
use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
};
use std::fs::File;
use std::io::Read;
use std::path::Path;
use anyhow::{Context, Result};
use itertools::Itertools;
@ -15,7 +13,7 @@ use crate::{
data::{Attenuation, Light, LightKind, Material, Object},
sphere::Sphere,
triangle::Triangle,
Scene,
Scene, texture::Texture,
},
Point, Vector,
};
@ -37,7 +35,8 @@ impl Scene {
};
let mut scene = Scene::default();
let mut material_color = None;
let mut material_idx = None;
let mut texture_idx = None;
for line in contents.lines() {
// Split lines into words. `parts' is an iterator, which is consumed upon
@ -167,7 +166,7 @@ impl Scene {
};
let idx = scene.materials.len();
material_color = Some(idx);
material_idx = Some(idx);
scene.materials.push(material);
}
@ -177,7 +176,8 @@ impl Scene {
scene.objects.push(Object {
kind: ObjectKind::Sphere(Sphere { center, radius }),
material: u!(material_color),
material_idx: u!(material_idx),
texture_idx,
});
}
@ -194,7 +194,8 @@ impl Scene {
radius,
length,
}),
material: u!(material_color),
material_idx: u!(material_idx),
texture_idx,
});
}
@ -237,7 +238,8 @@ impl Scene {
};
scene.objects.push(Object {
kind: ObjectKind::Triangle(triangle),
material: u!(material_color),
material_idx: u!(material_idx),
texture_idx,
});
#[derive(Debug, Copy, Clone, PartialEq)]
@ -293,8 +295,12 @@ impl Scene {
None => bail!("Did not provide path."),
};
let idx = scene.textures.len();
texture_idx = Some(idx);
let image = Image::from_file(path)?;
scene.textures.push(image);
let texture = Texture::new(image);
scene.textures.push(texture);
}
_ => bail!("Unknown keyword {keyword}"),

View file

@ -3,14 +3,15 @@ pub mod data;
pub mod illumination;
pub mod input_file;
pub mod sphere;
pub mod texture;
pub mod triangle;
use nalgebra::Vector2;
pub mod object;
use crate::image::{Color, Image};
use crate::{Point, Point2, Vector};
use self::data::{Attenuation, DepthCueing, Light, Material, Object};
use self::texture::Texture;
#[derive(Debug, Default)]
pub struct Scene {
@ -35,7 +36,7 @@ pub struct Scene {
pub objects: Vec<Object>,
/// List of textures
pub textures: Vec<Image>,
pub textures: Vec<Texture>,
/// Coordinates into a texture image
pub texture_vertices: Vec<Point2>,

View file

@ -0,0 +1,47 @@
use anyhow::Result;
use crate::ray::Ray;
use super::cylinder::Cylinder;
use super::illumination::IntersectionContext;
use super::sphere::Sphere;
use super::triangle::Triangle;
use super::Scene;
/// An object in the scene
#[derive(Debug)]
pub struct Object {
pub kind: ObjectKind,
/// Index into the scene's material color list
pub material_idx: usize,
/// If this object has a texture, this is the index into the texture list
pub texture_idx: Option<usize>,
}
#[derive(Debug)]
pub enum ObjectKind {
Sphere(Sphere),
Cylinder(Cylinder),
Triangle(Triangle),
}
impl ObjectKind {
/// Determine where the ray intersects this object, returning the earliest
/// time this happens. Returns None if no intersection occurs.
///
/// Also known as Trace_Ray in the slides, except not the part where it calls
/// Shade_Ray.
pub fn intersects_ray_at(
&self,
scene: &Scene,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
match self {
ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(scene, ray),
ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(scene, ray),
ObjectKind::Triangle(triangle) => triangle.intersects_ray_at(scene, ray),
}
}
}

View file

@ -0,0 +1,16 @@
use crate::image::{Color, Image};
#[derive(Debug)]
pub struct Texture(Image);
impl Texture {
pub fn new(image: Image) -> Self {
Self(image)
}
/// Returns a pixel at the given coordinate. For non-lattice coordinates,
/// interpolation of the image is done.
pub fn pixel_at(&self, x: f64, y: f64) -> Option<Color> {
todo!()
}
}

View file

@ -4,7 +4,7 @@ use ordered_float::NotNan;
use crate::ray::Ray;
use crate::utils::{cross, dot};
use crate::Point;
use super::illumination::IntersectionContext;
use super::Scene;

View file

@ -15,6 +15,7 @@
packages = (with pkgs; [
cargo-deny
cargo-edit
cargo-expand
cargo-flamegraph
cargo-watch
imagemagick