Add translated and rotated cuboids to cornell box scene.

Chose name 'cuboid' because 'box' is a module imported by the prelude in
rust and makes things complicated.
This commit is contained in:
Bill Thiede 2018-09-25 21:00:05 -07:00
parent f1fcbe7449
commit 7684bb2088
6 changed files with 264 additions and 0 deletions

90
rtiow/src/cuboid.rs Normal file
View File

@ -0,0 +1,90 @@
use std::sync::Arc;
use aabb::AABB;
use flip_normals::FlipNormals;
use hitable::Hit;
use hitable::HitRecord;
use hitable_list::HitableList;
use material::Material;
use ray::Ray;
use rect::XYRect;
use rect::XZRect;
use rect::YZRect;
use vec3::Vec3;
pub struct Cuboid {
p_min: Vec3,
p_max: Vec3,
walls: HitableList,
}
impl Cuboid {
pub fn new(p_min: Vec3, p_max: Vec3, material: Box<Material>) -> Cuboid {
let material = Arc::new(material);
Cuboid {
p_min,
p_max,
walls: HitableList::new(vec![
Box::new(XYRect::new(
p_min.x,
p_max.x,
p_min.y,
p_max.y,
p_max.z,
Box::new(Arc::clone(&material)),
)),
Box::new(FlipNormals::new(Box::new(XYRect::new(
p_min.x,
p_max.x,
p_min.y,
p_max.y,
p_min.z,
Box::new(Arc::clone(&material)),
)))),
Box::new(XZRect::new(
p_min.x,
p_max.x,
p_min.z,
p_max.z,
p_max.y,
Box::new(Arc::clone(&material)),
)),
Box::new(FlipNormals::new(Box::new(XZRect::new(
p_min.x,
p_max.x,
p_min.z,
p_max.z,
p_min.y,
Box::new(Arc::clone(&material)),
)))),
Box::new(YZRect::new(
p_min.y,
p_max.y,
p_min.z,
p_max.z,
p_max.x,
Box::new(Arc::clone(&material)),
)),
Box::new(FlipNormals::new(Box::new(YZRect::new(
p_min.y,
p_max.y,
p_min.z,
p_max.z,
p_min.x,
Box::new(Arc::clone(&material)),
)))),
]),
}
}
}
impl Hit for Cuboid {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
self.walls.hit(r, t_min, t_max)
}
fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option<AABB> {
Some(AABB::new(self.p_min, self.p_max))
}
}

View File

