De-boxed many uses of Hit and Material.

Use of generic parameter directly where possible in structures instead
of Box'd values.
Added Material implementations for Box<Material> and Arc<Material> to
aid in the automatic conversion when necessary to use a Sized value for
Material.
Implement From trait for [f32;3] to Vec3 to make some APIs Into<Vec3>
which is a bit nicer to use.
This commit is contained in:
Bill Thiede 2018-10-03 20:53:37 -07:00
parent 7684bb2088
commit 36b2fba5b7
18 changed files with 303 additions and 205 deletions

View File

@ -246,14 +246,12 @@ mod tests {
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., 0., 0.), Vec3::new(0., 0., 0.),
0.5, 0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.1, 0.2, 0.5))),
0.1, 0.2, 0.5,
))))),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(1., 0., 0.), Vec3::new(1., 0., 0.),
0.5, 0.5,
Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)), Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2),
)), )),
], ],
0., 0.,
@ -274,19 +272,17 @@ mod tests {
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., 0., 0.), Vec3::new(0., 0., 0.),
0.5, 0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.1, 0.2, 0.5))),
0.1, 0.2, 0.5,
))))),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(1., 0., 0.), Vec3::new(1., 0., 0.),
0.5, 0.5,
Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)), Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., 1., 0.), Vec3::new(0., 1., 0.),
0.5, 0.5,
Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)), Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2),
)), )),
], ],
0., 0.,

View File

@ -19,9 +19,7 @@ pub struct Cuboid {
} }
impl Cuboid { impl Cuboid {
pub fn new(p_min: Vec3, p_max: Vec3, material: Box<Material>) -> Cuboid { pub fn new(p_min: Vec3, p_max: Vec3, material: Arc<Material>) -> Cuboid {
let material = Arc::new(material);
Cuboid { Cuboid {
p_min, p_min,
p_max, p_max,
@ -32,48 +30,48 @@ impl Cuboid {
p_min.y, p_min.y,
p_max.y, p_max.y,
p_max.z, p_max.z,
Box::new(Arc::clone(&material)), Arc::clone(&material),
)), )),
Box::new(FlipNormals::new(Box::new(XYRect::new( Box::new(FlipNormals::new(XYRect::new(
p_min.x, p_min.x,
p_max.x, p_max.x,
p_min.y, p_min.y,
p_max.y, p_max.y,
p_min.z, p_min.z,
Box::new(Arc::clone(&material)), Arc::clone(&material),
)))), ))),
Box::new(XZRect::new( Box::new(XZRect::new(
p_min.x, p_min.x,
p_max.x, p_max.x,
p_min.z, p_min.z,
p_max.z, p_max.z,
p_max.y, p_max.y,
Box::new(Arc::clone(&material)), Arc::clone(&material),
)), )),
Box::new(FlipNormals::new(Box::new(XZRect::new( Box::new(FlipNormals::new(XZRect::new(
p_min.x, p_min.x,
p_max.x, p_max.x,
p_min.z, p_min.z,
p_max.z, p_max.z,
p_min.y, p_min.y,
Box::new(Arc::clone(&material)), Arc::clone(&material),
)))), ))),
Box::new(YZRect::new( Box::new(YZRect::new(
p_min.y, p_min.y,
p_max.y, p_max.y,
p_min.z, p_min.z,
p_max.z, p_max.z,
p_max.x, p_max.x,
Box::new(Arc::clone(&material)), Arc::clone(&material),
)), )),
Box::new(FlipNormals::new(Box::new(YZRect::new( Box::new(FlipNormals::new(YZRect::new(
p_min.y, p_min.y,
p_max.y, p_max.y,
p_min.z, p_min.z,
p_max.z, p_max.z,
p_min.x, p_min.x,
Box::new(Arc::clone(&material)), Arc::clone(&material),
)))), ))),
]), ]),
} }
} }

View File

@ -3,17 +3,26 @@ use hitable::Hit;
use hitable::HitRecord; use hitable::HitRecord;
use ray::Ray; use ray::Ray;
pub struct FlipNormals { pub struct FlipNormals<H>
hitable: Box<Hit>, where
H: Hit,
{
hitable: H,
} }
impl FlipNormals { impl<H> FlipNormals<H>
pub fn new(hitable: Box<Hit>) -> FlipNormals { where
H: Hit,
{
pub fn new(hitable: H) -> FlipNormals<H> {
FlipNormals { hitable } FlipNormals { hitable }
} }
} }
impl Hit for FlipNormals { impl<H> Hit for FlipNormals<H>
where
H: Hit,
{
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
if let Some(rec) = self.hitable.hit(r, t_min, t_max) { if let Some(rec) = self.hitable.hit(r, t_min, t_max) {
return Some(HitRecord { return Some(HitRecord {

View File

@ -38,7 +38,7 @@ pub trait Material: Send + Sync {
} }
} }
impl Material for Arc<Box<Material>> { impl Material for Arc<Material> {
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
(**self).scatter(r_in, rec) (**self).scatter(r_in, rec)
} }
@ -47,18 +47,36 @@ impl Material for Arc<Box<Material>> {
} }
} }
pub struct Lambertian { impl Material for Box<Material> {
// TODO(wathiede): implement texture sharing via references fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
albedo: Box<Texture>, (**self).scatter(r_in, rec)
}
fn emitted(&self, u: f32, v: f32, p: Vec3) -> Vec3 {
(**self).emitted(u, v, p)
}
} }
impl Lambertian { pub struct Lambertian<T>
pub fn new(texture: Box<Texture>) -> Lambertian { where
T: Texture,
{
// TODO(wathiede): implement texture sharing via references
albedo: T,
}
impl<T> Lambertian<T>
where
T: Texture,
{
pub fn new(texture: T) -> Lambertian<T> {
Lambertian { albedo: texture } Lambertian { albedo: texture }
} }
} }
impl Material for Lambertian { impl<T> Material for Lambertian<T>
where
T: Texture,
{
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse {
let target = rec.p + rec.normal + random_in_unit_sphere(); let target = rec.p + rec.normal + random_in_unit_sphere();
let (u, v) = rec.uv; let (u, v) = rec.uv;
@ -164,17 +182,26 @@ impl Material for Dielectric {
} }
} }
pub struct DiffuseLight { pub struct DiffuseLight<T>
emit: Box<Texture>, where
T: Texture,
{
emit: T,
} }
impl DiffuseLight { impl<T> DiffuseLight<T>
pub fn new(emit: Box<Texture>) -> DiffuseLight { where
T: Texture,
{
pub fn new(emit: T) -> DiffuseLight<T> {
DiffuseLight { emit } DiffuseLight { emit }
} }
} }
impl Material for DiffuseLight { impl<T> Material for DiffuseLight<T>
where
T: Texture,
{
fn scatter(&self, _r_in: &Ray, _rec: &HitRecord) -> ScatterResponse { fn scatter(&self, _r_in: &Ray, _rec: &HitRecord) -> ScatterResponse {
ScatterResponse { ScatterResponse {
scattered: Default::default(), scattered: Default::default(),

View File

@ -8,24 +8,30 @@ use sphere::get_sphere_uv;
use vec3::dot; use vec3::dot;
use vec3::Vec3; use vec3::Vec3;
pub struct MovingSphere { pub struct MovingSphere<M>
where
M: Material,
{
center0: Vec3, center0: Vec3,
center1: Vec3, center1: Vec3,
radius: f32, radius: f32,
material: Box<Material>, material: M,
time0: f32, time0: f32,
time1: f32, time1: f32,
} }
impl MovingSphere { impl<M> MovingSphere<M>
where
M: Material,
{
pub fn new( pub fn new(
center0: Vec3, center0: Vec3,
center1: Vec3, center1: Vec3,
radius: f32, radius: f32,
time0: f32, time0: f32,
time1: f32, time1: f32,
material: Box<Material>, material: M,
) -> MovingSphere { ) -> MovingSphere<M> {
MovingSphere { MovingSphere {
center0, center0,
center1, center1,
@ -41,7 +47,10 @@ impl MovingSphere {
} }
} }
impl Hit for MovingSphere { impl<M> Hit for MovingSphere<M>
where
M: Material,
{
fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option<HitRecord> { fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option<HitRecord> {
let oc = r.origin - self.center(r.time); let oc = r.origin - self.center(r.time);
let a = dot(r.direction, r.direction); let a = dot(r.direction, r.direction);
@ -58,7 +67,7 @@ impl Hit for MovingSphere {
uv, uv,
p: point, p: point,
normal: (point - self.center(r.time)) / self.radius, normal: (point - self.center(r.time)) / self.radius,
material: &*self.material, material: &self.material,
}); });
} }
let temp = (-b + (b * b - a * c).sqrt()) / a; let temp = (-b + (b * b - a * c).sqrt()) / a;
@ -70,7 +79,7 @@ impl Hit for MovingSphere {
uv, uv,
p: point, p: point,
normal: (point - self.center(r.time)) / self.radius, normal: (point - self.center(r.time)) / self.radius,
material: &*self.material, material: &self.material,
}); });
} }
} }

View File

@ -5,17 +5,23 @@ use material::Material;
use ray::Ray; use ray::Ray;
use vec3::Vec3; use vec3::Vec3;
pub struct XYRect { pub struct XYRect<M>
where
M: Material,
{
x0: f32, x0: f32,
x1: f32, x1: f32,
y0: f32, y0: f32,
y1: f32, y1: f32,
k: f32, k: f32,
material: Box<Material>, material: M,
} }
impl XYRect { impl<M> XYRect<M>
pub fn new(x0: f32, x1: f32, y0: f32, y1: f32, k: f32, material: Box<Material>) -> XYRect { where
M: Material,
{
pub fn new(x0: f32, x1: f32, y0: f32, y1: f32, k: f32, material: M) -> XYRect<M> {
XYRect { XYRect {
x0, x0,
x1, x1,
@ -27,7 +33,10 @@ impl XYRect {
} }
} }
impl Hit for XYRect { impl<M> Hit for XYRect<M>
where
M: Material,
{
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let t = (self.k - r.origin.z) / r.direction.z; let t = (self.k - r.origin.z) / r.direction.z;
if t < t_min || t > t_max { if t < t_min || t > t_max {
@ -45,7 +54,7 @@ impl Hit for XYRect {
uv: (u, v), uv: (u, v),
p: r.point_at_parameter(t), p: r.point_at_parameter(t),
normal: Vec3::new(0., 0., 1.), normal: Vec3::new(0., 0., 1.),
material: &*self.material, material: &self.material,
}) })
} }
@ -57,17 +66,23 @@ impl Hit for XYRect {
} }
} }
pub struct XZRect { pub struct XZRect<M>
where
M: Material,
{
x0: f32, x0: f32,
x1: f32, x1: f32,
z0: f32, z0: f32,
z1: f32, z1: f32,
k: f32, k: f32,
material: Box<Material>, material: M,
} }
impl XZRect { impl<M> XZRect<M>
pub fn new(x0: f32, x1: f32, z0: f32, z1: f32, k: f32, material: Box<Material>) -> XZRect { where
M: Material,
{
pub fn new(x0: f32, x1: f32, z0: f32, z1: f32, k: f32, material: M) -> XZRect<M> {
XZRect { XZRect {
x0, x0,
x1, x1,
@ -79,7 +94,10 @@ impl XZRect {
} }
} }
impl Hit for XZRect { impl<M> Hit for XZRect<M>
where
M: Material,
{
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let t = (self.k - r.origin.y) / r.direction.y; let t = (self.k - r.origin.y) / r.direction.y;
if t < t_min || t > t_max { if t < t_min || t > t_max {
@ -97,7 +115,7 @@ impl Hit for XZRect {
uv: (u, v), uv: (u, v),
p: r.point_at_parameter(t), p: r.point_at_parameter(t),
normal: Vec3::new(0., 1., 0.), normal: Vec3::new(0., 1., 0.),
material: &*self.material, material: &self.material,
}) })
} }
@ -109,17 +127,23 @@ impl Hit for XZRect {
} }
} }
pub struct YZRect { pub struct YZRect<M>
where
M: Material,
{
y0: f32, y0: f32,
y1: f32, y1: f32,
k: f32, k: f32,
z0: f32, z0: f32,
z1: f32, z1: f32,
material: Box<Material>, material: M,
} }
impl YZRect { impl<M> YZRect<M>
pub fn new(y0: f32, y1: f32, z0: f32, z1: f32, k: f32, material: Box<Material>) -> YZRect { where
M: Material,
{
pub fn new(y0: f32, y1: f32, z0: f32, z1: f32, k: f32, material: M) -> YZRect<M> {
YZRect { YZRect {
y0, y0,
y1, y1,
@ -131,7 +155,10 @@ impl YZRect {
} }
} }
impl Hit for YZRect { impl<M> Hit for YZRect<M>
where
M: Material,
{
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let t = (self.k - r.origin.x) / r.direction.x; let t = (self.k - r.origin.x) / r.direction.x;
if t < t_min || t > t_max { if t < t_min || t > t_max {
@ -149,7 +176,7 @@ impl Hit for YZRect {
uv: (u, v), uv: (u, v),
p: r.point_at_parameter(t), p: r.point_at_parameter(t),
normal: Vec3::new(1., 0., 0.), normal: Vec3::new(1., 0., 0.),
material: &*self.material, material: &self.material,
}) })
} }

View File

@ -79,7 +79,7 @@ pub struct Opt {
#[structopt(short = "s", long = "subsample", default_value = "8")] #[structopt(short = "s", long = "subsample", default_value = "8")]
pub subsamples: usize, pub subsamples: usize,
/// Select scene to render, one of: "bench", "book", "tutorial", "bvh", "test", "cornell_box" /// Select scene to render, one of: "bench", "book", "tutorial", "bvh", "test", "cornell_box"
#[structopt(long = "model", default_value = "cornell_box")] #[structopt(long = "model", default_value = "book")]
pub model: Model, pub model: Model,
/// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof /// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof
#[structopt(long = "pprof", parse(from_os_str))] #[structopt(long = "pprof", parse(from_os_str))]

View File

@ -8,15 +8,21 @@ use hitable::HitRecord;
use ray::Ray; use ray::Ray;
use vec3::Vec3; use vec3::Vec3;
pub struct RotateY { pub struct RotateY<H>
hitable: Box<Hit>, where
H: Hit,
{
hitable: H,
sin_theta: f32, sin_theta: f32,
cos_theta: f32, cos_theta: f32,
bbox: Option<AABB>, bbox: Option<AABB>,
} }
impl RotateY { impl<H> RotateY<H>
pub fn new(hitable: Box<Hit>, angle: f32) -> RotateY { where
H: Hit,
{
pub fn new(hitable: H, angle: f32) -> RotateY<H> {
let radians = PI / 180. * angle; let radians = PI / 180. * angle;
let sin_theta = radians.sin(); let sin_theta = radians.sin();
let cos_theta = radians.cos(); let cos_theta = radians.cos();
@ -55,7 +61,10 @@ impl RotateY {
} }
} }
impl Hit for RotateY { impl<H> Hit for RotateY<H>
where
H: Hit,
{
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let origin = Vec3::new( let origin = Vec3::new(
self.cos_theta * r.origin[0] - self.sin_theta * r.origin[2], self.cos_theta * r.origin[0] - self.sin_theta * r.origin[2],

View File

@ -46,9 +46,7 @@ pub fn new(opt: &Opt) -> Scene {
grid.push(Box::new(Sphere::new( grid.push(Box::new(Sphere::new(
Vec3::new(x_pos, 0., z_pos), Vec3::new(x_pos, 0., z_pos),
0.5, 0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(r, g, b))),
r, g, b,
))))),
))); )));
} }
} }

