library: use beevee instead of rust-bvh
I also used this opportunity to use `nalgebra::Unit` as much as possible.
This commit is contained in:
parent
d8a4a2eaad
commit
48bb3550cb
|
@ -19,7 +19,7 @@ name = "pathtracer"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bvh = "0.3.2"
|
beevee = { path = "../beevee" }
|
||||||
derive_more = "0.99.3"
|
derive_more = "0.99.3"
|
||||||
enum_dispatch = "0.2.1"
|
enum_dispatch = "0.2.1"
|
||||||
image = "0.23.0"
|
image = "0.23.0"
|
||||||
|
|
|
@ -2,14 +2,11 @@
|
||||||
|
|
||||||
//! A pathtracing crate
|
//! A pathtracing crate
|
||||||
|
|
||||||
use bvh::nalgebra::{Point2, Point3, Vector3};
|
/// 3D points and vectors
|
||||||
|
pub use beevee::{Point, Vector};
|
||||||
|
|
||||||
/// A 2D point coordinate
|
/// A 2D point coordinate
|
||||||
pub type Point2D = Point2<f32>;
|
pub type Point2D = nalgebra::Point2<f32>;
|
||||||
/// A 3D point coordinate
|
|
||||||
pub type Point = Point3<f32>;
|
|
||||||
/// A 3D vector
|
|
||||||
pub type Vector = Vector3<f32>;
|
|
||||||
|
|
||||||
pub mod core;
|
pub mod core;
|
||||||
pub mod light;
|
pub mod light;
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use super::{Light, SpatialLight};
|
use super::{Light, SpatialLight};
|
||||||
use crate::core::LinearColor;
|
use crate::core::LinearColor;
|
||||||
use crate::{Point, Vector};
|
use crate::{Point, Vector};
|
||||||
|
use nalgebra::Unit;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// Represent a light emanating from a far away source, with parallel rays on all points.
|
/// Represent a light emanating from a far away source, with parallel rays on all points.
|
||||||
#[derive(Debug, PartialEq, Deserialize)]
|
#[derive(Debug, PartialEq, Deserialize)]
|
||||||
pub struct DirectionalLight {
|
pub struct DirectionalLight {
|
||||||
#[serde(deserialize_with = "crate::serialize::vector_normalizer")]
|
#[serde(deserialize_with = "crate::serialize::vector_normalizer")]
|
||||||
direction: Vector,
|
direction: Unit<Vector>,
|
||||||
color: LinearColor,
|
color: LinearColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,15 +23,12 @@ impl DirectionalLight {
|
||||||
/// # use pathtracer::Vector;
|
/// # use pathtracer::Vector;
|
||||||
/// #
|
/// #
|
||||||
/// let dir_light = DirectionalLight::new(
|
/// let dir_light = DirectionalLight::new(
|
||||||
/// Vector::new(1.0, 0.0, 0.0),
|
/// Vector::x_axis(),
|
||||||
/// LinearColor::new(1.0, 0.0, 1.0),
|
/// LinearColor::new(1.0, 0.0, 1.0),
|
||||||
/// );
|
/// );
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new(direction: Vector, color: LinearColor) -> Self {
|
pub fn new(direction: Unit<Vector>, color: LinearColor) -> Self {
|
||||||
DirectionalLight {
|
DirectionalLight { direction, color }
|
||||||
direction: direction.normalize(),
|
|
||||||
color,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +39,8 @@ impl Light for DirectionalLight {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpatialLight for DirectionalLight {
|
impl SpatialLight for DirectionalLight {
|
||||||
fn to_source(&self, _: &Point) -> (Vector, f32) {
|
fn to_source(&self, _: &Point) -> (Unit<Vector>, f32) {
|
||||||
(self.direction * -1., std::f32::INFINITY)
|
(-self.direction, std::f32::INFINITY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +50,7 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn new_works() {
|
fn new_works() {
|
||||||
let direction = Vector::new(1., 0., 0.);
|
let direction = Vector::x_axis();
|
||||||
let color = LinearColor::new(1., 1., 1.);
|
let color = LinearColor::new(1., 1., 1.);
|
||||||
let light = DirectionalLight::new(direction, color.clone());
|
let light = DirectionalLight::new(direction, color.clone());
|
||||||
let res = DirectionalLight { direction, color };
|
let res = DirectionalLight { direction, color };
|
||||||
|
@ -60,7 +58,7 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn simple_light() -> impl SpatialLight {
|
fn simple_light() -> impl SpatialLight {
|
||||||
let direction = Vector::new(1., 0., 0.);
|
let direction = Vector::x_axis();
|
||||||
let color = LinearColor::new(1., 1., 1.);
|
let color = LinearColor::new(1., 1., 1.);
|
||||||
DirectionalLight::new(direction, color)
|
DirectionalLight::new(direction, color)
|
||||||
}
|
}
|
||||||
|
@ -76,7 +74,10 @@ mod test {
|
||||||
fn to_source_is_correct() {
|
fn to_source_is_correct() {
|
||||||
let light = simple_light();
|
let light = simple_light();
|
||||||
let ans = light.to_source(&Point::new(1., 0., 0.));
|
let ans = light.to_source(&Point::new(1., 0., 0.));
|
||||||
let expected = (Vector::new(-1., 0., 0.), std::f32::INFINITY);
|
let expected = (
|
||||||
|
Unit::new_normalize(Vector::new(-1., 0., 0.)),
|
||||||
|
std::f32::INFINITY,
|
||||||
|
);
|
||||||
assert_eq!(ans, expected)
|
assert_eq!(ans, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +87,7 @@ mod test {
|
||||||
let light: DirectionalLight = serde_yaml::from_str(yaml).unwrap();
|
let light: DirectionalLight = serde_yaml::from_str(yaml).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
light,
|
light,
|
||||||
DirectionalLight::new(Vector::new(1., 0., 0.), LinearColor::new(1., 0.5, 0.2))
|
DirectionalLight::new(Vector::x_axis(), LinearColor::new(1., 0.5, 0.2))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
use super::core::LinearColor;
|
use super::core::LinearColor;
|
||||||
use super::{Point, Vector};
|
use super::{Point, Vector};
|
||||||
|
use nalgebra::Unit;
|
||||||
|
|
||||||
/// Represent a light in the scene being rendered.
|
/// Represent a light in the scene being rendered.
|
||||||
pub trait Light: std::fmt::Debug {
|
pub trait Light: std::fmt::Debug {
|
||||||
|
@ -12,7 +13,7 @@ pub trait Light: std::fmt::Debug {
|
||||||
/// Represent a light which has an abstract position in the scene being rendered.
|
/// Represent a light which has an abstract position in the scene being rendered.
|
||||||
pub trait SpatialLight: Light {
|
pub trait SpatialLight: Light {
|
||||||
/// Get a unit vector from the origin to the position of the light, and its distance
|
/// Get a unit vector from the origin to the position of the light, and its distance
|
||||||
fn to_source(&self, origin: &Point) -> (Vector, f32);
|
fn to_source(&self, origin: &Point) -> (Unit<Vector>, f32);
|
||||||
}
|
}
|
||||||
|
|
||||||
mod ambient_light;
|
mod ambient_light;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::{Light, SpatialLight};
|
use super::{Light, SpatialLight};
|
||||||
use crate::core::LinearColor;
|
use crate::core::LinearColor;
|
||||||
use crate::{Point, Vector};
|
use crate::{Point, Vector};
|
||||||
|
use nalgebra::Unit;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// Represent a light emanating from a point in space, following the square distance law.
|
/// Represent a light emanating from a point in space, following the square distance law.
|
||||||
|
@ -38,10 +39,10 @@ impl Light for PointLight {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpatialLight for PointLight {
|
impl SpatialLight for PointLight {
|
||||||
fn to_source(&self, point: &Point) -> (Vector, f32) {
|
fn to_source(&self, point: &Point) -> (Unit<Vector>, f32) {
|
||||||
let delt = self.position - point;
|
let delt = self.position - point;
|
||||||
let dist = delt.norm();
|
let dist = delt.norm();
|
||||||
(delt.normalize(), dist)
|
(Unit::new_normalize(delt), dist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +76,7 @@ mod test {
|
||||||
fn to_source_is_correct() {
|
fn to_source_is_correct() {
|
||||||
let light = simple_light();
|
let light = simple_light();
|
||||||
let ans = light.to_source(&Point::new(1., 0., 0.));
|
let ans = light.to_source(&Point::new(1., 0., 0.));
|
||||||
let expected = (Vector::new(-1., 0., 0.), 1.);
|
let expected = (Unit::new_normalize(Vector::new(-1., 0., 0.)), 1.);
|
||||||
assert_eq!(ans, expected);
|
assert_eq!(ans, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::{Light, SpatialLight};
|
use super::{Light, SpatialLight};
|
||||||
use crate::core::LinearColor;
|
use crate::core::LinearColor;
|
||||||
use crate::{Point, Vector};
|
use crate::{Point, Vector};
|
||||||
|
use nalgebra::Unit;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
/// Represent a light emanating from a directed light-source, outputting rays in a cone.
|
/// Represent a light emanating from a directed light-source, outputting rays in a cone.
|
||||||
|
@ -9,7 +10,7 @@ use serde::{Deserialize, Deserializer};
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct SpotLight {
|
pub struct SpotLight {
|
||||||
position: Point,
|
position: Point,
|
||||||
direction: Vector,
|
direction: Unit<Vector>,
|
||||||
cosine_value: f32,
|
cosine_value: f32,
|
||||||
color: LinearColor,
|
color: LinearColor,
|
||||||
}
|
}
|
||||||
|
@ -18,13 +19,13 @@ impl SpotLight {
|
||||||
/// Construct a SpotLight with the given FOV in radian.
|
/// Construct a SpotLight with the given FOV in radian.
|
||||||
pub fn radians_new(
|
pub fn radians_new(
|
||||||
position: Point,
|
position: Point,
|
||||||
direction: Vector,
|
direction: Unit<Vector>,
|
||||||
fov_rad: f32,
|
fov_rad: f32,
|
||||||
color: LinearColor,
|
color: LinearColor,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
SpotLight {
|
SpotLight {
|
||||||
position,
|
position,
|
||||||
direction: direction.normalize(),
|
direction,
|
||||||
cosine_value: (fov_rad / 2.).cos(),
|
cosine_value: (fov_rad / 2.).cos(),
|
||||||
color,
|
color,
|
||||||
}
|
}
|
||||||
|
@ -33,7 +34,7 @@ impl SpotLight {
|
||||||
/// Construct a SpotLight with the given FOV in degrees.
|
/// Construct a SpotLight with the given FOV in degrees.
|
||||||
pub fn degrees_new(
|
pub fn degrees_new(
|
||||||
position: Point,
|
position: Point,
|
||||||
direction: Vector,
|
direction: Unit<Vector>,
|
||||||
fov_deg: f32,
|
fov_deg: f32,
|
||||||
color: LinearColor,
|
color: LinearColor,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
@ -59,10 +60,10 @@ impl Light for SpotLight {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpatialLight for SpotLight {
|
impl SpatialLight for SpotLight {
|
||||||
fn to_source(&self, point: &Point) -> (Vector, f32) {
|
fn to_source(&self, point: &Point) -> (Unit<Vector>, f32) {
|
||||||
let delt = self.position - point;
|
let delt = self.position - point;
|
||||||
let dist = delt.norm();
|
let dist = delt.norm();
|
||||||
(delt.normalize(), dist)
|
(Unit::new_normalize(delt), dist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +71,7 @@ impl SpatialLight for SpotLight {
|
||||||
struct SerializedSpotLight {
|
struct SerializedSpotLight {
|
||||||
position: Point,
|
position: Point,
|
||||||
#[serde(deserialize_with = "crate::serialize::vector_normalizer")]
|
#[serde(deserialize_with = "crate::serialize::vector_normalizer")]
|
||||||
direction: Vector,
|
direction: Unit<Vector>,
|
||||||
fov: f32,
|
fov: f32,
|
||||||
color: LinearColor,
|
color: LinearColor,
|
||||||
}
|
}
|
||||||
|
@ -99,7 +100,7 @@ mod test {
|
||||||
fn radian_new_works() {
|
fn radian_new_works() {
|
||||||
let light = SpotLight::radians_new(
|
let light = SpotLight::radians_new(
|
||||||
Point::origin(),
|
Point::origin(),
|
||||||
Vector::new(1., 0., 0.),
|
Vector::x_axis(),
|
||||||
std::f32::consts::PI / 2.,
|
std::f32::consts::PI / 2.,
|
||||||
LinearColor::new(1., 1., 1.),
|
LinearColor::new(1., 1., 1.),
|
||||||
);
|
);
|
||||||
|
@ -109,7 +110,7 @@ mod test {
|
||||||
light,
|
light,
|
||||||
SpotLight {
|
SpotLight {
|
||||||
position: Point::origin(),
|
position: Point::origin(),
|
||||||
direction: Vector::new(1., 0., 0.),
|
direction: Vector::x_axis(),
|
||||||
cosine_value: calculated_cosine_value,
|
cosine_value: calculated_cosine_value,
|
||||||
color: LinearColor::new(1., 1., 1.),
|
color: LinearColor::new(1., 1., 1.),
|
||||||
}
|
}
|
||||||
|
@ -122,7 +123,7 @@ mod test {
|
||||||
fn degrees_new_works() {
|
fn degrees_new_works() {
|
||||||
let light = SpotLight::degrees_new(
|
let light = SpotLight::degrees_new(
|
||||||
Point::origin(),
|
Point::origin(),
|
||||||
Vector::new(1., 0., 0.),
|
Vector::x_axis(),
|
||||||
60.,
|
60.,
|
||||||
LinearColor::new(1., 1., 1.),
|
LinearColor::new(1., 1., 1.),
|
||||||
);
|
);
|
||||||
|
@ -131,7 +132,7 @@ mod test {
|
||||||
light,
|
light,
|
||||||
SpotLight {
|
SpotLight {
|
||||||
position: Point::origin(),
|
position: Point::origin(),
|
||||||
direction: Vector::new(1., 0., 0.),
|
direction: Vector::x_axis(),
|
||||||
cosine_value: calculated_cosine_value,
|
cosine_value: calculated_cosine_value,
|
||||||
color: LinearColor::new(1., 1., 1.),
|
color: LinearColor::new(1., 1., 1.),
|
||||||
}
|
}
|
||||||
|
@ -143,7 +144,7 @@ mod test {
|
||||||
fn simple_light() -> impl SpatialLight {
|
fn simple_light() -> impl SpatialLight {
|
||||||
SpotLight::degrees_new(
|
SpotLight::degrees_new(
|
||||||
Point::origin(),
|
Point::origin(),
|
||||||
Vector::new(1., 0., 0.),
|
Vector::x_axis(),
|
||||||
90.,
|
90.,
|
||||||
LinearColor::new(1., 1., 1.),
|
LinearColor::new(1., 1., 1.),
|
||||||
)
|
)
|
||||||
|
@ -181,7 +182,7 @@ mod test {
|
||||||
fn to_source_is_correct() {
|
fn to_source_is_correct() {
|
||||||
let light = simple_light();
|
let light = simple_light();
|
||||||
let ans = light.to_source(&Point::new(1., 0., 0.));
|
let ans = light.to_source(&Point::new(1., 0., 0.));
|
||||||
let expected = (Vector::new(-1., 0., 0.), 1.);
|
let expected = (Unit::new_normalize(Vector::new(-1., 0., 0.)), 1.);
|
||||||
assert_eq!(ans, expected);
|
assert_eq!(ans, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,7 +199,7 @@ mod test {
|
||||||
light,
|
light,
|
||||||
SpotLight::degrees_new(
|
SpotLight::degrees_new(
|
||||||
Point::origin(),
|
Point::origin(),
|
||||||
Vector::new(1., 0., 0.),
|
Vector::x_axis(),
|
||||||
90.,
|
90.,
|
||||||
LinearColor::new(1., 0.5, 0.2)
|
LinearColor::new(1., 0.5, 0.2)
|
||||||
)
|
)
|
||||||
|
|
|
@ -141,7 +141,7 @@ mod test {
|
||||||
let expected = LightAggregate::new(
|
let expected = LightAggregate::new(
|
||||||
vec![AmbientLight::new(LinearColor::new(1., 0.5, 0.2))],
|
vec![AmbientLight::new(LinearColor::new(1., 0.5, 0.2))],
|
||||||
vec![DirectionalLight::new(
|
vec![DirectionalLight::new(
|
||||||
Vector::new(1., 0., 0.),
|
Vector::x_axis(),
|
||||||
LinearColor::new(1., 0.5, 0.2),
|
LinearColor::new(1., 0.5, 0.2),
|
||||||
)],
|
)],
|
||||||
vec![PointLight::new(
|
vec![PointLight::new(
|
||||||
|
@ -150,7 +150,7 @@ mod test {
|
||||||
)],
|
)],
|
||||||
vec![SpotLight::degrees_new(
|
vec![SpotLight::degrees_new(
|
||||||
Point::origin(),
|
Point::origin(),
|
||||||
Vector::new(1., 0., 0.),
|
Vector::x_axis(),
|
||||||
90.,
|
90.,
|
||||||
LinearColor::new(1., 0.5, 0.2),
|
LinearColor::new(1., 0.5, 0.2),
|
||||||
)],
|
)],
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
use crate::material::MaterialEnum;
|
use crate::material::MaterialEnum;
|
||||||
use crate::shape::{Shape, ShapeEnum};
|
use crate::shape::{Shape, ShapeEnum};
|
||||||
use crate::texture::TextureEnum;
|
use crate::texture::TextureEnum;
|
||||||
use bvh::aabb::{Bounded, AABB};
|
use crate::Point;
|
||||||
use bvh::bounding_hierarchy::BHShape;
|
use beevee::aabb::{Bounded, AABB};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// An object being rendered in the scene.
|
/// An object being rendered in the scene.
|
||||||
|
@ -60,14 +60,9 @@ impl Bounded for Object {
|
||||||
fn aabb(&self) -> AABB {
|
fn aabb(&self) -> AABB {
|
||||||
self.shape.aabb()
|
self.shape.aabb()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
impl BHShape for Object {
|
|
||||||
fn set_bh_node_index(&mut self, index: usize) {
|
|
||||||
self.index = index
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bh_node_index(&self) -> usize {
|
fn centroid(&self) -> Point {
|
||||||
self.index
|
self.shape.centroid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,7 +74,6 @@ mod test {
|
||||||
use crate::material::UniformMaterial;
|
use crate::material::UniformMaterial;
|
||||||
use crate::shape::Sphere;
|
use crate::shape::Sphere;
|
||||||
use crate::texture::UniformTexture;
|
use crate::texture::UniformTexture;
|
||||||
use crate::Point;
|
|
||||||
|
|
||||||
fn simple_object() -> Object {
|
fn simple_object() -> Object {
|
||||||
let shape = Sphere::new(Point::new(5., 0., 0.), 1.);
|
let shape = Sphere::new(Point::new(5., 0., 0.), 1.);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
//! Scene rendering logic
|
//! Scene rendering logic
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
|
||||||
|
|
||||||
use super::{light_aggregate::LightAggregate, object::Object, utils::*};
|
use super::{light_aggregate::LightAggregate, object::Object, utils::*};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{Camera, LightProperties, LinearColor, ReflTransEnum},
|
core::{Camera, LightProperties, LinearColor, ReflTransEnum},
|
||||||
|
@ -10,8 +8,9 @@ use crate::{
|
||||||
texture::Texture,
|
texture::Texture,
|
||||||
{Point, Vector},
|
{Point, Vector},
|
||||||
};
|
};
|
||||||
use bvh::{bvh::BVH, ray::Ray};
|
use beevee::{bvh::BVH, ray::Ray};
|
||||||
use image::RgbImage;
|
use image::RgbImage;
|
||||||
|
use nalgebra::Unit;
|
||||||
use rand::prelude::thread_rng;
|
use rand::prelude::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
|
@ -120,12 +119,12 @@ impl Scene {
|
||||||
fn pixel(&self, x: f32, y: f32) -> LinearColor {
|
fn pixel(&self, x: f32, y: f32) -> LinearColor {
|
||||||
let (x, y) = self.camera.film().pixel_ratio(x, y);
|
let (x, y) = self.camera.film().pixel_ratio(x, y);
|
||||||
let pixel = self.camera.film().pixel_at_ratio(x, y);
|
let pixel = self.camera.film().pixel_at_ratio(x, y);
|
||||||
let direction = (pixel - self.camera.origin()).normalize();
|
let direction = Unit::new_normalize(pixel - self.camera.origin());
|
||||||
let indices = RefractionInfo::with_index(self.diffraction_index);
|
let indices = RefractionInfo::with_index(self.diffraction_index);
|
||||||
self.cast_ray(Ray::new(pixel, direction))
|
self.cast_ray(Ray::new(pixel, direction))
|
||||||
.map_or_else(LinearColor::black, |(t, obj)| {
|
.map_or_else(LinearColor::black, |(t, obj)| {
|
||||||
self.color_at(
|
self.color_at(
|
||||||
pixel + direction * t,
|
pixel + direction.as_ref() * t,
|
||||||
obj,
|
obj,
|
||||||
direction,
|
direction,
|
||||||
self.reflection_limit,
|
self.reflection_limit,
|
||||||
|
@ -151,19 +150,15 @@ impl Scene {
|
||||||
|
|
||||||
fn cast_ray(&self, ray: Ray) -> Option<(f32, &Object)> {
|
fn cast_ray(&self, ray: Ray) -> Option<(f32, &Object)> {
|
||||||
self.bvh
|
self.bvh
|
||||||
.traverse(&ray, &self.objects)
|
.walk(&ray, &self.objects)
|
||||||
.iter()
|
.and_then(|o| o.shape.intersect(&ray).map(|t| (t, o)))
|
||||||
.filter_map(|obj| obj.shape.intersect(&ray).map(|distance| (distance, *obj)))
|
|
||||||
.min_by(|(dist_a, _), (dist_b, _)| {
|
|
||||||
dist_a.partial_cmp(dist_b).unwrap_or(Ordering::Equal)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_at(
|
fn color_at(
|
||||||
&self,
|
&self,
|
||||||
point: Point,
|
point: Point,
|
||||||
object: &Object,
|
object: &Object,
|
||||||
incident_ray: Vector,
|
incident_ray: Unit<Vector>,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
mut indices: RefractionInfo,
|
mut indices: RefractionInfo,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
|
@ -203,14 +198,14 @@ impl Scene {
|
||||||
&self,
|
&self,
|
||||||
point: Point,
|
point: Point,
|
||||||
transparency: f32,
|
transparency: f32,
|
||||||
refracted: Vector,
|
refracted: Unit<Vector>,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
indices: RefractionInfo,
|
indices: RefractionInfo,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
if transparency > 1e-5 && reflection_limit > 0 {
|
if transparency > 1e-5 && reflection_limit > 0 {
|
||||||
let refraction_start = point + refracted * 0.001;
|
let refraction_start = point + refracted.as_ref() * 0.001;
|
||||||
if let Some((t, obj)) = self.cast_ray(Ray::new(refraction_start, refracted)) {
|
if let Some((t, obj)) = self.cast_ray(Ray::new(refraction_start, refracted)) {
|
||||||
let resulting_position = refraction_start + refracted * t;
|
let resulting_position = refraction_start + refracted.as_ref() * t;
|
||||||
let refracted = self.color_at(
|
let refracted = self.color_at(
|
||||||
resulting_position,
|
resulting_position,
|
||||||
obj,
|
obj,
|
||||||
|
@ -227,14 +222,14 @@ impl Scene {
|
||||||
fn reflection(
|
fn reflection(
|
||||||
&self,
|
&self,
|
||||||
point: Point,
|
point: Point,
|
||||||
reflected: Vector,
|
reflected: Unit<Vector>,
|
||||||
reflection_limit: u32,
|
reflection_limit: u32,
|
||||||
indices: RefractionInfo,
|
indices: RefractionInfo,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
if reflection_limit > 0 {
|
if reflection_limit > 0 {
|
||||||
let reflection_start = point + reflected * 0.001;
|
let reflection_start = point + reflected.as_ref() * 0.001;
|
||||||
if let Some((t, obj)) = self.cast_ray(Ray::new(reflection_start, reflected)) {
|
if let Some((t, obj)) = self.cast_ray(Ray::new(reflection_start, reflected)) {
|
||||||
let resulting_position = reflection_start + reflected * t;
|
let resulting_position = reflection_start + reflected.as_ref() * t;
|
||||||
let color = self.color_at(
|
let color = self.color_at(
|
||||||
resulting_position,
|
resulting_position,
|
||||||
obj,
|
obj,
|
||||||
|
@ -253,8 +248,8 @@ impl Scene {
|
||||||
point: Point,
|
point: Point,
|
||||||
object_color: LinearColor,
|
object_color: LinearColor,
|
||||||
properties: &LightProperties,
|
properties: &LightProperties,
|
||||||
normal: Vector,
|
normal: Unit<Vector>,
|
||||||
reflected: Vector,
|
reflected: Unit<Vector>,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
let ambient = self.illuminate_ambient(object_color.clone());
|
let ambient = self.illuminate_ambient(object_color.clone());
|
||||||
let spatial = self.illuminate_spatial(point, properties, normal, reflected);
|
let spatial = self.illuminate_spatial(point, properties, normal, reflected);
|
||||||
|
@ -273,14 +268,14 @@ impl Scene {
|
||||||
&self,
|
&self,
|
||||||
point: Point,
|
point: Point,
|
||||||
properties: &LightProperties,
|
properties: &LightProperties,
|
||||||
normal: Vector,
|
normal: Unit<Vector>,
|
||||||
reflected: Vector,
|
reflected: Unit<Vector>,
|
||||||
) -> LinearColor {
|
) -> LinearColor {
|
||||||
self.lights
|
self.lights
|
||||||
.spatial_lights_iter()
|
.spatial_lights_iter()
|
||||||
.map(|light| {
|
.map(|light| {
|
||||||
let (direction, t) = light.to_source(&point);
|
let (direction, t) = light.to_source(&point);
|
||||||
let light_ray = Ray::new(point + 0.001 * direction, direction);
|
let light_ray = Ray::new(point + 0.001 * direction.as_ref(), direction);
|
||||||
match self.cast_ray(light_ray) {
|
match self.cast_ray(light_ray) {
|
||||||
// Take shadows into account
|
// Take shadows into account
|
||||||
Some((obstacle_t, _)) if obstacle_t < t => return LinearColor::black(),
|
Some((obstacle_t, _)) if obstacle_t < t => return LinearColor::black(),
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
use crate::Vector;
|
use crate::Vector;
|
||||||
|
use nalgebra::Unit;
|
||||||
|
|
||||||
pub fn reflected(incident: Vector, normal: Vector) -> Vector {
|
pub fn reflected(incident: Unit<Vector>, normal: Unit<Vector>) -> Unit<Vector> {
|
||||||
let proj = incident.dot(&normal);
|
let proj = incident.dot(&normal);
|
||||||
let delt = normal * (proj * 2.);
|
let delt = normal.into_inner() * (proj * 2.);
|
||||||
(incident - delt).normalize()
|
Unit::new_normalize(incident.as_ref() - delt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns None if the ray was totally reflected, Some(refracted_ray, reflected_amount) if not
|
/// Returns None if the ray was totally reflected, Some(refracted_ray, reflected_amount) if not
|
||||||
/// Adds an element to the top of indices that should be removed
|
|
||||||
pub fn refracted(
|
pub fn refracted(
|
||||||
incident: Vector,
|
incident: Unit<Vector>,
|
||||||
normal: Vector,
|
normal: Unit<Vector>,
|
||||||
indices: &mut RefractionInfo,
|
indices: &mut RefractionInfo,
|
||||||
new_index: f32,
|
new_index: f32,
|
||||||
) -> Option<(Vector, f32)> {
|
) -> Option<(Unit<Vector>, f32)> {
|
||||||
let cos1 = incident.dot(&normal);
|
let cos1 = incident.dot(&normal);
|
||||||
let normal = if cos1 < 0. {
|
let normal = if cos1 < 0. {
|
||||||
// Entering object, change the medium
|
// Entering object, change the medium
|
||||||
|
@ -32,12 +32,12 @@ pub fn refracted(
|
||||||
}
|
}
|
||||||
let cos1 = cos1.abs();
|
let cos1 = cos1.abs();
|
||||||
let cos2 = k.sqrt();
|
let cos2 = k.sqrt();
|
||||||
let refracted = eta * incident + (eta * cos1 - cos2) * normal;
|
let refracted = eta * incident.as_ref() + (eta * cos1 - cos2) * normal.as_ref();
|
||||||
let f_r = (n_2 * cos1 - n_1 * cos2) / (n_2 * cos1 + n_1 * cos2);
|
let f_r = (n_2 * cos1 - n_1 * cos2) / (n_2 * cos1 + n_1 * cos2);
|
||||||
let f_t = (n_1 * cos2 - n_2 * cos1) / (n_1 * cos2 + n_2 * cos1);
|
let f_t = (n_1 * cos2 - n_2 * cos1) / (n_1 * cos2 + n_2 * cos1);
|
||||||
let refl_t = (f_r * f_r + f_t * f_t) / 2.;
|
let refl_t = (f_r * f_r + f_t * f_t) / 2.;
|
||||||
//Some((refracted, 0.))
|
//Some((refracted, 0.))
|
||||||
Some((refracted.normalize(), refl_t))
|
Some((Unit::new_normalize(refracted), refl_t))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
//! Helper functions to deserialize `Vector` values.
|
//! Helper functions to deserialize `Vector` values.
|
||||||
|
|
||||||
use crate::Vector;
|
use crate::Vector;
|
||||||
|
use nalgebra::Unit;
|
||||||
use serde::de::{Deserialize, Deserializer};
|
use serde::de::{Deserialize, Deserializer};
|
||||||
|
|
||||||
/// Deserialize a vector.
|
/// Deserialize a vector.
|
||||||
///
|
///
|
||||||
/// Needs a custom implementation to make sur the vector is normalized when deserialized.
|
/// Needs a custom implementation to make sur the vector is normalized when deserialized.
|
||||||
pub fn vector_normalizer<'de, D>(deserializer: D) -> Result<Vector, D::Error>
|
pub fn vector_normalizer<'de, D>(deserializer: D) -> Result<Unit<Vector>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let v: Vector = Deserialize::deserialize(deserializer)?;
|
let v: Vector = Deserialize::deserialize(deserializer)?;
|
||||||
Ok(v.normalize())
|
Ok(Unit::new_normalize(v))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
//! Various shape implementations
|
//! Various shape implementations
|
||||||
|
|
||||||
use super::{Point, Point2D, Vector};
|
use super::{Point, Point2D, Vector};
|
||||||
use bvh::{
|
use beevee::{
|
||||||
aabb::{Bounded, AABB},
|
aabb::{Bounded, AABB},
|
||||||
ray::Ray,
|
ray::Ray,
|
||||||
};
|
};
|
||||||
|
use nalgebra::Unit;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// All the existing `Shape` implementation.
|
/// All the existing `Shape` implementation.
|
||||||
|
@ -24,17 +25,23 @@ pub trait Shape: std::fmt::Debug {
|
||||||
/// Return the distance at which the object intersects with the ray, or None if it does not.
|
/// Return the distance at which the object intersects with the ray, or None if it does not.
|
||||||
fn intersect(&self, ray: &Ray) -> Option<f32>;
|
fn intersect(&self, ray: &Ray) -> Option<f32>;
|
||||||
/// Return the unit vector corresponding to the normal at this point of the shape.
|
/// Return the unit vector corresponding to the normal at this point of the shape.
|
||||||
fn normal(&self, point: &Point) -> Vector;
|
fn normal(&self, point: &Point) -> Unit<Vector>;
|
||||||
/// Project the point from the shape's surface to its texel coordinates.
|
/// Project the point from the shape's surface to its texel coordinates.
|
||||||
fn project_texel(&self, point: &Point) -> Point2D;
|
fn project_texel(&self, point: &Point) -> Point2D;
|
||||||
/// Enclose the `Shape` in an axi-aligned bounding-box.
|
/// Enclose the `Shape` in an axi-aligned bounding-box.
|
||||||
fn aabb(&self) -> AABB;
|
fn aabb(&self) -> AABB;
|
||||||
|
/// Return the centroid of the shape.
|
||||||
|
fn centroid(&self) -> Point;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Bounded for dyn Shape {
|
impl Bounded for dyn Shape {
|
||||||
fn aabb(&self) -> AABB {
|
fn aabb(&self) -> AABB {
|
||||||
self.aabb()
|
self.aabb()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn centroid(&self) -> Point {
|
||||||
|
self.centroid()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod sphere;
|
mod sphere;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use super::Shape;
|
use super::Shape;
|
||||||
use crate::{Point, Point2D, Vector};
|
use crate::{Point, Point2D, Vector};
|
||||||
use bvh::aabb::AABB;
|
use beevee::aabb::AABB;
|
||||||
use bvh::ray::Ray;
|
use beevee::ray::Ray;
|
||||||
|
use nalgebra::Unit;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
/// Represent a sphere shape inside the scene.
|
/// Represent a sphere shape inside the scene.
|
||||||
|
@ -67,13 +68,13 @@ impl Shape for Sphere {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(&self, point: &Point) -> Vector {
|
fn normal(&self, point: &Point) -> Unit<Vector> {
|
||||||
let delt = if self.inverted {
|
let delt = if self.inverted {
|
||||||
self.center - point
|
self.center - point
|
||||||
} else {
|
} else {
|
||||||
point - self.center
|
point - self.center
|
||||||
};
|
};
|
||||||
delt.normalize()
|
Unit::new_normalize(delt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_texel(&self, point: &Point) -> Point2D {
|
fn project_texel(&self, point: &Point) -> Point2D {
|
||||||
|
@ -90,6 +91,10 @@ impl Shape for Sphere {
|
||||||
let max = self.center + delt;
|
let max = self.center + delt;
|
||||||
AABB::with_bounds(min, max)
|
AABB::with_bounds(min, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn centroid(&self) -> Point {
|
||||||
|
self.center
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -103,21 +108,30 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn intersect_along_axis_works() {
|
fn intersect_along_axis_works() {
|
||||||
let sphere = simple_sphere();
|
let sphere = simple_sphere();
|
||||||
let ray = Ray::new(Point::new(-2., 0., 0.), Vector::new(1., 0., 0.));
|
let ray = Ray::new(
|
||||||
|
Point::new(-2., 0., 0.),
|
||||||
|
Unit::new_normalize(Vector::new(1., 0., 0.)),
|
||||||
|
);
|
||||||
assert_eq!(sphere.intersect(&ray), Some(1.))
|
assert_eq!(sphere.intersect(&ray), Some(1.))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn non_intersect_along_axis_works() {
|
fn non_intersect_along_axis_works() {
|
||||||
let sphere = simple_sphere();
|
let sphere = simple_sphere();
|
||||||
let ray = Ray::new(Point::new(-2., 0., 0.), Vector::new(-1., 0., 0.));
|
let ray = Ray::new(
|
||||||
|
Point::new(-2., 0., 0.),
|
||||||
|
Unit::new_normalize(Vector::new(-1., 0., 0.)),
|
||||||
|
);
|
||||||
assert_eq!(sphere.intersect(&ray), None)
|
assert_eq!(sphere.intersect(&ray), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn intersect_not_on_axis() {
|
fn intersect_not_on_axis() {
|
||||||
let sphere = simple_sphere();
|
let sphere = simple_sphere();
|
||||||
let ray = Ray::new(Point::new(1., 1., 1.), Vector::new(-1., -1., -1.));
|
let ray = Ray::new(
|
||||||
|
Point::new(1., 1., 1.),
|
||||||
|
Unit::new_normalize(Vector::new(-1., -1., -1.)),
|
||||||
|
);
|
||||||
assert_eq!(sphere.intersect(&ray), Some(f32::sqrt(3.) - 1.))
|
assert_eq!(sphere.intersect(&ray), Some(f32::sqrt(3.) - 1.))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,7 +140,7 @@ mod test {
|
||||||
let sphere = simple_sphere();
|
let sphere = simple_sphere();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sphere.normal(&Point::new(-1., 0., 0.)),
|
sphere.normal(&Point::new(-1., 0., 0.)),
|
||||||
Vector::new(-1., 0., 0.)
|
Unit::new_normalize(Vector::new(-1., 0., 0.))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +149,7 @@ mod test {
|
||||||
let sphere = Sphere::inverted_new(Point::origin(), 1.);
|
let sphere = Sphere::inverted_new(Point::origin(), 1.);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
sphere.normal(&Point::new(-1., 0., 0.)),
|
sphere.normal(&Point::new(-1., 0., 0.)),
|
||||||
Vector::new(1., 0., 0.)
|
Unit::new_normalize(Vector::new(1., 0., 0.))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use super::Shape;
|
use super::Shape;
|
||||||
use crate::{Point, Point2D, Vector};
|
use crate::{Point, Point2D, Vector};
|
||||||
use bvh::aabb::AABB;
|
use beevee::aabb::AABB;
|
||||||
use bvh::ray::Ray;
|
use beevee::ray::Ray;
|
||||||
|
use nalgebra::Unit;
|
||||||
use serde::{Deserialize, Deserializer};
|
use serde::{Deserialize, Deserializer};
|
||||||
|
|
||||||
/// Represent a triangle inside the scene.
|
/// Represent a triangle inside the scene.
|
||||||
|
@ -88,8 +89,8 @@ impl Shape for Triangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(&self, _: &Point) -> Vector {
|
fn normal(&self, _: &Point) -> Unit<Vector> {
|
||||||
self.c0c1.cross(&self.c0c2).normalize()
|
Unit::new_normalize(self.c0c1.cross(&self.c0c2))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_texel(&self, point: &Point) -> Point2D {
|
fn project_texel(&self, point: &Point) -> Point2D {
|
||||||
|
@ -102,6 +103,10 @@ impl Shape for Triangle {
|
||||||
.grow(&(self.c0 + self.c0c1))
|
.grow(&(self.c0 + self.c0c1))
|
||||||
.grow(&(self.c0 + self.c0c2))
|
.grow(&(self.c0 + self.c0c2))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn centroid(&self) -> Point {
|
||||||
|
self.c0 + (self.c0c1 + self.c0c2) / 2.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -132,6 +137,7 @@ impl<'de> Deserialize<'de> for Triangle {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use nalgebra::Unit;
|
||||||
|
|
||||||
fn simple_triangle() -> Triangle {
|
fn simple_triangle() -> Triangle {
|
||||||
Triangle::new(
|
Triangle::new(
|
||||||
|
@ -146,7 +152,7 @@ mod test {
|
||||||
let triangle = simple_triangle();
|
let triangle = simple_triangle();
|
||||||
let ans = triangle.intersect(&Ray::new(
|
let ans = triangle.intersect(&Ray::new(
|
||||||
Point::new(-1., 0.5, 0.5),
|
Point::new(-1., 0.5, 0.5),
|
||||||
Vector::new(1., 0., 0.),
|
Unit::new_normalize(Vector::new(1., 0., 0.)),
|
||||||
));
|
));
|
||||||
assert_eq!(ans, Some(1.0))
|
assert_eq!(ans, Some(1.0))
|
||||||
}
|
}
|
||||||
|
@ -156,7 +162,7 @@ mod test {
|
||||||
let triangle = simple_triangle();
|
let triangle = simple_triangle();
|
||||||
let ans = triangle.intersect(&Ray::new(
|
let ans = triangle.intersect(&Ray::new(
|
||||||
Point::new(-1., 0.5, 0.),
|
Point::new(-1., 0.5, 0.),
|
||||||
Vector::new(1., 0., 0.5),
|
Unit::new_normalize(Vector::new(1., 0., 0.5)),
|
||||||
));
|
));
|
||||||
assert!(ans.is_some());
|
assert!(ans.is_some());
|
||||||
assert!((ans.unwrap() - f32::sqrt(1.0 + 0.25)).abs() < 1e-5)
|
assert!((ans.unwrap() - f32::sqrt(1.0 + 0.25)).abs() < 1e-5)
|
||||||
|
@ -165,7 +171,10 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn intersect_out_of_bounds_is_none() {
|
fn intersect_out_of_bounds_is_none() {
|
||||||
let triangle = simple_triangle();
|
let triangle = simple_triangle();
|
||||||
let ans = triangle.intersect(&Ray::new(Point::new(-1., 0.5, 0.), Vector::new(1., 1., 1.)));
|
let ans = triangle.intersect(&Ray::new(
|
||||||
|
Point::new(-1., 0.5, 0.),
|
||||||
|
Unit::new_normalize(Vector::new(1., 1., 1.)),
|
||||||
|
));
|
||||||
assert_eq!(ans, None)
|
assert_eq!(ans, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,7 +182,7 @@ mod test {
|
||||||
fn normal_works() {
|
fn normal_works() {
|
||||||
let triangle = simple_triangle();
|
let triangle = simple_triangle();
|
||||||
let normal = triangle.normal(&Point::origin());
|
let normal = triangle.normal(&Point::origin());
|
||||||
assert_eq!(normal, Vector::new(-1., 0., 0.));
|
assert_eq!(normal, Unit::new_normalize(Vector::new(-1., 0., 0.)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in a new issue