diff --git a/rtiow/images/envmap.jpg b/rtiow/images/envmap.jpg new file mode 100644 index 0000000..f9cdde8 Binary files /dev/null and b/rtiow/images/envmap.jpg differ diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index 6514196..79ef0fb 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -19,6 +19,8 @@ use camera::Camera; use hitable::Hit; use ray::Ray; use scenes; +use texture::EnvMap; +use texture::Texture; use vec3::Vec3; #[derive(Debug)] @@ -115,32 +117,47 @@ pub struct Scene { pub width: usize, pub height: usize, pub global_illumination: bool, + pub env_map: Option, } // color will trace ray up to 50 bounces deep accumulating color as it goes. If // global_illumination is true, a default light background color is assumed and will light the // world. If false, it is expected the scene has emissive light sources. -fn color(r: Ray, world: &Hit, depth: usize, global_illumination: bool) -> Vec3 { +fn color( + r: Ray, + world: &Hit, + depth: usize, + global_illumination: bool, + env_map: &Option, +) -> Vec3 { if let Some(rec) = world.hit(r, 0.001, std::f32::MAX) { let (u, v) = rec.uv; let emitted = rec.material.emitted(u, v, rec.p); let scatter_response = rec.material.scatter(&r, &rec); if depth < 50 && scatter_response.reflected { - return emitted + scatter_response.attenutation * color( - scatter_response.scattered, - world, - depth + 1, - global_illumination, - ); + return emitted + + scatter_response.attenutation + * color( + scatter_response.scattered, + world, + depth + 1, + global_illumination, + env_map, + ); } else { return emitted; } } if global_illumination { - // No hit, choose color from background. - let unit_direction = r.direction.unit_vector(); - let t = 0.5 * (unit_direction.y + 1.); - return Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t; + return match env_map { + Some(env_map) => env_map.color(r.direction.unit_vector()), + None => { + let unit_direction = r.direction.unit_vector(); + // No hit, choose color from background. + let t = 0.5 * (unit_direction.y + 1.); + Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t + } + }; } // No global illumination, so background is black. Vec3::new(0., 0., 0.) @@ -151,7 +168,13 @@ fn trace_pixel(x: usize, y: usize, scene: &Scene) -> Vec3 { let u = (rng.gen_range::(0., 1.) + x as f32) / scene.width as f32; let v = (rng.gen_range::(0., 1.) + y as f32) / scene.height as f32; let ray = scene.camera.get_ray(u, v); - color(ray, scene.world.as_ref(), 0, scene.global_illumination) + color( + ray, + scene.world.as_ref(), + 0, + scene.global_illumination, + &scene.env_map, + ) } fn render_worker( diff --git a/rtiow/src/scenes/bench.rs b/rtiow/src/scenes/bench.rs index 8ad75f7..d58c7a5 100644 --- a/rtiow/src/scenes/bench.rs +++ b/rtiow/src/scenes/bench.rs @@ -72,5 +72,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: true, + env_map: None, } } diff --git a/rtiow/src/scenes/book.rs b/rtiow/src/scenes/book.rs index 658f36d..3bb66c5 100644 --- a/rtiow/src/scenes/book.rs +++ b/rtiow/src/scenes/book.rs @@ -14,6 +14,7 @@ use renderer::Scene; use sphere::Sphere; use texture::CheckerTexture; use texture::ConstantTexture; +use texture::EnvMap; use vec3::Vec3; pub fn new(opt: &Opt) -> Scene { @@ -44,6 +45,8 @@ pub fn new(opt: &Opt) -> Scene { } else { Box::new(HitableList::new(random_scene(ground_color))) }; + let skybox_bytes = include_bytes!("../../images/envmap.jpg"); + let skybox = image::load_from_memory(skybox_bytes).unwrap().to_rgb(); Scene { camera, world, @@ -51,6 +54,7 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: true, + env_map: Some(EnvMap::new(skybox)), } } diff --git a/rtiow/src/scenes/bvh.rs b/rtiow/src/scenes/bvh.rs index edf2a4f..3ef5034 100644 --- a/rtiow/src/scenes/bvh.rs +++ b/rtiow/src/scenes/bvh.rs @@ -66,5 +66,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: true, + env_map: None, } } diff --git a/rtiow/src/scenes/cornell_box.rs b/rtiow/src/scenes/cornell_box.rs index ab5a8dd..5c8fcd8 100644 --- a/rtiow/src/scenes/cornell_box.rs +++ b/rtiow/src/scenes/cornell_box.rs @@ -133,5 +133,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: false, + env_map: None, } } diff --git a/rtiow/src/scenes/cornell_smoke.rs b/rtiow/src/scenes/cornell_smoke.rs index 470ede0..b80e466 100644 --- a/rtiow/src/scenes/cornell_smoke.rs +++ b/rtiow/src/scenes/cornell_smoke.rs @@ -118,5 +118,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: false, + env_map: None, } } diff --git a/rtiow/src/scenes/final_scene.rs b/rtiow/src/scenes/final_scene.rs index 2f6799d..9043fc9 100644 --- a/rtiow/src/scenes/final_scene.rs +++ b/rtiow/src/scenes/final_scene.rs @@ -169,5 +169,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: false, + env_map: None, } } diff --git a/rtiow/src/scenes/perlin_debug.rs b/rtiow/src/scenes/perlin_debug.rs index 4ee84f3..8f5abb4 100644 --- a/rtiow/src/scenes/perlin_debug.rs +++ b/rtiow/src/scenes/perlin_debug.rs @@ -69,5 +69,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: true, + env_map: None, } } diff --git a/rtiow/src/scenes/test.rs b/rtiow/src/scenes/test.rs index 9c3f361..bf9931f 100644 --- a/rtiow/src/scenes/test.rs +++ b/rtiow/src/scenes/test.rs @@ -145,5 +145,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: false, + env_map: None, } } diff --git a/rtiow/src/scenes/tutorial.rs b/rtiow/src/scenes/tutorial.rs index aace790..f0e5654 100644 --- a/rtiow/src/scenes/tutorial.rs +++ b/rtiow/src/scenes/tutorial.rs @@ -73,5 +73,6 @@ pub fn new(opt: &Opt) -> Scene { width: opt.width, height: opt.height, global_illumination: true, + env_map: None, } } diff --git a/rtiow/src/texture/envmap.rs b/rtiow/src/texture/envmap.rs new file mode 100644 index 0000000..ec03d93 --- /dev/null +++ b/rtiow/src/texture/envmap.rs @@ -0,0 +1,27 @@ +use std::f32; + +use image::RgbImage; + +use texture::ImageTexture; +use texture::Texture; +use vec3::Vec3; + +#[derive(Debug)] +pub struct EnvMap { + img_tex: ImageTexture, +} + +impl EnvMap { + pub fn new(img: RgbImage) -> EnvMap { + EnvMap { + img_tex: ImageTexture::new(img), + } + } + pub fn color(&self, ray: Vec3) -> Vec3 { + let zero = Vec3::new(0., 0., 0.); + let u = ray.x.atan2(ray.z) / f32::consts::PI /2.0 + 0.5; + let v = ray.y / 2.0 + 0.5; + // TODO(wathiede): properly wrap sphere to lat/lon. + self.img_tex.value(u, v, zero) + } +} diff --git a/rtiow/src/texture/image.rs b/rtiow/src/texture/image.rs index 3a5e9f8..db8bbe6 100644 --- a/rtiow/src/texture/image.rs +++ b/rtiow/src/texture/image.rs @@ -3,6 +3,7 @@ use image::RgbImage; use texture::Texture; use vec3::Vec3; +#[derive(Debug)] pub struct ImageTexture { img: RgbImage, width: f32, diff --git a/rtiow/src/texture/mod.rs b/rtiow/src/texture/mod.rs index f138a14..7b9956a 100644 --- a/rtiow/src/texture/mod.rs +++ b/rtiow/src/texture/mod.rs @@ -2,8 +2,10 @@ mod checker; mod constant; mod image; mod noise; +mod envmap; pub use texture::checker::CheckerTexture; pub use texture::constant::ConstantTexture; +pub use texture::envmap::EnvMap; pub use texture::image::ImageTexture; pub use texture::noise::NoiseTexture;