refactor: use 64-bit types instead of 32-bit
I don't know why I wasn't using 64 bit floats from the beginning, honestly. I had weird priorities back then
This commit is contained in:
parent
9879184d47
commit
763a4ff923
9 changed files with 469 additions and 243 deletions
136
src/camera.rs
136
src/camera.rs
|
|
@ -1,87 +1,123 @@
|
|||
extern crate nalgebra as na;
|
||||
|
||||
use na::*;
|
||||
use na::geometry::{Point2, Point3};
|
||||
use na::*;
|
||||
|
||||
use crate::types::Ray;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Camera {
|
||||
matrix: Isometry3<f32>, // The transformation that stores the
|
||||
// position and orientation of the camera. (Not actually a matrix, but w/e)
|
||||
matrix: Isometry3<f64>, // The transformation that stores the
|
||||
// position and orientation of the camera. (Not actually a matrix, but w/e)
|
||||
focal_length: f64, // The distance from the camera origin to the canvas.
|
||||
canvas_size: Vector2<f64>, // The size of the canvas within the world space.
|
||||
|
||||
focal_length: f32, // The distance from the camera origin to the canvas.
|
||||
canvas_size: Vector2<f32>, // The size of the canvas within the world space.
|
||||
|
||||
pub image_size: Vector2<u32> // The size of the final image in pixels.
|
||||
pub image_size: Vector2<u64>, // The size of the final image in pixels.
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
|
||||
// Constructs a new camera from a position and viewing direction.
|
||||
pub fn new_(pos: Point3<f32>, dir: Vector3<f32>, up: Vector3<f32>,
|
||||
focal_length: f32, aspect_ratio: f32, canvas_y: f32, image_y: u32) -> Self {
|
||||
pub fn new_(
|
||||
pos: Point3<f64>,
|
||||
dir: Vector3<f64>,
|
||||
up: Vector3<f64>,
|
||||
focal_length: f64,
|
||||
aspect_ratio: f64,
|
||||
canvas_y: f64,
|
||||
image_y: u64,
|
||||
) -> Self {
|
||||
let iso = Isometry3::face_towards(&pos, &(pos + dir), &up);
|
||||
Camera {
|
||||
matrix: iso,
|
||||
focal_length: focal_length,
|
||||
canvas_size: Vector2::new(canvas_y * aspect_ratio, canvas_y),
|
||||
image_size: Vector2::new((image_y as f32 * aspect_ratio) as u32, image_y)
|
||||
canvas_size: Vector2::new(canvas_y * aspect_ratio, canvas_y),
|
||||
image_size: Vector2::new((image_y as f64 * aspect_ratio) as u64, image_y),
|
||||
}
|
||||
}
|
||||
|
||||
// Constructs a new camera from a position and viewing direction
|
||||
// (assuming the camera is oriented upright).
|
||||
pub fn new(pos: Point3<f32>, dir: Vector3<f32>,
|
||||
focal_length: f32, aspect_ratio: f32, canvas_y: f32, image_y: u32) -> Self
|
||||
{ Camera::new_(pos, dir, Vector3::y(), focal_length, aspect_ratio, canvas_y, image_y) }
|
||||
pub fn new(
|
||||
pos: Point3<f64>,
|
||||
dir: Vector3<f64>,
|
||||
focal_length: f64,
|
||||
aspect_ratio: f64,
|
||||
canvas_y: f64,
|
||||
image_y: u64,
|
||||
) -> Self {
|
||||
Camera::new_(
|
||||
pos,
|
||||
dir,
|
||||
Vector3::y(),
|
||||
focal_length,
|
||||
aspect_ratio,
|
||||
canvas_y,
|
||||
image_y,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pos(&self) -> Point3<f32> { Point3::from(self.matrix.translation.vector) }
|
||||
pub fn pos(&self) -> Point3<f64> {
|
||||
Point3::from(self.matrix.translation.vector)
|
||||
}
|
||||
|
||||
// Takes a 2D point in the image space and
|
||||
// maps it to the 3D point on the canvas.
|
||||
fn project(&self, x: u32, y: u32) -> Point3<f32> {
|
||||
fn project(&self, x: u64, y: u64) -> Point3<f64> {
|
||||
// convert point from raster coordinates to center-based coordinates
|
||||
let pixelndc = Point2::new(x as f32 + 0.5 - self.image_size.x as f32 * 0.5, -(y as f32 + 0.5) + self.image_size.y as f32 * 0.5);
|
||||
let pixelndc = Point2::new(
|
||||
x as f64 + 0.5 - self.image_size.x as f64 * 0.5,
|
||||
-(y as f64 + 0.5) + self.image_size.y as f64 * 0.5,
|
||||
);
|
||||
|
||||
let point: Point3<f32> = Point::from(pixelndc.coords.component_div(&self.image_size.map(|x| x as f32))
|
||||
.component_mul(&self.canvas_size)
|
||||
.fixed_resize(self.focal_length));
|
||||
let point: Point3<f64> = Point::from(
|
||||
pixelndc
|
||||
.coords
|
||||
.component_div(&self.image_size.map(|x| x as f64))
|
||||
.component_mul(&self.canvas_size)
|
||||
.fixed_resize(self.focal_length),
|
||||
);
|
||||
self.matrix * point
|
||||
}
|
||||
|
||||
// Takes a 2D point in the image space and
|
||||
// returns a ray in the world space, for use in raytracing.
|
||||
pub fn raycast(&self, x: u32, y: u32) -> Ray {
|
||||
pub fn raycast(&self, x: u64, y: u64) -> Ray {
|
||||
Ray::from_points(self.pos(), self.project(x, y))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn round(point: Point3<f32>) -> Point3<f32> {
|
||||
fn round(point: Point3<f64>) -> Point3<f64> {
|
||||
Point::from(point.coords.map(|x| x.round()))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn camera_pos() {
|
||||
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0, 1.0,
|
||||
2.0, 800);
|
||||
let camera: Camera = Camera::new(
|
||||
Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
800,
|
||||
);
|
||||
|
||||
assert_eq!(camera.pos(), Point3::new(-5.0, 0.0, 0.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn camera_matrix1() {
|
||||
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0, 1.0,
|
||||
2.0, 800);
|
||||
let camera: Camera = Camera::new(
|
||||
Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
800,
|
||||
);
|
||||
|
||||
let point = Point3::new(0.0, 0.0, 4.0);
|
||||
let point = camera.matrix * point;
|
||||
|
|
@ -91,10 +127,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn camera_matrix2() {
|
||||
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0, 1.0,
|
||||
2.0, 800);
|
||||
let camera: Camera = Camera::new(
|
||||
Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
800,
|
||||
);
|
||||
|
||||
let point = Point3::new(4.0, 0.0, 0.0);
|
||||
let point = camera.matrix * point;
|
||||
|
|
@ -104,10 +144,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn camera_project1() {
|
||||
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0, 1.0,
|
||||
2.0, 800);
|
||||
let camera: Camera = Camera::new(
|
||||
Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
800,
|
||||
);
|
||||
|
||||
let point = camera.project(400, 400);
|
||||
let point = round(point); // round to avoid errors
|
||||
|
|
@ -116,10 +160,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn camera_project2() {
|
||||
let camera: Camera = Camera::new(Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0, 1.0,
|
||||
2.0, 800);
|
||||
let camera: Camera = Camera::new(
|
||||
Point3::new(-5.0, 0.0, 0.0),
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
1.0,
|
||||
1.0,
|
||||
2.0,
|
||||
800,
|
||||
);
|
||||
|
||||
let point = camera.project(0, 0);
|
||||
let point = round(point); // round to avoid errors
|
||||
|
|
|
|||
|
|
@ -1,19 +1,22 @@
|
|||
|
||||
mod sphere; pub use sphere::*;
|
||||
mod plane; pub use plane::*;
|
||||
mod triangle; pub use triangle::*;
|
||||
mod bound; pub use bound::*;
|
||||
mod point_light; pub use point_light::*;
|
||||
mod sphere;
|
||||
pub use sphere::*;
|
||||
mod plane;
|
||||
pub use plane::*;
|
||||
mod triangle;
|
||||
pub use triangle::*;
|
||||
mod bound;
|
||||
pub use bound::*;
|
||||
mod point_light;
|
||||
pub use point_light::*;
|
||||
|
||||
use crate::types::*;
|
||||
|
||||
// A trait for types that can be in Objects.
|
||||
pub trait Surface {
|
||||
|
||||
// Takes in a ray and performs an intersection test
|
||||
// on itself. If the ray intersects the object,
|
||||
// returns the distance to the intersection point.
|
||||
fn intersect(&self, ray: Ray) -> Option<f32>;
|
||||
fn intersect(&self, ray: Ray) -> Option<f64>;
|
||||
|
||||
// Takes in a point (assumed to be on the object's surface)
|
||||
// and returns the normal vector off of that point.
|
||||
|
|
@ -29,7 +32,7 @@ pub trait Surface {
|
|||
|
||||
pub struct Object {
|
||||
pub surface: Box<dyn Surface>,
|
||||
bound: Bound
|
||||
bound: Bound,
|
||||
}
|
||||
|
||||
impl Object {
|
||||
|
|
@ -38,18 +41,23 @@ impl Object {
|
|||
let bound = surface.bound();
|
||||
Object {
|
||||
surface: Box::new(surface),
|
||||
bound
|
||||
bound,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||
pub fn intersect(&self, ray: Ray) -> Option<f64> {
|
||||
if self.bound.is_intersected(ray) {
|
||||
self.surface.intersect(ray)
|
||||
} else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn normal(&self, point: Point3f) -> Unit3f {
|
||||
self.surface.normal(point)
|
||||
}
|
||||
pub fn get_texture(&self, point: Point3f) -> Texture {
|
||||
self.surface.get_texture(point)
|
||||
}
|
||||
pub fn normal(&self, point: Point3f) -> Unit3f { self.surface.normal(point) }
|
||||
pub fn get_texture(&self, point: Point3f) -> Texture { self.surface.get_texture(point) }
|
||||
}
|
||||
|
||||
pub trait Light {
|
||||
|
|
@ -60,7 +68,7 @@ pub trait Light {
|
|||
fn get_color(&self, point: Point3f) -> Color;
|
||||
|
||||
// Compute intensity on a point.
|
||||
fn intensity(&self, point: Point3f) -> f32;
|
||||
fn intensity(&self, point: Point3f) -> f64;
|
||||
|
||||
// Return the direction from the point to the light source.
|
||||
fn direction(&self, point: Point3f) -> Unit3f;
|
||||
|
|
@ -69,5 +77,5 @@ pub trait Light {
|
|||
pub struct Scene {
|
||||
pub objects: Vec<Object>,
|
||||
pub lights: Vec<Box<dyn Light>>,
|
||||
pub background: Color
|
||||
pub background: Color,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,15 +10,17 @@ use crate::types::*;
|
|||
#[derive(Debug)]
|
||||
pub struct Bound {
|
||||
pub center: Point3f,
|
||||
pub radius: f32,
|
||||
pub radius: f64,
|
||||
|
||||
// If true, then the bounding sphere is disabled.
|
||||
pub bypass: bool
|
||||
pub bypass: bool,
|
||||
}
|
||||
|
||||
impl Bound {
|
||||
pub fn is_intersected(&self, ray: Ray) -> bool {
|
||||
if self.bypass { return true; }
|
||||
if self.bypass {
|
||||
return true;
|
||||
}
|
||||
|
||||
let l = ray.origin - self.center;
|
||||
l.norm_squared() >= self.radius * self.radius
|
||||
|
|
@ -26,5 +28,11 @@ impl Bound {
|
|||
|
||||
// pub fn contains(&self, point: &Point3f) -> bool { distance(&self.center, point) < self.radius }
|
||||
|
||||
pub fn bypass() -> Self { Bound { center: Point3::origin(), radius: 0.0, bypass: true } }
|
||||
pub fn bypass() -> Self {
|
||||
Bound {
|
||||
center: Point3::origin(),
|
||||
radius: 0.0,
|
||||
bypass: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,81 +1,107 @@
|
|||
extern crate nalgebra as na;
|
||||
|
||||
use na::*;
|
||||
use na::geometry::Point3;
|
||||
use na::*;
|
||||
|
||||
use super::{bound::*, Surface};
|
||||
use crate::types::*;
|
||||
use super::{Surface, bound::*};
|
||||
|
||||
pub struct Plane {
|
||||
pub center: Point3f, // Plane origin (used for texture mapping).
|
||||
pub normal: Unit3f, // Precomputed plane normal.
|
||||
pub center: Point3f, // Plane origin (used for texture mapping).
|
||||
pub normal: Unit3f, // Precomputed plane normal.
|
||||
|
||||
x_axis: Vector3f, // Plane x-axis (The 3D direction that corresponds to the x-direction on the plane).
|
||||
y_axis: Vector3f, // Plane y-axis (The 3D direction that corresponds to the y-direction on the plane).
|
||||
|
||||
texture: Box<dyn Fn(f32, f32) -> Texture> // Texture map.
|
||||
// Input coordinates are defined in terms of the axes above.
|
||||
texture: Box<dyn Fn(f64, f64) -> Texture>, // Texture map.
|
||||
// Input coordinates are defined in terms of the axes above.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Plane {
|
||||
// Creates a new plane.
|
||||
pub fn new<F: 'static>(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self
|
||||
where F: Fn(f32, f32) -> Texture
|
||||
where
|
||||
F: Fn(f64, f64) -> Texture,
|
||||
{
|
||||
Plane {
|
||||
center,
|
||||
normal: Unit::new_normalize(x_axis.cross(&y_axis)),
|
||||
x_axis: x_axis,
|
||||
y_axis: y_axis,
|
||||
texture: Box::new(texture)
|
||||
texture: Box::new(texture),
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a new plane with the normal flipped.
|
||||
pub fn new_flip<F: 'static>(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self
|
||||
where F: Fn(f32, f32) -> Texture
|
||||
pub fn new_flip<F: 'static>(
|
||||
center: Point3f,
|
||||
x_axis: Vector3f,
|
||||
y_axis: Vector3f,
|
||||
texture: F,
|
||||
) -> Self
|
||||
where
|
||||
F: Fn(f64, f64) -> Texture,
|
||||
{
|
||||
Plane {
|
||||
center: center,
|
||||
normal: Unit::new_normalize(y_axis.cross(&x_axis)),
|
||||
x_axis: x_axis,
|
||||
y_axis: y_axis,
|
||||
texture: Box::new(texture)
|
||||
texture: Box::new(texture),
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a new plane of a solid color.
|
||||
pub fn new_solid(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: Texture) -> Self
|
||||
{ Plane::new(center, x_axis, y_axis, move |_, _| texture) }
|
||||
pub fn new_solid(
|
||||
center: Point3f,
|
||||
x_axis: Vector3f,
|
||||
y_axis: Vector3f,
|
||||
texture: Texture,
|
||||
) -> Self {
|
||||
Plane::new(center, x_axis, y_axis, move |_, _| texture)
|
||||
}
|
||||
|
||||
// Creates a new flipped plane of a solid color.
|
||||
pub fn new_solid_flip(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: Texture) -> Self
|
||||
{ Plane::new_flip(center, x_axis, y_axis, move |_, _| texture) }
|
||||
|
||||
pub fn new_solid_flip(
|
||||
center: Point3f,
|
||||
x_axis: Vector3f,
|
||||
y_axis: Vector3f,
|
||||
texture: Texture,
|
||||
) -> Self {
|
||||
Plane::new_flip(center, x_axis, y_axis, move |_, _| texture)
|
||||
}
|
||||
|
||||
// Creates a new XY-plane with the given texture map.
|
||||
pub fn xy(texture: impl 'static + Fn(f32, f32) -> Texture) -> Self
|
||||
{ Plane::new(Point3::origin(), Vector3::x(), Vector3::y(), texture) }
|
||||
pub fn xy(texture: impl 'static + Fn(f64, f64) -> Texture) -> Self {
|
||||
Plane::new(Point3::origin(), Vector3::x(), Vector3::y(), texture)
|
||||
}
|
||||
|
||||
// Creates a new XZ-plane with the given texture map.
|
||||
pub fn xz(texture: impl 'static + Fn(f32, f32) -> Texture) -> Self
|
||||
{ Plane::new(Point3::origin(), Vector3::x(), Vector3::z(), texture) }
|
||||
pub fn xz(texture: impl 'static + Fn(f64, f64) -> Texture) -> Self {
|
||||
Plane::new(Point3::origin(), Vector3::x(), Vector3::z(), texture)
|
||||
}
|
||||
}
|
||||
|
||||
impl Surface for Plane {
|
||||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||
|
||||
fn intersect(&self, ray: Ray) -> Option<f64> {
|
||||
let d = self.normal.dot(&ray.direction);
|
||||
if d > -1e-3 { return None; }
|
||||
if d > -1e-3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let t = (self.center - ray.origin).dot(&*self.normal) / d;
|
||||
|
||||
if t >= 0.0 { Some(t) }
|
||||
else { None }
|
||||
if t >= 0.0 {
|
||||
Some(t)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn normal(&self, _point: Point3f) -> Unit3f { self.normal }
|
||||
fn normal(&self, _point: Point3f) -> Unit3f {
|
||||
self.normal
|
||||
}
|
||||
|
||||
fn get_texture(&self, point: Point3f) -> Texture {
|
||||
let rel_pos = point - self.center;
|
||||
|
|
@ -89,5 +115,7 @@ impl Surface for Plane {
|
|||
|
||||
// Planes are infinite, so no finite
|
||||
// bounding sphere could possibly contain one.
|
||||
fn bound(&self) -> Bound { Bound::bypass() }
|
||||
fn bound(&self) -> Bound {
|
||||
Bound::bypass()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,33 +2,42 @@ extern crate nalgebra as na;
|
|||
|
||||
use na::*;
|
||||
|
||||
use crate::types::*;
|
||||
use super::*;
|
||||
use crate::types::*;
|
||||
|
||||
pub struct PointLight {
|
||||
pub pos: Point3f,
|
||||
pub color: Color,
|
||||
pub intensity: f32
|
||||
pub intensity: f64,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl PointLight {
|
||||
pub fn new(pos: Point3f, color: Color, intensity: f32) -> PointLight {
|
||||
PointLight { pos, color, intensity }
|
||||
pub fn new(pos: Point3f, color: Color, intensity: f64) -> PointLight {
|
||||
PointLight {
|
||||
pos,
|
||||
color,
|
||||
intensity,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Light for PointLight {
|
||||
fn check_shadow(&self, point: Point3f, objects: &Vec<Object>) -> bool {
|
||||
let max_d = distance(&self.pos, &point);
|
||||
objects.iter()
|
||||
.filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point)))
|
||||
.all(|d| d - max_d > -1e-3 )
|
||||
objects
|
||||
.iter()
|
||||
.filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point)))
|
||||
.all(|d| d - max_d > -1e-3)
|
||||
}
|
||||
|
||||
fn get_color(&self, _point: Point3f) -> Color { self.color }
|
||||
fn get_color(&self, _point: Point3f) -> Color {
|
||||
self.color
|
||||
}
|
||||
|
||||
fn intensity(&self, _point: Point3f) -> f32 { self.intensity }
|
||||
fn intensity(&self, _point: Point3f) -> f64 {
|
||||
self.intensity
|
||||
}
|
||||
|
||||
fn direction(&self, point: Point3f) -> Unit3f {
|
||||
Unit::new_normalize(self.pos - point)
|
||||
|
|
@ -42,7 +51,13 @@ mod tests {
|
|||
#[test]
|
||||
fn point_light_check_shadow() {
|
||||
let light = PointLight::new(Point3::new(0.0, 1.0, 0.0), Color::white(), 1.0);
|
||||
let block = Object::new(Sphere::new_solid(0.0, 0.5, 0.0, 0.1, Texture::new(0.0, 0.0, 0.0, 0.0)));
|
||||
let block = Object::new(Sphere::new_solid(
|
||||
0.0,
|
||||
0.5,
|
||||
0.0,
|
||||
0.1,
|
||||
Texture::new(0.0, 0.0, 0.0, 0.0),
|
||||
));
|
||||
|
||||
assert!(light.check_shadow(Point3::origin(), &Vec::new()));
|
||||
assert!(!light.check_shadow(Point3::origin(), &vec![block]));
|
||||
|
|
|
|||
|
|
@ -1,49 +1,57 @@
|
|||
extern crate nalgebra as na;
|
||||
|
||||
use std::f32::consts::PI;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use na::*;
|
||||
use na::geometry::Point3;
|
||||
use na::*;
|
||||
|
||||
use super::{bound::*, Surface};
|
||||
use crate::types::*;
|
||||
use super::{Surface, bound::*};
|
||||
|
||||
pub struct Sphere {
|
||||
pub center: Point3f, // Center point of the sphere.
|
||||
pub radius: f32, // Radius of the sphere.
|
||||
pub radius: f64, // Radius of the sphere.
|
||||
|
||||
texture: Box<dyn Fn(f32, f32) -> Texture> // Texture map.
|
||||
// Uses spherical coordinates (normalized from 0-1) as input.
|
||||
texture: Box<dyn Fn(f64, f64) -> Texture>, // Texture map.
|
||||
// Uses spherical coordinates (normalized from 0-1) as input.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Sphere {
|
||||
// Creates a new sphere.
|
||||
pub fn new<F: 'static>(x: f32, y: f32, z: f32, radius: f32, texture: F) -> Self
|
||||
where F: Fn(f32, f32) -> Texture
|
||||
pub fn new<F: 'static>(x: f64, y: f64, z: f64, radius: f64, texture: F) -> Self
|
||||
where
|
||||
F: Fn(f64, f64) -> Texture,
|
||||
{
|
||||
Sphere {
|
||||
center: Point3::new(x, y, z), radius,
|
||||
texture: Box::new(texture)
|
||||
center: Point3::new(x, y, z),
|
||||
radius,
|
||||
texture: Box::new(texture),
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a new sphere of a solid color.
|
||||
pub fn new_solid(x: f32, y: f32, z: f32, radius: f32, texture: Texture) -> Self
|
||||
{ Sphere::new(x, y, z, radius, move |_, _| texture) }
|
||||
pub fn new_solid(x: f64, y: f64, z: f64, radius: f64, texture: Texture) -> Self {
|
||||
Sphere::new(x, y, z, radius, move |_, _| texture)
|
||||
}
|
||||
}
|
||||
|
||||
impl Surface for Sphere {
|
||||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||
fn solve_quadratic(b: f32, c: f32) -> Option<(f32, f32)> {
|
||||
fn intersect(&self, ray: Ray) -> Option<f64> {
|
||||
fn solve_quadratic(b: f64, c: f64) -> Option<(f64, f64)> {
|
||||
let discr = b * b - 4.0 * c;
|
||||
|
||||
if discr < 0.0 { None }
|
||||
else if discr == 0.0 {
|
||||
if discr < 0.0 {
|
||||
None
|
||||
} else if discr == 0.0 {
|
||||
let x = -0.5 * b;
|
||||
Some((x, x))
|
||||
} else {
|
||||
let q = if b > 0.0 { -0.5 * (b + discr.sqrt()) } else { -0.5 * (b - discr.sqrt()) };
|
||||
let q = if b > 0.0 {
|
||||
-0.5 * (b + discr.sqrt())
|
||||
} else {
|
||||
-0.5 * (b - discr.sqrt())
|
||||
};
|
||||
Some((q, c / q))
|
||||
}
|
||||
}
|
||||
|
|
@ -54,11 +62,17 @@ impl Surface for Sphere {
|
|||
|
||||
let (mut t0, mut t1) = solve_quadratic(b, c)?;
|
||||
|
||||
if t0 > t1 { std::mem::swap(&mut t0, &mut t1); }
|
||||
if t0 > t1 {
|
||||
std::mem::swap(&mut t0, &mut t1);
|
||||
}
|
||||
|
||||
if t0 >= 0.0 { Some(t0) }
|
||||
else if t1 >= 0.0 { Some(t1) }
|
||||
else { None }
|
||||
if t0 >= 0.0 {
|
||||
Some(t0)
|
||||
} else if t1 >= 0.0 {
|
||||
Some(t1)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn normal(&self, point: Point3f) -> Unit3f {
|
||||
|
|
@ -77,5 +91,11 @@ impl Surface for Sphere {
|
|||
(*self.texture)(x, y)
|
||||
}
|
||||
|
||||
fn bound(&self) -> Bound { Bound { center: self.center, radius: self.radius, bypass: false } }
|
||||
fn bound(&self) -> Bound {
|
||||
Bound {
|
||||
center: self.center,
|
||||
radius: self.radius,
|
||||
bypass: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ extern crate nalgebra as na;
|
|||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use na::*;
|
||||
use na::geometry::Point3;
|
||||
use na::*;
|
||||
|
||||
use super::{bound::*, Surface};
|
||||
use crate::types::*;
|
||||
use super::{Surface, bound::*};
|
||||
|
||||
pub struct Triangle {
|
||||
pub v1: usize, // Handles to 3 vertices.
|
||||
|
|
@ -14,35 +14,45 @@ pub struct Triangle {
|
|||
pub v3: usize,
|
||||
|
||||
normal: Unit3f, // Precalculated normal vector.
|
||||
area: f32, // Precalculated area for barycentric calculations.
|
||||
area: f64, // Precalculated area for barycentric calculations.
|
||||
|
||||
texture: Box<dyn Fn(f32, f32, f32) -> Texture> // Texture map.
|
||||
// Uses barycentric coordinates as input.
|
||||
texture: Box<dyn Fn(f64, f64, f64) -> Texture>, // Texture map.
|
||||
// Uses barycentric coordinates as input.
|
||||
}
|
||||
|
||||
pub struct TriangleMesh {
|
||||
pub vertices: Vec<Point3f>,
|
||||
pub triangles: Vec<Triangle>
|
||||
pub triangles: Vec<Triangle>,
|
||||
}
|
||||
|
||||
fn tri_area(a: &Point3f, b: &Point3f, c: &Point3f) -> f32 {
|
||||
let prlg_area: f32 = (b - a).cross(&(c - a)).norm();
|
||||
fn tri_area(a: &Point3f, b: &Point3f, c: &Point3f) -> f64 {
|
||||
let prlg_area: f64 = (b - a).cross(&(c - a)).norm();
|
||||
prlg_area / 2.0
|
||||
}
|
||||
|
||||
impl Triangle {
|
||||
fn vertex1<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v1] }
|
||||
fn vertex2<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v2] }
|
||||
fn vertex3<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v3] }
|
||||
fn vertex1<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f {
|
||||
&vertices[self.v1]
|
||||
}
|
||||
fn vertex2<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f {
|
||||
&vertices[self.v2]
|
||||
}
|
||||
fn vertex3<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f {
|
||||
&vertices[self.v3]
|
||||
}
|
||||
|
||||
// Conversion of barycentric coordinates to
|
||||
// a point on the triangle.
|
||||
fn from_bary(&self, vertices: &Vec<Point3f>, t: f32, u: f32, v: f32) -> Point3f {
|
||||
Point::from(t * self.vertex1(vertices).coords + u * self.vertex2(vertices).coords + v * self.vertex3(vertices).coords)
|
||||
fn from_bary(&self, vertices: &Vec<Point3f>, t: f64, u: f64, v: f64) -> Point3f {
|
||||
Point::from(
|
||||
t * self.vertex1(vertices).coords
|
||||
+ u * self.vertex2(vertices).coords
|
||||
+ v * self.vertex3(vertices).coords,
|
||||
)
|
||||
}
|
||||
|
||||
// Conversion of a point to barycentric coordinates.
|
||||
fn to_bary(&self, vertices: &Vec<Point3f>, point: Point3f) -> (f32, f32, f32) {
|
||||
fn to_bary(&self, vertices: &Vec<Point3f>, point: Point3f) -> (f64, f64, f64) {
|
||||
let t = tri_area(self.vertex2(vertices), self.vertex3(vertices), &point) / self.area;
|
||||
let u = tri_area(self.vertex1(vertices), self.vertex3(vertices), &point) / self.area;
|
||||
let v = tri_area(self.vertex1(vertices), self.vertex2(vertices), &point) / self.area;
|
||||
|
|
@ -50,32 +60,39 @@ impl Triangle {
|
|||
(t, u, v)
|
||||
}
|
||||
|
||||
fn intersect_(&self, vertices: &Vec<Point3f>, ray: Ray) -> Option<(f32, f32, f32)> {
|
||||
fn intersect_(&self, vertices: &Vec<Point3f>, ray: Ray) -> Option<(f64, f64, f64)> {
|
||||
let vect2_1 = self.vertex2(vertices) - self.vertex1(vertices);
|
||||
let vect3_1 = self.vertex3(vertices) - self.vertex1(vertices);
|
||||
|
||||
let p_vect = ray.direction.cross(&vect3_1);
|
||||
let det = p_vect.dot(&vect2_1);
|
||||
|
||||
if det.abs() < 1e-3 { return None; }
|
||||
if det.abs() < 1e-3 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let t_vect = ray.origin - self.vertex1(vertices);
|
||||
let u = t_vect.dot(&p_vect) / det;
|
||||
|
||||
if u < 0.0 || u > 1.0 { return None; }
|
||||
if u < 0.0 || u > 1.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let q_vect = t_vect.cross(&vect2_1);
|
||||
let v = ray.direction.dot(&q_vect) / det;
|
||||
|
||||
if v < 0.0 || (u + v) > 1.0 { return None; }
|
||||
if v < 0.0 || (u + v) > 1.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let t = 1.0 - u - v;
|
||||
|
||||
Some((t, u, v))
|
||||
}
|
||||
|
||||
fn intersect(&self, vertices: &Vec<Point3f>, ray: Ray) -> Option<f32> {
|
||||
self.intersect_(vertices, ray).map(|(t, u, v)| distance(&ray.origin, &self.from_bary(vertices, t, u, v)))
|
||||
fn intersect(&self, vertices: &Vec<Point3f>, ray: Ray) -> Option<f64> {
|
||||
self.intersect_(vertices, ray)
|
||||
.map(|(t, u, v)| distance(&ray.origin, &self.from_bary(vertices, t, u, v)))
|
||||
}
|
||||
|
||||
fn get_texture(&self, vertices: &Vec<Point3f>, point: Point3f) -> Texture {
|
||||
|
|
@ -86,45 +103,81 @@ impl Triangle {
|
|||
|
||||
#[allow(dead_code)]
|
||||
impl TriangleMesh {
|
||||
pub fn new(vertices: Vec<Point3f>, tris: Vec<(usize, usize, usize, Box<dyn Fn(f32, f32, f32) -> Texture>)>) -> Self {
|
||||
let triangles = tris.into_iter()
|
||||
.map(|(v1, v2, v3, f)| Triangle {
|
||||
v1, v2, v3,
|
||||
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||
texture: f
|
||||
}).collect();
|
||||
TriangleMesh {
|
||||
vertices, triangles
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_solid(vertices: Vec<Point3f>, tris: Vec<(usize, usize, usize)>, texture: Texture) -> Self {
|
||||
let triangles = tris.into_iter()
|
||||
.map(|(v1, v2, v3)| Triangle {
|
||||
v1, v2, v3,
|
||||
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||
texture: Box::new(move |_, _, _| texture)
|
||||
}).collect();
|
||||
pub fn new(
|
||||
vertices: Vec<Point3f>,
|
||||
tris: Vec<(usize, usize, usize, Box<dyn Fn(f64, f64, f64) -> Texture>)>,
|
||||
) -> Self {
|
||||
let triangles = tris
|
||||
.into_iter()
|
||||
.map(|(v1, v2, v3, f)| Triangle {
|
||||
v1,
|
||||
v2,
|
||||
v3,
|
||||
normal: Unit::new_normalize(
|
||||
(&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1])),
|
||||
),
|
||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||
texture: f,
|
||||
})
|
||||
.collect();
|
||||
TriangleMesh {
|
||||
vertices,
|
||||
triangles
|
||||
triangles,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn singleton<F: 'static>(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: F) -> Self
|
||||
where F: Fn(f32, f32, f32) -> Texture
|
||||
{ TriangleMesh::new(vec![vertex1, vertex2, vertex3], vec![(0, 1, 2, Box::new(texture))]) }
|
||||
pub fn new_solid(
|
||||
vertices: Vec<Point3f>,
|
||||
tris: Vec<(usize, usize, usize)>,
|
||||
texture: Texture,
|
||||
) -> Self {
|
||||
let triangles = tris
|
||||
.into_iter()
|
||||
.map(|(v1, v2, v3)| Triangle {
|
||||
v1,
|
||||
v2,
|
||||
v3,
|
||||
normal: Unit::new_normalize(
|
||||
(&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1])),
|
||||
),
|
||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||
texture: Box::new(move |_, _, _| texture),
|
||||
})
|
||||
.collect();
|
||||
TriangleMesh {
|
||||
vertices,
|
||||
triangles,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn singleton_solid(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: Texture) -> Self
|
||||
{ TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| texture) }
|
||||
pub fn singleton<F: 'static>(
|
||||
vertex1: Point3f,
|
||||
vertex2: Point3f,
|
||||
vertex3: Point3f,
|
||||
texture: F,
|
||||
) -> Self
|
||||
where
|
||||
F: Fn(f64, f64, f64) -> Texture,
|
||||
{
|
||||
TriangleMesh::new(
|
||||
vec![vertex1, vertex2, vertex3],
|
||||
vec![(0, 1, 2, Box::new(texture))],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn singleton_solid(
|
||||
vertex1: Point3f,
|
||||
vertex2: Point3f,
|
||||
vertex3: Point3f,
|
||||
texture: Texture,
|
||||
) -> Self {
|
||||
TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| texture)
|
||||
}
|
||||
|
||||
fn closest_tri(&self, point: Point3f) -> &Triangle {
|
||||
self.triangles.iter()
|
||||
self.triangles
|
||||
.iter()
|
||||
.map(move |tri| {
|
||||
|
||||
let rel_pos = point - tri.vertex1(&self.vertices);
|
||||
let proj_point3 = rel_pos - (*tri.normal * tri.normal.dot(&rel_pos));
|
||||
|
||||
|
|
@ -139,15 +192,17 @@ impl TriangleMesh {
|
|||
(tri, distance(&point, &point_new))
|
||||
})
|
||||
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
||||
.unwrap().0
|
||||
.unwrap()
|
||||
.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Surface for TriangleMesh {
|
||||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||
self.triangles.iter()
|
||||
.filter_map(|tri| tri.intersect(&self.vertices, ray))
|
||||
.min_by(|a, b| a.partial_cmp(&b).unwrap_or(Ordering::Equal))
|
||||
fn intersect(&self, ray: Ray) -> Option<f64> {
|
||||
self.triangles
|
||||
.iter()
|
||||
.filter_map(|tri| tri.intersect(&self.vertices, ray))
|
||||
.min_by(|a, b| a.partial_cmp(&b).unwrap_or(Ordering::Equal))
|
||||
}
|
||||
|
||||
fn normal(&self, point: Point3f) -> Unit3f {
|
||||
|
|
@ -160,22 +215,26 @@ impl Surface for TriangleMesh {
|
|||
|
||||
// Uses Welzl's algorithm to solve the bounding sphere problem
|
||||
fn bound(&self) -> Bound {
|
||||
fn smallest_sphere_plane(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) {
|
||||
fn smallest_sphere_plane(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f64) {
|
||||
if points.len() == 0 || boundary.len() == 3 {
|
||||
match boundary.len() {
|
||||
0 => (Point3::new(0.0, 0.0, 0.0), 0.0),
|
||||
1 => (*boundary[0], 0.0),
|
||||
2 => { let half_span = 0.5 * (boundary[1] - boundary[0]);
|
||||
(*boundary[0] + half_span, half_span.norm()) },
|
||||
2 => {
|
||||
let half_span = 0.5 * (boundary[1] - boundary[0]);
|
||||
(*boundary[0] + half_span, half_span.norm())
|
||||
}
|
||||
3 => triangle_sphere(boundary[0], boundary[1], boundary[2]),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
let removed = points[0];
|
||||
let points = Vec::from(&points[1..]);
|
||||
|
||||
let bound = smallest_sphere(points.clone(), boundary.clone());
|
||||
if distance(&bound.0, removed) < bound.1 { return bound; }
|
||||
if distance(&bound.0, removed) < bound.1 {
|
||||
return bound;
|
||||
}
|
||||
|
||||
let mut boundary = boundary.clone();
|
||||
boundary.push(removed);
|
||||
|
|
@ -184,32 +243,44 @@ impl Surface for TriangleMesh {
|
|||
}
|
||||
}
|
||||
|
||||
fn triangle_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f) -> (Point3f, f32) {
|
||||
fn triangle_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f) -> (Point3f, f64) {
|
||||
let a = point3 - point1;
|
||||
let b = point2 - point1;
|
||||
|
||||
let crs = b.cross(&a);
|
||||
|
||||
let to_center = (crs.cross(&b) * a.norm_squared() + a.cross(&crs) * b.norm_squared())
|
||||
/ (2.0 * crs.norm_squared());
|
||||
/ (2.0 * crs.norm_squared());
|
||||
|
||||
let radius = to_center.norm();
|
||||
|
||||
(point1 + to_center, radius)
|
||||
}
|
||||
|
||||
fn tetrahedron_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f, point4: &Point3f) -> (Point3f, f32) {
|
||||
let matrix = Matrix4::from_rows(&[point1.to_homogeneous().transpose(),
|
||||
point2.to_homogeneous().transpose(),
|
||||
point3.to_homogeneous().transpose(),
|
||||
point4.to_homogeneous().transpose()]);
|
||||
fn tetrahedron_sphere(
|
||||
point1: &Point3f,
|
||||
point2: &Point3f,
|
||||
point3: &Point3f,
|
||||
point4: &Point3f,
|
||||
) -> (Point3f, f64) {
|
||||
let matrix = Matrix4::from_rows(&[
|
||||
point1.to_homogeneous().transpose(),
|
||||
point2.to_homogeneous().transpose(),
|
||||
point3.to_homogeneous().transpose(),
|
||||
point4.to_homogeneous().transpose(),
|
||||
]);
|
||||
|
||||
let a = matrix.determinant() * 2.0;
|
||||
|
||||
if (a != 0.0) {
|
||||
let mut matrix_mut = matrix.clone();
|
||||
|
||||
let squares = Vector4::new(point1.coords.norm_squared(), point2.coords.norm_squared(), point3.coords.norm_squared(), point4.coords.norm_squared());
|
||||
let squares = Vector4::new(
|
||||
point1.coords.norm_squared(),
|
||||
point2.coords.norm_squared(),
|
||||
point3.coords.norm_squared(),
|
||||
point4.coords.norm_squared(),
|
||||
);
|
||||
matrix_mut.set_column(0, &squares);
|
||||
let center_x = matrix_mut.determinant();
|
||||
|
||||
|
|
@ -231,23 +302,27 @@ impl Surface for TriangleMesh {
|
|||
}
|
||||
}
|
||||
|
||||
fn smallest_sphere(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) {
|
||||
fn smallest_sphere(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f64) {
|
||||
if points.len() == 0 || boundary.len() == 4 {
|
||||
match boundary.len() {
|
||||
0 => (Point3::new(0.0, 0.0, 0.0), 0.0),
|
||||
1 => (*boundary[0], 0.0),
|
||||
2 => { let half_span = 0.5 * (boundary[1] - boundary[0]);
|
||||
(*boundary[0] + half_span, half_span.norm()) },
|
||||
2 => {
|
||||
let half_span = 0.5 * (boundary[1] - boundary[0]);
|
||||
(*boundary[0] + half_span, half_span.norm())
|
||||
}
|
||||
3 => triangle_sphere(boundary[0], boundary[1], boundary[2]),
|
||||
4 => tetrahedron_sphere(boundary[0], boundary[1], boundary[2], boundary[3]),
|
||||
_ => unreachable!()
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
let removed = points[0];
|
||||
let points = Vec::from(&points[1..]);
|
||||
|
||||
let bound = smallest_sphere(points.clone(), boundary.clone());
|
||||
if distance(&bound.0, removed) < bound.1 { return bound; }
|
||||
if distance(&bound.0, removed) < bound.1 {
|
||||
return bound;
|
||||
}
|
||||
|
||||
let mut boundary = boundary.clone();
|
||||
boundary.push(removed);
|
||||
|
|
@ -257,14 +332,18 @@ impl Surface for TriangleMesh {
|
|||
}
|
||||
|
||||
extern crate rand;
|
||||
use rand::thread_rng;
|
||||
use rand::seq::SliceRandom;
|
||||
use rand::thread_rng;
|
||||
|
||||
let mut points: Vec<&Point3f> = self.vertices.iter().collect();
|
||||
points.shuffle(&mut thread_rng());
|
||||
|
||||
let (center, radius) = smallest_sphere(points, Vec::new());
|
||||
|
||||
Bound { center, radius: radius + 1e-3, bypass: false }
|
||||
Bound {
|
||||
center,
|
||||
radius: radius + 1e-3,
|
||||
bypass: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
extern crate nalgebra as na;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::f32::consts::PI;
|
||||
use std::f64::consts::PI;
|
||||
|
||||
use na::*;
|
||||
use na::geometry::Point3;
|
||||
use na::*;
|
||||
|
||||
use crate::object::*;
|
||||
use crate::types::*;
|
||||
|
||||
fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
||||
objects.iter()
|
||||
.filter_map(|obj| obj.intersect(ray)
|
||||
.map(|x| (obj, x)))
|
||||
fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f64)> {
|
||||
objects
|
||||
.iter()
|
||||
.filter_map(|obj| obj.intersect(ray).map(|x| (obj, x)))
|
||||
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
||||
}
|
||||
|
||||
|
|
@ -20,7 +20,10 @@ fn light_point(objects: &Vec<Object>, obj: &Object, point: Point3f, light: &dyn
|
|||
if light.check_shadow(point, objects) {
|
||||
let texture = obj.get_texture(point);
|
||||
|
||||
light.get_color(point) * (texture.albedo / PI) * light.intensity(point) * obj.normal(point).dot(&*light.direction(point))
|
||||
light.get_color(point)
|
||||
* (texture.albedo / PI)
|
||||
* light.intensity(point)
|
||||
* obj.normal(point).dot(&*light.direction(point))
|
||||
} else {
|
||||
// Point is in shadow
|
||||
Color::black()
|
||||
|
|
@ -32,8 +35,13 @@ pub fn cast_ray(ray: Ray, scene: &Scene) -> Color {
|
|||
let point = ray.project(dist);
|
||||
let surface_color = obj.get_texture(point).color;
|
||||
|
||||
scene.lights.iter()
|
||||
scene
|
||||
.lights
|
||||
.iter()
|
||||
.map(|light| light_point(&scene.objects, obj, point, &**light))
|
||||
.fold(Color::black(), |acc, c| acc + c) * surface_color
|
||||
} else { scene.background }
|
||||
.fold(Color::black(), |acc, c| acc + c)
|
||||
* surface_color
|
||||
} else {
|
||||
scene.background
|
||||
}
|
||||
}
|
||||
|
|
|
|||
82
src/types.rs
82
src/types.rs
|
|
@ -2,71 +2,83 @@ extern crate nalgebra as na;
|
|||
|
||||
use std::ops::{Add, Mul};
|
||||
|
||||
use na::*;
|
||||
use na::geometry::Point3;
|
||||
use na::*;
|
||||
|
||||
pub type Point3f = Point3<f32>;
|
||||
pub type Vector3f = Vector3<f32>;
|
||||
pub type Unit3f = Unit<Vector3<f32>>;
|
||||
pub type Point3f = Point3<f64>;
|
||||
pub type Vector3f = Vector3<f64>;
|
||||
pub type Unit3f = Unit<Vector3<f64>>;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Ray {
|
||||
pub origin: Point3f,
|
||||
pub direction: Unit3f
|
||||
pub direction: Unit3f,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
pub fn from_parts(origin: Point3f, direction: Unit3f) -> Self {
|
||||
Ray { origin, direction }
|
||||
}
|
||||
pub fn new(origin: Point3f, direction: Vector3f) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
|
||||
pub fn from_points(origin: Point3f, points_to: Point3f) -> Self { Ray::new(origin, points_to - origin) }
|
||||
pub fn new(origin: Point3f, direction: Vector3f) -> Self {
|
||||
Ray::from_parts(origin, Unit::new_normalize(direction))
|
||||
}
|
||||
pub fn from_points(origin: Point3f, points_to: Point3f) -> Self {
|
||||
Ray::new(origin, points_to - origin)
|
||||
}
|
||||
|
||||
pub fn project(&self, t: f32) -> Point3f { self.origin + t * self.direction.into_inner() }
|
||||
pub fn project(&self, t: f64) -> Point3f {
|
||||
self.origin + t * self.direction.into_inner()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Color {
|
||||
pub red: f32,
|
||||
pub green: f32,
|
||||
pub blue: f32,
|
||||
pub red: f64,
|
||||
pub green: f64,
|
||||
pub blue: f64,
|
||||
|
||||
_private: () // Private field prevents direct construction
|
||||
_private: (), // Private field prevents direct construction
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Color {
|
||||
pub fn new(red: f32, green: f32, blue: f32) -> Self {
|
||||
pub fn new(red: f64, green: f64, blue: f64) -> Self {
|
||||
Color {
|
||||
red: if red < 0.0 { 0.0 } else { red },
|
||||
red: if red < 0.0 { 0.0 } else { red },
|
||||
green: if green < 0.0 { 0.0 } else { green },
|
||||
blue: if blue < 0.0 { 0.0 } else { blue },
|
||||
blue: if blue < 0.0 { 0.0 } else { blue },
|
||||
|
||||
_private: ()
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_byte_array(&self) -> [u8; 3] {
|
||||
let red = (255.0 * self.red) as u8;
|
||||
let red = (255.0 * self.red) as u8;
|
||||
let green = (255.0 * self.green) as u8;
|
||||
let blue = (255.0 * self.blue) as u8;
|
||||
let blue = (255.0 * self.blue) as u8;
|
||||
[red, green, blue]
|
||||
}
|
||||
|
||||
pub fn gray(brightness: f32) -> Self { Color::new(brightness, brightness, brightness) }
|
||||
pub fn gray(brightness: f64) -> Self {
|
||||
Color::new(brightness, brightness, brightness)
|
||||
}
|
||||
|
||||
pub fn black() -> Self { Color::gray(0.0) }
|
||||
pub fn white() -> Self { Color::gray(1.0) }
|
||||
pub fn black() -> Self {
|
||||
Color::gray(0.0)
|
||||
}
|
||||
pub fn white() -> Self {
|
||||
Color::gray(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Color {
|
||||
type Output = Color;
|
||||
fn add(self, rhs: Color) -> Color {
|
||||
Color {
|
||||
red: self.red + rhs.red,
|
||||
red: self.red + rhs.red,
|
||||
green: self.green + rhs.green,
|
||||
blue: self.blue + rhs.blue,
|
||||
_private: ()
|
||||
blue: self.blue + rhs.blue,
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,22 +87,22 @@ impl Mul for Color {
|
|||
type Output = Color;
|
||||
fn mul(self, rhs: Color) -> Color {
|
||||
Color {
|
||||
red: self.red * rhs.red,
|
||||
red: self.red * rhs.red,
|
||||
green: self.green * rhs.green,
|
||||
blue: self.blue * rhs.blue,
|
||||
_private: ()
|
||||
blue: self.blue * rhs.blue,
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f32> for Color {
|
||||
impl Mul<f64> for Color {
|
||||
type Output = Color;
|
||||
fn mul(self, rhs: f32) -> Color {
|
||||
fn mul(self, rhs: f64) -> Color {
|
||||
Color {
|
||||
red: self.red * rhs,
|
||||
red: self.red * rhs,
|
||||
green: self.green * rhs,
|
||||
blue: self.blue * rhs,
|
||||
_private: ()
|
||||
blue: self.blue * rhs,
|
||||
_private: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -98,15 +110,15 @@ impl Mul<f32> for Color {
|
|||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct Texture {
|
||||
pub color: Color,
|
||||
pub albedo: f32
|
||||
pub albedo: f64,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Texture {
|
||||
pub fn new(red: f32, green: f32, blue: f32, albedo: f32) -> Self {
|
||||
pub fn new(red: f64, green: f64, blue: f64, albedo: f64) -> Self {
|
||||
Texture {
|
||||
color: Color::new(red, green, blue),
|
||||
albedo
|
||||
albedo,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue