2023-02-06 03:52:42 +00:00
|
|
|
use std::fmt::Debug;
|
|
|
|
|
|
|
|
use anyhow::Result;
|
|
|
|
use nalgebra::Vector3;
|
|
|
|
|
|
|
|
use crate::image::Color;
|
|
|
|
use crate::ray::Ray;
|
|
|
|
|
2023-02-15 07:20:03 +00:00
|
|
|
use super::Scene;
|
2023-02-15 07:22:42 +00:00
|
|
|
use super::illumination::IntersectionContext;
|
2023-02-15 07:20:03 +00:00
|
|
|
|
2023-02-06 03:52:42 +00:00
|
|
|
pub trait ObjectKind: Debug + Send + Sync {
|
|
|
|
/// 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.
|
|
|
|
fn intersects_ray_at(&self, ray: &Ray)
|
|
|
|
-> Result<Option<IntersectionContext>>;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An object in the scene
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Object {
|
|
|
|
pub kind: Box<dyn ObjectKind>,
|
|
|
|
|
|
|
|
/// Index into the scene's material color list
|
|
|
|
pub material: usize,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Rect {
|
|
|
|
pub upper_left: Vector3<f64>,
|
|
|
|
pub upper_right: Vector3<f64>,
|
|
|
|
pub lower_left: Vector3<f64>,
|
|
|
|
pub lower_right: Vector3<f64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Material {
|
|
|
|
pub diffuse_color: Vector3<f64>,
|
|
|
|
pub specular_color: Vector3<f64>,
|
|
|
|
|
|
|
|
pub k_a: f64,
|
|
|
|
pub k_d: f64,
|
|
|
|
pub k_s: f64,
|
|
|
|
pub exponent: f64,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum LightKind {
|
|
|
|
/// A point light source exists at a point and emits light in all directions
|
|
|
|
Point {
|
|
|
|
location: Vector3<f64>,
|
|
|
|
},
|
|
|
|
|
|
|
|
Directional {
|
|
|
|
direction: Vector3<f64>,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Light {
|
|
|
|
pub kind: LightKind,
|
|
|
|
pub color: Vector3<f64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
2023-02-15 07:20:03 +00:00
|
|
|
pub struct DepthCueing {
|
|
|
|
color: Color,
|
|
|
|
a_max: f64,
|
|
|
|
a_min: f64,
|
|
|
|
dist_max: f64,
|
|
|
|
dist_min: f64,
|
2023-02-06 03:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Scene {
|
|
|
|
/// Determine the boundaries of the viewing window in world coordinates
|
|
|
|
pub fn compute_viewing_window(&self, distance: f64) -> Rect {
|
|
|
|
// Compute viewing directions
|
|
|
|
let u = self.view_dir.cross(&self.up_dir).normalize();
|
|
|
|
let v = u.cross(&self.view_dir).normalize();
|
|
|
|
|
|
|
|
// Compute dimensions of viewing window based on field of view
|
|
|
|
let viewing_width = {
|
|
|
|
// Divide the angle in 2 since we are trying to use trig rules so we must
|
|
|
|
// get it from a right triangle
|
|
|
|
let half_hfov = self.hfov.to_radians() / 2.0;
|
|
|
|
|
|
|
|
// tan(hfov / 2) = w / 2d
|
|
|
|
let w_over_2d = half_hfov.tan();
|
|
|
|
|
|
|
|
// To find the viewing width we must multiply by 2d now
|
|
|
|
w_over_2d * 2.0 * distance
|
|
|
|
};
|
|
|
|
|
|
|
|
let aspect_ratio = self.image_width as f64 / self.image_height as f64;
|
|
|
|
let viewing_height = viewing_width / aspect_ratio;
|
|
|
|
|
|
|
|
// Compute viewing window corners
|
|
|
|
let n = self.view_dir.normalize();
|
|
|
|
#[rustfmt::skip] // Otherwise this line wraps over
|
|
|
|
let view_window = Rect {
|
|
|
|
upper_left: self.eye_pos + n * distance - u * (viewing_width / 2.0) + v * (viewing_height / 2.0),
|
|
|
|
upper_right: self.eye_pos + n * distance + u * (viewing_width / 2.0) + v * (viewing_height / 2.0),
|
|
|
|
lower_left: self.eye_pos + n * distance - u * (viewing_width / 2.0) - v * (viewing_height / 2.0),
|
|
|
|
lower_right: self.eye_pos + n * distance + u * (viewing_width / 2.0) - v * (viewing_height / 2.0),
|
|
|
|
};
|
|
|
|
|
|
|
|
view_window
|
|
|
|
}
|
|
|
|
}
|