aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJulian T <julian@jtle.dk>2021-08-05 15:44:40 +0200
committerJulian T <julian@jtle.dk>2021-08-05 15:44:40 +0200
commit3ef8f4d918406eec6bdc29e0ebd883fabfac9b2e (patch)
treeaa4b1aac1e165821c16f222ebfb9212a9740e98b /src
parent45119506c0293fdde6cef35f6e6f82d4055b46b6 (diff)
Add picture for c5505ab84820248c6dba35fc06aef9e0ced183derendered
Diffstat (limited to 'src')
-rw-r--r--src/camera/camera.rs135
-rw-r--r--src/camera/film.rs145
-rw-r--r--src/camera/filter/mod.rs32
-rw-r--r--src/camera/mod.rs13
-rw-r--r--src/core/bound2.rs127
-rw-r--r--src/core/bound3.rs113
-rw-r--r--src/core/mod.rs34
-rw-r--r--src/core/ray.rs35
-rw-r--r--src/core/spectrum.rs88
-rw-r--r--src/core/vector2.rs167
-rw-r--r--src/core/vector3.rs185
-rw-r--r--src/lib.rs41
-rw-r--r--src/main.rs63
-rw-r--r--src/material/dielectric.rs56
-rw-r--r--src/material/diffuse_light.rs27
-rw-r--r--src/material/lambertian.rs38
-rw-r--r--src/material/mod.rs25
-rw-r--r--src/material/reflectant.rs40
-rw-r--r--src/material/sky_light.rs19
-rw-r--r--src/render/coordinator.rs126
-rw-r--r--src/render/mod.rs5
-rw-r--r--src/render/task.rs49
-rw-r--r--src/sample/mod.rs46
-rw-r--r--src/sample/uniform.rs39
-rw-r--r--src/trace/mod.rs41
-rw-r--r--src/trace/pathtrace.rs60
-rw-r--r--src/world/container/list.rs56
-rw-r--r--src/world/container/mod.rs3
-rw-r--r--src/world/hittable.rs41
-rw-r--r--src/world/mod.rs42
-rw-r--r--src/world/scene.rs43
-rw-r--r--src/world/shapes/mod.rs30
-rw-r--r--src/world/shapes/sphere.rs98
33 files changed, 0 insertions, 2062 deletions
diff --git a/src/camera/camera.rs b/src/camera/camera.rs
deleted file mode 100644
index 7f9e79d..0000000
--- a/src/camera/camera.rs
+++ /dev/null
@@ -1,135 +0,0 @@
-//! Generates rays from screen coordinates
-//!
-//! Generates rays in world space from screen coordinates.
-//! Future versions should also simulate depth of field.
-//!
-//! # Examples
-//!
-//! ```
-//! use rendering::camera::{CameraSettings, Camera};
-//! use rendering::core::{Vector3f, Vector2f, Vector2i};
-//! use rendering::sample::UniformSampler;
-//!
-//! let set = CameraSettings {
-//! origin: Vector3f::new(10.0),
-//! target: Vector3f::new(0.0),
-//! up: Vector3f::new_xyz(0.0, 1.0, 0.0),
-//! fov: 90.0,
-//! filmsize: Vector2i::new(10),
-//! focus: None,
-//! aperture: None,
-//! };
-//!
-//! let cam = Camera::new(&set);
-//! let mut sampler = UniformSampler::new();
-//!
-//! let (r, _) = cam.generate_ray(&Vector2f::new(5.0), &mut sampler);
-//! let dir = r.direction;
-//!
-//! assert!(
-//! dir.x == -0.6031558065478413 &&
-//! dir.y == -0.6599739684616743 &&
-//! dir.z == -0.4479257014065748
-//! );
-//!
-//! ```
-use crate::Float;
-use crate::core::{Vector3f, Vector2f, Vector2i, Ray};
-use crate::sample::Sampler;
-
-/// A simple perspective camera
-pub struct Camera {
- /// The camera origin in the screen
- origin: Vector3f,
- /// Vector from camera origin to the screen lower left corner of the film plane
- screen_origin: Vector3f,
- /// Scaling vectors from screen_origin
- qx: Vector3f,
- qy: Vector3f,
-
- /// Value for depth of view
- lens_radius: Option<Float>,
-}
-
-/// Settings for initializing camera
-pub struct CameraSettings {
- /// Where rays originate from
- pub origin: Vector3f,
- /// Point where center of image is pointed at
- pub target: Vector3f,
- /// Vector that will be up in the resulting image
- pub up: Vector3f,
- /// The vertical field of view in degrees.
- /// Currently must be between [0; 180[.
- pub fov: Float,
- /// The film aspect ratio, height / width
- pub filmsize: Vector2i,
- /// The lens aperture
- ///
- /// Depth of view is disabled if None
- pub aperture: Option<Float>,
- /// The distance to the focus plane
- ///
- /// if None it will be set to the distance between origin and target
- pub focus: Option<Float>,
-}
-
-impl Camera {
- /// Create a new camera look at a target
- pub fn new(set: &CameraSettings) -> Camera {
- let filmsize = Vector2f::from(set.filmsize);
- // Calculate translation vectors
- let mut forward = set.target - set.origin;
-
- let focus = set.focus.unwrap_or_else(|| forward.length());
-
- forward.norm_in();
-
- let right = set.up.cross(&forward).norm();
- let newup = forward.cross(&right).norm();
-
- let aspect = (filmsize.y) / (filmsize.x);
- // Calculate screen size from fov and focus distance
- let width = 2.0 * focus * (set.fov / 2.0).to_radians().tan();
- let height = aspect * width;
-
- // Calculate screen scaling vectors
- let qx = right * (width / (filmsize.x - 1.0));
- let qy = newup * (height / (filmsize.y - 1.0));
-
- let screen_origin = forward * focus - (right * (width/2.0)) + (newup * (height/2.0));
-
- Camera {
- origin: set.origin,
- screen_origin,
- qx,
- qy,
- lens_radius: set.aperture.map(|a| a / 2.0),
- }
- }
-
- /// Generates a ray a screen space point
- ///
- /// The point coordinates should be between [0,1) with (0, 0) being the upper left corner
- ///
- /// Will return a ray and a weight
- ///
- /// The direction of the returned way is normalized
- pub fn generate_ray(&self, point: &Vector2f, sampler: &mut dyn Sampler) -> (Ray, Float) {
- // Depth of view origin offset
- let ooffset = match self.lens_radius {
- Some(r) => {
- let rand_dir = sampler.get_in_circle() * r;
- self.qx * rand_dir.x + self.qy * rand_dir.y
- },
- None => Vector3f::ZERO,
- };
-
- let dir = self.screen_origin + (self.qx * point.x) - (self.qy * point.y) - ooffset;
-
- (
- Ray { origin: self.origin + ooffset, direction: dir.norm() },
- 1.0
- )
- }
-}
diff --git a/src/camera/film.rs b/src/camera/film.rs
deleted file mode 100644
index 852ae9e..0000000
--- a/src/camera/film.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use crate::core::*;
-use crate::Float;
-use image::{RgbImage, Rgb};
-
-/// Contains the necesary values when doing calculations
-///
-/// This is not the final RGB value
-#[derive(Clone)]
-pub struct Pixel {
- /// The sum of the collected samples
- rgb: Spectrum,
- /// The amount of samples collected
- samples: u32,
-}
-
-pub struct Film {
- pub size: Vector2i,
- pub frame: Bound2i,
-
- pixels: Vec<Pixel>,
-}
-
-/// FilmTile is a small version of the Film used when rendering
-///
-/// This means that multiple threads can work on the same area and commit their changed when they
-/// are done.
-pub struct FilmTile {
- pub bounds: Bound2i,
- pub size: Vector2i,
-
- pixels: Vec<Pixel>,
-}
-
-impl Pixel {
- fn new() -> Pixel {
- Pixel {
- rgb: Default::default(),
- samples: 0,
- }
- }
-
- fn add(&mut self, c: &Spectrum, weight: Float) {
- self.rgb += &(*c * weight);
- self.samples += 1;
- }
-
- fn finalize_rgb(&self) -> [u8; 3] {
- let spc = (self.rgb / (self.samples as Float)).gamma_correct();
- let (r, g, b) = spc.to_rgb(255.0);
- [
- r as u8,
- g as u8,
- b as u8,
- ]
- }
-}
-
-impl std::ops::AddAssign<&Self> for Pixel {
- fn add_assign(&mut self, op: &Self) {
- self.rgb += &op.rgb;
- self.samples += op.samples;
- }
-}
-
-impl Film {
- pub fn new(size: Vector2i) -> Film {
- let area = size.x * size.y;
- Film {
- size,
- frame: Bound2i::new(&Vector2i::new(0), &size),
- pixels: vec![Pixel::new(); area as usize],
- }
- }
-
- /// Creates a new FilmTile from the specified bounds
- ///
- /// This tile can later be commited with the commit_tile function
- pub fn get_tile(&self, bound: &Bound2i) -> FilmTile {
- FilmTile::new(
- bound,
- )
-
- }
-
- /// Commit a tile back on the film
- ///
- /// This will lock the Film while the changes from the Tile is written
- pub fn commit_tile(&mut self, tile: &FilmTile) {
- let offset = tile.bounds.min;
-
- for y in 0 .. tile.size.y {
- let rowindex = (offset.y + y) * self.size.x;
- let prowindex = y * tile.size.x;
-
- for x in 0 .. tile.size.x {
- let index = offset.x + x + rowindex;
- let pindex: i32 = x + prowindex;
-
- self.pixels[index as usize] += &tile.pixels[pindex as usize];
- }
- }
- }
-
- pub fn finalize_image(&self) -> RgbImage {
- let mut img = RgbImage::new(self.size.x as u32, self.size.y as u32);
-
- for y in 0..self.size.y {
- let index = y * self.size.x;
- for x in 0..self.size.x {
- img.put_pixel(
- x as u32,
- y as u32,
- Rgb(self.pixels[(index + x) as usize].finalize_rgb()),
- );
- }
- }
-
- img
- }
-}
-
-impl FilmTile {
- fn new(bounds: &Bound2i) -> FilmTile {
- FilmTile {
- bounds: bounds.clone(),
- pixels: vec![Pixel::new(); bounds.area() as usize],
- size: bounds.diagonal(),
- }
- }
-
- /// Add a single sample sampled from the scene
- pub fn add_sample(&mut self, inp: &Vector2f, c: Spectrum) {
- let point = Vector2i::from(inp.floor());
- // Subtract the offset
- let point = (point - self.bounds.min).cap(self.size.x-1, self.size.y-1);
-
- let index = point.x + point.y * self.size.x;
-
- if let Some(pixel) = self.pixels.get_mut(index as usize) {
- pixel.add(&c, 1.0);
- } else {
- println!("Could not get pixel {} inp: {}, index: {}", point, inp, index);
- }
- }
-}
diff --git a/src/camera/filter/mod.rs b/src/camera/filter/mod.rs
deleted file mode 100644
index 01d094c..0000000
--- a/src/camera/filter/mod.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use crate::vector::Vector2f;
-use crate::Float;
-
-trait Eval {
- fn eval(&self, point: &Vector2f) -> Float;
-}
-
-pub struct Filter {
- eval: Box<dyn Eval>,
- pub radius: Vector2f,
-}
-
-struct BoxFilter {}
-
-// The same a no filter, and can give aliasing in final image
-impl Eval for BoxFilter {
- fn eval(&self, _: &Vector2f) -> Float {
- 1.0
- }
-}
-
-impl Eval for Filter {
- fn eval(&self, point: &Vector2f) -> Float {
- self.eval.eval(point)
- }
-}
-
-impl Filter {
- pub fn new_box(radius: Vector2f) -> Filter {
- Filter { radius: radius, eval: Box::new(BoxFilter {}) }
- }
-}
diff --git a/src/camera/mod.rs b/src/camera/mod.rs
deleted file mode 100644
index 999b5b5..0000000
--- a/src/camera/mod.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-//! Implements how light is captured on film and how rays are generated
-//!
-//! The Film module specifies how calculated spectrum values contribute to the final image.
-//!
-//! The Camera class generated rays that can be cast into the scene.
-//! This requires converting the film coordinates into real coordinates
-
-pub mod film;
-//pub mod filter;
-mod camera;
-
-pub use camera::{Camera, CameraSettings};
-pub use film::Film;
diff --git a/src/core/bound2.rs b/src/core/bound2.rs
deleted file mode 100644
index bc0e7cd..0000000
--- a/src/core/bound2.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-//! Implements a 2d axis aligned bounding box
-use crate::{Number, Float};
-use super::vector2::Vector2;
-use crate::core;
-use crate::core::{min, max};
-
-/// Implements a region between min and max
-#[derive(Clone)]
-pub struct Bound2<T: Number> {
- pub min: Vector2<T>,
- pub max: Vector2<T>
-}
-
-pub type Bound2i = Bound2<i32>;
-pub type Bound2f = Bound2<Float>;
-
-impl<T: Number> Bound2<T> {
- /// Creates a new bound from two points
- ///
- /// p0 does not have to be smaller than p1
- pub fn new(p0: &Vector2<T>, p1: &Vector2<T>) -> Self {
- let min = Vector2::new_xy(min(p0.x, p1.x), min(p0.y, p1.y));
- let max = Vector2::new_xy(max(p0.x, p1.x), max(p0.y, p1.y));
-
- Self { min, max }
- }
-
- pub fn new_xyxy(x1: T, y1: T, x2: T, y2: T) -> Self {
- Self::new(
- &Vector2::new_xy(x1, y1),
- &Vector2::new_xy(x2, y2),
- )
- }
-
- /// Finds the intersected area between two bounds
- pub fn intersect(&self, b: &Bound2<T>) -> Bound2<T> {
- Bound2::new(
- &Vector2::new_xy(max(self.min.x, b.min.x), max(self.min.y, b.min.y)),
- &Vector2::new_xy(min(self.max.x, b.max.x), min(self.max.y, b.max.y)),
- )
- }
-
-
- /// Calculates the diagonal vector
- ///
- /// Can be used to calculate the size of the bound
- ///
- /// # Examples
- ///
- /// ```
- /// use rendering::core::Bound2i;
- /// let b = Bound2i::new_xyxy(2, 2, 6, 7);
- /// let diag = b.diagonal();
- ///
- /// assert!(diag.x == 4 && diag.y == 5);
- /// ```
- pub fn diagonal(&self) -> Vector2<T> {
- self.max - self.min
- }
-
- /// Calculates the area of of the bounded region
- ///
- /// # Examples
- ///
- /// ```
- /// use rendering::core::Bound2i;
- /// let b = Bound2i::new_xyxy(10, 10, 20, 20);
- ///
- /// assert!(b.area() == 100);
- /// ```
- pub fn area(&self) -> T {
- let diag = self.diagonal();
- diag.x * diag.y
- }
-}
-
-impl From<&Bound2i> for Bound2f {
- fn from(b: &Bound2i) -> Self {
- Self {
- min: core::Vector2f::from(b.min),
- max: core::Vector2f::from(b.max),
- }
- }
-}
-
-impl From<&Bound2f> for Bound2i {
- fn from(b: &Bound2f) -> Self {
- Self {
- min: core::Vector2i::from(b.min),
- max: core::Vector2i::from(b.max),
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn create_test() -> Bound2<i32> {
- Bound2::new(
- &Vector2::new_xy(1, 2),
- &Vector2::new_xy(10, 3)
- )
- }
-
- #[test]
- fn area() {
- let b = create_test();
-
- assert!(b.area() == 9);
- }
-
- #[test]
- fn intersect_test() {
- let b1 = Bound2i::new_xyxy(10, 10, 20, 20);
- let b2 = Bound2i::new_xyxy(2, 11, 22, 17);
-
- let b = b1.intersect(&b2);
-
- assert!(
- b.min.x == 10 &&
- b.min.y == 11 &&
- b.max.x == 20 &&
- b.max.y == 17
- )
- }
-}
diff --git a/src/core/bound3.rs b/src/core/bound3.rs
deleted file mode 100644
index ce6bb09..0000000
--- a/src/core/bound3.rs
+++ /dev/null
@@ -1,113 +0,0 @@
-//! Implements 3d axis aligned bounding box
-use crate::{Number, Float};
-use super::vector3::{Vector3, Vector3f};
-use crate::core::{min, max};
-use crate::core::Ray;
-
-#[derive(Clone)]
-pub struct Bound3<T: Number> {
- pub min: Vector3<T>,
- pub max: Vector3<T>
-}
-
-pub type Bound3f = Bound3<Float>;
-
-impl<T: Number> Bound3<T> {
- /// Creates a bound from two points
- pub fn new(p0: Vector3<T>, p1: Vector3<T>) -> Self {
- // Elliminate some code duplication here
- let min = Vector3::new_xyz(
- min(p0.x, p1.x),
- min(p0.y, p1.y),
- min(p0.z, p1.z)
- );
- let max = Vector3::new_xyz(
- max(p0.x, p1.x),
- max(p0.y, p1.y),
- max(p0.z, p1.z)
- );
-
- Self {min, max}
- }
-
- pub fn combine(&self, op: &Self) -> Self {
- let min = Vector3::new_xyz(
- min(self.min.x, op.min.x),
- min(self.min.y, op.min.y),
- min(self.min.z, op.min.z)
- );
- let max = Vector3::new_xyz(
- max(self.max.x, op.max.x),
- max(self.max.y, op.max.y),
- max(self.max.z, op.max.z)
- );
-
- Self {min, max}
- }
-
- pub fn and(&self, op: &Self) -> Self {
- let min_b = Vector3::new_xyz(
- max(self.min.x, op.min.x),
- max(self.min.y, op.min.y),
- max(self.min.z, op.min.z)
- );
- let max_b = Vector3::new_xyz(
- min(self.max.x, op.max.x),
- min(self.max.y, op.max.y),
- min(self.max.z, op.max.z)
- );
-
- Self {min: min_b, max: max_b}
- }
-
- pub fn area(&self) -> T {
- let diag = self.max - self.min;
- diag.x * diag.y * diag.z
- }
-}
-
-impl Bound3f {
- pub const EMPTY: Bound3f = Bound3f{min: Vector3f::ZERO, max: Vector3f::ZERO};
-
- /// Calculate whether there is a intersect between a bounding box and a ray
- ///
- /// # Examples:
- ///
- /// ```
- /// use rendering::core::{Bound3f, Vector3f, Ray};
- /// use rendering::INFTY;
- /// let b = Bound3f::new(Vector3f::new(7.0), Vector3f::new(10.0));
- /// let r1 = Ray::new_to(Vector3f::new(0.0), Vector3f::new(5.0));
- /// let r2 = Ray::new_to(Vector3f::new(-0.0), Vector3f::new(-5.0));
- /// let r3 = Ray::new(Vector3f::new_xyz(-1.0, 0.0, 0.0), Vector3f::new_xyz(1.0, 0.0, 0.0));
- ///
- /// assert!(b.intersect(&r1, 0.0, INFTY));
- /// assert!(!b.intersect(&r2, 0.0, INFTY));
- /// assert!(!b.intersect(&r3, 0.0, INFTY));
- /// ```
- pub fn intersect(&self, ray: &Ray, t_min: Float, t_max: Float) -> bool {
- println!("BIN: {} -> {}", self.min, self.max);
- // Method stolen from Ray tracing the next week.
- // They mention its from pixar
- for i in 0..3 {
- let inv = 1.0 / ray.direction[i];
- let mut t0 = (self.min[i] - ray.origin[i]) * inv;
- let mut t1 = (self.max[i] - ray.origin[i]) * inv;
-
- if inv < 0.0 {
- let tmp = t0;
- t0 = t1;
- t1 = tmp;
- }
-
- let t_min = max(t0, t_min);
- let t_max = min(t1, t_max);
-
- if t_max <= t_min {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/src/core/mod.rs b/src/core/mod.rs
deleted file mode 100644
index 95d450c..0000000
--- a/src/core/mod.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-//! Contains a collection of core modules used by other modules
-//!
-//! Also creates a shortcut for some common types
-
-mod vector2;
-mod vector3;
-mod bound2;
-mod bound3;
-mod spectrum;
-mod ray;
-
-pub use vector2::{Vector2i, Vector2f};
-pub use vector3::Vector3f;
-pub use bound2::{Bound2i, Bound2f};
-pub use bound3::Bound3f;
-pub use spectrum::Spectrum;
-pub use ray::Ray;
-
-use crate::Number;
-
-pub fn min<T: Number> (a: T, b: T) -> T {
- if b < a {
- return b;
- }
- a
-}
-
-pub fn max<T: Number> (a: T, b: T) -> T {
- if b > a {
- return b;
- }
- a
-}
-
diff --git a/src/core/ray.rs b/src/core/ray.rs
deleted file mode 100644
index 19d3cf1..0000000
--- a/src/core/ray.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-//! The ray class used when probing the 3d scene
-use crate::core::Vector3f;
-use crate::Float;
-
-/// A ray that is sent into the world.
-/// This is the main type used for testing intersections.
-pub struct Ray {
- /// Origin of the ray
- pub origin: Vector3f,
- /// Direction is assumed to be a unit vector.
- pub direction: Vector3f,
-}
-
-impl Ray {
- pub fn new(origin: Vector3f, direction: Vector3f) -> Ray {
- Ray {
- origin,
- direction,
- }
- }
-
- pub fn new_to(origin: Vector3f, target: Vector3f) -> Ray {
- let dir = (target - origin).norm();
- Ray {
- origin,
- direction: dir
- }
- }
-
- /// Resolve a point on the ray at time t
- pub fn at(&self, t: Float) -> Vector3f {
- self.origin + self.direction * t
- }
-}
-
diff --git a/src/core/spectrum.rs b/src/core/spectrum.rs
deleted file mode 100644
index ed3505b..0000000
--- a/src/core/spectrum.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-//! Used to represent color
-//!
-//! Currently only implements RGB colors
-use crate::Float;
-
-// TODO implement SampledSpectrum instead for nicer images
-
-#[derive(Clone, Copy, Default)]
-pub struct Spectrum {
- c: [Float; 3],
-}
-
-impl Spectrum {
- pub const ZERO: Self = Spectrum { c: [0.0; 3] };
- pub const WHITE: Self = Spectrum { c: [1.0; 3] };
-
- pub fn new_rgb(r: Float, g: Float, b: Float) -> Spectrum {
- Spectrum { c: [r, g, b] }
- }
-
- pub fn to_rgb(&self, scale: Float) -> (Float, Float, Float) {
- (self.c[0] * scale, self.c[1] * scale, self.c[2] * scale)
- }
-
- pub fn gamma_correct(&self) -> Self {
- Self::new_rgb(
- self.c[0].sqrt(),
- self.c[1].sqrt(),
- self.c[2].sqrt(),
- )
- }
-}
-
-impl std::ops::Mul<Float> for Spectrum {
- type Output = Spectrum;
-
- fn mul(self, op: Float) -> Self::Output {
- Self::Output::new_rgb(
- self.c[0] * op,
- self.c[1] * op,
- self.c[2] * op,
- )
- }
-}
-
-impl std::ops::Mul for Spectrum {
- type Output = Spectrum;
-
- fn mul(self, op: Self) -> Self::Output {
- Self::Output::new_rgb(
- self.c[0] * op.c[0],
- self.c[1] * op.c[1],
- self.c[2] * op.c[2],
- )
- }
-}
-
-impl std::ops::Div<Float> for Spectrum {
- type Output = Spectrum;
-
- fn div(self, op: Float) -> Self::Output {
- Self::Output::new_rgb(
- self.c[0] / op,
- self.c[1] / op,
- self.c[2] / op,
- )
- }
-}
-
-impl std::ops::Add for Spectrum {
- type Output = Spectrum;
-
- fn add(self, op: Self) -> Self::Output {
- Self::Output::new_rgb(
- self.c[0] + op.c[0],
- self.c[1] + op.c[1],
- self.c[2] + op.c[2],
- )
- }
-}
-
-impl std::ops::AddAssign<&Self> for Spectrum {
- fn add_assign(&mut self, op: &Self) {
- self.c[0] += op.c[0];
- self.c[1] += op.c[1];
- self.c[2] += op.c[2];
- }
-}
diff --git a/src/core/vector2.rs b/src/core/vector2.rs
deleted file mode 100644
index 405b12a..0000000
--- a/src/core/vector2.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-//! Implements 2d vectors
-//!
-//! This is implemented generictly with types that fit in the Number trait
-use crate::{Float, Number};
-use std::ops::{Sub, Add, Mul, Div};
-use std::fmt;
-use std::cmp::min;
-
-#[derive(Clone, Copy)]
-pub struct Vector2<T: Number> {
- pub x: T,
- pub y: T,
-}
-
-pub type Vector2f = Vector2<Float>;
-pub type Vector2i = Vector2<i32>;
-
-
-impl<T: Number> Vector2<T> {
- pub fn new(initial: T) -> Vector2<T> {
- Vector2 { x: initial, y: initial }
- }
-
- pub fn new_xy(x: T, y: T) -> Vector2<T> {
- Vector2 { x, y }
- }
-}
-
-impl<T: Number> Sub for Vector2<T> {
- type Output = Self;
- fn sub(self, op: Self) -> Self::Output {
- Self::new_xy(
- self.x - op.x,
- self.y - op.y,
- )
- }
-}
-
-impl<T: Number> Add for Vector2<T> {
- type Output = Self;
- fn add(self, op: Self) -> Self::Output {
- Self::new_xy(
- self.x + op.x,
- self.y + op.y,
- )
- }
-}
-
-impl<T: Number> Mul for Vector2<T> {
- type Output = Self;
- fn mul(self, op: Self) -> Self::Output {
- Self::new_xy(
- self.x * op.x,
- self.y * op.y,
- )
- }
-}
-
-impl<T: Number> Mul<T> for Vector2<T> {
- type Output = Self;
- fn mul(self, op: T) -> Self::Output {
- Self::new_xy(
- self.x * op,
- self.y * op,
- )
- }
-}
-
-impl<T: Number> Div for Vector2<T> {
- type Output = Self;
- fn div(self, op: Self) -> Self::Output {
- Self::new_xy(
- self.x / op.x,
- self.y / op.y,
- )
- }
-}
-
-impl<T: Number> fmt::Display for Vector2<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_fmt(format_args!("[{}, {}]", self.x, self.y))
- }
-}
-
-impl From<Vector2i> for Vector2f {
- fn from(v: Vector2i) -> Self {
- Self {
- x: v.x as Float,
- y: v.y as Float,
- }
- }
-}
-
-impl From<Vector2f> for Vector2i {
- fn from(v: Vector2f) -> Self {
- Self {
- x: v.x as i32,
- y: v.y as i32,
- }
- }
-}
-
-impl Vector2i {
- pub const ZERO: Self = Vector2i {x: 0, y: 0};
-
- pub fn cap(&self, x: i32, y: i32) -> Self {
- Self::new_xy(
- min(self.x, x),
- min(self.y, y),
- )
- }
-}
-
-impl Vector2f {
- pub fn length(&self) -> Float {
- (self.x*self.x + self.y*self.y).sqrt()
- }
-
- pub fn ceil(&self) -> Self {
- Self::new_xy(
- self.x.ceil(),
- self.y.ceil()
- )
- }
-
- pub fn floor(&self) -> Self {
- Self::new_xy(
- self.x.floor(),
- self.y.floor()
- )
- }
-}
-
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn new_vec2() {
- let v = Vector2::new_xy(2.0, 10.0);
-
- assert!(v.x == 2.0 && v.y == 10.0);
-
- let v = Vector2::new(3);
-
- assert!(v.x == 3 && v.y == 3);
- }
-
- #[test]
- fn sub_vec2() {
- let v1 = Vector2::new_xy(10, 11);
- let v2 = Vector2::new_xy(2, 3);
-
- let v3 = v1-v2;
- assert!(v3.x == 8 && v3.y == 8);
- }
-
- #[test]
- fn add_vec2() {
- let v1 = Vector2::new_xy(10, 11);
- let v2 = Vector2::new_xy(2, 3);
-
- let v3 = v1+v2;
- assert!(v3.x == 12 && v3.y == 14);
- }
-}
diff --git a/src/core/vector3.rs b/src/core/vector3.rs
deleted file mode 100644
index 1cc6f60..0000000
--- a/src/core/vector3.rs
+++ /dev/null
@@ -1,185 +0,0 @@
-//! Implements 3d vectors
-//!
-//! Also add more 3d math things needed for shading and 3d calculations.
-use crate::{Float, Number, NEAR_ZERO};
-use std::ops::{Mul, Sub, Add, DivAssign, Neg, AddAssign, Index};
-use std::fmt;
-
-#[derive(Clone, Copy)]
-pub struct Vector3<T: Number> {
- pub x: T,
- pub y: T,
- pub z: T,
-}
-
-pub type Vector3f = Vector3<Float>;
-
-impl<T: Number> Vector3<T> {
- pub fn new(initial: T) -> Vector3<T> {
- Vector3 {
- x: initial,
- y: initial,
- z: initial,
- }
- }
-
- pub fn new_xyz(x: T, y: T, z: T) -> Vector3<T> {
- Vector3 { x, y, z}
- }
-}
-
-impl<T: Number> Sub for Vector3<T> {
- type Output = Self;
- fn sub(self, op: Self) -> Self::Output {
- Self::new_xyz(
- self.x - op.x,
- self.y - op.y,
- self.z - op.z,
- )
- }
-}
-
-impl<T: Number> Add for Vector3<T> {
- type Output = Self;
- fn add(self, op: Self) -> Self::Output {
- Self::new_xyz(
- self.x + op.x,
- self.y + op.y,
- self.z + op.z,
- )
- }
-}
-
-impl<T: Number> Add<T> for Vector3<T> {
- type Output = Self;
-
- fn add(self, op: T) -> Self::Output {
- Self::new_xyz(
- self.x + op,
- self.y + op,
- self.z + op,
- )
- }
-}
-
-impl<T: Number> Mul<T> for Vector3<T> {
- type Output = Self;
- fn mul(self, op: T) -> Self::Output {
- Self::Output::new_xyz(
- self.x * op,
- self.y * op,
- self.z * op,
- )
- }
-}
-
-impl<T: Number> Neg for Vector3<T> {
- type Output = Self;
-
- fn neg(self) -> Self::Output {
- Self::Output::new_xyz(
- -self.x,
- -self.y,
- -self.z,
- )
- }
-}
-
-impl<T: Number> AddAssign<&Self> for Vector3<T> {
- fn add_assign(&mut self, op: &Self) {
- self.x += op.x;
- self.y += op.y;
- self.z += op.z;
- }
-}
-
-impl<T: Number> DivAssign<T> for Vector3<T> {
- fn div_assign(&mut self, op: T) {
- self.x /= op;
- self.y /= op;
- self.z /= op;
- }
-}
-
-impl<T: Number> fmt::Display for Vector3<T> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- f.write_fmt(format_args!("[{}, {}, {}]", self.x, self.y, self.z))
- }
-}
-
-// Ohh god
-impl<T: Number> Index<u32> for Vector3<T> {
- type Output = T;
-
- fn index(&self, i: u32) -> &Self::Output {
- match i {
- 0 => &self.x,
- 1 => &self.y,
- 2 => &self.z,
- _ => panic!("index out of bounds: index {} is not possible with 3d vector", i)
- }
- }
-}
-
-impl Vector3f {
- pub const ZERO: Self = Vector3f {x: 0.0, y: 0.0, z: 0.0};
-
- /// Calculates the length times itself
- ///
- /// This is faster than using len * len as the square is ommited
- pub fn len_squared(&self) -> Float {
- self.x * self.x + self.y * self.y + self.z * self.z
- }
-
- pub fn length(&self) -> Float {
- self.len_squared().sqrt()
- }
-
- pub fn dot(&self, op: &Self) -> Float {
- self.x * op.x + self.y * op.y + self.z * op.z
- }
-
- /// Inplace normal instead of creating a new vector
- ///
- /// # Example
- ///
- /// ```
- /// use rendering::core::Vector3f;
- /// let mut v = Vector3f::new_xyz(10.0, 0.0, 0.0);
- /// v.norm_in();
- /// assert!(v.x == 1.0);
- /// ```
- pub fn norm_in(&mut self) {
- // TODO Experiment with checking for normality with len_squared
- let len = self.length();
- if len == 0.0 {
- *self = Self::new(0.0);
- }
-
- *self /= len;
- }
-
- pub fn norm(&self) -> Self {
- let mut new = *self;
- new.norm_in();
- new
- }
-
- pub fn cross(&self, op: &Self) -> Self {
- Self::new_xyz(
- self.y * op.z - self.z * op.y,
- self.z * op.x - self.x * op.z,
- self.x * op.y - self.y * op.x,
- )
-
- }
-
- /// Check if vector is close to [0, 0, 0]
- ///
- /// This is based on the NEAR_ZERO constant
- pub fn near_zero(&self) -> bool {
- (self.x.abs() < NEAR_ZERO) &&
- (self.y.abs() < NEAR_ZERO) &&
- (self.z.abs() < NEAR_ZERO)
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
deleted file mode 100644
index 0e6674d..0000000
--- a/src/lib.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-pub mod core;
-pub mod camera;
-pub mod render;
-pub mod world;
-pub mod trace;
-pub mod sample;
-pub mod material;
-
-use std::ops::{Add, Sub, Mul, DivAssign, AddAssign, Neg, Div};
-use std::cmp;
-use std::fmt;
-use std::f64::consts::PI;
-
-/// Trait used to implement generics
-///
-/// This is used in Bound and Vectors
-pub trait Number:
- Copy +
- cmp::PartialOrd +
- Sub<Output = Self> +
- Add<Output = Self> +
- Mul<Output = Self> +
- Neg<Output = Self> +
- Div<Output = Self> +
- DivAssign +
- AddAssign +
- fmt::Display
-{}
-
-impl Number for i32 {}
-impl Number for f32 {}
-impl Number for f64 {}
-
-/// Used for representing floating point values throughout the program
-///
-/// A higher precision type will require more ram
-pub type Float = f64;
-
-pub const M_PI: Float = PI;
-pub const NEAR_ZERO: Float = 1e-8;
-pub const INFTY: Float = f64::INFINITY;
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index dd67fdc..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-use rendering::camera::{Camera, Film, CameraSettings};
-use rendering::world::{Scene, Object, shapes::Sphere};
-use rendering::trace::DefaultTracer;
-use rendering::core::{Vector2i, Vector3f, Spectrum};
-use rendering::render::{RenderContext, RenderCoord};
-use rendering::sample::UniformSampler;
-use rendering::material::*;
-
-use std::sync::Arc;
-
-fn main() {
- let res = Vector2i::new_xy(500, 500);
-
- let cam = Camera::new(&CameraSettings {
- target: Vector3f::new_xyz(0.0, 0.0, -1.0),
- origin: Vector3f::new_xyz(0.0, 0.0, 1.0),
- up: Vector3f::new_xyz(0.0, 1.0, 0.0),
- fov: 45.0,
- filmsize: res,
- focus: None,
- aperture: Some(10.0),
- //aperture: None,
- });
-
- let brown = Arc::new(Lambertian::new(Spectrum::new_rgb(0.5, 0.3, 0.0)));
- let blue = Arc::new(Lambertian::new(Spectrum::new_rgb(0.0, 0.3, 0.7)));
- let green = Arc::new(Lambertian::new(Spectrum::new_rgb(0.0, 0.7, 0.3)));
- let metal = Arc::new(Reflectant::new(Spectrum::new_rgb(0.8, 0.8, 0.9), Some(1.0)));
- let glass = Arc::new(Dielectric::new(1.5));
- let sun = Arc::new(DiffuseLight::new_white(50.0));
-
- let mut scn = Scene::new();
- scn.add_objects(vec![
- Object::new(glass, Sphere::new(0.2, Vector3f::new_xyz(0.0, 0.0, -1.0))),
- Object::new(blue, Sphere::new(0.5, Vector3f::new_xyz(1.0, 0.0, -1.5))),
- Object::new(green, Sphere::new(0.3, Vector3f::new_xyz(0.5, 0.0, -2.5))),
- Object::new(brown, Sphere::new(100.0, Vector3f::new_xyz(0.0, -100.5, -1.0))),
- Object::new(metal, Sphere::new(0.2, Vector3f::new_xyz(-0.5, 0.0, -1.0))),
- Object::new(sun, Sphere::new(0.4, Vector3f::new_xyz(-1.0, 3.0, 0.0))),
- ]);
-
- let tracer = DefaultTracer::new(&scn, Some(50),
- //Some(Box::new(SkyLight::new()))
- None
- );
-
- let mut sampler = UniformSampler::new();
-
- let ctx = RenderContext { cam: &cam, trc: &tracer };
-
- let mut film = Film::new(res);
- {
- let coord = RenderCoord::new(&mut film, Vector2i::new_xy(50, 50), 16000);
-
- coord.run_threaded(&ctx, &mut sampler, 8);
- }
-
- let image = film.finalize_image();
- if let Err(e) = image.save("test.png") {
- println!("Failed to save {}", e);
- }
-
-}
diff --git a/src/material/dielectric.rs b/src/material/dielectric.rs
deleted file mode 100644
index c8dc279..0000000
--- a/src/material/dielectric.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use super::Material;
-use crate::core::{min, Vector3f, Spectrum, Ray};
-use crate::world::Intersection;
-use crate::sample::Sampler;
-use crate::Float;
-use crate::material::reflectant::reflect;
-
-pub struct Dielectric {
- ratio: Float,
-}
-
-// Implementation from RTIOW
-fn refract(v: Vector3f, n: Vector3f, r_ratio: Float, cos_theta: Float) -> Vector3f {
- let r_perp = (v + n * cos_theta) * r_ratio;
- let r_parallel = n * (-(1.0 - r_perp.len_squared()).abs().sqrt());
-
- r_perp + r_parallel
-}
-
-// Schlick Approximation, explained in RTIOW
-fn fresnel(cos: Float, ratio: Float) -> Float {
- let mut r0 = (1.0-ratio) / (1.0+ratio);
- r0 = r0 * r0;
-
- r0 + (1.0-r0)*(1.0-cos).powi(5)
-}
-
-impl Dielectric {
- pub fn new(ratio: Float) -> Self {
- Self { ratio }
- }
-}
-
-impl Material for Dielectric {
- // Implementation from RTIOW
- fn scatter(&self, ray: &Ray, i: &Intersection, sampler: &mut dyn Sampler) -> Option<(Spectrum, Ray)> {
- let ratio = if i.front {1.0/self.ratio} else {self.ratio};
-
- let ray_dir = ray.direction.norm();
- let cos_theta = min((-ray_dir).dot(&i.n), 1.0);
- let sin_theta = (1.0 - cos_theta*cos_theta).sqrt();
-
- // Test if it is possible for the ray the retract or if it must reflect.
- let cannot_refract = (ratio * sin_theta) > 1.0;
- let direction = if cannot_refract || (fresnel(cos_theta, ratio) > sampler.get_sample()) {
- reflect(ray_dir, i.n)
- } else {
- refract(ray_dir, i.n, ratio, cos_theta)
- };
-
- Some((
- Spectrum::WHITE,
- Ray::new(i.p, direction),
- ))
- }
-}
diff --git a/src/material/diffuse_light.rs b/src/material/diffuse_light.rs
deleted file mode 100644
index fe462a8..0000000
--- a/src/material/diffuse_light.rs
+++ /dev/null
@@ -1,27 +0,0 @@
-use super::Material;
-use crate::core::{Ray, Spectrum};
-use crate::Float;
-
-pub struct DiffuseLight {
- color: Spectrum,
-}
-
-impl DiffuseLight {
- pub fn new(c: Spectrum) -> Self {
- Self {
- color: c,
- }
- }
-
- pub fn new_white(s: Float) -> Self {
- Self {
- color: Spectrum::new_rgb(s, s, s),
- }
- }
-}
-
-impl Material for DiffuseLight {
- fn emitted(&self, _: &Ray) -> Option<Spectrum> {
- Some(self.color)
- }
-}
diff --git a/src/material/lambertian.rs b/src/material/lambertian.rs
deleted file mode 100644
index 3df6522..0000000
--- a/src/material/lambertian.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-use super::Material;
-use crate::core::{Ray, Spectrum};
-use crate::world::Intersection;
-use crate::sample::Sampler;
-
-use std::rc::Rc;
-
-pub struct Lambertian {
- color: Spectrum,
-}
-
-impl Lambertian {
- pub fn new(c: Spectrum) -> Lambertian {
- Lambertian {
- color: c,
- }
- }
-
- pub fn new_rc(c: Spectrum) -> Rc<dyn Material> {
- Rc::new(Self::new(c))
- }
-}
-
-impl Material for Lambertian {
- fn scatter(&self, _: &Ray, i: &Intersection, sampler: &mut dyn Sampler) -> Option<(Spectrum, Ray)> {
- let mut newray = Ray {
- origin: i.p,
- direction: i.n + sampler.get_unit_vector(),
- };
-
- // Make sure that the resulting direction is not (0, 0, 0)
- if newray.direction.near_zero() {
- newray.direction = i.n;
- }
-
- Some((self.color, newray))
- }
-}
diff --git a/src/material/mod.rs b/src/material/mod.rs
deleted file mode 100644
index 6732598..0000000
--- a/src/material/mod.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use crate::core::{Ray, Spectrum};
-use crate::world::Intersection;
-use crate::sample::Sampler;
-
-mod lambertian;
-mod diffuse_light;
-mod sky_light;
-mod dielectric;
-pub mod reflectant;
-
-pub use lambertian::Lambertian;
-pub use diffuse_light::DiffuseLight;
-pub use sky_light::SkyLight;
-pub use dielectric::Dielectric;
-pub use reflectant::Reflectant;
-
-pub trait Material: Sync + Send {
- fn scatter(&self, _: &Ray, _: &Intersection, _: &mut dyn Sampler) -> Option<(Spectrum, Ray)> {
- None
- }
-
- fn emitted(&self, _: &Ray) -> Option<Spectrum> {
- None
- }
-}
diff --git a/src/material/reflectant.rs b/src/material/reflectant.rs
deleted file mode 100644
index b5ec0ae..0000000
--- a/src/material/reflectant.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use crate::Float;
-use crate::core::{Ray, Spectrum, Vector3f};
-use crate::world::Intersection;
-use super::Material;
-use crate::sample::Sampler;
-
-pub struct Reflectant {
- color: Spectrum,
- fuzz: Option<Float>,
-}
-
-impl Reflectant {
- pub fn new(c: Spectrum, fuzz: Option<Float>) -> Reflectant {
- Reflectant {
- color: c,
- fuzz,
- }
- }
-}
-
-pub fn reflect(v: Vector3f, n: Vector3f) -> Vector3f {
- v - n * (2.0 * v.dot(&n))
-}
-
-impl Material for Reflectant {
- fn scatter(&self, ray: &Ray, i: &Intersection, sampler: &mut dyn Sampler) -> Option<(Spectrum, Ray)> {
- // Find reflectance vector
- let mut reflected = reflect(ray.direction, i.n);
- if let Some(fuzz) = self.fuzz {
- reflected += &(sampler.get_unit_vector() * fuzz);
- }
-
- Some((
- self.color,
- Ray::new(i.p, reflected.norm()),
- ))
- }
-}
-
-
diff --git a/src/material/sky_light.rs b/src/material/sky_light.rs
deleted file mode 100644
index e499b6e..0000000
--- a/src/material/sky_light.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-use super::Material;
-use crate::core::{Ray, Spectrum};
-
-pub struct SkyLight {
-}
-
-impl SkyLight {
- pub fn new() -> Self {
- Self {}
- }
-}
-
-impl Material for SkyLight {
- fn emitted(&self, ray: &Ray) -> Option<Spectrum> {
- let t = (ray.direction.norm().y + 1.0) * 0.5;
-
- Some(Spectrum::new_rgb(1.0, 1.0, 1.0) * (1.0-t) + Spectrum::new_rgb(0.5, 0.7, 1.0) * t)
- }
-}
diff --git a/src/render/coordinator.rs b/src/render/coordinator.rs
deleted file mode 100644
index e2f825b..0000000
--- a/src/render/coordinator.rs
+++ /dev/null
@@ -1,126 +0,0 @@
-//! Split a image into small tiles
-//!
-//! This enables multithreaded rendering.
-
-use super::{RenderTask, RenderContext};
-use crate::camera::Film;
-use crate::core::{Bound2i, Vector2i};
-use crate::sample::Sampler;
-
-use std::sync::{Mutex};
-
-struct Tiler {
- tilesize: Vector2i,
-
- fullbound: Bound2i,
- tilemap_size: Vector2i,
- tilemap_area: i32,
-
- next_tile: i32,
- tiles_done: i32,
-}
-
-pub struct RenderCoord<'a> {
- film: Mutex<&'a mut Film>,
- samples: u32,
- tiler: Mutex<Tiler>,
-}
-
-impl Tiler {
- pub fn new(fullsize: Vector2i, tilesize: Vector2i) -> Tiler {
- assert!(tilesize.x != 0 && tilesize.y != 0);
-
- let tilemap_size = fullsize / tilesize;
-
- Self {
- tilesize,
-
- fullbound: Bound2i::new(&Vector2i::ZERO, &fullsize),
- tilemap_size,
- tilemap_area: (tilemap_size.x * tilemap_size.y),
-
- next_tile: 0,
- tiles_done: 0,
- }
-
- }
-
- pub fn next_tile(&mut self) -> Option<(Bound2i, i32)> {
- // Check if we are outside
- if self.next_tile >= self.tilemap_area {
- return None;
- }
-
- // Convert the tile to xy in tilemap
- let tile = Vector2i::new_xy(self.next_tile / self.tilemap_size.x, self.next_tile % self.tilemap_size.x);
- let tile_index = self.next_tile;
-
- self.next_tile += 1;
-
- // Create a bound from the tilecoordinate
- let tile_start = tile * self.tilesize;
- // We need to make sure the resulting tile is inside the image bound
- Some((
- Bound2i::new(&tile_start, &(tile_start + self.tilesize)).intersect(&self.fullbound),
- tile_index,
- ))
- }
-
- /// Mark a index as done and return the overall process
- pub fn mark_done(&mut self, _index: i32) -> f32 {
- self.tiles_done += 1;
-
- println!("Progress: {}/{}", self.tiles_done, self.tilemap_area);
- self.tiles_done as f32 / self.tilemap_area as f32
- }
-}
-
-impl<'a> RenderCoord<'a> {
- pub fn new(film: &'a mut Film, tilesize: Vector2i, samples: u32) -> Self {
- let size = film.size;
-
- Self {
- film: Mutex::new(film),
- samples,
- tiler: Mutex::new(Tiler::new(size, tilesize)),
- }
- }
-
- pub fn next_task(&self) -> Option<RenderTask> {
- let (tile_bound, index) = self.tiler.lock().unwrap().next_tile()?;
-
- let film_tile = {
- let film = self.film.lock().unwrap();
- Box::new(film.get_tile(&tile_bound))
- };
-
- Some(RenderTask::new(film_tile, self.samples, index))
- }
-
- pub fn finish_task(&self, task: &RenderTask) {
- {
- let mut film = self.film.lock().unwrap();
- film.commit_tile(task.tile.as_ref());
- }
-
- self.tiler.lock().unwrap().mark_done(task.tile_index);
- }
-
- pub fn work(&self, ctx: &RenderContext, sampler: &mut dyn Sampler) {
- while let Some(mut task) = self.next_task() {
- task.render(ctx, sampler);
- self.finish_task(&task);
- }
- }
-
- pub fn run_threaded(&self, ctx: &RenderContext, sampler: &mut (dyn Sampler + Send), threads: u32) {
- crossbeam::scope(|scope| {
- for _ in 0..threads {
- let mut sampler = sampler.clone_and_seed();
- scope.spawn(move |_| {
- self.work(ctx, sampler.as_mut());
- });
- }
- }).unwrap();
- }
-}
diff --git a/src/render/mod.rs b/src/render/mod.rs
deleted file mode 100644
index b81aaf5..0000000
--- a/src/render/mod.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-mod task;
-mod coordinator;
-
-pub use task::{RenderContext, RenderTask};
-pub use coordinator::RenderCoord;
diff --git a/src/render/task.rs b/src/render/task.rs
deleted file mode 100644
index 017fe24..0000000
--- a/src/render/task.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-//! Implements the main render loop
-//!
-//! This is not a final design
-use crate::camera::film::FilmTile;
-use crate::camera::Camera;
-use crate::trace::{DefaultTracer, Tracer};
-use crate::sample::Sampler;
-
-use crate::core::{Vector2f};
-use crate::Float;
-
-pub struct RenderTask {
- pub tile: Box<FilmTile>,
- samples: u32,
- pub tile_index: i32,
-}
-
-pub struct RenderContext<'a> {
- pub cam: &'a Camera,
- pub trc: &'a DefaultTracer<'a>,
-}
-
-impl RenderTask {
- pub fn new(tile: Box<FilmTile>, samples: u32, tile_index: i32) -> Self {
- Self { tile, samples, tile_index }
- }
-
- fn render_at(&mut self, ctx: &RenderContext, x: i32, y: i32, sampler: &mut dyn Sampler) {
- let corner = Vector2f::new_xy(x as Float, y as Float);
-
- for _ in 0..self.samples {
- let p = corner + sampler.get_sample_2d();
-
- // Create a ray
- let (r, _) = ctx.cam.generate_ray(&p, sampler);
-
- self.tile.add_sample(&p, ctx.trc.trace(sampler, &r));
- }
- }
-
- pub fn render(&mut self, ctx: &RenderContext, sampler: &mut dyn Sampler) {
- let b = self.tile.bounds.clone();
- for y in b.min.y .. b.max.y {
- for x in b.min.x .. b.max.x {
- self.render_at(ctx, x, y, sampler);
- }
- }
- }
-}
diff --git a/src/sample/mod.rs b/src/sample/mod.rs
deleted file mode 100644
index 1a53921..0000000
--- a/src/sample/mod.rs
+++ /dev/null
@@ -1,46 +0,0 @@
-use crate::{M_PI, Float};
-use crate::core::{Vector3f, Vector2f};
-
-mod uniform;
-
-pub use uniform::UniformSampler;
-
-fn distribute_between(x: Float, a: Float, b: Float) -> Float {
- x * (b - a) + a
-}
-
-pub trait Sampler {
- fn get_sample(&mut self) -> Float;
-
- fn get_sample_2d(&mut self) -> Vector2f {
- Vector2f::new_xy(self.get_sample(), self.get_sample())
- }
-
- fn clone_and_seed(&mut self) -> Box<dyn Sampler + Send>;
-
- fn get_unit_vector(&mut self) -> Vector3f {
- let s2d = self.get_sample_2d();
-
- let lambda = distribute_between(s2d.x, -M_PI, M_PI);
- let costheta = 2.0 * s2d.y - 1.0;
- let sintheta = costheta.acos().sin();
-
- Vector3f::new_xyz(
- lambda.cos() * sintheta,
- lambda.sin() * sintheta,
- costheta,
- )
- }
-
- fn get_in_circle(&mut self) -> Vector2f {
- let s2d = self.get_sample_2d();
-
- let d = s2d.x.sqrt();
- let theta = s2d.y * 2.0 * M_PI;
-
- Vector2f::new_xy(
- d * theta.cos(),
- d * theta.sin(),
- )
- }
-}
diff --git a/src/sample/uniform.rs b/src/sample/uniform.rs
deleted file mode 100644
index c144f27..0000000
--- a/src/sample/uniform.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-use crate::Float;
-use super::Sampler;
-
-use rand::prelude::*;
-use rand::distributions::Uniform;
-use rand_pcg::Pcg32;
-
-#[derive(Clone)]
-pub struct UniformSampler {
- r: Pcg32,
- d: Uniform<Float>,
-}
-
-impl UniformSampler {
- pub fn new() -> Self {
- Self::default()
- }
-}
-
-impl Default for UniformSampler {
- fn default() -> Self {
- Self {
- r: Pcg32::seed_from_u64(1),
- d: Uniform::new(0.0, 1.0),
- }
- }
-}
-
-impl Sampler for UniformSampler {
- fn get_sample(&mut self) -> Float {
- self.d.sample(&mut self.r)
- }
-
- fn clone_and_seed(&mut self) -> Box<dyn Sampler + Send> {
- let mut n = self.clone();
- n.r = Pcg32::seed_from_u64(self.r.next_u64());
- Box::new(n)
- }
-}
diff --git a/src/trace/mod.rs b/src/trace/mod.rs
deleted file mode 100644
index d45fb23..0000000
--- a/src/trace/mod.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use crate::world::{Hittable, Scene};
-use crate::core::{Spectrum, Ray, Vector3f};
-use crate::sample::Sampler;
-
-mod pathtrace;
-pub use pathtrace::PathTracer;
-
-/// Simple surface normal tracer
-///
-/// This ray tracer bases color values on the hit surface normals
-pub struct NormTracer<'a> {
- scn: &'a Scene,
-}
-
-/// Alias for chosen trace implementation.
-///
-/// This is swiched at compile time to save alot of time.
-pub type DefaultTracer<'a> = PathTracer<'a>;
-
-pub trait Tracer {
- fn trace(&self, sampler: &mut dyn Sampler, ray: &Ray) -> Spectrum;
-}
-
-impl NormTracer<'_> {
- pub fn new(scn: &Scene) -> NormTracer {
- NormTracer {scn}
- }
-}
-
-impl Tracer for NormTracer<'_> {
- fn trace(&self, _: &mut dyn Sampler, ray: &Ray) -> Spectrum {
- // Trace ray, we dont care about material
- if let Some(i) = self.scn.intersect(ray) {
- let norm = i.n * 0.5 + Vector3f::new(0.5);
-
- return Spectrum::new_rgb(norm.x, norm.y, norm.z);
- }
-
- Spectrum::new_rgb(0.0, 0.0, 0.0)
- }
-}
diff --git a/src/trace/pathtrace.rs b/src/trace/pathtrace.rs
deleted file mode 100644
index f53e433..0000000
--- a/src/trace/pathtrace.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-use crate::world::{Hittable, Scene};
-use crate::core::{Ray, Spectrum};
-use crate::material::Material;
-use crate::sample::Sampler;
-use super::Tracer;
-
-pub struct PathTracer<'a> {
- depth: i32,
- scn: &'a Scene,
- background: Option<Box<dyn Material>>,
-}
-
-impl PathTracer<'_> {
- pub fn new(scn: &Scene, depth: Option<i32>, background: Option<Box<dyn Material>>) -> PathTracer {
- let depth = depth.unwrap_or(-1);
-
- PathTracer {
- depth,
- scn,
- background,
- }
- }
-
- pub fn trace_recur(&self, sampler: &mut dyn Sampler, ray: &Ray, depth: i32) -> Spectrum {
-
- if depth == 0 {
- return Spectrum::ZERO;
- }
-
- if let Some(i) = self.scn.intersect(ray) {
- // Extract material or default
- if let Some(mat) = i.m {
- let mut col = Spectrum::ZERO;
-
- if let Some((scalar, nray)) = mat.scatter(ray, &i, sampler) {
- col += &(self.trace_recur(sampler, &nray, depth-1) * scalar);
- }
-
- if let Some(c) = mat.emitted(ray) {
- col += &c;
- }
-
- return col;
- }
- }
-
- // If no color return background
- if let Some(back) = &self.background {
- back.emitted(ray).unwrap_or(Spectrum::ZERO)
- } else {
- Spectrum::ZERO
- }
- }
-}
-
-impl Tracer for PathTracer<'_> {
- fn trace(&self, sampler: &mut dyn Sampler, ray: &Ray) -> Spectrum {
- self.trace_recur(sampler, ray, self.depth)
- }
-}
diff --git a/src/world/container/list.rs b/src/world/container/list.rs
deleted file mode 100644
index 22b6d88..0000000
--- a/src/world/container/list.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use crate::world::{Object, Hittable, Intersection};
-use crate::core::{Bound3f, Ray};
-
-
-pub struct HittableList {
- elems: Vec<Object>,
-}
-
-impl HittableList {
- pub fn new() -> Self {
- Self::default()
- }
-
- pub fn add(&mut self, h: Object) {
- self.elems.push(h);
- }
-}
-
-impl Hittable for HittableList {
- fn intersect(&self, ray: &Ray) -> Option<Intersection> {
- let mut min: Option<Intersection> = None;
-
- for e in self.elems.iter() {
- if let Some(i) = e.intersect(&ray) {
- match min {
- // Do nothing if distance is bigger than min
- Some(ref min_i) if min_i.t < i.t => {},
- // If no existing min or closer than
- _ => min = Some(i),
- }
- }
- }
-
- min
- }
-
- fn bounding_box(&self) -> Bound3f {
- let mut bound: Bound3f = Bound3f::EMPTY;
-
- for e in self.elems.iter() {
- let eb = e.bounding_box();
- bound = bound.combine(&eb);
- }
-
- bound
- }
-}
-
-impl Default for HittableList {
- fn default() -> Self {
- Self {
- elems: Vec::new(),
- }
- }
-}
-
diff --git a/src/world/container/mod.rs b/src/world/container/mod.rs
deleted file mode 100644
index 35d4693..0000000
--- a/src/world/container/mod.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-mod list;
-
-pub use list::HittableList;
diff --git a/src/world/hittable.rs b/src/world/hittable.rs
deleted file mode 100644
index e11a3bc..0000000
--- a/src/world/hittable.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-use crate::core::{Vector3f, Bound3f, Ray};
-use crate::Float;
-use crate::material::Material;
-
-/// Returns the context of a intersection
-pub struct Intersection<'a> {
- /// Normal vector at intersection
- pub n: Vector3f,
- pub p: Vector3f,
- pub front: bool,
- pub t: Float,
- pub m: Option<&'a dyn Material>,
-}
-
-impl<'a> Intersection<'a> {
- pub fn new(out_normal: Vector3f, point: Vector3f, ray: &Ray, t: Float) -> Self {
- let front = ray.direction.dot(&out_normal) < 0.0;
- Intersection {
- n: { if front { out_normal } else { -out_normal } },
- front,
- p: point,
- m: None,
- t,
- }
- }
-
- pub fn add_material_if_none(&mut self, mat: &'a dyn Material) {
- if let None = self.m {
- self.m = Some(mat);
- }
- }
-}
-
-/// Defines a common trait for objects in the scene
-pub trait Hittable: Sync + Send {
- /// Returns the intersection with ray
- fn intersect(&self, ray: &Ray) -> Option<Intersection>;
-
- /// Returns the axis alligned bounding box containing self
- fn bounding_box(&self) -> Bound3f;
-}
diff --git a/src/world/mod.rs b/src/world/mod.rs
deleted file mode 100644
index cba6ddc..0000000
--- a/src/world/mod.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-//! Manages world objects, and implements intersection
-pub mod shapes;
-
-mod scene;
-pub mod container;
-mod hittable;
-pub use scene::*;
-pub use hittable::{Intersection, Hittable};
-pub use shapes::Shape;
-
-use std::sync::Arc;
-use crate::material::Material;
-use crate::core::{Bound3f, Ray};
-
-pub struct Object {
- pub shape: Shape,
- pub mat: Arc<dyn Material>,
-}
-
-impl Object {
- pub fn new<T: Into<Shape>>(mat: Arc<dyn Material>, shape: T) -> Self {
- Object {
- mat,
- shape: shape.into(),
- }
- }
-}
-
-impl Hittable for Object {
- fn intersect(&self, ray: &Ray) -> Option<Intersection> {
- if let Some(mut inter) = self.shape.intersect(ray) {
- inter.add_material_if_none(self.mat.as_ref());
- Some(inter)
- } else {
- None
- }
- }
-
- fn bounding_box(&self) -> Bound3f {
- self.shape.bounding_box()
- }
-}
diff --git a/src/world/scene.rs b/src/world/scene.rs
deleted file mode 100644
index 87bec1f..0000000
--- a/src/world/scene.rs
+++ /dev/null
@@ -1,43 +0,0 @@
-use crate::core::{Bound3f, Ray};
-
-use super::{Object, container, Hittable, Intersection};
-
-type Container = container::HittableList;
-
-pub struct Scene {
- content: Container,
-}
-
-impl Scene {
- pub fn new() -> Self {
- Self::default()
- }
-
- pub fn add_object(&mut self, obj: Object) {
- self.content.add(obj);
- }
-
- pub fn add_objects(&mut self, objs: Vec<Object>) {
- for obj in objs {
- self.add_object(obj);
- }
- }
-}
-
-impl Hittable for Scene {
- fn intersect(&self, ray: &Ray) -> Option<Intersection> {
- self.content.intersect(ray)
- }
-
- fn bounding_box(&self) -> Bound3f {
- self.content.bounding_box()
- }
-}
-
-impl Default for Scene {
- fn default() -> Self {
- Self {
- content: Container::new(),
- }
- }
-}
diff --git a/src/world/shapes/mod.rs b/src/world/shapes/mod.rs
deleted file mode 100644
index a11df5d..0000000
--- a/src/world/shapes/mod.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-mod sphere;
-
-pub use sphere::Sphere;
-
-use crate::world::{Hittable, Intersection};
-use crate::core::{Bound3f, Ray};
-
-pub enum Shape {
- Sphere(Sphere),
-}
-
-impl Hittable for Shape {
- fn intersect(&self, ray: &Ray) -> Option<Intersection> {
- match self {
- Self::Sphere(sph) => sph.intersect(ray)
- }
- }
-
- fn bounding_box(&self) -> Bound3f {
- match self {
- Self::Sphere(sph) => sph.bounding_box()
- }
- }
-}
-
-impl From<Sphere> for Shape {
- fn from(s: Sphere) -> Self {
- Self::Sphere(s)
- }
-}
diff --git a/src/world/shapes/sphere.rs b/src/world/shapes/sphere.rs
deleted file mode 100644
index 1df9c35..0000000
--- a/src/world/shapes/sphere.rs
+++ /dev/null
@@ -1,98 +0,0 @@
-//! Implements sphere
-//!
-//! Spheres are relatively easy to calculate intersections between
-use crate::{Float, NEAR_ZERO};
-use crate::core::{Ray, Vector3f, Bound3f};
-use crate::world::{Hittable, Intersection};
-
-pub struct Sphere {
- radius: Float,
- center: Vector3f,
-}
-
-impl Sphere {
- pub fn new(radius: Float, center: Vector3f) -> Sphere {
- Sphere {
- radius,
- center,
- }
- }
-
- fn norm_at(&self, point: &Vector3f) -> Vector3f {
- let mut v = *point - self.center;
- v /= self.radius;
- v
- }
-}
-
-impl Hittable for Sphere {
- // Implementation from ray tracing in a weekend
- fn intersect(&self, ray: &Ray) -> Option<Intersection> {
- let oc = ray.origin - self.center;
- let a = ray.direction.len_squared();
- let half_b = oc.dot(&ray.direction);
- let c = oc.len_squared() - self.radius * self.radius;
- let disc = half_b*half_b - a*c;
-
- if disc < 0.0 {
- None
- } else {
- let disc_sqrt = disc.sqrt();
-
- let mut distance = -half_b - disc_sqrt;
- if distance <= NEAR_ZERO {
- distance = -half_b + disc_sqrt;
- }
- if distance <= NEAR_ZERO {
- return None;
- }
-
- distance /= a;
- let w = ray.at(distance);
- Some(Intersection::new(
- self.norm_at(&w),
- w,
- ray,
- distance,
- ))
- }
-
- }
-
- /// Box containing the circle
- ///
- /// # Examples
- ///
- /// ```
- /// use rendering::core::Vector3f;
- /// use rendering::world::{Hittable, shapes::Sphere};
- ///
- /// let sph = Sphere::new(1.0, Vector3f::new(0.0));
- /// let b = sph.bounding_box();
- ///
- /// assert!(b.min.x == -1.0 && b.min.y == -1.0 && b.min.z == -1.0);
- /// assert!(b.max.x == 1.0 && b.max.y == 1.0 && b.max.z == 1.0);
- fn bounding_box(&self) -> Bound3f {
- let offset = Vector3f::new(self.radius);
-
- Bound3f::new(self.center - offset, self.center + offset)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn sphere_intersect() {
- let sph = Sphere::new(2.0, Vector3f::new_xyz(2.0, 3.0, 4.0));
-
- let ray = Ray {
- origin: Vector3f::new_xyz(1.0, 0.0, 0.0),
- direction: Vector3f::new_xyz(0.0, 1.0, 1.5).norm(),
- };
-
- let dist = sph.intersect(&ray).unwrap();
- assert!((dist.t - 3.28).abs() < 0.01);
- }
-}