View File

@ -7,13 +7,13 @@ use hitable_list::HitableList;
use kdtree::KDTree; use kdtree::KDTree;
use material::Dielectric; use material::Dielectric;
use material::Lambertian; use material::Lambertian;
use material::Material;
use material::Metal; use material::Metal;
use renderer::Opt; use renderer::Opt;
use renderer::Scene; use renderer::Scene;
use sphere::Sphere; use sphere::Sphere;
use texture::CheckerTexture; use texture::CheckerTexture;
use texture::ConstantTexture; use texture::ConstantTexture;
use texture::Texture;
use vec3::Vec3; use vec3::Vec3;
pub fn new(opt: &Opt) -> Scene { pub fn new(opt: &Opt) -> Scene {
@ -35,9 +35,9 @@ pub fn new(opt: &Opt) -> Scene {
time_max, time_max,
); );
let ground_color = if opt.use_accel { let ground_color = if opt.use_accel {
Vec3::new(1.0, 0.4, 0.4) [1.0, 0.4, 0.4]
} else { } else {
Vec3::new(0.4, 1.0, 0.4) [0.4, 1.0, 0.4]
}; };
let world: Box<Hit> = if opt.use_accel { let world: Box<Hit> = if opt.use_accel {
Box::new(KDTree::new(random_scene(ground_color), time_min, time_max)) Box::new(KDTree::new(random_scene(ground_color), time_min, time_max))
@ -54,22 +54,25 @@ pub fn new(opt: &Opt) -> Scene {
} }
} }
fn random_scene(ground_color: Vec3) -> Vec<Box<Hit>> { fn random_scene<V>(ground_color: V) -> Vec<Box<Hit>>
where
V: Into<Vec3>,
{
let mut rng = rand::thread_rng(); let mut rng = rand::thread_rng();
let checker = true; let checker = true;
let ground_texture: Box<Texture> = if checker { let ground_material: Box<Material> = if checker {
Box::new(CheckerTexture::new( Box::new(Lambertian::new(CheckerTexture::new(
Box::new(ConstantTexture::new(Vec3::new(0., 0., 0.))), ConstantTexture::new([0., 0., 0.]),
Box::new(ConstantTexture::new(ground_color)), ConstantTexture::new(ground_color),
)) )))
} else { } else {
Box::new(ConstantTexture::new(ground_color)) Box::new(Lambertian::new(ConstantTexture::new(ground_color)))
}; };
let mut objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new( let mut objects: Vec<Box<Hit>> = vec![Box::new(Sphere::new(
Vec3::new(0., -1000., 0.), [0., -1000., 0.],
1000., 1000.,
Box::new(Lambertian::new(ground_texture)), ground_material,
))]; ))];
let mut random = || rng.gen_range::<f32>(0., 1.); let mut random = || rng.gen_range::<f32>(0., 1.);
@ -78,34 +81,34 @@ fn random_scene(ground_color: Vec3) -> Vec<Box<Hit>> {
let choose_mat = random(); let choose_mat = random();
let center = Vec3::new(a as f32 + 0.9 * random(), 0.2, b as f32 + 0.9 * random()); let center = Vec3::new(a as f32 + 0.9 * random(), 0.2, b as f32 + 0.9 * random());
if (center - Vec3::new(4., 0.2, 0.)).length() > 0.9 { if (center - Vec3::new(4., 0.2, 0.)).length() > 0.9 {
let sphere = if choose_mat < 0.8 { let sphere: Box<Hit> = if choose_mat < 0.8 {
// diffuse // diffuse
Box::new(Sphere::new( Box::new(Sphere::new(
center, center,
0.2, 0.2,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new([
random() * random(), random() * random(),
random() * random(), random() * random(),
random() * random(), random() * random(),
))))), ])),
)) ))
} else if choose_mat < 0.95 { } else if choose_mat < 0.95 {
// metal // metal
Box::new(Sphere::new( Box::new(Sphere::new(
center, center,
0.2, 0.2,
Box::new(Metal::new( Metal::new(
Vec3::new( Vec3::new(
0.5 * (1. + random()), 0.5 * (1. + random()),
0.5 * (1. + random()), 0.5 * (1. + random()),
0.5 * (1. + random()), 0.5 * (1. + random()),
), ),
0.5 * random(), 0.5 * random(),
)), ),
)) ))
} else { } else {
// glass // glass
Box::new(Sphere::new(center, 0.2, Box::new(Dielectric::new(1.5)))) Box::new(Sphere::new(center, 0.2, Dielectric::new(1.5)))
}; };
objects.push(sphere); objects.push(sphere);
}; };
@ -116,19 +119,17 @@ fn random_scene(ground_color: Vec3) -> Vec<Box<Hit>> {
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., 1., 0.), Vec3::new(0., 1., 0.),
1.0, 1.0,
Box::new(Dielectric::new(1.5)), Dielectric::new(1.5),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(-4., 1., 0.), Vec3::new(-4., 1., 0.),
1.0, 1.0,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.4, 0.2, 0.1))),
0.4, 0.2, 0.1,
))))),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(4., 1., 0.), Vec3::new(4., 1., 0.),
1.0, 1.0,
Box::new(Metal::new(Vec3::new(0.7, 0.6, 0.5), 0.0)), Metal::new(Vec3::new(0.7, 0.6, 0.5), 0.0),
)), )),
]; ];
objects.extend(more); objects.extend(more);

View File

@ -33,21 +33,17 @@ pub fn new(opt: &Opt) -> Scene {
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., 0., -1.), Vec3::new(0., 0., -1.),
0.5, 0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.1, 0.2, 0.5))),
0.1, 0.2, 0.5,
))))),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., -100.5, -1.), Vec3::new(0., -100.5, -1.),
100., 100.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.8, 0.8, 0.8))),
0.8, 0.8, 0.8,
))))),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(1., 0., -1.), Vec3::new(1., 0., -1.),
0.5, 0.5,
Box::new(Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2)), Metal::new(Vec3::new(0.6, 0.6, 0.6), 0.2),
)), )),
Box::new(MovingSphere::new( Box::new(MovingSphere::new(
Vec3::new(-1., 0., -1.25), Vec3::new(-1., 0., -1.25),
@ -55,9 +51,7 @@ pub fn new(opt: &Opt) -> Scene {
0.5, 0.5,
time_min, time_min,
time_max, time_max,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.2, 0.8, 0.2))),
0.2, 0.8, 0.2,
))))),
)), )),
], ],
time_min, time_min,

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use camera::Camera; use camera::Camera;
use cuboid::Cuboid; use cuboid::Cuboid;
use flip_normals::FlipNormals; use flip_normals::FlipNormals;
@ -38,43 +40,41 @@ pub fn new(opt: &Opt) -> Scene {
let objects: Vec<Box<Hit>> = vec![ let objects: Vec<Box<Hit>> = vec![
// Box1 // Box1
Box::new(Translate::new( Box::new(Translate::new(
Box::new(RotateY::new( RotateY::new(
Box::new(Cuboid::new( Cuboid::new(
Vec3::new(0., 0., 0.), Vec3::new(0., 0., 0.),
Vec3::new(165., 165., 165.), Vec3::new(165., 165., 165.),
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Arc::new(Lambertian::new(ConstantTexture::new(Vec3::new(
0.73, 0.73, 0.73, 0.73, 0.73, 0.73,
))))), )))),
)), ),
-18., -18.,
)), ),
Vec3::new(100., 0., 0.), Vec3::new(100., 0., 0.),
)), )),
// Box2 // Box2
Box::new(Translate::new( Box::new(Translate::new(
Box::new(RotateY::new( RotateY::new(
Box::new(Cuboid::new( Cuboid::new(
Vec3::new(0., 0., 0.), Vec3::new(0., 0., 0.),
Vec3::new(165., 330., 165.), Vec3::new(165., 330., 165.),
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Arc::new(Lambertian::new(ConstantTexture::new(Vec3::new(
0.73, 0.73, 0.73, 0.73, 0.73, 0.73,
))))), )))),
)), ),
15., 15.,
)), ),
Vec3::new(265., 0., 295.), Vec3::new(265., 0., 295.),
)), )),
// Green wall left // Green wall left
Box::new(FlipNormals::new(Box::new(YZRect::new( Box::new(FlipNormals::new(YZRect::new(
0., 0.,
555., 555.,
0., 0.,
555., 555.,
555., 555.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.12, 0.45, 0.15))),
0.12, 0.45, 0.15, ))),
))))),
)))),
// Red floor right // Red floor right
Box::new(YZRect::new( Box::new(YZRect::new(
0., 0.,
@ -82,9 +82,7 @@ pub fn new(opt: &Opt) -> Scene {
0., 0.,
555., 555.,
0., 0.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.65, 0.05, 0.05))),
0.65, 0.05, 0.05,
))))),
)), )),
// Light in ceiling // Light in ceiling
Box::new(XZRect::new( Box::new(XZRect::new(
@ -93,21 +91,17 @@ pub fn new(opt: &Opt) -> Scene {
227., 227.,
332., 332.,
554., 554.,
Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( DiffuseLight::new(ConstantTexture::new(Vec3::new(15., 15., 15.))),
Vec3::new(15., 15., 15.),
)))),
)), )),
// Grey ceiling // Grey ceiling
Box::new(FlipNormals::new(Box::new(XZRect::new( Box::new(FlipNormals::new(XZRect::new(
0., 0.,
555., 555.,
0., 0.,
555., 555.,
555., 555.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.73, 0.73, 0.73))),
0.73, 0.73, 0.73, ))),
))))),
)))),
// Grey floor // Grey floor
Box::new(XZRect::new( Box::new(XZRect::new(
0., 0.,
@ -115,21 +109,17 @@ pub fn new(opt: &Opt) -> Scene {
0., 0.,
555., 555.,
0., 0.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.73, 0.73, 0.73))),
0.73, 0.73, 0.73,
))))),
)), )),
// Grey back wall // Grey back wall
Box::new(FlipNormals::new(Box::new(XYRect::new( Box::new(FlipNormals::new(XYRect::new(
0., 0.,
555., 555.,
0., 0.,
555., 555.,
555., 555.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.73, 0.73, 0.73))),
0.73, 0.73, 0.73, ))),
))))),
)))),
]; ];
let world: Box<Hit> = if opt.use_accel { let world: Box<Hit> = if opt.use_accel {
Box::new(KDTree::new(objects, time_min, time_max)) Box::new(KDTree::new(objects, time_min, time_max))

View File

@ -45,17 +45,13 @@ pub fn new(opt: &Opt) -> Scene {
let it = ImageTexture::new(image::load_from_memory(world_image_bytes).unwrap().to_rgb()); let it = ImageTexture::new(image::load_from_memory(world_image_bytes).unwrap().to_rgb());
let objects: Vec<Box<Hit>> = vec![ let objects: Vec<Box<Hit>> = vec![
// Big sphere // Big sphere
Box::new(Sphere::new( Box::new(Sphere::new(Vec3::new(0., 2., 0.), 2.0, Lambertian::new(it))),
Vec3::new(0., 2., 0.),
2.0,
Box::new(Lambertian::new(Box::new(it))),
)),
// Earth sized sphere // Earth sized sphere
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., -1000., 0.), Vec3::new(0., -1000., 0.),
1000., 1000.,
// Box::new(Lambertian::new(ground_color)), // Box::new(Lambertian::new(ground_color)),
Box::new(Lambertian::new(Box::new(NoiseTexture::with_scale(10.)))), Lambertian::new(NoiseTexture::with_scale(10.)),
)), )),
Box::new(XZRect::new( Box::new(XZRect::new(
-100., -100.,
@ -63,9 +59,7 @@ pub fn new(opt: &Opt) -> Scene {
-100., -100.,
1000., 1000.,
60., 60.,
Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( DiffuseLight::new(ConstantTexture::new(Vec3::new(1., 1., 1.))),
Vec3::new(1., 1., 1.),
)))),
)), )),
Box::new(YZRect::new( Box::new(YZRect::new(
1., 1.,
@ -73,9 +67,7 @@ pub fn new(opt: &Opt) -> Scene {
-1., -1.,
1., 1.,
4., 4.,
Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( DiffuseLight::new(ConstantTexture::new(Vec3::new(4., 0., 4.))),
Vec3::new(4., 0., 4.),
)))),
)), )),
Box::new(YZRect::new( Box::new(YZRect::new(
1., 1.,
@ -83,9 +75,7 @@ pub fn new(opt: &Opt) -> Scene {
-1., -1.,
1., 1.,
-4., -4.,
Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( DiffuseLight::new(ConstantTexture::new(Vec3::new(0., 4., 0.))),
Vec3::new(0., 4., 0.),
)))),
)), )),
Box::new(XZRect::new( Box::new(XZRect::new(
-1., -1.,
@ -93,9 +83,7 @@ pub fn new(opt: &Opt) -> Scene {
-1., -1.,
1., 1.,
6., 6.,
Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( DiffuseLight::new(ConstantTexture::new(Vec3::new(4., 4., 0.))),
Vec3::new(4., 4., 0.),
)))),
)), )),
Box::new(XYRect::new( Box::new(XYRect::new(
-1., -1.,
@ -103,9 +91,7 @@ pub fn new(opt: &Opt) -> Scene {
1., 1.,
3., 3.,
-4., -4.,
Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( DiffuseLight::new(ConstantTexture::new(Vec3::new(0., 0., 4.))),
Vec3::new(0., 0., 4.),
)))),
)), )),
Box::new(XYRect::new( Box::new(XYRect::new(
-1., -1.,
@ -113,23 +99,21 @@ pub fn new(opt: &Opt) -> Scene {
1., 1.,
3., 3.,
4., 4.,
Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( DiffuseLight::new(ConstantTexture::new(Vec3::new(0., 4., 4.))),
Vec3::new(0., 4., 4.),
)))),
)), )),
/* /*
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., 0., 0.), Vec3::new(0., 0., 0.),
0.5, 0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Box::new(Lambertian::new(ConstantTexture::new(Vec3::new(
0.1, 0.2, 0.5, 0.1, 0.2, 0.5,
))))), )))),
)), )),
// Shiny sphere // Shiny sphere
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(1., 0., 0.), Vec3::new(1., 0., 0.),
0.5, 0.5,
Box::new(Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2)), Metal::new(Vec3::new(0.8, 0.8, 0.8), 0.2),
)), )),
Box::new(MovingSphere::new( Box::new(MovingSphere::new(
Vec3::new(-1., 0., -0.25), Vec3::new(-1., 0., -0.25),
@ -137,9 +121,9 @@ pub fn new(opt: &Opt) -> Scene {
0.5, 0.5,
0., 0.,
1., 1.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(
0.2, 0.8, 0.2, 0.2, 0.8, 0.2,
))))), ))),
)), )),
*/ */
]; ];

View File

@ -30,9 +30,9 @@ pub fn new(opt: &Opt) -> Scene {
time_max, time_max,
); );
let ground_color = if opt.use_accel { let ground_color = if opt.use_accel {
Box::new(ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4))) ConstantTexture::new(Vec3::new(1.0, 0.4, 0.4))
} else { } else {
Box::new(ConstantTexture::new(Vec3::new(0.4, 1.0, 0.4))) ConstantTexture::new(Vec3::new(0.4, 1.0, 0.4))
}; };
let objects: Vec<Box<Hit>> = vec![ let objects: Vec<Box<Hit>> = vec![
@ -40,19 +40,17 @@ pub fn new(opt: &Opt) -> Scene {
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., 0., -1.), Vec3::new(0., 0., -1.),
0.5, 0.5,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.1, 0.2, 0.5))),
0.1, 0.2, 0.5,
))))),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(0., -100.5, -1.), Vec3::new(0., -100.5, -1.),
100., 100.,
Box::new(Lambertian::new(ground_color)), Lambertian::new(ground_color),
)), )),
Box::new(Sphere::new( Box::new(Sphere::new(
Vec3::new(1., 0., -1.), Vec3::new(1., 0., -1.),
0.5, 0.5,
Box::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2)), Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.2),
)), )),
Box::new(MovingSphere::new( Box::new(MovingSphere::new(
Vec3::new(-1., 0., -1.25), Vec3::new(-1., 0., -1.25),
@ -60,9 +58,7 @@ pub fn new(opt: &Opt) -> Scene {
0.5, 0.5,
0., 0.,
1., 1.,
Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( Lambertian::new(ConstantTexture::new(Vec3::new(0.2, 0.8, 0.2))),
0.2, 0.8, 0.2,
))))),
)), )),
]; ];
let world: Box<Hit> = if opt.use_accel { let world: Box<Hit> = if opt.use_accel {

View File

@ -8,10 +8,13 @@ use ray::Ray;
use vec3::dot; use vec3::dot;
use vec3::Vec3; use vec3::Vec3;
pub struct Sphere { pub struct Sphere<M>
where
M: Material,
{
center: Vec3, center: Vec3,
radius: f32, radius: f32,
material: Box<Material>, material: M,
} }
pub fn get_sphere_uv(p: Vec3) -> (f32, f32) { pub fn get_sphere_uv(p: Vec3) -> (f32, f32) {
@ -22,17 +25,26 @@ pub fn get_sphere_uv(p: Vec3) -> (f32, f32) {
(u, v) (u, v)
} }
impl Sphere { impl<M> Sphere<M>
pub fn new(center: Vec3, radius: f32, material: Box<Material>) -> Sphere { where
M: Material,
{
pub fn new<V>(center: V, radius: f32, material: M) -> Sphere<M>
where
V: Into<Vec3>,
{
Sphere { Sphere {
center, center: center.into(),
radius, radius,
material, material,
} }
} }
} }
impl Hit for Sphere { impl<M> Hit for Sphere<M>
where
M: Material,
{
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
let oc = r.origin - self.center; let oc = r.origin - self.center;
let a = dot(r.direction, r.direction); let a = dot(r.direction, r.direction);
@ -49,7 +61,7 @@ impl Hit for Sphere {
uv, uv,
p: point, p: point,
normal: (point - self.center) / self.radius, normal: (point - self.center) / self.radius,
material: &*self.material, material: &self.material,
}); });
} }
let temp = (-b + (b * b - a * c).sqrt()) / a; let temp = (-b + (b * b - a * c).sqrt()) / a;
@ -61,7 +73,7 @@ impl Hit for Sphere {
uv, uv,
p: point, p: point,
normal: (point - self.center) / self.radius, normal: (point - self.center) / self.radius,
material: &*self.material, material: &self.material,
}); });
} }
} }

View File

@ -7,13 +7,19 @@ pub trait Texture: Send + Sync {
fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3; fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3;
} }
#[derive(Debug, PartialEq)]
pub struct ConstantTexture { pub struct ConstantTexture {
color: Vec3, color: Vec3,
} }
impl ConstantTexture { impl ConstantTexture {
pub fn new(color: Vec3) -> ConstantTexture { pub fn new<V>(color: V) -> ConstantTexture
ConstantTexture { color } where
V: Into<Vec3>,
{
ConstantTexture {
color: color.into(),
}
} }
} }
@ -23,18 +29,27 @@ impl Texture for ConstantTexture {
} }
} }
pub struct CheckerTexture { pub struct CheckerTexture<T>
odd: Box<Texture>, where
even: Box<Texture>, T: Texture,
{
odd: T,
even: T,
} }
impl CheckerTexture { impl<T> CheckerTexture<T>
pub fn new(odd: Box<Texture>, even: Box<Texture>) -> CheckerTexture { where
T: Texture,
{
pub fn new(odd: T, even: T) -> CheckerTexture<T> {
CheckerTexture { odd, even } CheckerTexture { odd, even }
} }
} }
impl Texture for CheckerTexture { impl<T> Texture for CheckerTexture<T>
where
T: Texture,
{
fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3 { fn value(&self, u: f32, v: f32, p: Vec3) -> Vec3 {
let sines = (10. * p.x).sin() * (10. * p.y).sin() * (10. * p.z).sin(); let sines = (10. * p.x).sin() * (10. * p.y).sin() * (10. * p.z).sin();
if sines < 0. { if sines < 0. {
@ -106,3 +121,16 @@ impl Texture for ImageTexture {
rgb rgb
} }
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn constant_texture_from_array() {
assert_eq!(
ConstantTexture::new(Vec3::new(1., 2., 3.)),
ConstantTexture::new([1., 2., 3.])
);
}
}

View File

@ -4,18 +4,27 @@ use hitable::HitRecord;
use ray::Ray; use ray::Ray;
use vec3::Vec3; use vec3::Vec3;
pub struct Translate { pub struct Translate<H>
hitable: Box<Hit>, where
H: Hit,
{
hitable: H,
offset: Vec3, offset: Vec3,
} }
impl Translate { impl<H> Translate<H>
pub fn new(hitable: Box<Hit>, offset: Vec3) -> Translate { where
H: Hit,
{
pub fn new(hitable: H, offset: Vec3) -> Translate<H> {
Translate { hitable, offset } Translate { hitable, offset }
} }
} }
impl Hit for Translate { impl<H> Hit for Translate<H>
where
H: Hit,
{
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> { 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); 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) { if let Some(rec) = self.hitable.hit(moved_r, t_min, t_max) {

View File

@ -1,3 +1,4 @@
use std::convert::From;
use std::fmt; use std::fmt;
use std::num::ParseFloatError; use std::num::ParseFloatError;
use std::ops::Add; use std::ops::Add;
@ -49,6 +50,16 @@ impl Vec3 {
} }
} }
impl From<[f32; 3]> for Vec3 {
fn from(v: [f32; 3]) -> Self {
Vec3 {
x: v[0],
y: v[1],
z: v[2],
}
}
}
impl fmt::Display for Vec3 { impl fmt::Display for Vec3 {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {} {}", self.x, self.y, self.z) write!(f, "{} {} {}", self.x, self.y, self.z)