can create a stripped/basic version of cornell box
1. Refactored the project 2. Changed src/shapes/rectangle.rs to make it easy to create rectangles aligned with any(X/Y/Z) Axis.
This commit is contained in:
parent
298e0a2301
commit
9d82129891
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -331,6 +331,7 @@ name = "rtnw"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"image",
|
||||
"num-traits",
|
||||
"rand",
|
||||
"rayon",
|
||||
"sdl2",
|
||||
|
|
|
@ -3,14 +3,14 @@ authors = ["ishanjain28 <ishanjain28@gmail.com>"]
|
|||
edition = "2018"
|
||||
name = "rtnw"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
num-traits = "*"
|
||||
rayon = "1.5.0"
|
||||
|
||||
[dependencies.image]
|
||||
default-features = false
|
||||
features = ["jpeg", "png"]
|
||||
version = "0.23.14"
|
||||
default-features = false
|
||||
|
||||
[dependencies.rand]
|
||||
features = ["small_rng"]
|
||||
|
|
19
src/aabb.rs
19
src/aabb.rs
|
@ -13,9 +13,9 @@ impl Aabb {
|
|||
|
||||
pub fn hit(&self, ray: &Ray, mut t_min: f64, mut t_max: f64) -> bool {
|
||||
for i in 0..=2 {
|
||||
let inverse_dir = 1.0 / ray.direction()[i];
|
||||
let mut t0 = (self.min[i] - ray.origin()[i]) * inverse_dir;
|
||||
let mut t1 = (self.max[i] - ray.origin()[i]) * inverse_dir;
|
||||
let inverse_dir = 1.0 / ray.direction[i];
|
||||
let mut t0 = (self.min[i] - ray.origin[i]) * inverse_dir;
|
||||
let mut t1 = (self.max[i] - ray.origin[i]) * inverse_dir;
|
||||
if inverse_dir < 0.0 {
|
||||
std::mem::swap(&mut t0, &mut t1);
|
||||
}
|
||||
|
@ -31,17 +31,8 @@ impl Aabb {
|
|||
}
|
||||
|
||||
pub fn surrounding_box(box0: Aabb, box1: Aabb) -> Self {
|
||||
let smol_box = Vec3::new(
|
||||
box0.min.x().min(box1.min.x()),
|
||||
box0.min.y().min(box1.min.y()),
|
||||
box0.min.z().min(box1.min.z()),
|
||||
);
|
||||
|
||||
let big_box = Vec3::new(
|
||||
box0.max.x().max(box1.max.x()),
|
||||
box0.max.y().max(box1.max.y()),
|
||||
box0.max.z().max(box1.max.z()),
|
||||
);
|
||||
let smol_box = Vec3::min(box0.min, box1.min);
|
||||
let big_box = Vec3::max(box0.max, box1.max);
|
||||
|
||||
Self {
|
||||
min: smol_box,
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use crate::{
|
||||
demos::{Demo, ParallelHit},
|
||||
hitable::{
|
||||
shapes::{MovingSphere, Sphere},
|
||||
BvhNode,
|
||||
},
|
||||
materials::{Dielectric, Lambertian, Metal},
|
||||
shapes::{MovingSphere, Sphere},
|
||||
texture::{Checker, Solid},
|
||||
types::Vec3,
|
||||
BvhNode, Camera,
|
||||
Camera,
|
||||
};
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
use std::sync::Arc;
|
||||
|
@ -18,6 +21,10 @@ impl Demo for CheckeredMotionBlur {
|
|||
"checkered_motion_blur"
|
||||
}
|
||||
|
||||
fn get_background(&self) -> Vec3 {
|
||||
Vec3::new(0.7, 0.8, 1.0)
|
||||
}
|
||||
|
||||
fn world(&self) -> Self::DemoT {
|
||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(500);
|
||||
|
||||
|
|
110
src/demos/cornell_box.rs
Normal file
110
src/demos/cornell_box.rs
Normal file
|
@ -0,0 +1,110 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use rand::{prelude::SmallRng, SeedableRng};
|
||||
|
||||
use crate::{
|
||||
demos::{Demo, ParallelHit},
|
||||
hitable::shapes::{Cuboid, RectBuilder},
|
||||
materials::{DiffuseLight, Lambertian, MaterialBuilder},
|
||||
texture::Solid,
|
||||
types::Vec3,
|
||||
BvhNode, Camera,
|
||||
};
|
||||
|
||||
pub struct CornellBox {}
|
||||
|
||||
impl Demo for CornellBox {
|
||||
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"cornell_box"
|
||||
}
|
||||
|
||||
fn world(&self) -> Self::DemoT {
|
||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(8);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
|
||||
|
||||
let red = Lambertian::new(Solid::new(Vec3::new(0.65, 0.05, 0.05)));
|
||||
let white = Lambertian::new(Solid::new(Vec3::splat(0.73)));
|
||||
let green = Lambertian::new(Solid::new(Vec3::new(0.12, 0.45, 0.15)));
|
||||
let light = DiffuseLight::new(Solid::new(Vec3::splat(15.0)));
|
||||
|
||||
world.push(Arc::new(
|
||||
RectBuilder
|
||||
.y(0.0..=555.0)
|
||||
.z(0.0..=555.0)
|
||||
.x(555.0)
|
||||
.material(green),
|
||||
));
|
||||
world.push(Arc::new(
|
||||
RectBuilder
|
||||
.y(0.0..=555.0)
|
||||
.z(0.0..=555.0)
|
||||
.x(0.0)
|
||||
.material(red),
|
||||
));
|
||||
world.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(213.0..=343.0)
|
||||
.z(227.0..=332.0)
|
||||
.y(554.0)
|
||||
.material(light),
|
||||
));
|
||||
|
||||
world.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(0.0..=555.0)
|
||||
.z(0.0..=555.0)
|
||||
.y(0.0)
|
||||
.material(white),
|
||||
));
|
||||
world.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(0.0..=555.0)
|
||||
.z(0.0..=555.0)
|
||||
.y(555.0)
|
||||
.material(white),
|
||||
));
|
||||
world.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(0.0..=555.0)
|
||||
.y(0.0..=555.0)
|
||||
.z(555.0)
|
||||
.material(white),
|
||||
));
|
||||
|
||||
// Add the two boxes
|
||||
world.push(Arc::new(Cuboid::new(
|
||||
Vec3::new(136.0, 0.0, 65.0),
|
||||
Vec3::new(295.0, 165.0, 230.0),
|
||||
white,
|
||||
)));
|
||||
world.push(Arc::new(Cuboid::new(
|
||||
Vec3::new(265.0, 0.0, 295.0),
|
||||
Vec3::new(430.0, 330.0, 460.0),
|
||||
white,
|
||||
)));
|
||||
|
||||
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
|
||||
}
|
||||
|
||||
fn camera(&self, aspect_ratio: f64) -> Camera {
|
||||
let lookfrom = Vec3::new(278.0, 278.0, -800.0);
|
||||
let lookat = Vec3::new(278.0, 278.0, 0.0);
|
||||
let aperture = 0.1;
|
||||
let focus_distance = 40.0;
|
||||
Camera::new(
|
||||
lookfrom,
|
||||
lookat,
|
||||
Vec3::new(0.0, 1.0, 0.0),
|
||||
40.0,
|
||||
aspect_ratio,
|
||||
aperture,
|
||||
focus_distance,
|
||||
0.0,
|
||||
1.0,
|
||||
)
|
||||
}
|
||||
}
|
|
@ -4,8 +4,8 @@ use rand::{prelude::SmallRng, SeedableRng};
|
|||
|
||||
use crate::{
|
||||
demos::{Demo, ParallelHit},
|
||||
hitable::shapes::Sphere,
|
||||
materials::Lambertian,
|
||||
shapes::Sphere,
|
||||
texture::ImageTexture,
|
||||
types::Vec3,
|
||||
BvhNode, Camera,
|
||||
|
@ -20,6 +20,10 @@ impl Demo for ImageTextureDemo {
|
|||
"image_texture"
|
||||
}
|
||||
|
||||
fn get_background(&self) -> Vec3 {
|
||||
Vec3::new(0.7, 0.8, 1.0)
|
||||
}
|
||||
|
||||
fn world(&self) -> Self::DemoT {
|
||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(1);
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
use crate::{types::Vec3, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION};
|
||||
use crate::{
|
||||
hitable::Hitable,
|
||||
types::{Color, Vec3},
|
||||
Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
|
||||
};
|
||||
use rand::{rngs::SmallRng, Rng, SeedableRng};
|
||||
use rayon::prelude::*;
|
||||
use std::{
|
||||
|
@ -9,12 +13,14 @@ use std::{
|
|||
};
|
||||
|
||||
mod checkered_motion_blur;
|
||||
mod cornell_box;
|
||||
mod image_texture;
|
||||
mod perlin_noise_ball;
|
||||
mod simple_light;
|
||||
mod two_spheres;
|
||||
|
||||
pub use checkered_motion_blur::CheckeredMotionBlur;
|
||||
pub use cornell_box::CornellBox;
|
||||
pub use image_texture::ImageTextureDemo;
|
||||
pub use perlin_noise_ball::PerlinNoiseBall;
|
||||
pub use simple_light::SimpleLight;
|
||||
|
@ -55,7 +61,7 @@ pub trait Demo: Send + Sync {
|
|||
fn camera(&self, aspect_ratio: f64) -> Camera;
|
||||
|
||||
fn get_background(&self) -> Vec3 {
|
||||
Vec3::new(0.7, 0.8, 1.0)
|
||||
Vec3::new(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
fn render_chunk(&self, chunk: &mut Chunk, camera: &Camera, world: &Self::DemoT, samples: u16) {
|
||||
|
@ -156,14 +162,16 @@ pub trait Demo: Send + Sync {
|
|||
|
||||
#[inline]
|
||||
fn update_rgb(&self, buffer: &mut [u8], color: Vec3, offset: usize) {
|
||||
let color: Color = color.into();
|
||||
|
||||
if let Some(pos) = buffer.get_mut(offset) {
|
||||
*pos = (255.99 * color.r().sqrt()) as u8;
|
||||
*pos = color.0;
|
||||
}
|
||||
if let Some(pos) = buffer.get_mut(offset + 1) {
|
||||
*pos = (255.99 * color.g().sqrt()) as u8;
|
||||
*pos = color.1
|
||||
}
|
||||
if let Some(pos) = buffer.get_mut(offset + 2) {
|
||||
*pos = (255.99 * color.b().sqrt()) as u8;
|
||||
*pos = color.2;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,11 +4,11 @@ use rand::{prelude::SmallRng, SeedableRng};
|
|||
|
||||
use crate::{
|
||||
demos::{Demo, ParallelHit},
|
||||
hitable::{shapes::Sphere, BvhNode},
|
||||
materials::Lambertian,
|
||||
shapes::Sphere,
|
||||
texture::PerlinNoise,
|
||||
types::Vec3,
|
||||
BvhNode, Camera,
|
||||
Camera,
|
||||
};
|
||||
|
||||
pub struct PerlinNoiseBall {}
|
||||
|
@ -20,6 +20,10 @@ impl Demo for PerlinNoiseBall {
|
|||
"perlin_noise"
|
||||
}
|
||||
|
||||
fn get_background(&self) -> Vec3 {
|
||||
Vec3::new(0.7, 0.8, 1.0)
|
||||
}
|
||||
|
||||
fn world(&self) -> Self::DemoT {
|
||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(2);
|
||||
|
||||
|
|
|
@ -4,11 +4,14 @@ use rand::{prelude::SmallRng, SeedableRng};
|
|||
|
||||
use crate::{
|
||||
demos::{Demo, ParallelHit},
|
||||
materials::{DiffuseLight, Lambertian},
|
||||
shapes::{Sphere, XyRectangle},
|
||||
hitable::{
|
||||
shapes::{RectBuilder, Sphere},
|
||||
BvhNode,
|
||||
},
|
||||
materials::{DiffuseLight, Lambertian, MaterialBuilder},
|
||||
texture::{PerlinNoise, Solid},
|
||||
types::Vec3,
|
||||
BvhNode, Camera,
|
||||
Camera,
|
||||
};
|
||||
|
||||
pub struct SimpleLight {}
|
||||
|
@ -20,10 +23,6 @@ impl Demo for SimpleLight {
|
|||
"simple_light"
|
||||
}
|
||||
|
||||
fn get_background(&self) -> Vec3 {
|
||||
Vec3::new(0.0, 0.0, 0.0)
|
||||
}
|
||||
|
||||
fn world(&self) -> Self::DemoT {
|
||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(5);
|
||||
let mut rng = rand::thread_rng();
|
||||
|
@ -39,14 +38,14 @@ impl Demo for SimpleLight {
|
|||
2.0,
|
||||
Lambertian::new(PerlinNoise::with_scale(&mut rng, 4.0)),
|
||||
)));
|
||||
world.push(Arc::new(XyRectangle::new(
|
||||
3.0,
|
||||
5.0,
|
||||
1.0,
|
||||
3.0,
|
||||
-2.0,
|
||||
DiffuseLight::new(Solid::new(Vec3::new(4.0, 4.0, 4.0))),
|
||||
)));
|
||||
|
||||
world.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(3.0..=5.0)
|
||||
.y(1.0..=3.0)
|
||||
.z(-2.0)
|
||||
.material(DiffuseLight::new(Solid::new(Vec3::new(4.0, 4.0, 4.0)))),
|
||||
));
|
||||
world.push(Arc::new(Sphere::new(
|
||||
Vec3::new(0.0, 7.0, 0.0),
|
||||
2.0,
|
||||
|
|
|
@ -4,11 +4,11 @@ use rand::{prelude::SmallRng, SeedableRng};
|
|||
|
||||
use crate::{
|
||||
demos::{Demo, ParallelHit},
|
||||
hitable::{shapes::Sphere, BvhNode},
|
||||
materials::Lambertian,
|
||||
shapes::Sphere,
|
||||
texture::{Checker, Solid},
|
||||
types::Vec3,
|
||||
BvhNode, Camera,
|
||||
Camera,
|
||||
};
|
||||
|
||||
pub struct TwoSpheres {}
|
||||
|
@ -20,6 +20,10 @@ impl Demo for TwoSpheres {
|
|||
"two_checkered_sphere"
|
||||
}
|
||||
|
||||
fn get_background(&self) -> Vec3 {
|
||||
Vec3::new(0.7, 0.8, 1.0)
|
||||
}
|
||||
|
||||
fn world(&self) -> Self::DemoT {
|
||||
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(2);
|
||||
|
||||
|
|
|
@ -2,7 +2,11 @@ use std::cmp::Ordering;
|
|||
|
||||
use rand::{prelude::SliceRandom, Rng};
|
||||
|
||||
use crate::{types::Ray, Aabb, HitRecord, Hitable};
|
||||
use crate::{
|
||||
hitable::{HitRecord, Hitable},
|
||||
types::Ray,
|
||||
Aabb,
|
||||
};
|
||||
|
||||
pub struct BvhNode<T: Hitable> {
|
||||
bounding_box: Aabb,
|
|
@ -28,6 +28,8 @@ pub struct HitRecord<'a> {
|
|||
/// texture coordinates for an object
|
||||
pub u: f64,
|
||||
pub v: f64,
|
||||
|
||||
pub front_face: bool,
|
||||
}
|
||||
|
||||
impl<'a> HitRecord<'a> {
|
||||
|
@ -45,6 +47,17 @@ impl<'a> HitRecord<'a> {
|
|||
material,
|
||||
u,
|
||||
v,
|
||||
front_face: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_face_normal(&mut self, ray: &Ray) {
|
||||
self.front_face = ray.direction.dot(&self.normal) < 0.0;
|
||||
|
||||
self.normal = if self.front_face {
|
||||
self.normal
|
||||
} else {
|
||||
-self.normal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,3 +76,37 @@ impl<T: Hitable + ?Sized> Hitable for Arc<T> {
|
|||
self.as_ref().bounding_box(t0, t1)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Translate<T> {
|
||||
object: T,
|
||||
offset: Vec3,
|
||||
}
|
||||
|
||||
impl<T> Translate<T> {
|
||||
pub fn new(object: T, offset: Vec3) -> Self {
|
||||
Self { object, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Hitable> Hitable for Translate<T> {
|
||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||
let moved_ray = Ray::new(ray.origin - self.offset, ray.direction, ray.time());
|
||||
|
||||
if let Some(mut hit) = self.object.hit(&moved_ray, t_min, t_max) {
|
||||
hit.p += self.offset;
|
||||
hit.set_face_normal(&moved_ray);
|
||||
|
||||
Some(hit)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
|
||||
if let Some(bbox) = self.object.bounding_box(t0, t1) {
|
||||
Some(Aabb::new(bbox.min + self.offset, bbox.max + self.offset))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,11 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{demos::ParallelHit, types::Ray, Aabb, HitRecord, Hitable};
|
||||
use crate::{
|
||||
demos::ParallelHit,
|
||||
hitable::{HitRecord, Hitable},
|
||||
types::Ray,
|
||||
Aabb,
|
||||
};
|
||||
|
||||
pub struct HitableList {
|
||||
pub list: Vec<Arc<dyn ParallelHit>>,
|
7
src/hitable/mod.rs
Normal file
7
src/hitable/mod.rs
Normal file
|
@ -0,0 +1,7 @@
|
|||
pub mod bvh;
|
||||
pub mod hitable;
|
||||
pub mod hitable_list;
|
||||
pub mod shapes;
|
||||
|
||||
pub use bvh::*;
|
||||
pub use hitable::*;
|
87
src/hitable/shapes/cuboid.rs
Normal file
87
src/hitable/shapes/cuboid.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
hitable::{hitable_list::HitableList, shapes::RectBuilder, HitRecord, Hitable},
|
||||
materials::{Material, MaterialBuilder},
|
||||
types::{Ray, Vec3},
|
||||
Aabb,
|
||||
};
|
||||
|
||||
pub struct Cuboid {
|
||||
min: Vec3,
|
||||
max: Vec3,
|
||||
sides: HitableList,
|
||||
}
|
||||
|
||||
impl Cuboid {
|
||||
pub fn new(p0: Vec3, p1: Vec3, mat: impl Material + Clone + 'static) -> Self {
|
||||
Self {
|
||||
min: p0,
|
||||
max: p1,
|
||||
sides: Self::build_cuboid(p0, p1, mat),
|
||||
}
|
||||
}
|
||||
|
||||
fn build_cuboid(p0: Vec3, p1: Vec3, mat: impl Material + Clone + 'static) -> HitableList {
|
||||
let mut sides = HitableList {
|
||||
list: Vec::with_capacity(6),
|
||||
};
|
||||
|
||||
sides.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(p0.x()..=p1.x())
|
||||
.y(p0.y()..=p1.y())
|
||||
.z(p1.z())
|
||||
.material(mat.clone()),
|
||||
));
|
||||
sides.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(p0.x()..=p1.x())
|
||||
.y(p0.y()..=p1.y())
|
||||
.z(p0.z())
|
||||
.material(mat.clone()),
|
||||
));
|
||||
|
||||
sides.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(p0.x()..=p1.x())
|
||||
.z(p0.z()..=p1.z())
|
||||
.y(p1.y())
|
||||
.material(mat.clone()),
|
||||
));
|
||||
sides.push(Arc::new(
|
||||
RectBuilder
|
||||
.x(p0.x()..=p1.x())
|
||||
.z(p0.z()..=p1.z())
|
||||
.y(p0.y())
|
||||
.material(mat.clone()),
|
||||
));
|
||||
|
||||
sides.push(Arc::new(
|
||||
RectBuilder
|
||||
.y(p0.y()..=p1.y())
|
||||
.z(p0.z()..=p1.z())
|
||||
.x(p1.x())
|
||||
.material(mat.clone()),
|
||||
));
|
||||
sides.push(Arc::new(
|
||||
RectBuilder
|
||||
.y(p0.y()..=p1.y())
|
||||
.z(p0.z()..=p1.z())
|
||||
.x(p0.x())
|
||||
.material(mat),
|
||||
));
|
||||
|
||||
sides
|
||||
}
|
||||
}
|
||||
|
||||
impl Hitable for Cuboid {
|
||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||
self.sides.hit(ray, t_min, t_max)
|
||||
}
|
||||
|
||||
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
||||
Some(Aabb::new(self.min, self.max))
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
mod cuboid;
|
||||
mod moving_sphere;
|
||||
mod rectangle;
|
||||
mod sphere;
|
||||
mod xy_rectangle;
|
||||
|
||||
pub use cuboid::Cuboid;
|
||||
pub use moving_sphere::MovingSphere;
|
||||
pub use rectangle::RectBuilder;
|
||||
pub use sphere::Sphere;
|
||||
pub use xy_rectangle::XyRectangle;
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
hitable::{HitRecord, Hitable},
|
||||
types::{Ray, Vec3},
|
||||
Aabb, HitRecord, Hitable, Material,
|
||||
Aabb, Material,
|
||||
};
|
||||
|
||||
pub struct MovingSphere<T: Material + Sized> {
|
||||
|
@ -53,9 +54,9 @@ impl<T: Material + Sized> MovingSphere<T> {
|
|||
|
||||
impl<T: Material + Sized> Hitable for MovingSphere<T> {
|
||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||
let oc = ray.origin() - self.center(ray.time());
|
||||
let a = ray.direction().dot(&ray.direction());
|
||||
let b = oc.dot(&ray.direction());
|
||||
let oc = ray.origin - self.center(ray.time());
|
||||
let a = ray.direction.dot(&ray.direction);
|
||||
let b = oc.dot(&ray.direction);
|
||||
let c = oc.dot(&oc) - self.radius * self.radius;
|
||||
|
||||
let discriminant = b * b - a * c;
|
175
src/hitable/shapes/rectangle.rs
Normal file
175
src/hitable/shapes/rectangle.rs
Normal file
|
@ -0,0 +1,175 @@
|
|||
use std::{marker::PhantomData, ops::RangeInclusive};
|
||||
|
||||
use crate::{
|
||||
hitable::{HitRecord, Hitable},
|
||||
materials::MaterialBuilder,
|
||||
types::{Ray, Vec3},
|
||||
Aabb, Dimension, Material, X, Y, Z,
|
||||
};
|
||||
|
||||
type DimRange = RangeInclusive<f64>;
|
||||
|
||||
pub struct Rectangle<D1, D2, D3, T> {
|
||||
d1_range: DimRange,
|
||||
d2_range: DimRange,
|
||||
d3: f64,
|
||||
material: T,
|
||||
tag: PhantomData<(D1, D2, D3)>,
|
||||
}
|
||||
|
||||
impl<D1, D2, D3, T> Hitable for Rectangle<D1, D2, D3, T>
|
||||
where
|
||||
T: Material,
|
||||
D1: Dimension,
|
||||
D2: Dimension,
|
||||
D3: Dimension,
|
||||
{
|
||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||
let t = (self.d3 - ray.origin.get::<D3>()) / ray.direction.get::<D3>();
|
||||
|
||||
if t < t_min || t > t_max {
|
||||
return None;
|
||||
}
|
||||
|
||||
let d1 = ray.origin.get::<D1>() + t * ray.direction.get::<D1>();
|
||||
let d2 = ray.origin.get::<D2>() + t * ray.direction.get::<D2>();
|
||||
|
||||
if !self.d1_range.contains(&d1) || !self.d2_range.contains(&d2) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let u = (d1 - self.d1_range.start()) / (self.d1_range.end() - self.d1_range.start());
|
||||
let v = (d2 - self.d2_range.start()) / (self.d2_range.end() - self.d2_range.start());
|
||||
|
||||
let mut hit_rec = HitRecord::new(
|
||||
t,
|
||||
ray.point_at_parameter(t),
|
||||
Vec3::splat(0.0).set::<D3>(1.0),
|
||||
&self.material,
|
||||
(u, v),
|
||||
);
|
||||
|
||||
hit_rec.set_face_normal(ray);
|
||||
|
||||
Some(hit_rec)
|
||||
}
|
||||
|
||||
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
||||
// Since this is a axis aligned Rectangle and we are using AABB BVH, Gap between the rectangle and
|
||||
// the bounding box will be infinitely small
|
||||
|
||||
let (&d1_0, &d1_1) = (self.d1_range.start(), self.d1_range.end());
|
||||
let (&d2_0, &d2_1) = (self.d2_range.start(), self.d2_range.end());
|
||||
|
||||
let min = Vec3::splat(self.d3 - 0.0001)
|
||||
.set::<D1>(d1_0)
|
||||
.set::<D2>(d2_0);
|
||||
let max = Vec3::splat(self.d3 + 0.0001)
|
||||
.set::<D1>(d1_1)
|
||||
.set::<D2>(d2_1);
|
||||
|
||||
Some(Aabb::new(min, max))
|
||||
}
|
||||
}
|
||||
|
||||
// taken from, https://github.com/Globidev/toy-rt/blob/master/trt-core/src/hit/rect.rs#L74
|
||||
// because it's amazing!
|
||||
|
||||
pub struct RectBuilder;
|
||||
|
||||
macro_rules! builder {
|
||||
($name:ident, $dim:ty) => {
|
||||
pub fn $name(self, range: RangeInclusive<f64>) -> OneBoundedRectBuilder<$dim> {
|
||||
OneBoundedRectBuilder {
|
||||
range,
|
||||
tag: PhantomData,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub struct OneBoundedRectBuilder<D> {
|
||||
range: DimRange,
|
||||
tag: PhantomData<D>,
|
||||
}
|
||||
|
||||
impl RectBuilder {
|
||||
builder!(x, X);
|
||||
builder!(y, Y);
|
||||
builder!(z, Z);
|
||||
}
|
||||
|
||||
macro_rules! one_bounded_rect_builder {
|
||||
($name:ident, $dim1: ty, $dim2: ty) => {
|
||||
pub fn $name(self, d2_range: DimRange) -> TwoBoundedRectBuilder<$dim1, $dim2> {
|
||||
TwoBoundedRectBuilder {
|
||||
d1_range: self.range,
|
||||
d2_range,
|
||||
tag: PhantomData,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl OneBoundedRectBuilder<X> {
|
||||
one_bounded_rect_builder!(y, X, Y);
|
||||
one_bounded_rect_builder!(z, X, Z);
|
||||
}
|
||||
impl OneBoundedRectBuilder<Y> {
|
||||
one_bounded_rect_builder!(x, Y, X);
|
||||
one_bounded_rect_builder!(z, Y, Z);
|
||||
}
|
||||
impl OneBoundedRectBuilder<Z> {
|
||||
one_bounded_rect_builder!(x, Z, X);
|
||||
one_bounded_rect_builder!(y, Z, Y);
|
||||
}
|
||||
|
||||
pub struct TwoBoundedRectBuilder<D1, D2> {
|
||||
d1_range: DimRange,
|
||||
d2_range: DimRange,
|
||||
tag: PhantomData<(D1, D2)>,
|
||||
}
|
||||
|
||||
macro_rules! two_bounded_rect_builder {
|
||||
($name:ident, $dim1: ty, $dim2: ty, $dim3: ty) => {
|
||||
pub fn $name(self, $name: f64) -> ThreeBoundedRectBuilder<$dim1, $dim2, $dim3> {
|
||||
ThreeBoundedRectBuilder {
|
||||
d1_range: self.d1_range,
|
||||
d2_range: self.d2_range,
|
||||
d3: $name,
|
||||
tag: PhantomData,
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl TwoBoundedRectBuilder<X, Y> {
|
||||
two_bounded_rect_builder!(z, X, Y, Z);
|
||||
}
|
||||
impl TwoBoundedRectBuilder<X, Z> {
|
||||
two_bounded_rect_builder!(y, X, Z, Y);
|
||||
}
|
||||
impl TwoBoundedRectBuilder<Y, Z> {
|
||||
two_bounded_rect_builder!(x, Y, Z, X);
|
||||
}
|
||||
|
||||
pub struct ThreeBoundedRectBuilder<D1, D2, D3> {
|
||||
d1_range: DimRange,
|
||||
d2_range: DimRange,
|
||||
d3: f64,
|
||||
tag: PhantomData<(D1, D2, D3)>,
|
||||
}
|
||||
|
||||
impl<D1, D2, D3, T> MaterialBuilder<T> for ThreeBoundedRectBuilder<D1, D2, D3> {
|
||||
type Finished = Rectangle<D1, D2, D3, T>;
|
||||
|
||||
fn material(self, material: T) -> Self::Finished {
|
||||
Rectangle {
|
||||
d1_range: self.d1_range,
|
||||
d2_range: self.d2_range,
|
||||
d3: self.d3,
|
||||
material,
|
||||
tag: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
hitable::{HitRecord, Hitable},
|
||||
types::{Ray, Vec3},
|
||||
Aabb, HitRecord, Hitable, Material,
|
||||
Aabb, Material,
|
||||
};
|
||||
|
||||
pub struct Sphere<T: Material + Sized> {
|
||||
|
@ -34,9 +35,9 @@ impl<T: Material + Sized> Sphere<T> {
|
|||
|
||||
impl<T: Material + Sized> Hitable for Sphere<T> {
|
||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||
let oc = ray.origin() - self.center;
|
||||
let a = ray.direction().dot(&ray.direction());
|
||||
let b = oc.dot(&ray.direction());
|
||||
let oc = ray.origin - self.center;
|
||||
let a = ray.direction.dot(&ray.direction);
|
||||
let b = oc.dot(&ray.direction);
|
||||
let c = oc.dot(&oc) - self.radius * self.radius;
|
||||
|
||||
// The discriminant is calculated using b^2 - 4 * a * c
|
23
src/main.rs
23
src/main.rs
|
@ -1,33 +1,32 @@
|
|||
#![allow(clippy::suspicious_arithmetic_impl)]
|
||||
|
||||
mod aabb;
|
||||
mod bvh;
|
||||
mod camera;
|
||||
mod demos;
|
||||
mod hitable;
|
||||
mod hitable_list;
|
||||
mod materials;
|
||||
mod shapes;
|
||||
mod texture;
|
||||
mod types;
|
||||
|
||||
pub use aabb::Aabb;
|
||||
pub use bvh::BvhNode;
|
||||
pub use camera::Camera;
|
||||
pub use hitable::{HitRecord, Hitable};
|
||||
pub use hitable_list::HitableList;
|
||||
pub use materials::Material;
|
||||
pub use texture::Texture;
|
||||
pub use types::{Dimension, X, Y, Z};
|
||||
|
||||
use crate::hitable::BvhNode;
|
||||
use demos::Demo;
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
const NUM_SAMPLES: u16 = 1000;
|
||||
pub trait Asf64: num_traits::AsPrimitive<f64> {}
|
||||
impl<T: num_traits::AsPrimitive<f64>> Asf64 for T {}
|
||||
|
||||
const NUM_SAMPLES: u16 = 100;
|
||||
const VERTICAL_PARTITION: usize = 12;
|
||||
const HORIZONTAL_PARTITION: usize = 12;
|
||||
const WIDTH: usize = 2560;
|
||||
const HEIGHT: usize = 1440;
|
||||
const WIDTH: usize = 1500;
|
||||
const HEIGHT: usize = 1500;
|
||||
|
||||
fn main() -> Result<(), String> {
|
||||
run(WIDTH, HEIGHT)
|
||||
|
@ -67,7 +66,7 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
|||
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::SimpleLight {};
|
||||
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::CornellBox {};
|
||||
let mut should_update = true;
|
||||
|
||||
loop {
|
||||
|
@ -104,6 +103,10 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
|||
active_demo = &demos::SimpleLight {};
|
||||
should_update = true;
|
||||
}
|
||||
Some(Keycode::Num6) => {
|
||||
active_demo = &demos::CornellBox {};
|
||||
should_update = true;
|
||||
}
|
||||
None => unreachable!(),
|
||||
_ => (),
|
||||
};
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
use rand::{prelude::SmallRng, Rng};
|
||||
|
||||
use crate::{
|
||||
hitable::HitRecord,
|
||||
materials::{reflect, refract, schlick},
|
||||
types::{Ray, Vec3},
|
||||
HitRecord, Material,
|
||||
Material,
|
||||
};
|
||||
|
||||
pub struct Dielectric {
|
||||
reflection_index: f64,
|
||||
refraction_index: f64,
|
||||
}
|
||||
|
||||
impl Dielectric {
|
||||
pub fn new(reflection_index: f64) -> Self {
|
||||
Self { reflection_index }
|
||||
pub fn new(refraction_index: f64) -> Self {
|
||||
Self { refraction_index }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,44 +24,37 @@ impl Material for Dielectric {
|
|||
hit_rec: &HitRecord,
|
||||
rng: &mut SmallRng,
|
||||
) -> (Vec3, Option<Ray>) {
|
||||
let reflected_ray = reflect(ray_in.direction(), hit_rec.normal);
|
||||
// Glass absorbs nothing! So, Attenuation is always going to be 1.0 for this
|
||||
let attenuation = Vec3::new(1.0, 1.0, 1.0);
|
||||
|
||||
let (outward_normal, ni_over_nt, cosine) = if ray_in.direction().dot(&hit_rec.normal) > 0.0
|
||||
{
|
||||
(
|
||||
-hit_rec.normal,
|
||||
self.reflection_index,
|
||||
(ray_in.direction().dot(&hit_rec.normal) * self.reflection_index)
|
||||
/ ray_in.direction().length(),
|
||||
)
|
||||
let refraction_ratio = if hit_rec.front_face {
|
||||
1.0 / self.refraction_index
|
||||
} else {
|
||||
(
|
||||
hit_rec.normal,
|
||||
1.0 / self.reflection_index,
|
||||
(-ray_in.direction().dot(&hit_rec.normal)) / ray_in.direction().length(),
|
||||
)
|
||||
self.refraction_index
|
||||
};
|
||||
|
||||
if let Some(refracted_ray) = refract(ray_in.direction(), outward_normal, ni_over_nt) {
|
||||
let reflect_prob = schlick(cosine, self.reflection_index);
|
||||
let unit_direction = ray_in.direction.unit_vector();
|
||||
let cosine = (-unit_direction).dot(&hit_rec.normal).min(1.0);
|
||||
let sin_theta = (1.0 - cosine * cosine).sqrt();
|
||||
|
||||
if rng.gen::<f64>() < reflect_prob {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, refracted_ray, ray_in.time())),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let cannot_refract = refraction_ratio * sin_theta > 1.0;
|
||||
|
||||
if cannot_refract || schlick(cosine, refraction_ratio) > rng.gen::<f64>() {
|
||||
let direction = reflect(unit_direction, hit_rec.normal);
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, reflected_ray, ray_in.time())),
|
||||
Some(Ray::new(hit_rec.p, direction, ray_in.time())),
|
||||
)
|
||||
} else if let Some(direction) = refract(unit_direction, hit_rec.normal, refraction_ratio) {
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, direction, ray_in.time())),
|
||||
)
|
||||
} else {
|
||||
let direction = reflect(unit_direction, hit_rec.normal);
|
||||
(
|
||||
attenuation,
|
||||
Some(Ray::new(hit_rec.p, direction, ray_in.time())),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
use rand::prelude::SmallRng;
|
||||
|
||||
use crate::{
|
||||
hitable::HitRecord,
|
||||
materials::random_point_in_unit_sphere,
|
||||
types::{Ray, Vec3},
|
||||
HitRecord, Material, Texture,
|
||||
Material, Texture,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Lambertian<T: Texture> {
|
||||
albedo: T,
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use rand::prelude::SmallRng;
|
||||
|
||||
use crate::{
|
||||
hitable::HitRecord,
|
||||
materials::{random_point_in_unit_sphere, reflect},
|
||||
types::{Ray, Vec3},
|
||||
HitRecord, Material,
|
||||
Material,
|
||||
};
|
||||
|
||||
pub struct Metal {
|
||||
|
@ -28,14 +29,14 @@ impl Material for Metal {
|
|||
hit_rec: &HitRecord,
|
||||
rng: &mut SmallRng,
|
||||
) -> (Vec3, Option<Ray>) {
|
||||
let reflected_ray = reflect(ray_in.direction().unit_vector(), hit_rec.normal);
|
||||
let reflected_ray = reflect(ray_in.direction.unit_vector(), hit_rec.normal);
|
||||
let scattered_ray = Ray::new(
|
||||
hit_rec.p,
|
||||
reflected_ray + random_point_in_unit_sphere(rng) * self.fuzz,
|
||||
ray_in.time(),
|
||||
);
|
||||
|
||||
if scattered_ray.direction().dot(&hit_rec.normal) > 0.0 {
|
||||
if scattered_ray.direction.dot(&hit_rec.normal) > 0.0 {
|
||||
(self.albedo, Some(scattered_ray))
|
||||
} else {
|
||||
(self.albedo, None)
|
||||
|
|
|
@ -10,8 +10,8 @@ pub use metal::Metal;
|
|||
use rand::{prelude::SmallRng, Rng};
|
||||
|
||||
use crate::{
|
||||
hitable::HitRecord,
|
||||
types::{Ray, Vec3},
|
||||
HitRecord,
|
||||
};
|
||||
|
||||
pub trait Material: Send + Sync {
|
||||
|
@ -23,11 +23,11 @@ pub trait Material: Send + Sync {
|
|||
_hit_rec: &HitRecord,
|
||||
_rng: &mut SmallRng,
|
||||
) -> (Vec3, Option<Ray>) {
|
||||
(Vec3::new(0.0, 0.0, 0.0), None)
|
||||
(Vec3::splat(0.0), None)
|
||||
}
|
||||
|
||||
fn emit(&self, _u: f64, _v: f64, _p: Vec3) -> Vec3 {
|
||||
Vec3::new(0.0, 0.0, 0.0)
|
||||
Vec3::splat(0.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,11 +56,15 @@ fn refract(incident: Vec3, normal: Vec3, ni_over_nt: f64) -> Option<Vec3> {
|
|||
}
|
||||
|
||||
fn random_point_in_unit_sphere<R: Rng + ?Sized>(rng: &mut R) -> Vec3 {
|
||||
let mut point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
||||
- Vec3::new(1.0, 1.0, 1.0);
|
||||
let mut point = Vec3::random(rng) * 2.0 - Vec3::splat(1.0);
|
||||
while point.sq_len() >= 1.0 {
|
||||
point = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), rng.gen::<f64>()) * 2.0
|
||||
- Vec3::new(1.0, 1.0, 1.0);
|
||||
point = Vec3::random(rng) * 2.0 - Vec3::splat(1.0);
|
||||
}
|
||||
point
|
||||
}
|
||||
|
||||
pub trait MaterialBuilder<T> {
|
||||
type Finished;
|
||||
|
||||
fn material(self, material: T) -> Self::Finished;
|
||||
}
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
use crate::{
|
||||
types::{Ray, Vec3},
|
||||
Aabb, HitRecord, Hitable, Material,
|
||||
};
|
||||
|
||||
pub struct XyRectangle<T: Material> {
|
||||
x0: f64,
|
||||
x1: f64,
|
||||
y0: f64,
|
||||
y1: f64,
|
||||
k: f64,
|
||||
material: T,
|
||||
}
|
||||
|
||||
impl<T: Material> XyRectangle<T> {
|
||||
pub fn new(x0: f64, x1: f64, y0: f64, y1: f64, k: f64, material: T) -> Self {
|
||||
Self {
|
||||
x0,
|
||||
x1,
|
||||
y0,
|
||||
y1,
|
||||
k,
|
||||
material,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Material> Hitable for XyRectangle<T> {
|
||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||
let t = (self.k - ray.origin().z()) / ray.direction().z();
|
||||
|
||||
if t < t_min || t > t_max {
|
||||
None
|
||||
} else {
|
||||
let x = ray.origin().x() + t * ray.direction().x();
|
||||
let y = ray.origin().y() + t * ray.direction().y();
|
||||
|
||||
if x < self.x0 || x > self.x1 || y < self.y0 || y > self.y1 {
|
||||
None
|
||||
} else {
|
||||
let u = (x - self.x0) / (self.x1 - self.x0);
|
||||
let v = (y - self.y0) / (self.y1 - self.y0);
|
||||
|
||||
Some(HitRecord::new(
|
||||
t,
|
||||
ray.point_at_parameter(t),
|
||||
Vec3::new(0.0, 0.0, 1.0),
|
||||
&self.material,
|
||||
(u, v),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
||||
// Since this is a axis aligned Rectangle and we are using AABB BVH, Gap between the rectangle and
|
||||
// the bounding box will be infinitely small
|
||||
|
||||
Some(Aabb::new(
|
||||
Vec3::new(self.x0, self.y0, self.k - 0.0001),
|
||||
Vec3::new(self.x1, self.y1, self.k + 0.0001),
|
||||
))
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{types::Vec3, Texture};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Solid {
|
||||
color: Vec3,
|
||||
}
|
||||
|
|
12
src/types/color.rs
Normal file
12
src/types/color.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
use crate::types::Vec3;
|
||||
|
||||
pub struct Color(pub u8, pub u8, pub u8);
|
||||
|
||||
impl From<Vec3> for Color {
|
||||
fn from(v: Vec3) -> Self {
|
||||
let v = v.sqrt() * 255.99;
|
||||
let (r, g, b) = (v.x(), v.y(), v.z());
|
||||
|
||||
Self(r as u8, g as u8, b as u8)
|
||||
}
|
||||
}
|
21
src/types/dimension.rs
Normal file
21
src/types/dimension.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
// Taken from, https://github.com/Globidev/toy-rt/tree/master/trt-core/src/dimension.rs
|
||||
|
||||
pub trait Dimension {
|
||||
const INDEX: usize;
|
||||
}
|
||||
|
||||
pub struct X;
|
||||
pub struct Y;
|
||||
pub struct Z;
|
||||
|
||||
impl Dimension for X {
|
||||
const INDEX: usize = 0;
|
||||
}
|
||||
|
||||
impl Dimension for Y {
|
||||
const INDEX: usize = 1;
|
||||
}
|
||||
|
||||
impl Dimension for Z {
|
||||
const INDEX: usize = 2;
|
||||
}
|
|
@ -1,5 +1,9 @@
|
|||
mod color;
|
||||
mod dimension;
|
||||
mod ray;
|
||||
mod vec3;
|
||||
|
||||
pub use color::Color;
|
||||
pub use dimension::{Dimension, X, Y, Z};
|
||||
pub use ray::Ray;
|
||||
pub use vec3::Vec3;
|
||||
|
|
|
@ -1,28 +1,25 @@
|
|||
use rand::prelude::SmallRng;
|
||||
|
||||
use crate::{types::Vec3, Hitable};
|
||||
use crate::{hitable::Hitable, types::Vec3};
|
||||
|
||||
pub struct Ray {
|
||||
a: Vec3,
|
||||
b: Vec3,
|
||||
pub origin: Vec3,
|
||||
pub direction: Vec3,
|
||||
time: f64,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
pub fn new(a: Vec3, b: Vec3, time: f64) -> Ray {
|
||||
Ray { a, b, time }
|
||||
}
|
||||
#[inline]
|
||||
pub const fn origin(&self) -> Vec3 {
|
||||
self.a
|
||||
}
|
||||
#[inline]
|
||||
pub const fn direction(&self) -> Vec3 {
|
||||
self.b
|
||||
pub fn new(origin: Vec3, direction: Vec3, time: f64) -> Ray {
|
||||
Ray {
|
||||
origin,
|
||||
direction,
|
||||
time,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn point_at_parameter(&self, t: f64) -> Vec3 {
|
||||
self.a + self.b * t
|
||||
self.origin + self.direction * t
|
||||
}
|
||||
#[inline]
|
||||
pub const fn time(&self) -> f64 {
|
||||
|
@ -38,7 +35,7 @@ impl Ray {
|
|||
) -> Vec3 {
|
||||
if let Some(hit_rec) = world.hit(self, 0.001, std::f64::MAX) {
|
||||
if depth >= 50 {
|
||||
Vec3::new(0.0, 0.0, 0.0)
|
||||
Vec3::splat(0.0f64)
|
||||
} else {
|
||||
let material = hit_rec.material;
|
||||
let emitted_color = hit_rec.material.emit(hit_rec.u, hit_rec.v, hit_rec.p);
|
||||
|
|
|
@ -5,37 +5,45 @@ use std::{
|
|||
|
||||
use rand::Rng;
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
use crate::{Asf64, Dimension, X, Y, Z};
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone)]
|
||||
pub struct Vec3([f64; 3]);
|
||||
|
||||
impl Vec3 {
|
||||
#[inline]
|
||||
pub const fn new(a: f64, b: f64, c: f64) -> Vec3 {
|
||||
Vec3([a, b, c])
|
||||
pub fn new(a: impl Asf64, b: impl Asf64, c: impl Asf64) -> Vec3 {
|
||||
Vec3([a.as_(), b.as_(), c.as_()])
|
||||
}
|
||||
|
||||
pub fn splat(xyz: impl Asf64) -> Self {
|
||||
Self::new(xyz, xyz, xyz)
|
||||
}
|
||||
|
||||
pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Self {
|
||||
Self(rng.gen())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn x(&self) -> f64 {
|
||||
self[0]
|
||||
self.get::<X>()
|
||||
}
|
||||
#[inline]
|
||||
pub fn y(&self) -> f64 {
|
||||
self[1]
|
||||
self.get::<Y>()
|
||||
}
|
||||
#[inline]
|
||||
pub fn z(&self) -> f64 {
|
||||
self[2]
|
||||
self.get::<Z>()
|
||||
}
|
||||
#[inline]
|
||||
pub fn r(&self) -> f64 {
|
||||
self[0]
|
||||
|
||||
pub fn get<D: Dimension>(&self) -> f64 {
|
||||
self.0[D::INDEX]
|
||||
}
|
||||
#[inline]
|
||||
pub fn g(&self) -> f64 {
|
||||
self[1]
|
||||
}
|
||||
#[inline]
|
||||
pub fn b(&self) -> f64 {
|
||||
self[2]
|
||||
|
||||
pub fn set<D: Dimension>(mut self, value: f64) -> Self {
|
||||
self.0[D::INDEX] = value;
|
||||
self
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -45,40 +53,55 @@ impl Vec3 {
|
|||
|
||||
#[inline]
|
||||
pub fn sq_len(&self) -> f64 {
|
||||
self[0] * self[0] + self[1] * self[1] + self[2] * self[2]
|
||||
self.x() * self.x() + self.y() * self.y() + self.z() * self.z()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn dot(&self, v: &Vec3) -> f64 {
|
||||
self[0] * v[0] + self[1] * v[1] + self[2] * v[2]
|
||||
self.x() * v.x() + self.y() * v.y() + self.z() * v.z()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn cross(&self, v: &Vec3) -> Vec3 {
|
||||
Vec3([
|
||||
self[1] * v[2] - self[2] * v[1],
|
||||
self[2] * v[0] - self[0] * v[2],
|
||||
self[0] * v[1] - self[1] * v[0],
|
||||
self.y() * v.z() - self.z() * v.y(),
|
||||
self.z() * v.x() - self.x() * v.z(),
|
||||
self.x() * v.y() - self.y() * v.x(),
|
||||
])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn make_unit_vector(&mut self) {
|
||||
let k = 1.0f64 / (self[0] * self[0] + self[1] * self[1] + self[2] * self[2]);
|
||||
self[0] *= k;
|
||||
self[1] *= k;
|
||||
self[2] *= k;
|
||||
pub fn unit_vector(self) -> Vec3 {
|
||||
self / self.length()
|
||||
}
|
||||
|
||||
pub fn min(self, other: Self) -> Vec3 {
|
||||
Self([
|
||||
self.x().min(other.x()),
|
||||
self.y().min(other.y()),
|
||||
self.z().min(other.z()),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn max(self, other: Self) -> Vec3 {
|
||||
Self([
|
||||
self.x().max(other.x()),
|
||||
self.y().max(other.y()),
|
||||
self.z().max(other.z()),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn min_element(self) -> f64 {
|
||||
self.x().min(self.y()).min(self.z())
|
||||
}
|
||||
|
||||
pub fn max_element(self) -> f64 {
|
||||
self.x().max(self.y()).max(self.z())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn unit_vector(&self) -> Vec3 {
|
||||
let length = self.length();
|
||||
Vec3([self[0] / length, self[1] / length, self[2] / length])
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn random<R: Rng + ?Sized>(rng: &mut R) -> Vec3 {
|
||||
Vec3([rng.gen(), rng.gen(), rng.gen()])
|
||||
pub fn sqrt(self) -> Self {
|
||||
Vec3::new(self.x().sqrt(), self.y().sqrt(), self.z().sqrt())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -86,7 +109,7 @@ impl Add for Vec3 {
|
|||
type Output = Vec3;
|
||||
|
||||
fn add(self, o: Vec3) -> Vec3 {
|
||||
Vec3([self[0] + o[0], self[1] + o[1], self[2] + o[2]])
|
||||
Vec3([self.x() + o.x(), self.y() + o.y(), self.z() + o.z()])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -102,15 +125,15 @@ impl Sub for Vec3 {
|
|||
type Output = Vec3;
|
||||
|
||||
fn sub(self, o: Vec3) -> Vec3 {
|
||||
Vec3([self[0] - o[0], self[1] - o[1], self[2] - o[2]])
|
||||
Vec3([self.x() - o.x(), self.y() - o.y(), self.z() - o.z()])
|
||||
}
|
||||
}
|
||||
|
||||
impl SubAssign for Vec3 {
|
||||
fn sub_assign(&mut self, o: Vec3) {
|
||||
self[0] -= o[0];
|
||||
self[1] -= o[1];
|
||||
self[2] -= o[2];
|
||||
self.0[0] -= o.0[0];
|
||||
self.0[1] -= o.0[1];
|
||||
self.0[2] -= o.0[2];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,37 +141,37 @@ impl Neg for Vec3 {
|
|||
type Output = Vec3;
|
||||
|
||||
fn neg(self) -> Vec3 {
|
||||
Vec3([-self[0], -self[1], -self[2]])
|
||||
Vec3([-self.x(), -self.y(), -self.z()])
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<Vec3> for Vec3 {
|
||||
fn mul_assign(&mut self, o: Vec3) {
|
||||
self[0] *= o[0];
|
||||
self[1] *= o[1];
|
||||
self[2] *= o[2];
|
||||
self.0[0] *= o.0[0];
|
||||
self.0[1] *= o.0[1];
|
||||
self.0[2] *= o.0[2];
|
||||
}
|
||||
}
|
||||
|
||||
impl MulAssign<f64> for Vec3 {
|
||||
fn mul_assign(&mut self, o: f64) {
|
||||
self[0] *= o;
|
||||
self[1] *= o;
|
||||
self[2] *= o;
|
||||
self.0[0] *= o;
|
||||
self.0[1] *= o;
|
||||
self.0[2] *= o;
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Vec3 {
|
||||
type Output = Vec3;
|
||||
fn mul(self, o: f64) -> Vec3 {
|
||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
||||
Vec3([self.x() * o, self.y() * o, self.z() * o])
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vec3> for Vec3 {
|
||||
type Output = Vec3;
|
||||
fn mul(self, o: Vec3) -> Vec3 {
|
||||
Vec3([self[0] * o[0], self[1] * o[1], self[2] * o[2]])
|
||||
Vec3([self.x() * o.x(), self.y() * o.y(), self.z() * o.z()])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -156,7 +179,7 @@ impl Div<Vec3> for Vec3 {
|
|||
type Output = Vec3;
|
||||
|
||||
fn div(self, o: Vec3) -> Vec3 {
|
||||
Vec3([self[0] / o[0], self[1] / o[1], self[2] / o[2]])
|
||||
Vec3([self.x() / o.x(), self.y() / o.y(), self.z() / o.z()])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,7 +188,7 @@ impl Div<f64> for Vec3 {
|
|||
|
||||
fn div(self, o: f64) -> Vec3 {
|
||||
let o = 1.0 / o;
|
||||
Vec3([self[0] * o, self[1] * o, self[2] * o])
|
||||
Vec3([self.x() * o, self.y() * o, self.z() * o])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,6 +217,17 @@ impl IndexMut<usize> for Vec3 {
|
|||
|
||||
impl Display for Vec3 {
|
||||
fn fmt(&self, f: &mut Formatter) -> FmtResult {
|
||||
f.write_fmt(format_args!("{} {} {}", self[0], self[1], self[2]))
|
||||
f.write_fmt(format_args!(
|
||||
"{} {} {}",
|
||||
self.get::<X>(),
|
||||
self.get::<Y>(),
|
||||
self.get::<Z>()
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Asf64, B: Asf64, C: Asf64> From<(A, B, C)> for Vec3 {
|
||||
fn from((x, y, z): (A, B, C)) -> Self {
|
||||
Self::new(x, y, z)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user