1
0

refactored some code and added two spheres

This commit is contained in:
Ishan Jain 2021-03-02 20:31:22 +05:30
parent 6037fe2802
commit b4d7ddfc4d
No known key found for this signature in database
GPG Key ID: F261A0E73038D89D
5 changed files with 242 additions and 126 deletions

View File

@ -0,0 +1,119 @@
use crate::{
demos::{Demo, ParallelHit},
materials::{Dielectric, Lambertian, Metal},
shapes::{MovingSphere, Sphere},
texture::{Checker, Solid},
types::Vec3,
BvhNode, Camera,
};
use rand::{rngs::SmallRng, Rng, SeedableRng};
use std::sync::Arc;
pub struct CheckeredMotionBlur {}
impl Demo for CheckeredMotionBlur {
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
fn name(&self) -> &'static str {
"checkered_motion_blur"
}
fn world(&self) -> Self::DemoT {
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(500);
let mut rng = rand::thread_rng();
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
world.push(Arc::new(Sphere::new(
Vec3::new(0.0, -1000.0, 0.0),
1000.0,
Lambertian::new(Checker::new(
Solid::new(Vec3::new(0.2, 0.3, 0.1)),
Solid::new(Vec3::new(0.9, 0.9, 0.9)),
)),
)));
let radius = 0.2;
let l = Vec3::new(4.0, 0.2, 0.0);
for a in -10..10 {
let a = a as f64;
for b in -10..10 {
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(Arc::new(MovingSphere::new(
center,
center + Vec3::new(0.0, 0.5 * rng.gen::<f64>(), 0.0),
0.0,
1.0,
radius,
Lambertian::new(Solid::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(Arc::new(Sphere::new(
center,
radius,
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(Arc::new(Sphere::new(center, radius, Dielectric::new(1.5))));
}
}
}
}
world.push(Arc::new(Sphere::new(
Vec3::new(0.0, 1.0, 0.0),
1.0,
Dielectric::new(1.5),
)));
world.push(Arc::new(Sphere::new(
Vec3::new(-4.0, 1.0, 0.0),
1.0,
Lambertian::new(Solid::new(Vec3::new(0.4, 0.2, 0.1))),
)));
world.push(Arc::new(Sphere::new(
Vec3::new(4.0, 1.0, 0.0),
1.0,
Metal::with_fuzz(Vec3::new(0.7, 0.6, 0.5), 0.0),
)));
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
}
fn camera(&self, aspect_ratio: f64) -> Camera {
let lookfrom = Vec3::new(13.0, 2.0, 3.0);
let lookat = Vec3::new(0.0, 0.0, 0.0);
let aperture = 0.1;
let focus_distance = 10.0;
Camera::new(
lookfrom,
lookat,
Vec3::new(0.0, 1.0, 0.0),
20.0,
aspect_ratio,
aperture,
focus_distance,
0.0,
1.0,
)
}
}

View File

@ -1,9 +1,6 @@
use crate::{ use crate::{
materials::{Dielectric, Lambertian, Metal},
shapes::{MovingSphere, Sphere},
texture::{Checker, Solid},
types::{Ray, Vec3}, types::{Ray, Vec3},
BvhNode, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION, Camera, Hitable, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
}; };
use rand::{rngs::SmallRng, Rng, SeedableRng}; use rand::{rngs::SmallRng, Rng, SeedableRng};
use rayon::prelude::*; use rayon::prelude::*;
@ -14,6 +11,12 @@ use std::{
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
mod checkered_motion_blur;
mod two_spheres;
pub use checkered_motion_blur::CheckeredMotionBlur;
pub use two_spheres::TwoSpheres;
#[derive(Debug)] #[derive(Debug)]
pub struct Chunk { pub struct Chunk {
num: usize, num: usize,
@ -39,113 +42,16 @@ impl Display for Chunk {
pub trait ParallelHit: Hitable + Send + Sync {} pub trait ParallelHit: Hitable + Send + Sync {}
impl<T: Hitable + Send + Sync> ParallelHit for T {} impl<T: Hitable + Send + Sync> ParallelHit for T {}
pub struct Demo; pub trait Demo: Send + Sync {
type DemoT: Hitable + Send + Sync;
impl Demo { fn name(&self) -> &'static str;
pub fn name(&self) -> &'static str {
"motion_blur"
}
fn world(&self) -> impl Hitable { fn world(&self) -> Self::DemoT;
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(500);
let mut rng = rand::thread_rng(); fn camera(&self, aspect_ratio: f64) -> Camera;
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
world.push(Arc::new(Sphere::new( fn render_chunk(&self, chunk: &mut Chunk, camera: &Camera, world: &Self::DemoT, samples: u8) {
Vec3::new(0.0, -1000.0, 0.0),
1000.0,
Lambertian::new(Checker::new(
Solid::new(Vec3::new(0.2, 0.3, 0.1)),
Solid::new(Vec3::new(0.9, 0.9, 0.9)),
)),
)));
let radius = 0.2;
let l = Vec3::new(4.0, 0.2, 0.0);
for a in -10..10 {
let a = a as f64;
for b in -10..10 {
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(Arc::new(MovingSphere::new(
center,
center + Vec3::new(0.0, 0.5 * rng.gen::<f64>(), 0.0),
0.0,
1.0,
radius,
Lambertian::new(Solid::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(Arc::new(Sphere::new(
center,
radius,
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(Arc::new(Sphere::new(center, radius, Dielectric::new(1.5))));
}
}
}
}
world.push(Arc::new(Sphere::new(
Vec3::new(0.0, 1.0, 0.0),
1.0,
Dielectric::new(1.5),
)));
world.push(Arc::new(Sphere::new(
Vec3::new(-4.0, 1.0, 0.0),
1.0,
Lambertian::new(Solid::new(Vec3::new(0.4, 0.2, 0.1))),
)));
world.push(Arc::new(Sphere::new(
Vec3::new(4.0, 1.0, 0.0),
1.0,
Metal::with_fuzz(Vec3::new(0.7, 0.6, 0.5), 0.0),
)));
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
}
fn camera(&self, aspect_ratio: f64) -> Camera {
let lookfrom = Vec3::new(13.0, 2.0, 3.0);
let lookat = Vec3::new(0.0, 0.0, 0.0);
let aperture = 0.1;
let focus_distance = 10.0;
Camera::new(
lookfrom,
lookat,
Vec3::new(0.0, 1.0, 0.0),
20.0,
aspect_ratio,
aperture,
focus_distance,
0.0,
1.0,
)
}
fn render_chunk(&self, chunk: &mut Chunk, camera: &Camera, world: &impl Hitable, samples: u8) {
let &mut Chunk { let &mut Chunk {
num: _, num: _,
x, x,
@ -180,7 +86,7 @@ impl Demo {
}); });
} }
pub fn render(&self, buf: &mut Vec<u8>, x: usize, y: usize, samples: u8) { fn render(&self, buf: &mut Vec<u8>, x: usize, y: usize, samples: u8) {
let world = self.world(); let world = self.world();
let delta_x = x / VERTICAL_PARTITION; let delta_x = x / VERTICAL_PARTITION;
let delta_y = y / HORIZONTAL_PARTITION; let delta_y = y / HORIZONTAL_PARTITION;
@ -253,10 +159,16 @@ impl Demo {
} }
} }
pub fn save_as_ppm(&self, buf: &[u8], width: usize, height: usize) { fn save_as_ppm(&self, buf: &[u8], width: usize, height: usize, samples: u8) {
let header = format!("P3\n{} {}\n255\n", width, height); let header = format!("P3\n{} {}\n255\n", width, height);
let mut file = match File::create(&format!("{}-{}x{}.ppm", self.name(), width, height)) { let mut file = match File::create(&format!(
"{}-{}x{}_{}.ppm",
self.name(),
width,
height,
samples,
)) {
Ok(file) => file, Ok(file) => file,
Err(e) => panic!("couldn't create {}: {}", self.name(), e), Err(e) => panic!("couldn't create {}: {}", self.name(), e),
}; };

67
src/demos/two_spheres.rs Normal file
View File

@ -0,0 +1,67 @@
use std::sync::Arc;
use rand::{prelude::SmallRng, SeedableRng};
use crate::{
demos::{Demo, ParallelHit},
materials::Lambertian,
shapes::Sphere,
texture::{Checker, Solid},
types::Vec3,
BvhNode, Camera,
};
pub struct TwoSpheres {}
impl Demo for TwoSpheres {
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
fn name(&self) -> &'static str {
"two_checkered_sphere"
}
fn world(&self) -> Self::DemoT {
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(500);
let mut rng = rand::thread_rng();
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
world.push(Arc::new(Sphere::new(
Vec3::new(0.0, -10.0, 0.0),
10.0,
Lambertian::new(Checker::new(
Solid::new(Vec3::new(0.2, 0.3, 0.1)),
Solid::new(Vec3::new(0.9, 0.9, 0.9)),
)),
)));
world.push(Arc::new(Sphere::new(
Vec3::new(0.0, 10.0, 0.0),
10.0,
Lambertian::new(Checker::new(
Solid::new(Vec3::new(0.2, 0.3, 0.1)),
Solid::new(Vec3::new(0.9, 0.9, 0.9)),
)),
)));
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
}
fn camera(&self, aspect_ratio: f64) -> Camera {
let lookfrom = Vec3::new(13.0, 2.0, 3.0);
let lookat = Vec3::new(0.0, 0.0, 0.0);
let aperture = 0.1;
let focus_distance = 10.0;
Camera::new(
lookfrom,
lookat,
Vec3::new(0.0, 1.0, 0.0),
20.0,
aspect_ratio,
aperture,
focus_distance,
0.0,
1.0,
)
}
}

View File

@ -1,6 +1,6 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{demo::ParallelHit, types::Ray, Aabb, HitRecord, Hitable}; use crate::{demos::ParallelHit, types::Ray, Aabb, HitRecord, Hitable};
pub struct HitableList { pub struct HitableList {
pub list: Vec<Arc<dyn ParallelHit>>, pub list: Vec<Arc<dyn ParallelHit>>,

View File

@ -3,7 +3,7 @@
mod aabb; mod aabb;
mod bvh; mod bvh;
mod camera; mod camera;
mod demo; mod demos;
mod hitable; mod hitable;
mod hitable_list; mod hitable_list;
mod materials; mod materials;
@ -11,17 +11,19 @@ mod shapes;
mod texture; mod texture;
mod types; mod types;
pub use camera::Camera;
pub use aabb::Aabb; pub use aabb::Aabb;
pub use bvh::BvhNode; pub use bvh::BvhNode;
pub use camera::Camera;
pub use hitable::{HitRecord, Hitable}; pub use hitable::{HitRecord, Hitable};
pub use hitable_list::HitableList; pub use hitable_list::HitableList;
pub use materials::Material; pub use materials::Material;
use std::time::Instant;
pub use texture::Texture; pub use texture::Texture;
const NUM_SAMPLES: u8 = 20; use demos::Demo;
use std::time::Instant;
const NUM_SAMPLES: u8 = 25;
const VERTICAL_PARTITION: usize = 12; const VERTICAL_PARTITION: usize = 12;
const HORIZONTAL_PARTITION: usize = 12; const HORIZONTAL_PARTITION: usize = 12;
const WIDTH: usize = 1920; const WIDTH: usize = 1920;
@ -33,16 +35,18 @@ fn main() -> Result<(), String> {
#[cfg(feature = "gui")] #[cfg(feature = "gui")]
fn run(mut width: usize, mut height: usize) -> Result<(), String> { fn run(mut width: usize, mut height: usize) -> Result<(), String> {
use demos::ParallelHit;
use sdl2::{ use sdl2::{
event::{Event, WindowEvent}, event::{Event, WindowEvent},
keyboard::Keycode, keyboard::Keycode,
pixels::PixelFormatEnum, pixels::PixelFormatEnum,
}; };
use std::sync::Arc;
let sdl_ctx = sdl2::init()?; let sdl_ctx = sdl2::init()?;
let video_subsys = sdl_ctx.video()?; let video_subsys = sdl_ctx.video()?;
let window = video_subsys let window = video_subsys
.window("Ray tracing in a weekend", width as u32, height as u32) .window("Ray tracing the Next Week", width as u32, height as u32)
.position_centered() .position_centered()
.build() .build()
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
@ -63,9 +67,10 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
.create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32) .create_texture_static(PixelFormatEnum::BGR888, width as u32, height as u32)
.map_err(|e| e.to_string())?; .map_err(|e| e.to_string())?;
let active_demo = demo::Demo; let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> =
&demos::CheckeredMotionBlur {};
let mut should_update = true; let mut should_update = true;
loop { loop {
for event in event_pump.poll_iter() { for event in event_pump.poll_iter() {
match event { match event {
@ -77,9 +82,17 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
Event::KeyUp { keycode, .. } => { Event::KeyUp { keycode, .. } => {
match keycode { match keycode {
Some(Keycode::S) => { Some(Keycode::S) => {
active_demo.save_as_ppm(&buffer, width, height); active_demo.save_as_ppm(&buffer, width, height, NUM_SAMPLES);
should_update = false; should_update = false;
} }
Some(Keycode::Num1) => {
active_demo = &demos::CheckeredMotionBlur {};
should_update = true;
}
Some(Keycode::Num2) => {
active_demo = &demos::TwoSpheres {};
should_update = true;
}
None => unreachable!(), None => unreachable!(),
_ => (), _ => (),
}; };
@ -107,7 +120,6 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
active_demo.name(), active_demo.name(),
now.elapsed().as_secs_f64() now.elapsed().as_secs_f64()
); );
texture.update(None, &buffer, width * 4).unwrap(); texture.update(None, &buffer, width * 4).unwrap();
canvas.copy(&texture, None, None).unwrap(); canvas.copy(&texture, None, None).unwrap();
canvas.present(); canvas.present();
@ -118,9 +130,16 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
#[cfg(not(feature = "gui"))] #[cfg(not(feature = "gui"))]
fn run(width: usize, height: usize) -> Result<(), String> { fn run(width: usize, height: usize) -> Result<(), String> {
let mut buffer = vec![0; width * height * 4]; run_and_save_demo(demos::CheckeredMotionBlur {}, width, height);
let demo = demo::Demo; run_and_save_demo(demos::TwoSpheres {}, width, height);
Ok(())
}
#[cfg(not(feature = "gui"))]
fn run_and_save_demo(demo: impl Demo, width: usize, height: usize) {
let mut buffer = vec![0; width * height * 4];
println!( println!(
"Starting {} at {}x{} with {} samples", "Starting {} at {}x{} with {} samples",
@ -129,6 +148,7 @@ fn run(width: usize, height: usize) -> Result<(), String> {
height, height,
NUM_SAMPLES NUM_SAMPLES
); );
let now = Instant::now(); let now = Instant::now();
demo.render(&mut buffer, width, height, NUM_SAMPLES); demo.render(&mut buffer, width, height, NUM_SAMPLES);
println!( println!(
@ -137,7 +157,5 @@ fn run(width: usize, height: usize) -> Result<(), String> {
now.elapsed().as_secs_f64() now.elapsed().as_secs_f64()
); );
demo.save_as_ppm(&buffer, width, height); demo.save_as_ppm(&buffer, width, height, NUM_SAMPLES);
Ok(())
} }