@ -1,6 +1,7 @@
pub mod aabb;
pub mod bvh;
pub mod camera;
pub mod cuboid;
pub mod flip_normals;
pub mod hitable;
pub mod hitable_list;
@ -11,9 +12,11 @@ pub mod perlin;
pub mod ray;
pub mod rect;
pub mod renderer;
pub mod rotate;
pub mod scenes;
pub mod sphere;
pub mod texture;
pub mod translate;
pub mod vec3;
extern crate crossbeam_channel;

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
extern crate rand;
use self::rand::Rng;
@ -36,6 +38,15 @@ pub trait Material: Send + Sync {
}
}
impl Material for Arc<Box<Material>> {
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
(**self).scatter(r_in, rec)
}
fn emitted(&self, u: f32, v: f32, p: Vec3) -> Vec3 {
(**self).emitted(u, v, p)
}
}
pub struct Lambertian {
// TODO(wathiede): implement texture sharing via references
albedo: Box<Texture>,

90
rtiow/src/rotate.rs Normal file
View File

@ -0,0 +1,90 @@
use std::f32::consts::PI;
use std::f32::MAX;
use std::f32::MIN;
use aabb::AABB;
use hitable::Hit;
use hitable::HitRecord;
use ray::Ray;
use vec3::Vec3;
pub struct RotateY {
hitable: Box<Hit>,
sin_theta: f32,
cos_theta: f32,
bbox: Option<AABB>,
}
impl RotateY {
pub fn new(hitable: Box<Hit>, angle: f32) -> RotateY {
let radians = PI / 180. * angle;
let sin_theta = radians.sin();
let cos_theta = radians.cos();
let mut min = vec![MAX, MAX, MAX];
let mut max = vec![MIN, MIN, MIN];
let bbox = hitable.bounding_box(0., 1.).unwrap();
for i in 0..2 {
for j in 0..2 {
for k in 0..2 {
let x = i as f32 * bbox.max().x + (1 - i) as f32 * bbox.min().x;
let y = j as f32 * bbox.max().y + (1 - j) as f32 * bbox.min().y;
let z = k as f32 * bbox.max().z + (1 - k) as f32 * bbox.min().z;
let new_x = cos_theta * x + sin_theta * z;
let new_z = -sin_theta * x + cos_theta * z;
let tester = Vec3::new(new_x, y, new_z);
for c in 0..3 {
if tester[c] > max[c] {
max[c] = tester[c];
}
if tester[c] < min[c] {
min[c] = tester[c];
}
}
}
}
}
RotateY {
hitable,
sin_theta,
cos_theta,
bbox: Some(AABB::new(
Vec3::new(min[0], min[1], min[2]),
Vec3::new(max[0], max[1], max[2]),
)),
}
}
}
impl Hit for RotateY {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let origin = Vec3::new(
self.cos_theta * r.origin[0] - self.sin_theta * r.origin[2],
r.origin.y,
self.sin_theta * r.origin[0] + self.cos_theta * r.origin[2],
);
let direction = Vec3::new(
self.cos_theta * r.direction[0] - self.sin_theta * r.direction[2],
r.direction.y,
self.sin_theta * r.direction[0] + self.cos_theta * r.direction[2],
);
let rotated_r = Ray::new(origin, direction, r.time);
if let Some(rec) = self.hitable.hit(rotated_r, t_min, t_max) {
let p = Vec3::new(
self.cos_theta * rec.p[0] + self.sin_theta * rec.p[2],
rec.p[1],
-self.sin_theta * rec.p[0] + self.cos_theta * rec.p[2],
);
let normal = Vec3::new(
self.cos_theta * rec.normal[0] + self.sin_theta * rec.normal[2],
rec.normal[1],
-self.sin_theta * rec.normal[0] + self.cos_theta * rec.normal[2],
);
return Some(HitRecord { p, normal, ..rec });
}
None
}
fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option<AABB> {
self.bbox.clone()
}
}

View File

@ -1,4 +1,5 @@
use camera::Camera;
use cuboid::Cuboid;
use flip_normals::FlipNormals;
use hitable::Hit;
use hitable_list::HitableList;
@ -10,7 +11,9 @@ use rect::XZRect;
use rect::YZRect;
use renderer::Opt;
use renderer::Scene;
use rotate::RotateY;
use texture::ConstantTexture;
use translate::Translate;
use vec3::Vec3;
pub fn new(opt: &Opt) -> Scene {
@ -33,6 +36,34 @@ pub fn new(opt: &Opt) -> Scene {
);
let objects: Vec<Box<Hit>> = vec![
// Box1
Box::new(Translate::new(
Box::new(RotateY::new(
Box::new(Cuboid::new(
Vec3::new(0., 0., 0.),
Vec3::new(165., 165., 165.),
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new(
0.73, 0.73, 0.73,
))))),
)),
-18.,
)),
Vec3::new(100., 0., 0.),
)),
// Box2
Box::new(Translate::new(
Box::new(RotateY::new(
Box::new(Cuboid::new(
Vec3::new(0., 0., 0.),
Vec3::new(165., 330., 165.),
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new(
0.73, 0.73, 0.73,
))))),
)),
15.,
)),
Vec3::new(265., 0., 295.),
)),
// Green wall left
Box::new(FlipNormals::new(Box::new(YZRect::new(
0.,

39
rtiow/src/translate.rs Normal file
View File

@ -0,0 +1,39 @@
use aabb::AABB;
use hitable::Hit;
use hitable::HitRecord;
use ray::Ray;
use vec3::Vec3;
pub struct Translate {
hitable: Box<Hit>,
offset: Vec3,
}
impl Translate {
pub fn new(hitable: Box<Hit>, offset: Vec3) -> Translate {
Translate { hitable, offset }
}
}
impl Hit for Translate {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let moved_r = Ray::new(r.origin - self.offset, r.direction, r.time);
if let Some(rec) = self.hitable.hit(moved_r, t_min, t_max) {
return Some(HitRecord {
p: rec.p + self.offset,
..rec
});
}
None
}
fn bounding_box(&self, t_min: f32, t_max: f32) -> Option<AABB> {
if let Some(bbox) = self.hitable.bounding_box(t_min, t_max) {
return Some(AABB::new(
bbox.min() + self.offset,
bbox.max() + self.offset,
));
}
None
}
}