Completed the instances section
This commit is contained in:
parent
9d82129891
commit
73e7085818
|
@ -4,20 +4,23 @@ use rand::{prelude::SmallRng, SeedableRng};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Demo, ParallelHit},
|
demos::{Demo, ParallelHit},
|
||||||
hitable::shapes::{Cuboid, RectBuilder},
|
hitable::{
|
||||||
|
shapes::{Cuboid, RectBuilder},
|
||||||
|
Hitable,
|
||||||
|
},
|
||||||
materials::{DiffuseLight, Lambertian, MaterialBuilder},
|
materials::{DiffuseLight, Lambertian, MaterialBuilder},
|
||||||
texture::Solid,
|
texture::Solid,
|
||||||
types::Vec3,
|
types::Vec3,
|
||||||
BvhNode, Camera,
|
BvhNode, Camera,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct CornellBox {}
|
pub struct Instances {}
|
||||||
|
|
||||||
impl Demo for CornellBox {
|
impl Demo for Instances {
|
||||||
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
|
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
|
||||||
|
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"cornell_box"
|
"instances"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn world(&self) -> Self::DemoT {
|
fn world(&self) -> Self::DemoT {
|
||||||
|
@ -76,16 +79,20 @@ impl Demo for CornellBox {
|
||||||
));
|
));
|
||||||
|
|
||||||
// Add the two boxes
|
// Add the two boxes
|
||||||
world.push(Arc::new(Cuboid::new(
|
world.push(Arc::new(
|
||||||
Vec3::new(136.0, 0.0, 65.0),
|
Cuboid::new(
|
||||||
Vec3::new(295.0, 165.0, 230.0),
|
Vec3::new(0.0, 0.0, 0.0),
|
||||||
|
Vec3::new(165.0, 330.0, 165.0),
|
||||||
white,
|
white,
|
||||||
)));
|
)
|
||||||
world.push(Arc::new(Cuboid::new(
|
.rotate_y(15.0)
|
||||||
Vec3::new(265.0, 0.0, 295.0),
|
.translate(Vec3::new(265.0, 0.0, 295.0)),
|
||||||
Vec3::new(430.0, 330.0, 460.0),
|
));
|
||||||
white,
|
world.push(Arc::new(
|
||||||
)));
|
Cuboid::new(Vec3::new(0.0, 0.0, 0.0), Vec3::splat(165.0), white)
|
||||||
|
.rotate_y(-18.0)
|
||||||
|
.translate(Vec3::new(130.0, 0.0, 65.0)),
|
||||||
|
));
|
||||||
|
|
||||||
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
|
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
|
||||||
}
|
}
|
|
@ -13,15 +13,15 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
mod checkered_motion_blur;
|
mod checkered_motion_blur;
|
||||||
mod cornell_box;
|
|
||||||
mod image_texture;
|
mod image_texture;
|
||||||
|
mod instances;
|
||||||
mod perlin_noise_ball;
|
mod perlin_noise_ball;
|
||||||
mod simple_light;
|
mod simple_light;
|
||||||
mod two_spheres;
|
mod two_spheres;
|
||||||
|
|
||||||
pub use checkered_motion_blur::CheckeredMotionBlur;
|
pub use checkered_motion_blur::CheckeredMotionBlur;
|
||||||
pub use cornell_box::CornellBox;
|
|
||||||
pub use image_texture::ImageTextureDemo;
|
pub use image_texture::ImageTextureDemo;
|
||||||
|
pub use instances::Instances;
|
||||||
pub use perlin_noise_ball::PerlinNoiseBall;
|
pub use perlin_noise_ball::PerlinNoiseBall;
|
||||||
pub use simple_light::SimpleLight;
|
pub use simple_light::SimpleLight;
|
||||||
pub use two_spheres::TwoSpheres;
|
pub use two_spheres::TwoSpheres;
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
types::{Ray, Vec3},
|
|
||||||
Aabb, Material,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct HitRecord<'a> {
|
|
||||||
/// Rays are represented by A + t * B
|
|
||||||
/// where A is the source point and B destination point
|
|
||||||
/// by adjusting t we can move forward/back on the ray
|
|
||||||
///
|
|
||||||
/// t is the point at which a ray intersected another object.
|
|
||||||
/// As in, If we put this value of t in A + t * B equation, We'll get the exact
|
|
||||||
/// point at which a ray intersects some other object
|
|
||||||
pub t: f64,
|
|
||||||
/// Ray object otherwise is represented by the Source/Destination points
|
|
||||||
/// p is what we get when we perform the operation, A + t * B
|
|
||||||
/// i.e. A vector from Ray source to the point t
|
|
||||||
pub p: Vec3,
|
|
||||||
|
|
||||||
/// unit outward facing normal
|
|
||||||
pub normal: Vec3,
|
|
||||||
|
|
||||||
/// material if any of the surface
|
|
||||||
pub material: &'a dyn Material,
|
|
||||||
|
|
||||||
/// texture coordinates for an object
|
|
||||||
pub u: f64,
|
|
||||||
pub v: f64,
|
|
||||||
|
|
||||||
pub front_face: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> HitRecord<'a> {
|
|
||||||
pub fn new(
|
|
||||||
t: f64,
|
|
||||||
p: Vec3,
|
|
||||||
normal: Vec3,
|
|
||||||
material: &'a dyn Material,
|
|
||||||
(u, v): (f64, f64),
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
t,
|
|
||||||
p,
|
|
||||||
normal,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Hitable {
|
|
||||||
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord>;
|
|
||||||
|
|
||||||
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Hitable + ?Sized> Hitable for Arc<T> {
|
|
||||||
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
|
||||||
self.as_ref().hit(ray, t_min, t_max)
|
|
||||||
}
|
|
||||||
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
|
|
||||||
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,7 +1,116 @@
|
||||||
pub mod bvh;
|
pub mod bvh;
|
||||||
pub mod hitable;
|
|
||||||
pub mod hitable_list;
|
pub mod hitable_list;
|
||||||
|
mod rotate;
|
||||||
pub mod shapes;
|
pub mod shapes;
|
||||||
|
mod translate;
|
||||||
|
|
||||||
pub use bvh::*;
|
pub use bvh::*;
|
||||||
pub use hitable::*;
|
pub use translate::*;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
hitable::rotate::Rotate,
|
||||||
|
types::{Ray, Vec3},
|
||||||
|
Aabb, Material, X, Y, Z,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct HitRecord<'a> {
|
||||||
|
/// Rays are represented by A + t * B
|
||||||
|
/// where A is the source point and B destination point
|
||||||
|
/// by adjusting t we can move forward/back on the ray
|
||||||
|
///
|
||||||
|
/// t is the point at which a ray intersected another object.
|
||||||
|
/// As in, If we put this value of t in A + t * B equation, We'll get the exact
|
||||||
|
/// point at which a ray intersects some other object
|
||||||
|
pub t: f64,
|
||||||
|
/// Ray object otherwise is represented by the Source/Destination points
|
||||||
|
/// p is what we get when we perform the operation, A + t * B
|
||||||
|
/// i.e. A vector from Ray source to the point t
|
||||||
|
pub p: Vec3,
|
||||||
|
|
||||||
|
/// unit outward facing normal
|
||||||
|
pub normal: Vec3,
|
||||||
|
|
||||||
|
/// material if any of the surface
|
||||||
|
pub material: &'a dyn Material,
|
||||||
|
|
||||||
|
/// texture coordinates for an object
|
||||||
|
pub u: f64,
|
||||||
|
pub v: f64,
|
||||||
|
|
||||||
|
pub front_face: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> HitRecord<'a> {
|
||||||
|
pub fn new(
|
||||||
|
t: f64,
|
||||||
|
p: Vec3,
|
||||||
|
normal: Vec3,
|
||||||
|
material: &'a dyn Material,
|
||||||
|
(u, v): (f64, f64),
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
t,
|
||||||
|
p,
|
||||||
|
normal,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Hitable {
|
||||||
|
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord>;
|
||||||
|
|
||||||
|
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb>;
|
||||||
|
|
||||||
|
fn translate(self, offset: impl Into<Vec3>) -> Translate<Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Translate::new(self, offset.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_x(self, angle: f64) -> Rotate<X, Y, Z, Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Rotate::new(self, angle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_y(self, angle: f64) -> Rotate<Y, X, Z, Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Rotate::new(self, angle)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rotate_z(self, angle: f64) -> Rotate<Z, Y, X, Self>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
Rotate::new(self, angle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Hitable + ?Sized> Hitable for Arc<T> {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
self.as_ref().hit(ray, t_min, t_max)
|
||||||
|
}
|
||||||
|
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
|
||||||
|
self.as_ref().bounding_box(t0, t1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
130
src/hitable/rotate.rs
Normal file
130
src/hitable/rotate.rs
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
|
types::{Ray, Vec3},
|
||||||
|
Aabb, Dimension, X, Y, Z,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Rotate<D1, D2, D3, T: Hitable> {
|
||||||
|
hitable: T,
|
||||||
|
sin_theta: f64,
|
||||||
|
cos_theta: f64,
|
||||||
|
bbox: Option<Aabb>,
|
||||||
|
|
||||||
|
_tag: PhantomData<(D1, D2, D3)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D1, D2, D3, T> Rotate<D1, D2, D3, T>
|
||||||
|
where
|
||||||
|
D1: Dimension,
|
||||||
|
D2: Dimension,
|
||||||
|
D3: Dimension,
|
||||||
|
T: Hitable,
|
||||||
|
{
|
||||||
|
pub fn new(object: T, angle: f64) -> Rotate<D1, D2, D3, T> {
|
||||||
|
let radians = angle.to_radians();
|
||||||
|
let sin_theta = radians.sin();
|
||||||
|
let cos_theta = radians.cos();
|
||||||
|
|
||||||
|
let mut min = Vec3::splat(f64::MAX);
|
||||||
|
let mut max = Vec3::splat(-f64::MAX);
|
||||||
|
|
||||||
|
let bbox = if let Some(bbox) = object.bounding_box(0.0, 1.0) {
|
||||||
|
for i in 0..2 {
|
||||||
|
let i = i as f64;
|
||||||
|
for j in 0..2 {
|
||||||
|
let j = j as f64;
|
||||||
|
for k in 0..2 {
|
||||||
|
let k = k as f64;
|
||||||
|
|
||||||
|
// D1 will be the axis about which we are rotating
|
||||||
|
let d1 = i * bbox.max.get::<D1>() + (1.0 - i) * bbox.min.get::<D1>();
|
||||||
|
|
||||||
|
let d2 = j * bbox.max.get::<D2>() + (1.0 - j) * bbox.min.get::<D2>();
|
||||||
|
let d3 = k * bbox.max.get::<D3>() + (1.0 - k) * bbox.min.get::<D3>();
|
||||||
|
|
||||||
|
let new_d2 = cos_theta * d2 + sin_theta * d3;
|
||||||
|
let new_d3 = -sin_theta * d2 + cos_theta * d3;
|
||||||
|
|
||||||
|
let tester = Vec3::splat(0.0)
|
||||||
|
.set::<D1>(d1)
|
||||||
|
.set::<D2>(new_d2)
|
||||||
|
.set::<D3>(new_d3);
|
||||||
|
|
||||||
|
min = Vec3::min(tester, min);
|
||||||
|
max = Vec3::max(tester, max);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Aabb::new(min, max)
|
||||||
|
} else {
|
||||||
|
Aabb::new(min, max)
|
||||||
|
};
|
||||||
|
|
||||||
|
Rotate {
|
||||||
|
hitable: object,
|
||||||
|
sin_theta,
|
||||||
|
cos_theta,
|
||||||
|
bbox: Some(bbox),
|
||||||
|
_tag: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<D1, D2, D3, T> Hitable for Rotate<D1, D2, D3, T>
|
||||||
|
where
|
||||||
|
D1: Dimension,
|
||||||
|
D2: Dimension,
|
||||||
|
D3: Dimension,
|
||||||
|
T: Hitable,
|
||||||
|
{
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
let origin = ray
|
||||||
|
.origin
|
||||||
|
.set::<D2>(
|
||||||
|
self.cos_theta * ray.origin.get::<D2>() - self.sin_theta * ray.origin.get::<D3>(),
|
||||||
|
)
|
||||||
|
.set::<D3>(
|
||||||
|
self.sin_theta * ray.origin.get::<D2>() + self.cos_theta * ray.origin.get::<D3>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let direction = ray
|
||||||
|
.direction
|
||||||
|
.set::<D2>(
|
||||||
|
self.cos_theta * ray.direction.get::<D2>()
|
||||||
|
- self.sin_theta * ray.direction.get::<D3>(),
|
||||||
|
)
|
||||||
|
.set::<D3>(
|
||||||
|
self.sin_theta * ray.direction.get::<D2>()
|
||||||
|
+ self.cos_theta * ray.direction.get::<D3>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let rotated_ray = Ray::new(origin, direction, ray.time());
|
||||||
|
|
||||||
|
let mut hit = self.hitable.hit(&rotated_ray, t_min, t_max)?;
|
||||||
|
|
||||||
|
hit.p = hit
|
||||||
|
.p
|
||||||
|
.set::<D2>(self.cos_theta * hit.p.get::<D2>() + self.sin_theta * hit.p.get::<D3>())
|
||||||
|
.set::<D3>(-self.sin_theta * hit.p.get::<D2>() + self.cos_theta * hit.p.get::<D3>());
|
||||||
|
|
||||||
|
hit.normal = hit
|
||||||
|
.normal
|
||||||
|
.set::<D2>(
|
||||||
|
self.cos_theta * hit.normal.get::<D2>() + self.sin_theta * hit.normal.get::<D3>(),
|
||||||
|
)
|
||||||
|
.set::<D3>(
|
||||||
|
-self.sin_theta * hit.normal.get::<D2>() + self.cos_theta * hit.normal.get::<D3>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
hit.set_face_normal(&rotated_ray);
|
||||||
|
|
||||||
|
Some(hit)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _t0: f64, _t1: f64) -> Option<Aabb> {
|
||||||
|
self.bbox
|
||||||
|
}
|
||||||
|
}
|
37
src/hitable/translate.rs
Normal file
37
src/hitable/translate.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
use crate::{
|
||||||
|
hitable::{HitRecord, Hitable},
|
||||||
|
types::{Ray, Vec3},
|
||||||
|
Aabb,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Translate<T> {
|
||||||
|
object: T,
|
||||||
|
offset: Vec3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Translate<T> {
|
||||||
|
pub const 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> {
|
||||||
|
self.object
|
||||||
|
.bounding_box(t0, t1)
|
||||||
|
.map(|bbox| Aabb::new(bbox.min + self.offset, bbox.max + self.offset))
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,7 +66,7 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
||||||
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
|
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::CornellBox {};
|
let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::Instances {};
|
||||||
let mut should_update = true;
|
let mut should_update = true;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
@ -104,7 +104,7 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
||||||
should_update = true;
|
should_update = true;
|
||||||
}
|
}
|
||||||
Some(Keycode::Num6) => {
|
Some(Keycode::Num6) => {
|
||||||
active_demo = &demos::CornellBox {};
|
active_demo = &demos::Instances {};
|
||||||
should_update = true;
|
should_update = true;
|
||||||
}
|
}
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
|
|
Loading…
Reference in New Issue
Block a user