Minor refactor
This commit is contained in:
parent
05462fde28
commit
8ab0a9d139
11 changed files with 130 additions and 81 deletions
|
@ -1,12 +1,12 @@
|
||||||
use generator::{done, Gn};
|
|
||||||
use itertools::Itertools;
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufRead, BufReader, Read, Write},
|
io::{BufRead, BufReader, Read, Write},
|
||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::{Context, Result};
|
||||||
|
use generator::{done, Gn};
|
||||||
|
use itertools::Itertools;
|
||||||
use nalgebra::Vector3;
|
use nalgebra::Vector3;
|
||||||
|
|
||||||
/// A pixel color represented by a red, green, and blue value in the range 0-1.
|
/// 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 line_reader = BufReader::new(r);
|
||||||
|
|
||||||
let mut header = String::new();
|
let mut header = String::new();
|
||||||
line_reader.read_line(&mut header)?;
|
line_reader
|
||||||
let parts = header.split(" ").collect::<Vec<_>>();
|
.read_line(&mut header)
|
||||||
|
.context("Could not read line")?;
|
||||||
|
let parts = header.trim().split(" ").collect::<Vec<_>>();
|
||||||
|
|
||||||
let width = parts[1].parse::<usize>()?;
|
let width = parts[1].parse::<usize>().context("Could not read width")?;
|
||||||
let height = parts[2].parse::<usize>()?;
|
let height = parts[2].parse::<usize>().context("Could not read height")?;
|
||||||
let max_value = parts[3].parse::<usize>()?;
|
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| {
|
let numbers = Gn::<()>::new_scoped(move |mut s| {
|
||||||
macro_rules! gen_try {
|
macro_rules! gen_try {
|
||||||
($expr:expr) => {
|
($expr:expr, $str:expr $(, $($arg:expr),* $(,)?)?) => {
|
||||||
match $expr {
|
match $expr {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
s.yield_(Err(anyhow::Error::from(e)));
|
s.yield_(
|
||||||
|
Err(anyhow::Error::from(e))
|
||||||
|
.with_context(|| format!($str $(, $($arg,)*)?)),
|
||||||
|
);
|
||||||
done!();
|
done!();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,11 +67,12 @@ impl Image {
|
||||||
}
|
}
|
||||||
|
|
||||||
for line in line_reader.lines() {
|
for line in line_reader.lines() {
|
||||||
let line = gen_try!(line);
|
let line = gen_try!(line, "Could not read line");
|
||||||
let parts = line.split_whitespace();
|
let parts = line.trim().split_whitespace();
|
||||||
|
|
||||||
for part in parts {
|
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));
|
s.yield_(Ok(int));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,9 +87,9 @@ impl Image {
|
||||||
None => bail!("Not enough elements"),
|
None => bail!("Not enough elements"),
|
||||||
};
|
};
|
||||||
|
|
||||||
let r = r? / max_value as f64;
|
let r = r? as f64 / max_value as f64;
|
||||||
let g = g? / max_value as f64;
|
let g = g? as f64 / max_value as f64;
|
||||||
let b = b? / max_value as f64;
|
let b = b? as f64 / max_value as f64;
|
||||||
|
|
||||||
let color = Color::new(r, g, b);
|
let color = Color::new(r, g, b);
|
||||||
data.push(color);
|
data.push(color);
|
||||||
|
|
|
@ -112,8 +112,9 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
Ok(match earliest_intersection {
|
Ok(match earliest_intersection {
|
||||||
// Take the object's material color
|
// Take the object's material color
|
||||||
Some((obj_idx, intersection_context, object)) => scene
|
Some((obj_idx, intersection_context, object)) => {
|
||||||
.compute_pixel_color(obj_idx, object.material, intersection_context),
|
scene.compute_pixel_color(obj_idx, object, intersection_context)
|
||||||
|
}
|
||||||
|
|
||||||
// There was no intersection, so this should default to the scene's
|
// There was no intersection, so this should default to the scene's
|
||||||
// background color
|
// background color
|
||||||
|
|
|
@ -200,7 +200,7 @@ mod tests {
|
||||||
let ray = Ray::from_endpoints(eye, end);
|
let ray = Ray::from_endpoints(eye, end);
|
||||||
|
|
||||||
let scene = Scene::default();
|
let scene = Scene::default();
|
||||||
let res = cylinder.intersects_ray_at(&scene, &ray);
|
let _res = cylinder.intersects_ray_at(&scene, &ray);
|
||||||
// panic!("Result: {res:?}");
|
// panic!("Result: {res:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,53 +1,11 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
use crate::image::Color;
|
use crate::image::Color;
|
||||||
use crate::ray::Ray;
|
|
||||||
use crate::utils::cross;
|
use crate::utils::cross;
|
||||||
use crate::Point;
|
use crate::Point;
|
||||||
|
|
||||||
use super::cylinder::Cylinder;
|
|
||||||
use super::illumination::IntersectionContext;
|
|
||||||
use super::sphere::Sphere;
|
|
||||||
use super::triangle::Triangle;
|
|
||||||
use super::Scene;
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Rect {
|
pub struct Rect {
|
||||||
pub upper_left: Point,
|
pub upper_left: Point,
|
||||||
|
|
|
@ -25,17 +25,27 @@ impl Scene {
|
||||||
pub fn compute_pixel_color(
|
pub fn compute_pixel_color(
|
||||||
&self,
|
&self,
|
||||||
obj_idx: usize,
|
obj_idx: usize,
|
||||||
material_idx: usize,
|
object: &Object,
|
||||||
intersection_context: IntersectionContext,
|
intersection_context: IntersectionContext,
|
||||||
) -> Color {
|
) -> Color {
|
||||||
// TODO: Does it make sense to make this function fallible from an API
|
// TODO: Does it make sense to make this function fallible from an API
|
||||||
// design standpoint?
|
// design standpoint?
|
||||||
let material = match self.materials.get(material_idx) {
|
let material = match self.materials.get(object.material_idx) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => return self.bkg_color,
|
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
|
// Diffuse and specular lighting for each separate light
|
||||||
let diffuse_and_specular: Color = self
|
let diffuse_and_specular: Color = self
|
||||||
|
@ -51,7 +61,7 @@ impl Scene {
|
||||||
((light_direction + viewer_direction) / 2.0).normalize();
|
((light_direction + viewer_direction) / 2.0).normalize();
|
||||||
|
|
||||||
let diffuse_component = material.k_d
|
let diffuse_component = material.k_d
|
||||||
* material.diffuse_color
|
* diffuse_color
|
||||||
* dot(normal, light_direction).max(0.0);
|
* dot(normal, light_direction).max(0.0);
|
||||||
|
|
||||||
let specular_component = material.k_s
|
let specular_component = material.k_s
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
use std::{
|
use std::fs::File;
|
||||||
fs::File,
|
use std::io::Read;
|
||||||
io::Read,
|
use std::path::Path;
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -15,7 +13,7 @@ use crate::{
|
||||||
data::{Attenuation, Light, LightKind, Material, Object},
|
data::{Attenuation, Light, LightKind, Material, Object},
|
||||||
sphere::Sphere,
|
sphere::Sphere,
|
||||||
triangle::Triangle,
|
triangle::Triangle,
|
||||||
Scene,
|
Scene, texture::Texture,
|
||||||
},
|
},
|
||||||
Point, Vector,
|
Point, Vector,
|
||||||
};
|
};
|
||||||
|
@ -37,7 +35,8 @@ impl Scene {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut scene = Scene::default();
|
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() {
|
for line in contents.lines() {
|
||||||
// Split lines into words. `parts' is an iterator, which is consumed upon
|
// Split lines into words. `parts' is an iterator, which is consumed upon
|
||||||
|
@ -167,7 +166,7 @@ impl Scene {
|
||||||
};
|
};
|
||||||
|
|
||||||
let idx = scene.materials.len();
|
let idx = scene.materials.len();
|
||||||
material_color = Some(idx);
|
material_idx = Some(idx);
|
||||||
scene.materials.push(material);
|
scene.materials.push(material);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +176,8 @@ impl Scene {
|
||||||
|
|
||||||
scene.objects.push(Object {
|
scene.objects.push(Object {
|
||||||
kind: ObjectKind::Sphere(Sphere { center, radius }),
|
kind: ObjectKind::Sphere(Sphere { center, radius }),
|
||||||
material: u!(material_color),
|
material_idx: u!(material_idx),
|
||||||
|
texture_idx,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +194,8 @@ impl Scene {
|
||||||
radius,
|
radius,
|
||||||
length,
|
length,
|
||||||
}),
|
}),
|
||||||
material: u!(material_color),
|
material_idx: u!(material_idx),
|
||||||
|
texture_idx,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +238,8 @@ impl Scene {
|
||||||
};
|
};
|
||||||
scene.objects.push(Object {
|
scene.objects.push(Object {
|
||||||
kind: ObjectKind::Triangle(triangle),
|
kind: ObjectKind::Triangle(triangle),
|
||||||
material: u!(material_color),
|
material_idx: u!(material_idx),
|
||||||
|
texture_idx,
|
||||||
});
|
});
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
@ -293,8 +295,12 @@ impl Scene {
|
||||||
None => bail!("Did not provide path."),
|
None => bail!("Did not provide path."),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let idx = scene.textures.len();
|
||||||
|
texture_idx = Some(idx);
|
||||||
|
|
||||||
let image = Image::from_file(path)?;
|
let image = Image::from_file(path)?;
|
||||||
scene.textures.push(image);
|
let texture = Texture::new(image);
|
||||||
|
scene.textures.push(texture);
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => bail!("Unknown keyword {keyword}"),
|
_ => bail!("Unknown keyword {keyword}"),
|
||||||
|
|
|
@ -3,14 +3,15 @@ pub mod data;
|
||||||
pub mod illumination;
|
pub mod illumination;
|
||||||
pub mod input_file;
|
pub mod input_file;
|
||||||
pub mod sphere;
|
pub mod sphere;
|
||||||
|
pub mod texture;
|
||||||
pub mod triangle;
|
pub mod triangle;
|
||||||
|
pub mod object;
|
||||||
use nalgebra::Vector2;
|
|
||||||
|
|
||||||
use crate::image::{Color, Image};
|
use crate::image::{Color, Image};
|
||||||
use crate::{Point, Point2, Vector};
|
use crate::{Point, Point2, Vector};
|
||||||
|
|
||||||
use self::data::{Attenuation, DepthCueing, Light, Material, Object};
|
use self::data::{Attenuation, DepthCueing, Light, Material, Object};
|
||||||
|
use self::texture::Texture;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
|
@ -35,7 +36,7 @@ pub struct Scene {
|
||||||
pub objects: Vec<Object>,
|
pub objects: Vec<Object>,
|
||||||
|
|
||||||
/// List of textures
|
/// List of textures
|
||||||
pub textures: Vec<Image>,
|
pub textures: Vec<Texture>,
|
||||||
|
|
||||||
/// Coordinates into a texture image
|
/// Coordinates into a texture image
|
||||||
pub texture_vertices: Vec<Point2>,
|
pub texture_vertices: Vec<Point2>,
|
||||||
|
|
47
assignment-1c/src/scene/object.rs
Normal file
47
assignment-1c/src/scene/object.rs
Normal 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
assignment-1c/src/scene/texture.rs
Normal file
16
assignment-1c/src/scene/texture.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,7 +4,7 @@ use ordered_float::NotNan;
|
||||||
|
|
||||||
use crate::ray::Ray;
|
use crate::ray::Ray;
|
||||||
use crate::utils::{cross, dot};
|
use crate::utils::{cross, dot};
|
||||||
use crate::Point;
|
|
||||||
|
|
||||||
use super::illumination::IntersectionContext;
|
use super::illumination::IntersectionContext;
|
||||||
use super::Scene;
|
use super::Scene;
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
packages = (with pkgs; [
|
packages = (with pkgs; [
|
||||||
cargo-deny
|
cargo-deny
|
||||||
cargo-edit
|
cargo-edit
|
||||||
|
cargo-expand
|
||||||
cargo-flamegraph
|
cargo-flamegraph
|
||||||
cargo-watch
|
cargo-watch
|
||||||
imagemagick
|
imagemagick
|
||||||
|
|
Loading…
Reference in a new issue