Add environment mapping for background.

This commit is contained in:
Bill Thiede 2019-01-29 10:20:31 -08:00
parent 815e647ad5
commit ce2d22b0b5
14 changed files with 77 additions and 12 deletions

BIN
rtiow/images/envmap.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 MiB

View File

@ -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<EnvMap>,
}
// 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<EnvMap>,
) -> 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::<f32>(0., 1.) + x as f32) / scene.width as f32;
let v = (rng.gen_range::<f32>(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(

View File

@ -72,5 +72,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: true,
env_map: None,
}
}

View File

@ -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)),
}
}

View File

@ -66,5 +66,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: true,
env_map: None,
}
}

View File

@ -133,5 +133,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: false,
env_map: None,
}
}

View File

@ -118,5 +118,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: false,
env_map: None,
}
}

View File

@ -169,5 +169,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: false,
env_map: None,
}
}

View File

@ -69,5 +69,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: true,
env_map: None,
}
}

View File

@ -145,5 +145,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: false,
env_map: None,
}
}

View File

@ -73,5 +73,6 @@ pub fn new(opt: &Opt) -> Scene {
width: opt.width,
height: opt.height,
global_illumination: true,
env_map: None,
}
}

View File

@ -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)
}
}

View File

@ -3,6 +3,7 @@ use image::RgbImage;
use texture::Texture;
use vec3::Vec3;
#[derive(Debug)]
pub struct ImageTexture {
img: RgbImage,
width: f32,

View File

@ -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;