Completed the instances section
This commit is contained in:
parent
9d82129891
commit
73e7085818
|
@ -4,20 +4,23 @@ use rand::{prelude::SmallRng, SeedableRng};
|
|||
|
||||
use crate::{
|
||||
demos::{Demo, ParallelHit},
|
||||
hitable::shapes::{Cuboid, RectBuilder},
|
||||
hitable::{
|
||||
shapes::{Cuboid, RectBuilder},
|
||||
Hitable,
|
||||
},
|
||||
materials::{DiffuseLight, Lambertian, MaterialBuilder},
|
||||
texture::Solid,
|
||||
types::Vec3,
|
||||
BvhNode, Camera,
|
||||
};
|
||||
|
||||
pub struct CornellBox {}
|
||||
pub struct Instances {}
|
||||
|
||||
impl Demo for CornellBox {
|
||||
impl Demo for Instances {
|
||||
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
|
||||
|
||||
fn name(&self) -> &'static str {
|
||||
"cornell_box"
|
||||
"instances"
|
||||
}
|
||||
|
||||
fn world(&self) -> Self::DemoT {
|
||||
|
@ -76,16 +79,20 @@ impl Demo for CornellBox {
|
|||
));
|
||||
|
||||
// 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),
|
||||
world.push(Arc::new(
|
||||
Cuboid::new(
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
Vec3::new(165.0, 330.0, 165.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,
|
||||
)));
|
||||
)
|
||||
.rotate_y(15.0)
|
||||
.translate(Vec3::new(265.0, 0.0, 295.0)),
|
||||
));
|
||||
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)
|
||||
}
|
|
@ -13,15 +13,15 @@ use std::{
|
|||
};
|
||||
|
||||
mod checkered_motion_blur;
|
||||
mod cornell_box;
|
||||
mod image_texture;
|
||||
mod instances;
|
||||
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 instances::Instances;
|
||||
pub use perlin_noise_ball::PerlinNoiseBall;
|
||||
pub use simple_light::SimpleLight;
|
||||
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 hitable;
|
||||
pub mod hitable_list;
|
||||
mod rotate;
|
||||
pub mod shapes;
|
||||
mod translate;
|
||||
|
||||
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)
|
||||
.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;
|
||||
|
||||
loop {
|
||||
|
@ -104,7 +104,7 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
|
|||
should_update = true;
|
||||
}
|
||||
Some(Keycode::Num6) => {
|
||||
active_demo = &demos::CornellBox {};
|
||||
active_demo = &demos::Instances {};
|
||||
should_update = true;
|
||||
}
|
||||
None => unreachable!(),
|
||||
|
|
Loading…
Reference in New Issue
Block a user