diff --git a/rtiow/src/lib.rs b/rtiow/src/lib.rs index 25132bc..912a780 100644 --- a/rtiow/src/lib.rs +++ b/rtiow/src/lib.rs @@ -8,6 +8,7 @@ pub mod material; pub mod moving_sphere; pub mod perlin; pub mod ray; +pub mod rect; pub mod renderer; pub mod scenes; pub mod sphere; diff --git a/rtiow/src/material.rs b/rtiow/src/material.rs index 2c55dfe..cfd1b24 100644 --- a/rtiow/src/material.rs +++ b/rtiow/src/material.rs @@ -31,6 +31,9 @@ pub struct ScatterResponse { pub trait Material: Send + Sync { fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse; + fn emitted(&self, _u: f32, _v: f32, _p: Vec3) -> Vec3 { + Vec3::new(0., 0., 0.) + } } pub struct Lambertian { @@ -149,3 +152,27 @@ impl Material for Dielectric { } } } + +pub struct DiffuseLight { + emit: Box, +} + +impl DiffuseLight { + pub fn new(emit: Box) -> DiffuseLight { + DiffuseLight { emit } + } +} + +impl Material for DiffuseLight { + fn scatter(&self, _r_in: &Ray, _rec: &HitRecord) -> ScatterResponse { + ScatterResponse { + scattered: Default::default(), + attenutation: Default::default(), + reflected: false, + } + } + + fn emitted(&self, u: f32, v: f32, p: Vec3) -> Vec3 { + self.emit.value(u, v, p) + } +} diff --git a/rtiow/src/rect.rs b/rtiow/src/rect.rs new file mode 100644 index 0000000..2998601 --- /dev/null +++ b/rtiow/src/rect.rs @@ -0,0 +1,58 @@ +use aabb::AABB; +use hitable::Hit; +use hitable::HitRecord; +use material::Material; +use ray::Ray; +use vec3::Vec3; + +pub struct XYRect { + x0: f32, + x1: f32, + y0: f32, + y1: f32, + k: f32, + material: Box, +} + +impl XYRect { + pub fn new(x0: f32, x1: f32, y0: f32, y1: f32, k: f32, material: Box) -> XYRect { + XYRect { + x0, + x1, + y0, + y1, + k, + material, + } + } +} + +impl Hit for XYRect { + fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { + let t = (self.k - r.origin.z) / r.direction.z; + if t < t_min || t > t_max { + return None; + } + let x = r.origin.x + t * r.direction.x; + let y = r.origin.y + t * r.direction.y; + if x < self.x0 || x > self.x1 || y < self.y0 || y > self.y1 { + return None; + } + let u = (x - self.x0) / (self.x1 - self.x0); + let v = (y - self.y0) / (self.y1 - self.y0); + Some(HitRecord { + t, + uv: (u, v), + p: r.point_at_parameter(t), + normal: Vec3::new(0., 0., 1.), + material: &*self.material, + }) + } + + fn bounding_box(&self, _t_min: f32, _t_max: f32) -> Option { + Some(AABB::new( + Vec3::new(self.x0, self.y0, self.k - 0.0001), + Vec3::new(self.x1, self.y1, self.k + 0.0001), + )) + } +} diff --git a/rtiow/src/renderer.rs b/rtiow/src/renderer.rs index 7e27638..cf3bc2c 100644 --- a/rtiow/src/renderer.rs +++ b/rtiow/src/renderer.rs @@ -96,21 +96,36 @@ pub struct Scene { pub subsamples: usize, pub width: usize, pub height: usize, + pub global_illumination: bool, } -fn color(r: Ray, world: &Hit, depth: usize) -> Vec3 { +// 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 { 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 scatter_response.attenutation - * color(scatter_response.scattered, world, depth + 1); + return emitted + scatter_response.attenutation * color( + scatter_response.scattered, + world, + depth + 1, + global_illumination, + ); + } else { + return emitted; } - return Default::default(); } - // No hit, choose color from background. - let unit_direction = r.direction.unit_vector(); - let t = 0.5 * (unit_direction.y + 1.); - Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t + 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; + } + // No global illumination, so background is black. + Vec3::new(0., 0., 0.) } fn trace_pixel(x: usize, y: usize, scene: &Scene) -> Vec3 { @@ -118,7 +133,7 @@ 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) + color(ray, scene.world.as_ref(), 0, scene.global_illumination) } fn render_worker( diff --git a/rtiow/src/scenes/bench.rs b/rtiow/src/scenes/bench.rs index f09e9ff..f5f3470 100644 --- a/rtiow/src/scenes/bench.rs +++ b/rtiow/src/scenes/bench.rs @@ -73,5 +73,6 @@ pub fn new(opt: &Opt) -> Scene { subsamples: opt.subsamples, width: opt.width, height: opt.height, + global_illumination: true, } } diff --git a/rtiow/src/scenes/book.rs b/rtiow/src/scenes/book.rs index 6d14a4c..4e33bf8 100644 --- a/rtiow/src/scenes/book.rs +++ b/rtiow/src/scenes/book.rs @@ -50,6 +50,7 @@ pub fn new(opt: &Opt) -> Scene { subsamples: opt.subsamples, width: opt.width, height: opt.height, + global_illumination: true, } } diff --git a/rtiow/src/scenes/bvh.rs b/rtiow/src/scenes/bvh.rs index fe14d44..51792bc 100644 --- a/rtiow/src/scenes/bvh.rs +++ b/rtiow/src/scenes/bvh.rs @@ -71,5 +71,6 @@ pub fn new(opt: &Opt) -> Scene { subsamples: opt.subsamples, width: opt.width, height: opt.height, + global_illumination: true, } } diff --git a/rtiow/src/scenes/test.rs b/rtiow/src/scenes/test.rs index 95fec0b..4bc7a23 100644 --- a/rtiow/src/scenes/test.rs +++ b/rtiow/src/scenes/test.rs @@ -4,7 +4,9 @@ use camera::Camera; use hitable::Hit; use hitable_list::HitableList; use kdtree::KDTree; +use material::DiffuseLight; use material::Lambertian; +use rect::XYRect; use renderer::Opt; use renderer::Scene; use sphere::Sphere; @@ -14,7 +16,7 @@ use texture::NoiseTexture; use vec3::Vec3; pub fn new(opt: &Opt) -> Scene { - let lookfrom = Vec3::new(0., 5., 20.); + let lookfrom = Vec3::new(30., 2., 0.); let lookat = Vec3::new(0., 1., 0.); let dist_to_focus = 10.0; let aperture = 0.0; @@ -53,6 +55,26 @@ pub fn new(opt: &Opt) -> Scene { // Box::new(Lambertian::new(ground_color)), Box::new(Lambertian::new(Box::new(NoiseTexture::with_scale(10.)))), )), + Box::new(XYRect::new( + 0., + 4., + 0., + 4., + -4., + Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( + Vec3::new(4., 4., 4.), + )))), + )), + Box::new(XYRect::new( + 0., + 4., + 0., + 4., + 4., + Box::new(DiffuseLight::new(Box::new(ConstantTexture::new( + Vec3::new(0., 4., 4.), + )))), + )), /* Box::new(Sphere::new( Vec3::new(0., 0., 0.), @@ -90,5 +112,6 @@ pub fn new(opt: &Opt) -> Scene { subsamples: opt.subsamples, width: opt.width, height: opt.height, + global_illumination: false, } } diff --git a/rtiow/src/scenes/tutorial.rs b/rtiow/src/scenes/tutorial.rs index 2d8548b..7955545 100644 --- a/rtiow/src/scenes/tutorial.rs +++ b/rtiow/src/scenes/tutorial.rs @@ -76,5 +76,6 @@ pub fn new(opt: &Opt) -> Scene { subsamples: opt.subsamples, width: opt.width, height: opt.height, + global_illumination: true, } } diff --git a/rtiow/src/texture.rs b/rtiow/src/texture.rs index 9a47133..a0eeeb7 100644 --- a/rtiow/src/texture.rs +++ b/rtiow/src/texture.rs @@ -1,7 +1,6 @@ use image::RgbImage; use perlin::turb; -use perlin::GENERATOR; use vec3::Vec3; pub trait Texture: Send + Sync {