Solved 1c sample 1

This commit is contained in:
Michael Zhang 2023-02-24 01:50:33 -06:00
parent 973e03df4d
commit a725cc2e39
11 changed files with 388 additions and 175 deletions

View file

@ -0,0 +1,45 @@
imsize 640 480
eye 0 0 15
viewdir 0 0 -1
hfov 60
updir 0 1 0
bkgcolor 0.5 0.5 0.5
depthcueing 0.5 0.5 0.5 1 0.4 60 0
light 10 10 -10 1 1 1 1
mtlcolor 0.5 1 0.5 1 1 1 0.2 1 0.1 5
sphere 4.5 4.5 -20 4.5
sphere -4.5 -4.5 -20 4.5
mtlcolor 1 0.5 0.5 1 1 1 0.2 0.8 0 5
sphere -10 0 -30 4
sphere -20 0 -30 4
sphere -30 0 -30 4
sphere -40 0 -30 4
sphere 0 0 -30 4
sphere 10 0 -30 4
sphere 20 0 -30 4
sphere 30 0 -30 4
sphere 40 0 -30 4
sphere -10 -10 -30 4
sphere -20 -10 -30 4
sphere -30 -10 -30 4
sphere -40 -10 -30 4
sphere 0 -10 -30 4
sphere 10 -10 -30 4
sphere 20 -10 -30 4
sphere 30 -10 -30 4
sphere 40 -10 -30 4
sphere -10 10 -30 4
sphere -20 10 -30 4
sphere -30 10 -30 4
sphere -40 10 -30 4
sphere 0 10 -30 4
sphere 10 10 -30 4
sphere 20 10 -30 4
sphere 30 10 -30 4
sphere 40 10 -30 4

View file

@ -1,5 +1,3 @@
#![doc = include_str!("../README.md")]
use nalgebra::Vector3;
#[macro_use]

View file

@ -91,7 +91,7 @@ fn main() -> Result<()> {
.iter()
.enumerate()
.filter_map(|(i, object)| {
match object.kind.intersects_ray_at(&ray) {
match object.kind.intersects_ray_at(&scene, &ray) {
Ok(Some(t)) => {
// Return both the t and the sphere, because we want to sort on
// the t but later retrieve attributes from the sphere

View file

@ -7,6 +7,7 @@ use crate::Vector;
use crate::{ray::Ray, Point};
use super::illumination::IntersectionContext;
use super::Scene;
#[derive(Debug)]
pub struct Cylinder {
@ -23,6 +24,7 @@ impl Cylinder {
/// If there is no intersection point, returns None.
pub fn intersects_ray_at(
&self,
_: &Scene,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
// Determine rotation matrix for turning the cylinder upright along the
@ -180,7 +182,7 @@ impl Cylinder {
#[cfg(test)]
mod tests {
use crate::{ray::Ray, Point, Vector};
use crate::{ray::Ray, scene::Scene, Point, Vector};
use super::Cylinder;
@ -197,7 +199,8 @@ mod tests {
let end = Point::new(0.0, 2.0, 2.0);
let ray = Ray::from_endpoints(eye, end);
let res = cylinder.intersects_ray_at(&ray);
panic!("Result: {res:?}");
let scene = Scene::default();
let res = cylinder.intersects_ray_at(&scene, &ray);
// panic!("Result: {res:?}");
}
}

View file

@ -10,12 +10,14 @@ use crate::Point;
use super::cylinder::Cylinder;
use super::illumination::IntersectionContext;
use super::sphere::Sphere;
use super::triangle::FlatTriangle;
use super::Scene;
#[derive(Debug)]
pub enum ObjectKind {
Sphere(Sphere),
Cylinder(Cylinder),
FlatTriangle(FlatTriangle),
}
impl ObjectKind {
@ -26,11 +28,15 @@ impl ObjectKind {
/// Shade_Ray.
pub fn intersects_ray_at(
&self,
scene: &Scene,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
match self {
ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(ray),
ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(ray),
ObjectKind::Sphere(sphere) => sphere.intersects_ray_at(scene, ray),
ObjectKind::Cylinder(cylinder) => cylinder.intersects_ray_at(scene, ray),
ObjectKind::FlatTriangle(triangle) => {
triangle.intersects_ray_at(scene, ray)
}
}
}
}

View file

@ -151,7 +151,8 @@ impl Scene {
// This list will be a set of opacities
let intersections = other_objects
.filter_map(|(_, object)| {
let intersection_context = match object.kind.intersects_ray_at(&ray) {
let intersection_context =
match object.kind.intersects_ray_at(&self, &ray) {
Ok(v) => v,
Err(err) => {
error!("Error while performing shadow casting: {err}");
@ -228,7 +229,8 @@ impl Scene {
direction,
};
let intersection_context = match object.kind.intersects_ray_at(&ray) {
let intersection_context =
match object.kind.intersects_ray_at(&self, &ray) {
Ok(Some(v)) => v,
Ok(None) => return false,
Err(err) => {

View file

@ -1,8 +1,8 @@
use std::{fs::File, io::Read, path::Path, str::FromStr};
use std::{fs::File, io::Read, path::Path};
use anyhow::Result;
use itertools::Itertools;
use nalgebra::{Vector2, Vector3};
use nalgebra::Vector3;
use crate::{
image::Color,
@ -10,6 +10,7 @@ use crate::{
cylinder::Cylinder,
data::{Attenuation, Light, LightKind, Material, Object},
sphere::Sphere,
triangle::FlatTriangle,
Scene,
},
Point, Vector,
@ -33,27 +34,13 @@ impl Scene {
let mut material_color = None;
for line in contents.lines() {
// Split lines into words, and identify the keyword
let mut parts = line.split_whitespace();
let keyword = match parts.next() {
Some(v) => v,
None => continue,
};
if keyword == "imsize" {
let parts = parts
.map(|s| s.parse::<usize>().map_err(|e| e.into()))
.collect::<Result<Vec<_>>>()?;
if let [width, height] = parts[..] {
scene.image_width = width;
scene.image_height = height;
}
} else if keyword == "projection" {
if let Some("parallel") = parts.next() {
scene.parallel_projection = true;
}
}
// Do float parsing instead
else {
/// Shortcut for reading something from the iterator and converting it
/// into the appropriate format
macro_rules! r {
@ -79,6 +66,16 @@ impl Scene {
}
match keyword {
"imsize" => {
scene.image_width = r!(usize);
scene.image_height = r!(usize);
}
"projection" => {
if let Some("parallel") = parts.next() {
scene.parallel_projection = true;
}
}
"eye" => scene.eye_pos = r!(Vector3<f64>),
"viewdir" => scene.view_dir = r!(Vector3<f64>),
"updir" => scene.up_dir = r!(Vector3<f64>),
@ -196,8 +193,74 @@ impl Scene {
});
}
_ => bail!("Unknown keyword {keyword}"),
// Assignment 1C: Triangles and textures
// v x y z
"v" => scene.vertices.push(r!(Vector)),
// vn nx ny nz
"vn" => scene.normals.push(r!(Vector)),
// f v1 v2 v3
// f v1//n1 v2//n2 v3//n3
"f" => {
use TriangleVertex::*;
let v1 = r!(TriangleVertex);
let v2 = r!(TriangleVertex);
let v3 = r!(TriangleVertex);
match (v1, v2, v3) {
(Flat(v1), Flat(v2), Flat(v3)) => {
let vertices = Vector3::new(v1, v2, v3);
scene.objects.push(Object {
kind: ObjectKind::FlatTriangle(FlatTriangle { vertices }),
material: mat!(),
});
}
(Smooth(v1, n1), Smooth(v2, n2), Smooth(v3, n3)) => {
scene.smooth_triangles.push(((v1, n1), (v2, n2), (v3, n3)))
}
_ => bail!("Must all be either v_idx or v_idx//n_idx"),
}
enum TriangleVertex {
Flat(usize),
Smooth(usize, usize),
}
impl Construct for TriangleVertex {
type Args = ();
fn construct<'a, I>(it: &mut I, _: Self::Args) -> Result<Self>
where
I: Iterator<Item = &'a str>,
{
let s = match it.next() {
Some(v) => v,
None => bail!("Waiting on another"),
};
// Note: indexed by 1 not 0, so we will just do the subtraction
// here to avoid having to deal with it later
let parts = s.split("//").collect_vec();
ensure!(parts.len() >= 1 && parts.len() <= 2);
let v_idx: usize = parts[0].parse()?;
Ok(match parts.len() {
1 => TriangleVertex::Flat(v_idx - 1),
2 => {
let n_idx: usize = parts[1].parse()?;
TriangleVertex::Smooth(v_idx - 1, n_idx - 1)
}
_ => bail!("Invalid"),
})
}
}
}
"texture" => {}
_ => bail!("Unknown keyword {keyword}"),
}
}
@ -219,7 +282,7 @@ macro_rules! impl_construct {
impl Construct for $ty {
type Args = ();
fn construct<'a, I>(it: &mut I, args: Self::Args) -> Result<Self>
fn construct<'a, I>(it: &mut I, _: Self::Args) -> Result<Self>
where
I: Iterator<Item = &'a str>,
{
@ -240,7 +303,7 @@ impl_construct!(usize);
impl Construct for Vector3<f64> {
type Args = ();
fn construct<'a, I>(it: &mut I, args: Self::Args) -> Result<Self>
fn construct<'a, I>(it: &mut I, _: Self::Args) -> Result<Self>
where
I: Iterator<Item = &'a str>,
{

View file

@ -3,8 +3,7 @@ pub mod data;
pub mod illumination;
pub mod input_file;
pub mod sphere;
use nalgebra::{Matrix2x3, Vector3};
pub mod triangle;
use crate::image::{Color, Image};
use crate::{Point, Vector};
@ -35,8 +34,8 @@ pub struct Scene {
pub textures: Vec<Image>,
pub vertices: Vec<Point>,
pub flat_triangles: Vec<Vector3<usize>>,
pub flat_triangles: Vec<(usize, usize, usize)>,
pub normals: Vec<Vector>,
pub smooth_triangles: Vec<Matrix2x3<usize>>,
pub smooth_triangles: Vec<((usize, usize), (usize, usize), (usize, usize))>,
}

View file

@ -4,6 +4,7 @@ use ordered_float::NotNan;
use crate::{ray::Ray, utils::min_f64, Point};
use super::illumination::IntersectionContext;
use super::Scene;
#[derive(Debug)]
pub struct Sphere {
@ -18,6 +19,7 @@ impl Sphere {
/// If there is no intersection point, returns None.
pub fn intersects_ray_at(
&self,
_: &Scene,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
let a = ray.direction.norm();

View file

@ -0,0 +1,95 @@
use anyhow::Result;
use nalgebra::{Matrix2, Vector2, Vector3};
use ordered_float::NotNan;
use crate::ray::Ray;
use crate::utils::{cross, dot};
use crate::Point;
use super::illumination::IntersectionContext;
use super::Scene;
#[derive(Debug)]
pub struct FlatTriangle {
pub vertices: Vector3<usize>,
}
impl FlatTriangle {
pub fn intersects_ray_at(
&self,
scene: &Scene,
ray: &Ray,
) -> Result<Option<IntersectionContext>> {
let p0 = scene.vertices[self.vertices.x];
let p1 = scene.vertices[self.vertices.y];
let p2 = scene.vertices[self.vertices.z];
// Solve for the plane equation coefficients A, B, C, D such that:
//
// $$
// Ax + By + Cz + D = 0
// $$
let e1 = p1 - p0;
let e2 = p2 - p0;
let n = cross(e1, e2);
let a = n.x;
let b = n.y;
let c = n.z;
// Sub in p0 to solve for D
let d = -(a * p0.x + b * p0.y + c * p0.z);
// Find the intersection point
let time = {
let (x0, y0, z0, xd, yd, zd) =
match (ray.origin.as_slice(), ray.direction.as_slice()) {
([x0, y0, z0], [xd, yd, zd]) => (x0, y0, z0, xd, yd, zd),
_ => unreachable!("lol rip no tuple interface"),
};
let denom = a * xd + b * yd + c * zd;
if denom == 0.0 {
// The ray is parallel to the plane, so there is no intersection point.
return Ok(None);
};
-(a * x0 + b * y0 + c * z0 + d) / denom
};
let time = NotNan::new(time)?;
let point = ray.eval(*time);
// Use barycentric coordinates to determine if the point is inside of the
// triangle
{
// p = p0 + beta * e1 + gamma * e2
// Using the whack linear algebra approach derived on slide 57
let ep = point - p0;
let d = Matrix2::new(dot(e1, e1), dot(e1, e2), dot(e2, e1), dot(e2, e2));
let p = Vector2::new(dot(e1, ep), dot(e2, ep));
let d_inv = match d.try_inverse() {
Some(v) => v,
// TODO: Whack
None => return Ok(None),
};
let sol = d_inv * p;
let beta = sol.x;
let gamma = sol.y;
// Slide 46
let alpha = 1.0 - beta - gamma;
// Each of alpha, beta, and gamma must be between 0 and 1
if ![alpha, beta, gamma].into_iter().all(|v| 0.0 < v && v < 1.0) {
return Ok(None);
}
}
let normal = n.normalize();
Ok(Some(IntersectionContext {
time,
point,
normal,
}))
}
}