Solved 1c sample 1
This commit is contained in:
parent
973e03df4d
commit
a725cc2e39
11 changed files with 388 additions and 175 deletions
45
assignment-1c/examples/soft-shadow-demo.txt
Normal file
45
assignment-1c/examples/soft-shadow-demo.txt
Normal 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
|
|
@ -1,5 +1,3 @@
|
|||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use nalgebra::Vector3;
|
||||
|
||||
#[macro_use]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:?}");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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>,
|
||||
{
|
||||
|
|
|
@ -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))>,
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
95
assignment-1c/src/scene/triangle.rs
Normal file
95
assignment-1c/src/scene/triangle.rs
Normal 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,
|
||||
}))
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue