Implement DiffuseLight material.

Allow Scenes to define global_illumination so scenes without lighting
can continue to work.
This commit is contained in:
Bill Thiede 2018-09-23 18:07:21 -07:00
parent 34ce373269
commit 9c5233e057
10 changed files with 138 additions and 11 deletions

View File

@ -8,6 +8,7 @@ pub mod material;
pub mod moving_sphere; pub mod moving_sphere;
pub mod perlin; pub mod perlin;
pub mod ray; pub mod ray;
pub mod rect;
pub mod renderer; pub mod renderer;
pub mod scenes; pub mod scenes;
pub mod sphere; pub mod sphere;

View File

@ -31,6 +31,9 @@ pub struct ScatterResponse {
pub trait Material: Send + Sync { pub trait Material: Send + Sync {
fn scatter(&self, r_in: &Ray, rec: &HitRecord) -> ScatterResponse; 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 { pub struct Lambertian {
@ -149,3 +152,27 @@ impl Material for Dielectric {
} }
} }
} }
pub struct DiffuseLight {
emit: Box<Texture>,
}
impl DiffuseLight {
pub fn new(emit: Box<Texture>) -> 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)
}
}

58
rtiow/src/rect.rs Normal file
View File

@ -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<Material>,
}
impl XYRect {
pub fn new(x0: f32, x1: f32, y0: f32, y1: f32, k: f32, material: Box<Material>) -> XYRect {
XYRect {
x0,
x1,
y0,
y1,
k,
material,
}
}
}
impl Hit for XYRect {
fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option<HitRecord> {
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<AABB> {
Some(AABB::new(
Vec3::new(self.x0, self.y0, self.k - 0.0001),
Vec3::new(self.x1, self.y1, self.k + 0.0001),
))
}
}

View File

@ -96,21 +96,36 @@ pub struct Scene {
pub subsamples: usize, pub subsamples: usize,
pub width: usize, pub width: usize,
pub height: 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) { 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); let scatter_response = rec.material.scatter(&r, &rec);
if depth < 50 && scatter_response.reflected { if depth < 50 && scatter_response.reflected {
return scatter_response.attenutation return emitted + scatter_response.attenutation * color(
* color(scatter_response.scattered, world, depth + 1); scatter_response.scattered,
world,
depth + 1,
global_illumination,
);
} else {
return emitted;
} }
return Default::default();
} }
// No hit, choose color from background. if global_illumination {
let unit_direction = r.direction.unit_vector(); // No hit, choose color from background.
let t = 0.5 * (unit_direction.y + 1.); let unit_direction = r.direction.unit_vector();
Vec3::new(1., 1., 1.) * (1. - t) + Vec3::new(0.5, 0.7, 1.) * t 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 { 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::<f32>(0., 1.) + x as f32) / scene.width as f32; 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 v = (rng.gen_range::<f32>(0., 1.) + y as f32) / scene.height as f32;
let ray = scene.camera.get_ray(u, v); 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( fn render_worker(

View File

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

View File

@ -50,6 +50,7 @@ pub fn new(opt: &Opt) -> Scene {
subsamples: opt.subsamples, subsamples: opt.subsamples,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: true,
} }
} }

View File

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

View File

@ -4,7 +4,9 @@ use camera::Camera;
use hitable::Hit; use hitable::Hit;
use hitable_list::HitableList; use hitable_list::HitableList;
use kdtree::KDTree; use kdtree::KDTree;
use material::DiffuseLight;
use material::Lambertian; use material::Lambertian;
use rect::XYRect;
use renderer::Opt; use renderer::Opt;
use renderer::Scene; use renderer::Scene;
use sphere::Sphere; use sphere::Sphere;
@ -14,7 +16,7 @@ use texture::NoiseTexture;
use vec3::Vec3; use vec3::Vec3;
pub fn new(opt: &Opt) -> Scene { 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 lookat = Vec3::new(0., 1., 0.);
let dist_to_focus = 10.0; let dist_to_focus = 10.0;
let aperture = 0.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(ground_color)),
Box::new(Lambertian::new(Box::new(NoiseTexture::with_scale(10.)))), 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( Box::new(Sphere::new(
Vec3::new(0., 0., 0.), Vec3::new(0., 0., 0.),
@ -90,5 +112,6 @@ pub fn new(opt: &Opt) -> Scene {
subsamples: opt.subsamples, subsamples: opt.subsamples,
width: opt.width, width: opt.width,
height: opt.height, height: opt.height,
global_illumination: false,
} }
} }

View File

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

View File

@ -1,7 +1,6 @@
use image::RgbImage; use image::RgbImage;
use perlin::turb; use perlin::turb;
use perlin::GENERATOR;
use vec3::Vec3; use vec3::Vec3;
pub trait Texture: Send + Sync { pub trait Texture: Send + Sync {