running into some nan problems
This commit is contained in:
parent
c3589bdc1b
commit
dd83ff2c9e
13 changed files with 255 additions and 83 deletions
|
@ -12,6 +12,9 @@ pub mod ray;
|
||||||
pub mod scene;
|
pub mod scene;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
// Creating a bunch of aliases here to make it more obvious which one I'm
|
||||||
|
// expecting a variable to be
|
||||||
|
|
||||||
pub type Point2 = Vector2<f64>;
|
pub type Point2 = Vector2<f64>;
|
||||||
pub type Point = Vector3<f64>;
|
pub type Point = Vector3<f64>;
|
||||||
pub type Vector = Vector3<f64>;
|
pub type Vector = Vector3<f64>;
|
||||||
|
|
|
@ -84,40 +84,7 @@ fn main() -> Result<()> {
|
||||||
|
|
||||||
let ray = Ray::from_endpoints(ray_start, pixel_in_space);
|
let ray = Ray::from_endpoints(ray_start, pixel_in_space);
|
||||||
|
|
||||||
let intersections = scene
|
scene.trace_single_ray(ray, 0)
|
||||||
.objects
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.filter_map(|(i, object)| {
|
|
||||||
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
|
|
||||||
Some(Ok((i, t, object)))
|
|
||||||
}
|
|
||||||
Ok(None) => None,
|
|
||||||
Err(err) => {
|
|
||||||
error!("Error: {err}");
|
|
||||||
Some(Err(err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>>>()?;
|
|
||||||
|
|
||||||
// Sort the list of intersection times by the lowest one.
|
|
||||||
let earliest_intersection =
|
|
||||||
intersections.into_iter().min_by_key(|(_, t, _)| t.time);
|
|
||||||
|
|
||||||
Ok(match earliest_intersection {
|
|
||||||
// Take the object's material color
|
|
||||||
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
|
|
||||||
None => scene.bkg_color,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>>>()?;
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,10 @@ pub struct Ray {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ray {
|
impl Ray {
|
||||||
|
pub fn new(origin: Point, direction: Vector) -> Self {
|
||||||
|
Ray { origin, direction }
|
||||||
|
}
|
||||||
|
|
||||||
/// Construct a ray from endpoints
|
/// Construct a ray from endpoints
|
||||||
pub fn from_endpoints(start: Point, end: Point) -> Self {
|
pub fn from_endpoints(start: Point, end: Point) -> Self {
|
||||||
let delta = (end - start).normalize();
|
let delta = (end - start).normalize();
|
||||||
|
|
|
@ -116,6 +116,7 @@ impl Cylinder {
|
||||||
time,
|
time,
|
||||||
point: ray_point,
|
point: ray_point,
|
||||||
normal,
|
normal,
|
||||||
|
exiting: todo!(),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -165,6 +166,7 @@ impl Cylinder {
|
||||||
time,
|
time,
|
||||||
point: ray_point,
|
point: ray_point,
|
||||||
normal,
|
normal,
|
||||||
|
exiting: todo!(),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,12 @@ pub struct Material {
|
||||||
pub k_d: f64,
|
pub k_d: f64,
|
||||||
pub k_s: f64,
|
pub k_s: f64,
|
||||||
pub exponent: f64,
|
pub exponent: f64,
|
||||||
|
|
||||||
|
/// Opacity
|
||||||
|
pub alpha: f64,
|
||||||
|
|
||||||
|
/// Index of refraction
|
||||||
|
pub eta: f64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -11,11 +11,14 @@ use rayon::prelude::{
|
||||||
use crate::{image::Color, ray::Ray, utils::dot, Point, Vector};
|
use crate::{image::Color, ray::Ray, utils::dot, Point, Vector};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
data::{DepthCueing, Light, LightKind},
|
data::{DepthCueing, Light, LightKind, Material},
|
||||||
object::Object,
|
object::Object,
|
||||||
Scene,
|
Scene,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO: Is this a good constant?
|
||||||
|
const JITTER_CONST: f64 = 0.05;
|
||||||
|
|
||||||
impl Scene {
|
impl Scene {
|
||||||
/// Determine the color that should be used to fill this pixel.
|
/// Determine the color that should be used to fill this pixel.
|
||||||
///
|
///
|
||||||
|
@ -28,7 +31,9 @@ impl Scene {
|
||||||
&self,
|
&self,
|
||||||
obj_idx: usize,
|
obj_idx: usize,
|
||||||
object: &Object,
|
object: &Object,
|
||||||
|
incident_ray: Ray,
|
||||||
intersection_context: IntersectionContext,
|
intersection_context: IntersectionContext,
|
||||||
|
depth: usize,
|
||||||
) -> Result<Color> {
|
) -> Result<Color> {
|
||||||
let material = match self.materials.get(object.material_idx) {
|
let material = match self.materials.get(object.material_idx) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
|
@ -110,7 +115,73 @@ impl Scene {
|
||||||
})
|
})
|
||||||
.sum();
|
.sum();
|
||||||
|
|
||||||
let color = ambient_component + diffuse_and_specular;
|
let specular_reflection: Color = {
|
||||||
|
let reflection_ray = self.compute_reflection_ray(
|
||||||
|
incident_ray.direction,
|
||||||
|
intersection_context.normal,
|
||||||
|
);
|
||||||
|
|
||||||
|
let fresnel_coefficient = self.compute_fresnel_coefficient(
|
||||||
|
&material,
|
||||||
|
incident_ray.direction,
|
||||||
|
intersection_context.normal,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Jitter a bit to reduce acne
|
||||||
|
let origin = intersection_context.point;
|
||||||
|
let origin = origin + JITTER_CONST * reflection_ray;
|
||||||
|
|
||||||
|
let ray = Ray::new(origin, reflection_ray);
|
||||||
|
let r_lambda = self.trace_single_ray(ray, depth + 1)?;
|
||||||
|
|
||||||
|
fresnel_coefficient * r_lambda
|
||||||
|
};
|
||||||
|
|
||||||
|
let transparency = {
|
||||||
|
let n = intersection_context.normal;
|
||||||
|
let i = incident_ray.direction;
|
||||||
|
|
||||||
|
let (eta_i, eta_t) = match intersection_context.exiting {
|
||||||
|
true => (material.eta, 1.0),
|
||||||
|
false => (1.0, material.eta),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Slide 69
|
||||||
|
let cos_theta_i = dot(i, n);
|
||||||
|
assert!(cos_theta_i != std::f64::NAN);
|
||||||
|
let sin_theta_i = (1.0 - cos_theta_i.powi(2)).sqrt();
|
||||||
|
assert!(sin_theta_i != std::f64::NAN);
|
||||||
|
let sin_theta_t = (eta_i / eta_t) * sin_theta_i;
|
||||||
|
assert!(sin_theta_t != std::f64::NAN);
|
||||||
|
let cos_theta_t = (1.0 - sin_theta_t.powi(2)).sqrt();
|
||||||
|
assert!(cos_theta_t != std::f64::NAN);
|
||||||
|
|
||||||
|
let fresnel_coefficient =
|
||||||
|
self.compute_fresnel_coefficient(&material, i, n);
|
||||||
|
|
||||||
|
// Calculate refraction direction
|
||||||
|
let a = (-n).normalize() * cos_theta_t;
|
||||||
|
let s_direction = cos_theta_i * n - i;
|
||||||
|
let m_unit = s_direction.normalize();
|
||||||
|
let b = m_unit * sin_theta_t;
|
||||||
|
let t = a + b;
|
||||||
|
|
||||||
|
// Jitter a bit to reduce acne
|
||||||
|
// TODO: Is this a good constant?
|
||||||
|
let origin = intersection_context.point;
|
||||||
|
let origin = origin + JITTER_CONST * t;
|
||||||
|
|
||||||
|
let ray = Ray::new(origin, t);
|
||||||
|
let t_lambda = self.trace_single_ray(ray, depth + 1)?;
|
||||||
|
|
||||||
|
(1.0 - fresnel_coefficient) * (1.0 - material.alpha) * t_lambda
|
||||||
|
};
|
||||||
|
|
||||||
|
// This is the result of the Phong illumination equation.
|
||||||
|
let color = ambient_component
|
||||||
|
+ diffuse_and_specular
|
||||||
|
+ specular_reflection
|
||||||
|
+ transparency;
|
||||||
|
|
||||||
// Apply depth cueing to the result
|
// Apply depth cueing to the result
|
||||||
let a_dc = {
|
let a_dc = {
|
||||||
|
@ -265,6 +336,34 @@ impl Scene {
|
||||||
|
|
||||||
(JITTER_RAYS - num_obstructed_rays) as f64 / JITTER_RAYS as f64
|
(JITTER_RAYS - num_obstructed_rays) as f64 / JITTER_RAYS as f64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn compute_fresnel_coefficient(
|
||||||
|
&self,
|
||||||
|
material: &Material,
|
||||||
|
incident_ray: Vector,
|
||||||
|
normal: Vector,
|
||||||
|
) -> f64 {
|
||||||
|
let cos_theta_i = dot(incident_ray, normal);
|
||||||
|
|
||||||
|
let f0 = ((material.eta - 1.0) / (material.eta + 1.0)).powi(2);
|
||||||
|
let fr = f0 * 1.0 + (1.0 - f0) * (1.0 - cos_theta_i).powi(5);
|
||||||
|
|
||||||
|
fr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_reflection_ray(
|
||||||
|
&self,
|
||||||
|
incident_ray: Vector,
|
||||||
|
normal: Vector,
|
||||||
|
) -> Vector {
|
||||||
|
let opposite_incident_ray = (-incident_ray).normalize();
|
||||||
|
let unit_normal = normal.normalize();
|
||||||
|
|
||||||
|
let a = dot(unit_normal, opposite_incident_ray);
|
||||||
|
let r = 2.0 * (a * unit_normal) - opposite_incident_ray;
|
||||||
|
|
||||||
|
r
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information about an intersection
|
/// Information about an intersection
|
||||||
|
@ -287,6 +386,9 @@ pub struct IntersectionContext {
|
||||||
/// intersection point
|
/// intersection point
|
||||||
#[derivative(PartialEq = "ignore", Ord = "ignore")]
|
#[derivative(PartialEq = "ignore", Ord = "ignore")]
|
||||||
pub normal: Vector,
|
pub normal: Vector,
|
||||||
|
|
||||||
|
/// Is this ray exiting the material at the intersection point?
|
||||||
|
pub exiting: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for IntersectionContext {}
|
impl Eq for IntersectionContext {}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
pub mod triangle_vertex;
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
@ -11,9 +13,10 @@ use crate::{
|
||||||
scene::{
|
scene::{
|
||||||
cylinder::Cylinder,
|
cylinder::Cylinder,
|
||||||
data::{Attenuation, Light, LightKind, Material},
|
data::{Attenuation, Light, LightKind, Material},
|
||||||
|
input_file::triangle_vertex::TriangleVertex,
|
||||||
object::{Object, ObjectKind},
|
object::{Object, ObjectKind},
|
||||||
sphere::Sphere,
|
sphere::Sphere,
|
||||||
texture::{Texture, NormalMap},
|
texture::{NormalMap, Texture},
|
||||||
triangle::Triangle,
|
triangle::Triangle,
|
||||||
Scene,
|
Scene,
|
||||||
},
|
},
|
||||||
|
@ -149,7 +152,7 @@ impl Scene {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// mtlcolor Odr Odg Odb Osr Osg Osb ka kd ks n
|
// mtlcolor Odr Odg Odb Osr Osg Osb ka kd ks n alpha eta
|
||||||
"mtlcolor" => {
|
"mtlcolor" => {
|
||||||
let diffuse_color = r!(Color);
|
let diffuse_color = r!(Color);
|
||||||
let specular_color = r!(Color);
|
let specular_color = r!(Color);
|
||||||
|
@ -157,6 +160,8 @@ impl Scene {
|
||||||
let k_d = r!(f64);
|
let k_d = r!(f64);
|
||||||
let k_s = r!(f64);
|
let k_s = r!(f64);
|
||||||
let exponent = r!(f64);
|
let exponent = r!(f64);
|
||||||
|
let alpha = r!(f64);
|
||||||
|
let eta = r!(f64);
|
||||||
|
|
||||||
let material = Material {
|
let material = Material {
|
||||||
diffuse_color,
|
diffuse_color,
|
||||||
|
@ -165,6 +170,8 @@ impl Scene {
|
||||||
k_d,
|
k_d,
|
||||||
k_s,
|
k_s,
|
||||||
exponent,
|
exponent,
|
||||||
|
alpha,
|
||||||
|
eta,
|
||||||
};
|
};
|
||||||
|
|
||||||
let idx = scene.materials.len();
|
let idx = scene.materials.len();
|
||||||
|
@ -351,48 +358,3 @@ impl Construct for Vector3<f64> {
|
||||||
Ok(Vector3::new(x, y, z))
|
Ok(Vector3::new(x, y, z))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
|
||||||
struct TriangleVertex {
|
|
||||||
vertex_idx: usize,
|
|
||||||
normal_idx: Option<usize>,
|
|
||||||
texture_idx: Option<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() <= 3);
|
|
||||||
let vertex_idx: usize = parts[0].parse::<usize>()? - 1;
|
|
||||||
|
|
||||||
let texture_idx =
|
|
||||||
match parts.get(1).and_then(|s| (!s.is_empty()).then(|| *s)) {
|
|
||||||
Some(s) => Some(s.parse::<usize>()? - 1),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let normal_idx =
|
|
||||||
match parts.get(2).and_then(|s| (!s.is_empty()).then(|| *s)) {
|
|
||||||
Some(s) => Some(s.parse::<usize>()? - 1),
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(TriangleVertex {
|
|
||||||
vertex_idx,
|
|
||||||
texture_idx,
|
|
||||||
normal_idx,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
49
assignment-1d/src/scene/input_file/triangle_vertex.rs
Normal file
49
assignment-1d/src/scene/input_file/triangle_vertex.rs
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use super::Construct;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
|
pub struct TriangleVertex {
|
||||||
|
pub vertex_idx: usize,
|
||||||
|
pub normal_idx: Option<usize>,
|
||||||
|
pub texture_idx: Option<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() <= 3);
|
||||||
|
let vertex_idx: usize = parts[0].parse::<usize>()? - 1;
|
||||||
|
|
||||||
|
let texture_idx =
|
||||||
|
match parts.get(1).and_then(|s| (!s.is_empty()).then(|| *s)) {
|
||||||
|
Some(s) => Some(s.parse::<usize>()? - 1),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let normal_idx =
|
||||||
|
match parts.get(2).and_then(|s| (!s.is_empty()).then(|| *s)) {
|
||||||
|
Some(s) => Some(s.parse::<usize>()? - 1),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(TriangleVertex {
|
||||||
|
vertex_idx,
|
||||||
|
texture_idx,
|
||||||
|
normal_idx,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
6
assignment-1d/src/scene/materials.rs
Normal file
6
assignment-1d/src/scene/materials.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use super::data::Material;
|
||||||
|
|
||||||
|
pub struct MaterialStack<'a> {
|
||||||
|
stack: Vec<&'a f64>,
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ pub mod object;
|
||||||
pub mod sphere;
|
pub mod sphere;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
pub mod triangle;
|
pub mod triangle;
|
||||||
|
pub mod tracing;
|
||||||
|
pub mod materials;
|
||||||
|
|
||||||
use crate::image::Color;
|
use crate::image::Color;
|
||||||
use crate::{Point, Point2, Vector};
|
use crate::{Point, Point2, Vector};
|
||||||
|
|
|
@ -33,6 +33,7 @@ impl Sphere {
|
||||||
+ (ray.origin.y - self.center.y).powi(2)
|
+ (ray.origin.y - self.center.y).powi(2)
|
||||||
+ (ray.origin.z - self.center.z).powi(2)
|
+ (ray.origin.z - self.center.z).powi(2)
|
||||||
- self.radius.powi(2);
|
- self.radius.powi(2);
|
||||||
|
|
||||||
let discriminant = b * b - 4.0 * a * c;
|
let discriminant = b * b - 4.0 * a * c;
|
||||||
|
|
||||||
let time = match discriminant {
|
let time = match discriminant {
|
||||||
|
@ -68,10 +69,22 @@ impl Sphere {
|
||||||
let point = ray.eval(*time);
|
let point = ray.eval(*time);
|
||||||
let normal = (point - self.center).normalize();
|
let normal = (point - self.center).normalize();
|
||||||
|
|
||||||
|
let exiting = {
|
||||||
|
// To figure out if we're exiting, just test if the origin is inside the
|
||||||
|
// sphere
|
||||||
|
|
||||||
|
let dx = ray.origin.x - self.center.x;
|
||||||
|
let dy = ray.origin.y - self.center.y;
|
||||||
|
let dz = ray.origin.z - self.center.z;
|
||||||
|
|
||||||
|
dx.powi(2) + dy.powi(2) + dz.powi(2) < self.radius.powi(2)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Some(IntersectionContext {
|
Ok(Some(IntersectionContext {
|
||||||
time,
|
time,
|
||||||
point,
|
point,
|
||||||
normal,
|
normal,
|
||||||
|
exiting,
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
55
assignment-1d/src/scene/tracing.rs
Normal file
55
assignment-1d/src/scene/tracing.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::{image::Color, ray::Ray};
|
||||||
|
|
||||||
|
use super::Scene;
|
||||||
|
|
||||||
|
const MAX_RECURSION_DEPTH: usize = 10_usize;
|
||||||
|
|
||||||
|
impl Scene {
|
||||||
|
pub fn trace_single_ray(&self, ray: Ray, depth: usize) -> Result<Color> {
|
||||||
|
if depth > MAX_RECURSION_DEPTH {
|
||||||
|
return Ok(Color::new(0.0, 0.0, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let intersections = self
|
||||||
|
.objects
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, object)| {
|
||||||
|
match object.kind.intersects_ray_at(&self, &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
|
||||||
|
Some(Ok((i, t, object)))
|
||||||
|
}
|
||||||
|
Ok(None) => None,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Error: {err}");
|
||||||
|
Some(Err(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>>>()?;
|
||||||
|
|
||||||
|
// Sort the list of intersection times by the lowest one.
|
||||||
|
let earliest_intersection =
|
||||||
|
intersections.into_iter().min_by_key(|(_, t, _)| t.time);
|
||||||
|
|
||||||
|
Ok(match earliest_intersection {
|
||||||
|
// Take the object's material color
|
||||||
|
Some((obj_idx, intersection_context, object)) => self
|
||||||
|
.compute_pixel_color(
|
||||||
|
obj_idx,
|
||||||
|
object,
|
||||||
|
ray,
|
||||||
|
intersection_context,
|
||||||
|
depth,
|
||||||
|
)?,
|
||||||
|
|
||||||
|
// There was no intersection, so this should default to the scene's
|
||||||
|
// background color
|
||||||
|
None => self.bkg_color,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -100,6 +100,7 @@ impl Triangle {
|
||||||
time,
|
time,
|
||||||
point,
|
point,
|
||||||
normal,
|
normal,
|
||||||
|
exiting: todo!(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue