diff --git a/rtiow/images/world.jpg b/rtiow/images/world.jpg new file mode 100644 index 0000000..f6a9de4 Binary files /dev/null and b/rtiow/images/world.jpg differ diff --git a/rtiow/src/cube.rs b/rtiow/src/cube.rs deleted file mode 100644 index 5d59b98..0000000 --- a/rtiow/src/cube.rs +++ /dev/null @@ -1,67 +0,0 @@ -use aabb::AABB; -use hitable::Hit; -use hitable::HitRecord; -use material::Material; -use ray::Ray; -use vec3::dot; -use vec3::Vec3; - -// Cube is an axis-aligned cube with dimensions length x length x length with center in the middle. -pub struct Cube { - center: Vec3, - length: f32, - material: Box, -} - -impl Cube { - pub fn new(center: Vec3, length: f32, material: Box) -> Cube { - Cube { - center, - length, - material, - } - } -} - -impl Hit for Cube { - fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { - info!("Cube info"); - eprintln!("Cube eprintln"); - // See if it hit the positive X wall first. - let oc = r.origin - self.center; - let a = dot(r.direction, r.direction); - let b = dot(oc, r.direction); - let c = dot(oc, oc) - self.length * self.length; - let discriminant = b * b - a * c; - if discriminant > 0. { - let temp = (-b - (b * b - a * c).sqrt()) / a; - if temp < t_max && temp > t_min { - let point = r.point_at_parameter(temp); - return Some(HitRecord { - t: temp, - p: point, - normal: (point - self.center) / self.length, - material: &*self.material, - }); - } - let temp = (-b + (b * b - a * c).sqrt()) / a; - if temp < t_max && temp > t_min { - let point = r.point_at_parameter(temp); - return Some(HitRecord { - t: temp, - p: point, - normal: (point - self.center) / self.length, - material: &*self.material, - }); - } - } - None - } - - fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option { - Some(AABB::new( - self.center - Vec3::new(self.length / 2., self.length / 2., self.length / 2.), - self.center + Vec3::new(self.length / 2., self.length / 2., self.length / 2.), - )) - } -} diff --git a/rtiow/src/hitable.rs b/rtiow/src/hitable.rs index ddb27bd..d07254c 100644 --- a/rtiow/src/hitable.rs +++ b/rtiow/src/hitable.rs @@ -5,6 +5,7 @@ use vec3::Vec3; pub struct HitRecord<'m> { pub t: f32, + pub uv: (f32, f32), pub p: Vec3, pub normal: Vec3, pub material: &'m Material, diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index b3c4c07..25132bc 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -1,7 +1,6 @@ pub mod aabb; pub mod bvh; pub mod camera; -pub mod cube; pub mod hitable; pub mod hitable_list; pub mod kdtree; diff --git a/rtiow/src/material.rs b/rtiow/src/material.rs index f127241..2c55dfe 100644 --- a/rtiow/src/material.rs +++ b/rtiow/src/material.rs @@ -47,8 +47,9 @@ impl Lambertian { impl Material for Lambertian { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse { let target = rec.p + rec.normal + random_in_unit_sphere(); + let (u, v) = rec.uv; ScatterResponse { - attenutation: self.albedo.value(0., 0., rec.p), + attenutation: self.albedo.value(u, v, rec.p), scattered: Ray::new(rec.p, target - rec.p, r_in.time), reflected: true, } diff --git a/rtiow/src/moving_sphere.rs b/rtiow/src/moving_sphere.rs index b5583ac..eb8e5b1 100644 --- a/rtiow/src/moving_sphere.rs +++ b/rtiow/src/moving_sphere.rs @@ -4,6 +4,7 @@ use hitable::Hit; use hitable::HitRecord; use material::Material; use ray::Ray; +use sphere::get_sphere_uv; use vec3::dot; use vec3::Vec3; @@ -51,8 +52,10 @@ impl Hit for MovingSphere { let temp = (-b - (b * b - a * c).sqrt()) / a; if temp < t1 && temp > t0 { let point = r.point_at_parameter(temp); + let uv = get_sphere_uv((point - self.center(r.time)) / self.radius); return Some(HitRecord { t: temp, + uv, p: point, normal: (point - self.center(r.time)) / self.radius, material: &*self.material, @@ -61,8 +64,10 @@ impl Hit for MovingSphere { let temp = (-b + (b * b - a * c).sqrt()) / a; if temp < t1 && temp > t0 { let point = r.point_at_parameter(temp); + let uv = get_sphere_uv((point - self.center(r.time)) / self.radius); return Some(HitRecord { t: temp, + uv, p: point, normal: (point - self.center(r.time)) / self.radius, material: &*self.material, diff --git a/rtiow/src/perlin.rs b/rtiow/src/perlin.rs index 2d19976..2c3a393 100644 --- a/rtiow/src/perlin.rs +++ b/rtiow/src/perlin.rs @@ -74,6 +74,18 @@ fn perlin_interp(c: [[[Vec3; 2]; 2]; 2], u: f32, v: f32, w: f32) -> f32 { accum } +pub fn turb(p: Vec3, depth: usize) -> f32 { + let mut accum = 0.; + let mut temp_p = p; + let mut weight = 1.; + for _ in 0..depth { + accum += weight * GENERATOR.noise(temp_p); + weight *= 0.5; + temp_p = temp_p * 0.2; + } + accum.abs() +} + impl Perlin { fn new() -> Perlin { Perlin { diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index f9f14b3..7e27638 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -24,7 +24,6 @@ pub enum Model { Bench, Book, Tutorial, - Cube, BVH, Test, } @@ -34,7 +33,6 @@ impl Model { match self { Model::Book => scenes::book::new(&opt), Model::Bench => scenes::bench::new(&opt), - Model::Cube => scenes::cube::new(&opt), Model::Tutorial => scenes::tutorial::new(&opt), Model::BVH => scenes::bvh::new(&opt), Model::Test => scenes::test::new(&opt), @@ -58,7 +56,6 @@ impl str::FromStr for Model { "bench" => Ok(Model::Bench), "book" => Ok(Model::Book), "tutorial" => Ok(Model::Tutorial), - "cube" => Ok(Model::Cube), "bvh" => Ok(Model::BVH), "test" => Ok(Model::Test), _ => Err(ModelParseError(s.to_owned())), @@ -78,7 +75,7 @@ pub struct Opt { /// Sub-samples per pixel #[structopt(short = "s", long = "subsample", default_value = "8")] pub subsamples: usize, - /// Select scene to render, one of: "bench", "book", "tutorial" "cube", "bvh", "test" + /// Select scene to render, one of: "bench", "book", "tutorial", "bvh", "test" #[structopt(long = "model", default_value = "test")] pub model: Model, /// Path to store pprof profile data, i.e. /tmp/cpuprofile.pprof diff --git a/rtiow/src/scenes/cube.rs b/rtiow/src/scenes/cube.rs deleted file mode 100644 index 261e516..0000000 --- a/rtiow/src/scenes/cube.rs +++ /dev/null @@ -1,60 +0,0 @@ -use camera::Camera; -use cube::Cube; -use hitable::Hit; -use hitable_list::HitableList; -use material::Lambertian; -use renderer::Opt; -use renderer::Scene; -use sphere::Sphere; -use texture::ConstantTexture; -use vec3::Vec3; - -pub fn new(opt: &Opt) -> Scene { - let lookfrom = Vec3::new(3., 3., 2.); - let lookat = Vec3::new(0., 0., 0.); - let dist_to_focus = (lookfrom - lookat).length(); - let aperture = 0.1; - let time_min = 0.; - let time_max = 1.; - let camera = Camera::new( - lookfrom, - lookat, - Vec3::new(0., 1., 0.), - 45., - opt.width as f32 / opt.height as f32, - aperture, - dist_to_focus, - time_min, - time_max, - ); - let world: Box = Box::new(HitableList::new(vec![ - Box::new(Sphere::new( - Vec3::new(0., 0., -1.), - 0.5, - Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( - 0.1, 0.2, 0.5, - ))))), - )), - Box::new(Cube::new( - Vec3::new(0., 0., 0.), - 0.5, - Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( - 0.5, 0.2, 0.1, - ))))), - )), - Box::new(Sphere::new( - Vec3::new(0., -100.5, -1.), - 100., - Box::new(Lambertian::new(Box::new(ConstantTexture::new(Vec3::new( - 0.8, 0.8, 0.8, - ))))), - )), - ])); - Scene { - camera, - world, - subsamples: opt.subsamples, - width: opt.width, - height: opt.height, - } -} diff --git a/rtiow/src/scenes/mod.rs b/rtiow/src/scenes/mod.rs index ebccd6f..2ccfcef 100644 --- a/rtiow/src/scenes/mod.rs +++ b/rtiow/src/scenes/mod.rs @@ -1,6 +1,5 @@ pub mod bench; pub mod book; pub mod bvh; -pub mod cube; pub mod test; pub mod tutorial; diff --git a/rtiow/src/scenes/test.rs b/rtiow/src/scenes/test.rs index 99b63c5..95fec0b 100644 --- a/rtiow/src/scenes/test.rs +++ b/rtiow/src/scenes/test.rs @@ -1,29 +1,30 @@ +use image; + use camera::Camera; use hitable::Hit; use hitable_list::HitableList; use kdtree::KDTree; use material::Lambertian; -use material::Metal; -use moving_sphere::MovingSphere; use renderer::Opt; use renderer::Scene; use sphere::Sphere; use texture::ConstantTexture; +use texture::ImageTexture; use texture::NoiseTexture; use vec3::Vec3; pub fn new(opt: &Opt) -> Scene { - let lookfrom = Vec3::new(5., 5., 5.); - let lookat = Vec3::new(0., 0., 0.); - let dist_to_focus = (lookfrom - lookat).length(); - let aperture = 0.1; + let lookfrom = Vec3::new(0., 5., 20.); + let lookat = Vec3::new(0., 1., 0.); + let dist_to_focus = 10.0; + let aperture = 0.0; let time_min = 0.; let time_max = 1.; let camera = Camera::new( lookfrom, lookat, Vec3::new(0., 1., 0.), - 45., + 20., opt.width as f32 / opt.height as f32, aperture, dist_to_focus, @@ -36,7 +37,23 @@ pub fn new(opt: &Opt) -> Scene { Box::new(ConstantTexture::new(Vec3::new(0.4, 1.0, 0.4))) }; + let world_image_bytes = include_bytes!("../../images/world.jpg"); + let it = ImageTexture::new(image::load_from_memory(world_image_bytes).unwrap().to_rgb()); let objects: Vec> = vec![ + // Big sphere + Box::new(Sphere::new( + Vec3::new(0., 2., 0.), + 2.0, + Box::new(Lambertian::new(Box::new(it))), + )), + // Earth sized sphere + Box::new(Sphere::new( + Vec3::new(0., -1000., 0.), + 1000., + // Box::new(Lambertian::new(ground_color)), + Box::new(Lambertian::new(Box::new(NoiseTexture::with_scale(10.)))), + )), + /* Box::new(Sphere::new( Vec3::new(0., 0., 0.), 0.5, @@ -44,19 +61,6 @@ pub fn new(opt: &Opt) -> Scene { 0.1, 0.2, 0.5, ))))), )), - // Big sphere - Box::new(Sphere::new( - Vec3::new(0., 0., -2.), - 1.0, - Box::new(Lambertian::new(Box::new(NoiseTexture::with_scale(10.)))), - )), - // Earth sized sphere - Box::new(Sphere::new( - Vec3::new(0., -100.5, 0.), - 100., - // Box::new(Lambertian::new(ground_color)), - Box::new(Lambertian::new(Box::new(NoiseTexture::new()))), - )), // Shiny sphere Box::new(Sphere::new( Vec3::new(1., 0., 0.), @@ -73,6 +77,7 @@ pub fn new(opt: &Opt) -> Scene { 0.2, 0.8, 0.2, ))))), )), + */ ]; let world: Box = if opt.use_accel { Box::new(KDTree::new(objects, time_min, time_max)) diff --git a/rtiow/src/sphere.rs b/rtiow/src/sphere.rs index ca1624d..3f0e49e 100644 --- a/rtiow/src/sphere.rs +++ b/rtiow/src/sphere.rs @@ -1,3 +1,5 @@ +use std::f32::consts::PI; + use aabb::AABB; use hitable::Hit; use hitable::HitRecord; @@ -12,6 +14,14 @@ pub struct Sphere { material: Box, } +pub fn get_sphere_uv(p: Vec3) -> (f32, f32) { + let phi = p.z.atan2(p.x); + let theta = p.y.asin(); + let u = 1. - (phi + PI) / (2. * PI); + let v = (theta + PI / 2.) / PI; + (u, v) +} + impl Sphere { pub fn new(center: Vec3, radius: f32, material: Box) -> Sphere { Sphere { @@ -33,8 +43,10 @@ impl Hit for Sphere { let temp = (-b - (b * b - a * c).sqrt()) / a; if temp < t_max && temp > t_min { let point = r.point_at_parameter(temp); + let uv = get_sphere_uv((point - self.center) / self.radius); return Some(HitRecord { t: temp, + uv, p: point, normal: (point - self.center) / self.radius, material: &*self.material, @@ -43,8 +55,10 @@ impl Hit for Sphere { let temp = (-b + (b * b - a * c).sqrt()) / a; if temp < t_max && temp > t_min { let point = r.point_at_parameter(temp); + let uv = get_sphere_uv((point - self.center) / self.radius); return Some(HitRecord { t: temp, + uv, p: point, normal: (point - self.center) / self.radius, material: &*self.material, diff --git a/rtiow/src/texture.rs b/rtiow/src/texture.rs index 6ca0399..9a47133 100644 --- a/rtiow/src/texture.rs +++ b/rtiow/src/texture.rs @@ -1,3 +1,6 @@ +use image::RgbImage; + +use perlin::turb; use perlin::GENERATOR; use vec3::Vec3; @@ -58,6 +61,37 @@ impl NoiseTexture { impl Texture for NoiseTexture { fn value(&self, _u: f32, _v: f32, p: Vec3) -> Vec3 { - Vec3::new(1., 1., 1.) * 0.5 * (1. + GENERATOR.noise(self.scale * p)) + //Vec3::new(1., 1., 1.) * turb(self.scale * p, 7) + //Vec3::new(1., 1., 1.) * 0.5 * (1. + turb(self.scale * p, 7)) + Vec3::new(1., 1., 1.) * 0.5 * (1. + (self.scale * p.x + 5. * turb(p, 7)).sin()) + //Vec3::new(1., 1., 1.) * 0.5 * (1. + GENERATOR.noise(self.scale * p)) + } +} + +pub struct ImageTexture { + img: RgbImage, + width: f32, + height: f32, +} + +impl ImageTexture { + pub fn new(img: RgbImage) -> ImageTexture { + let (w, h) = img.dimensions(); + ImageTexture { + img, + width: w as f32, + height: h as f32, + } + } +} + +impl Texture for ImageTexture { + fn value(&self, u: f32, v: f32, _p: Vec3) -> Vec3 { + // Wrap texcoords by default. + let x = (u % 1. * self.width) as u32; + let y = ((1. - v % 1.) * self.height) as u32; + let p = self.img.get_pixel(x, y); + let rgb = Vec3::new(p[0] as f32 / 255., p[1] as f32 / 255., p[2] as f32 / 255.); + rgb } }