Added Defocus Blur and Final Scene, Some refactors

1. Added Defocus Blur Scene and the Final Scene
2. Refactored how the fields from `Chunk` were referenced
in `render_chunk` methods
3. Fixed a issue in camera where it remained out of focus regardless of
   focus distance value
4. Fixed a issue in random_in_unit_disc where it kept generating
random nums in a weird space than a disc
This commit is contained in:
Ishan Jain 2020-03-12 21:17:39 +05:30
parent f75d0d2b7d
commit 0da70546cd
16 changed files with 291 additions and 88 deletions

View File

@ -45,8 +45,8 @@ impl Camera {
- u * focus_distance * half_width
- v * focus_distance * half_height
- w * focus_distance;
let horizontal = u * half_width * 2.0;
let vertical = v * half_height * 2.0;
let horizontal = u * half_width * focus_distance * 2.0;
let vertical = v * half_height * focus_distance * 2.0;
let lens_radius = aperture / 2.0;
Self {
@ -73,7 +73,7 @@ impl Camera {
}
fn random_in_unit_disk(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
let mut p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0);
let mut p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 1.0, 0.0);
while p.dot(&p) >= 1.0 {
p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0);

View File

@ -39,14 +39,14 @@ impl Demo for DefocusBlur {
Box::new(Sphere::with_material(
Vec3::new(0.0, -100.5, -1.0),
100.0,
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.0))),
Box::new(Lambertian::new(Vec3::new(0.2, 0.2, 0.6))),
)),
],
})
}
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
let lookfrom = Vec3::new(3.0, 3.0, 2.0);
let lookfrom = Vec3::new(3.0, 3.0, 3.0);
let lookat = Vec3::new(0.0, 0.0, -1.0);
let aperture = 2.0;
let distance_to_focus = (lookfrom - lookat).length();
@ -69,13 +69,15 @@ impl Demo for DefocusBlur {
world: Option<&HitableList>,
samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let camera = camera.unwrap();
let world = world.unwrap();

View File

@ -56,13 +56,15 @@ impl Demo for DielectricMaterial {
world: Option<&HitableList>,
samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let camera = camera.unwrap();
let world = world.unwrap();

View File

@ -30,13 +30,15 @@ impl Demo for DiffuseMaterials {
world: Option<&HitableList>,
samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let camera = camera.unwrap();
let world = world.unwrap();

177
src/demos/final_scene.rs Normal file
View File

@ -0,0 +1,177 @@
use {
crate::{
demos::{Chunk, Demo},
types::{
material::{Dielectric, Lambertian, Metal},
Hitable, HitableList, Ray, Sphere, Vec3,
},
Camera,
},
rand::Rng,
};
pub struct FinalScene;
impl Demo for FinalScene {
fn name(&self) -> &'static str {
"final-scene"
}
fn world(&self) -> Option<HitableList> {
let mut world = HitableList {
list: Vec::with_capacity(500),
};
world.push(Box::new(Sphere::with_material(
Vec3::new(0.0, -1000.0, 0.0),
1000.0,
Box::new(Lambertian::new(Vec3::new(0.5, 0.5, 0.5))),
)));
let mut rng = rand::thread_rng();
let radius = 0.2;
let l = Vec3::new(4.0, 0.2, 0.0);
for a in -11..11 {
let a = a as f64;
for b in -11..11 {
let b = b as f64;
let choose_material_probability = rng.gen::<f64>();
let center = Vec3::new(a + 0.9 * rng.gen::<f64>(), 0.2, b + 0.9 * rng.gen::<f64>());
if (center - l).length() > 0.9 {
if choose_material_probability < 0.8 {
// diffuse material
world.push(Box::new(Sphere::with_material(
center,
radius,
Box::new(Lambertian::new(Vec3::new(
rng.gen::<f64>() * rng.gen::<f64>(),
rng.gen::<f64>() * rng.gen::<f64>(),
rng.gen::<f64>() * rng.gen::<f64>(),
))),
)));
} else if choose_material_probability < 0.95 {
// metal material
world.push(Box::new(Sphere::with_material(
center,
radius,
Box::new(Metal::with_fuzz(
Vec3::new(
(1.0 + rng.gen::<f64>()) * 0.5,
(1.0 + rng.gen::<f64>()) * 0.5,
(1.0 + rng.gen::<f64>()) * 0.5,
),
0.5 * rng.gen::<f64>(),
)),
)));
} else {
// glass material
world.push(Box::new(Sphere::with_material(
center,
radius,
Box::new(Dielectric::new(1.5)),
)));
}
}
}
}
world.push(Box::new(Sphere::with_material(
Vec3::new(0.0, 1.0, 0.0),
1.0,
Box::new(Dielectric::new(1.5)),
)));
world.push(Box::new(Sphere::with_material(
Vec3::new(-4.0, 1.0, 0.0),
1.0,
Box::new(Lambertian::new(Vec3::new(0.4, 0.2, 0.1))),
)));
world.push(Box::new(Sphere::with_material(
Vec3::new(4.0, 1.0, 0.0),
1.0,
Box::new(Metal::with_fuzz(Vec3::new(0.7, 0.6, 0.5), 0.0)),
)));
println!("world size = {}", world.list.len());
Some(world)
}
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
let lookfrom = Vec3::new(13.0, 2.0, 3.0);
let lookat = Vec3::new(0.0, 0.0, 0.0);
let camera = Camera::new(
lookfrom,
lookat,
Vec3::new(0.0, 1.0, 0.0),
20.0,
aspect_ratio,
0.1,
10.0,
);
Some(camera)
}
fn render_chunk(
&self,
chunk: &mut Chunk,
camera: Option<&Camera>,
world: Option<&HitableList>,
samples: u8,
) {
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let camera = camera.unwrap();
let world = world.unwrap();
let mut rng = rand::thread_rng();
let mut offset = 0;
for j in start_y..start_y + ny {
for i in start_x..start_x + nx {
let mut color = Vec3::new(0.0, 0.0, 0.0);
for _s in 0..samples {
let u = (i as f64 + rng.gen::<f64>()) / x as f64;
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
let ray = camera.get_ray(u, v);
color += calc_color(ray, &world, 0);
}
color /= samples as f64;
// gamma 2 corrected
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
buffer[offset + 2] = (255.99 * color.b().sqrt()) as u8;
offset += 4;
}
}
}
}
fn calc_color(ray: Ray, world: &HitableList, depth: u32) -> Vec3 {
if let Some(hit_rec) = world.hit(&ray, 0.001, std::f64::MAX) {
if depth >= 50 {
Vec3::new(0.0, 0.0, 0.0)
} else {
let material = hit_rec.material.as_ref();
if let (attenuation, Some(scattered_ray)) = material.unwrap().scatter(&ray, &hit_rec) {
calc_color(scattered_ray, &world, depth + 1) * attenuation
} else {
Vec3::new(0.0, 0.0, 0.0)
}
}
} else {
let unit_direction = ray.direction().unit_vector();
let t = 0.5 * (unit_direction.y() + 1.0);
Vec3::new(1.0, 1.0, 1.0) * (1.0 - t) + Vec3::new(0.5, 0.7, 1.0) * t
}
}

View File

@ -26,13 +26,15 @@ impl Demo for HitableSphere {
world: Option<&HitableList>,
_samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let world = world.unwrap();
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);

View File

@ -18,13 +18,15 @@ impl Demo for LinearGradientRectangle {
_world: Option<&HitableList>,
_samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
// -2.0 and 4.0 in lower_left_corner and horizontal respectively
// because our canvas is in 2:1 ratio

View File

@ -51,13 +51,15 @@ impl Demo for Materials {
world: Option<&HitableList>,
samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let camera = camera.unwrap();
let mut rng = rand::thread_rng();

View File

@ -1,6 +1,7 @@
mod defocus_blur;
mod dielectric_material;
mod diffuse_materials;
mod final_scene;
mod hitable_sphere;
mod linear_gradient_rectangle;
mod materials;
@ -13,6 +14,7 @@ mod surface_normal_sphere;
pub use defocus_blur::DefocusBlur;
pub use dielectric_material::DielectricMaterial;
pub use diffuse_materials::DiffuseMaterials;
pub use final_scene::FinalScene;
pub use hitable_sphere::HitableSphere;
pub use linear_gradient_rectangle::LinearGradientRectangle;
pub use materials::Materials;
@ -35,7 +37,6 @@ use {
},
};
#[derive(Debug)]
pub struct Chunk {
x: usize,
y: usize,

View File

@ -66,13 +66,15 @@ impl Demo for PositionableCamera {
world: Option<&HitableList>,
samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let camera = camera.unwrap();
let world = world.unwrap();
let mut rng = rand::thread_rng();

View File

@ -29,13 +29,15 @@ impl Demo for SimpleAntialiasing {
world: Option<&HitableList>,
samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let camera = camera.unwrap();
let mut rng = rand::thread_rng();

View File

@ -18,14 +18,15 @@ impl Demo for SimpleRectangle {
_world: Option<&HitableList>,
_samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
let mut offset = 0;
for j in start_y..start_y + ny {

View File

@ -20,14 +20,15 @@ impl Demo for SimpleSphere {
_world: Option<&HitableList>,
_samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
// Usually, lower_left_corner should've been -1.0,-1.0,-1.0 and
// horizontal should've been 2.0,0.0,0.0
// but we are working with a canvas that is 2:1 in size.

View File

@ -19,14 +19,15 @@ impl Demo for SurfaceNormalSphere {
_world: Option<&HitableList>,
_samples: u8,
) {
let x = chunk.x;
let y = chunk.y;
let nx = chunk.nx;
let ny = chunk.ny;
let start_x = chunk.start_x;
let start_y = chunk.start_y;
let buffer = &mut chunk.buffer;
let &mut Chunk {
x,
y,
nx,
ny,
start_x,
start_y,
ref mut buffer,
} = chunk;
// Usually, lower_left_corner should've been -1.0,-1.0,-1.0 and
// horizontal should've been 2.0,0.0,0.0
// but we are working with a canvas that is 2:1 in size.

View File

@ -23,8 +23,7 @@ const HORIZONTAL_PARTITION: usize = 8;
fn main() -> Result<(), String> {
let sdl_ctx = sdl2::init()?;
let video_subsys = sdl_ctx.video()?;
let (mut width, mut height) = (1280usize, 640usize);
let (mut width, mut height): (usize, usize) = (1600, 800);
let window = video_subsys
.window("Ray tracing in a weekend", width as u32, height as u32)
@ -81,6 +80,7 @@ fn main() -> Result<(), String> {
Some(Keycode::Num9) => active_demo = Box::new(demos::DielectricMaterial),
Some(Keycode::Num0) => active_demo = Box::new(demos::PositionableCamera),
Some(Keycode::Minus) => active_demo = Box::new(demos::DefocusBlur),
Some(Keycode::Equals) => active_demo = Box::new(demos::FinalScene),
None => unreachable!(),
_ => (),
};

View File

@ -17,3 +17,9 @@ impl Hitable for HitableList {
hit_rec
}
}
impl HitableList {
pub fn push(&mut self, obj: Box<dyn Hitable>) {
self.list.push(obj);
}
}