Refactored to improve some performance.
1. Removed duplication to increase performance a little bit 2. Fixed some minor rendering related issues 3. Added a Defocus Blur Demo
This commit is contained in:
parent
52c6c76c0d
commit
f75d0d2b7d
|
@ -1,10 +1,19 @@
|
||||||
use crate::types::{Ray, Vec3};
|
use {
|
||||||
|
crate::types::{Ray, Vec3},
|
||||||
|
rand::Rng,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Camera {
|
pub struct Camera {
|
||||||
pub origin: Vec3,
|
origin: Vec3,
|
||||||
pub horizontal: Vec3,
|
horizontal: Vec3,
|
||||||
pub vertical: Vec3,
|
vertical: Vec3,
|
||||||
pub lower_left_corner: Vec3,
|
lower_left_corner: Vec3,
|
||||||
|
lens_radius: f64,
|
||||||
|
|
||||||
|
// position vectors
|
||||||
|
u: Vec3,
|
||||||
|
v: Vec3,
|
||||||
|
w: Vec3,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Camera {
|
impl Camera {
|
||||||
|
@ -13,7 +22,15 @@ impl Camera {
|
||||||
// look_at is the point where camera is looking
|
// look_at is the point where camera is looking
|
||||||
// v_up is camera's up vector. i.e. it points upwards from the camera
|
// v_up is camera's up vector. i.e. it points upwards from the camera
|
||||||
// orthogonal to look_from - look_at vector
|
// orthogonal to look_from - look_at vector
|
||||||
pub fn new(look_from: Vec3, look_at: Vec3, v_up: Vec3, vertical_fov: f64, aspect: f64) -> Self {
|
pub fn new(
|
||||||
|
look_from: Vec3,
|
||||||
|
look_at: Vec3,
|
||||||
|
v_up: Vec3,
|
||||||
|
vertical_fov: f64,
|
||||||
|
aspect: f64,
|
||||||
|
aperture: f64,
|
||||||
|
focus_distance: f64,
|
||||||
|
) -> Self {
|
||||||
// convert degree to radian
|
// convert degree to radian
|
||||||
let angle = vertical_fov * std::f64::consts::PI / 180.0;
|
let angle = vertical_fov * std::f64::consts::PI / 180.0;
|
||||||
let half_height = (angle / 2.0).tan();
|
let half_height = (angle / 2.0).tan();
|
||||||
|
@ -24,35 +41,42 @@ impl Camera {
|
||||||
let u = v_up.cross(&w).unit_vector();
|
let u = v_up.cross(&w).unit_vector();
|
||||||
let v = w.cross(&u);
|
let v = w.cross(&u);
|
||||||
|
|
||||||
let lower_left_corner = origin - u * half_width - v * half_height - w;
|
let lower_left_corner = origin
|
||||||
|
- u * focus_distance * half_width
|
||||||
|
- v * focus_distance * half_height
|
||||||
|
- w * focus_distance;
|
||||||
let horizontal = u * half_width * 2.0;
|
let horizontal = u * half_width * 2.0;
|
||||||
let vertical = v * half_height * 2.0;
|
let vertical = v * half_height * 2.0;
|
||||||
|
let lens_radius = aperture / 2.0;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
lens_radius,
|
||||||
lower_left_corner,
|
lower_left_corner,
|
||||||
horizontal,
|
horizontal,
|
||||||
vertical,
|
vertical,
|
||||||
origin,
|
origin,
|
||||||
|
u,
|
||||||
|
v,
|
||||||
|
w,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_ray(&self, u: f64, v: f64) -> Ray {
|
pub fn get_ray(&self, u: f64, v: f64) -> Ray {
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let rd = random_in_unit_disk(&mut rng) * self.lens_radius;
|
||||||
|
let offset = self.u * rd.x() + self.v * rd.y();
|
||||||
Ray::new(
|
Ray::new(
|
||||||
self.origin,
|
self.origin + offset,
|
||||||
self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin,
|
self.lower_left_corner + self.horizontal * u + self.vertical * v - self.origin - offset,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::default::Default for Camera {
|
fn random_in_unit_disk(rng: &mut rand::rngs::ThreadRng) -> Vec3 {
|
||||||
fn default() -> Self {
|
let mut p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0);
|
||||||
Camera::new(
|
|
||||||
Vec3::new(0.0, 0.0, 0.0),
|
while p.dot(&p) >= 1.0 {
|
||||||
Vec3::new(0.0, 0.0, -1.0),
|
p = Vec3::new(rng.gen::<f64>(), rng.gen::<f64>(), 0.0) * 2.0 - Vec3::new(1.0, 0.0, 0.0);
|
||||||
Vec3::new(0.0, 1.0, 0.0),
|
|
||||||
90.0,
|
|
||||||
// 2:1 aspect ratio width:height
|
|
||||||
2.0,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
p
|
||||||
}
|
}
|
||||||
|
|
125
src/demos/defocus_blur.rs
Normal file
125
src/demos/defocus_blur.rs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
use {
|
||||||
|
crate::{
|
||||||
|
demos::{Chunk, Demo},
|
||||||
|
types::{
|
||||||
|
material::{Dielectric, Lambertian, Metal},
|
||||||
|
Hitable, HitableList, Ray, Sphere, Vec3,
|
||||||
|
},
|
||||||
|
Camera,
|
||||||
|
},
|
||||||
|
rand::Rng,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct DefocusBlur;
|
||||||
|
|
||||||
|
impl Demo for DefocusBlur {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"defocus-blur"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn world(&self) -> Option<HitableList> {
|
||||||
|
let radius = (std::f64::consts::PI / 4.0).cos();
|
||||||
|
Some(HitableList {
|
||||||
|
list: vec![
|
||||||
|
Box::new(Sphere::with_material(
|
||||||
|
Vec3::new(-radius, 0.0, -1.0),
|
||||||
|
radius,
|
||||||
|
Box::new(Lambertian::new(Vec3::new(0.0, 0.0, 1.0))),
|
||||||
|
)),
|
||||||
|
Box::new(Sphere::with_material(
|
||||||
|
Vec3::new(radius, 0.0, -1.0),
|
||||||
|
radius,
|
||||||
|
Box::new(Metal::with_fuzz(Vec3::new(0.5, 0.5, 0.0), 0.2)),
|
||||||
|
)),
|
||||||
|
Box::new(Sphere::with_material(
|
||||||
|
Vec3::new(-3.0 * radius, 0.0, -1.0),
|
||||||
|
radius,
|
||||||
|
Box::new(Dielectric::new(1.5)),
|
||||||
|
)),
|
||||||
|
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))),
|
||||||
|
)),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
|
||||||
|
let lookfrom = Vec3::new(3.0, 3.0, 2.0);
|
||||||
|
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
||||||
|
let aperture = 2.0;
|
||||||
|
let distance_to_focus = (lookfrom - lookat).length();
|
||||||
|
let camera = Camera::new(
|
||||||
|
lookfrom,
|
||||||
|
lookat,
|
||||||
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
20.0,
|
||||||
|
aspect_ratio,
|
||||||
|
aperture,
|
||||||
|
distance_to_focus,
|
||||||
|
);
|
||||||
|
Some(camera)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
camera: Option<&Camera>,
|
||||||
|
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 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
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,16 +17,8 @@ impl Demo for DielectricMaterial {
|
||||||
"dielectric-material"
|
"dielectric-material"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, samples: u8) {
|
fn world(&self) -> Option<HitableList> {
|
||||||
let x = chunk.x;
|
Some(HitableList {
|
||||||
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 world = HitableList {
|
|
||||||
list: vec![
|
list: vec![
|
||||||
Box::new(Sphere::with_material(
|
Box::new(Sphere::with_material(
|
||||||
Vec3::new(0.0, 0.0, -1.0),
|
Vec3::new(0.0, 0.0, -1.0),
|
||||||
|
@ -54,9 +46,26 @@ impl Demo for DielectricMaterial {
|
||||||
Box::new(Dielectric::new(1.5)),
|
Box::new(Dielectric::new(1.5)),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
camera: Option<&Camera>,
|
||||||
|
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 camera = camera.unwrap();
|
||||||
|
let world = world.unwrap();
|
||||||
|
|
||||||
let camera: Camera = Default::default();
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,22 @@ impl Demo for DiffuseMaterials {
|
||||||
"diffuse-materials"
|
"diffuse-materials"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, samples: u8) {
|
fn world(&self) -> Option<HitableList> {
|
||||||
|
Some(HitableList {
|
||||||
|
list: vec![
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
camera: Option<&Camera>,
|
||||||
|
world: Option<&HitableList>,
|
||||||
|
samples: u8,
|
||||||
|
) {
|
||||||
let x = chunk.x;
|
let x = chunk.x;
|
||||||
let y = chunk.y;
|
let y = chunk.y;
|
||||||
let nx = chunk.nx;
|
let nx = chunk.nx;
|
||||||
|
@ -22,15 +37,9 @@ impl Demo for DiffuseMaterials {
|
||||||
let start_x = chunk.start_x;
|
let start_x = chunk.start_x;
|
||||||
let start_y = chunk.start_y;
|
let start_y = chunk.start_y;
|
||||||
let buffer = &mut chunk.buffer;
|
let buffer = &mut chunk.buffer;
|
||||||
|
let camera = camera.unwrap();
|
||||||
|
let world = world.unwrap();
|
||||||
|
|
||||||
let world = HitableList {
|
|
||||||
list: vec![
|
|
||||||
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
|
||||||
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let camera: Camera = Default::default();
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in start_y..start_y + ny {
|
for j in start_y..start_y + ny {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{Hitable, HitableList, Ray, Sphere, Vec3},
|
types::{Hitable, HitableList, Ray, Sphere, Vec3},
|
||||||
|
Camera,
|
||||||
};
|
};
|
||||||
pub struct HitableSphere;
|
pub struct HitableSphere;
|
||||||
|
|
||||||
|
@ -9,7 +10,22 @@ impl Demo for HitableSphere {
|
||||||
"sphere-using-hit-table"
|
"sphere-using-hit-table"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, _samples: u8) {
|
fn world(&self) -> Option<HitableList> {
|
||||||
|
Some(HitableList {
|
||||||
|
list: vec![
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
_camera: Option<&Camera>,
|
||||||
|
world: Option<&HitableList>,
|
||||||
|
_samples: u8,
|
||||||
|
) {
|
||||||
let x = chunk.x;
|
let x = chunk.x;
|
||||||
let y = chunk.y;
|
let y = chunk.y;
|
||||||
let nx = chunk.nx;
|
let nx = chunk.nx;
|
||||||
|
@ -17,18 +33,13 @@ impl Demo for HitableSphere {
|
||||||
let start_x = chunk.start_x;
|
let start_x = chunk.start_x;
|
||||||
let start_y = chunk.start_y;
|
let start_y = chunk.start_y;
|
||||||
let buffer = &mut chunk.buffer;
|
let buffer = &mut chunk.buffer;
|
||||||
|
let world = world.unwrap();
|
||||||
|
|
||||||
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
let lower_left_corner = Vec3::new(-2.0, -1.0, -1.0);
|
||||||
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
let horizontal = Vec3::new(4.0, 0.0, 0.0);
|
||||||
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
let vertical = Vec3::new(0.0, 2.0, 0.0);
|
||||||
let origin = Vec3::new(0.0, 0.0, 0.0);
|
let origin = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
let world = HitableList {
|
|
||||||
list: vec![
|
|
||||||
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
|
||||||
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
for j in start_y..start_y + ny {
|
for j in start_y..start_y + ny {
|
||||||
for i in start_x..start_x + nx {
|
for i in start_x..start_x + nx {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
pub struct LinearGradientRectangle;
|
pub struct LinearGradientRectangle;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{Ray, Vec3},
|
types::{HitableList, Ray, Vec3},
|
||||||
|
Camera,
|
||||||
{demos::Chunk, Demo},
|
{demos::Chunk, Demo},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -10,7 +11,13 @@ impl Demo for LinearGradientRectangle {
|
||||||
"linear-gradient-rectangle"
|
"linear-gradient-rectangle"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, _samples: u8) {
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
_camera: Option<&Camera>,
|
||||||
|
_world: Option<&HitableList>,
|
||||||
|
_samples: u8,
|
||||||
|
) {
|
||||||
let x = chunk.x;
|
let x = chunk.x;
|
||||||
let y = chunk.y;
|
let y = chunk.y;
|
||||||
let nx = chunk.nx;
|
let nx = chunk.nx;
|
||||||
|
|
|
@ -17,16 +17,8 @@ impl Demo for Materials {
|
||||||
"metal-material"
|
"metal-material"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, samples: u8) {
|
fn world(&self) -> Option<HitableList> {
|
||||||
let x = chunk.x;
|
Some(HitableList {
|
||||||
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 world = HitableList {
|
|
||||||
list: vec![
|
list: vec![
|
||||||
Box::new(Sphere::with_material(
|
Box::new(Sphere::with_material(
|
||||||
Vec3::new(0.0, 0.0, -1.0),
|
Vec3::new(0.0, 0.0, -1.0),
|
||||||
|
@ -49,9 +41,25 @@ impl Demo for Materials {
|
||||||
Box::new(Metal::with_fuzz(Vec3::new(0.8, 0.8, 0.8), 0.5)),
|
Box::new(Metal::with_fuzz(Vec3::new(0.8, 0.8, 0.8), 0.5)),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
camera: Option<&Camera>,
|
||||||
|
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 camera = camera.unwrap();
|
||||||
|
|
||||||
let camera: Camera = Default::default();
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
|
@ -63,7 +71,7 @@ impl Demo for Materials {
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
||||||
|
|
||||||
let ray = camera.get_ray(u, v);
|
let ray = camera.get_ray(u, v);
|
||||||
color += calc_color(ray, &world, 0);
|
color += calc_color(ray, world.unwrap(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f64;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
mod defocus_blur;
|
||||||
mod dielectric_material;
|
mod dielectric_material;
|
||||||
mod diffuse_materials;
|
mod diffuse_materials;
|
||||||
mod hitable_sphere;
|
mod hitable_sphere;
|
||||||
|
@ -9,6 +10,7 @@ mod simple_rectangle;
|
||||||
mod simple_sphere;
|
mod simple_sphere;
|
||||||
mod surface_normal_sphere;
|
mod surface_normal_sphere;
|
||||||
|
|
||||||
|
pub use defocus_blur::DefocusBlur;
|
||||||
pub use dielectric_material::DielectricMaterial;
|
pub use dielectric_material::DielectricMaterial;
|
||||||
pub use diffuse_materials::DiffuseMaterials;
|
pub use diffuse_materials::DiffuseMaterials;
|
||||||
pub use hitable_sphere::HitableSphere;
|
pub use hitable_sphere::HitableSphere;
|
||||||
|
@ -21,7 +23,10 @@ pub use simple_sphere::SimpleSphere;
|
||||||
pub use surface_normal_sphere::SurfaceNormalSphere;
|
pub use surface_normal_sphere::SurfaceNormalSphere;
|
||||||
|
|
||||||
use {
|
use {
|
||||||
crate::{HORIZONTAL_PARTITION, VERTICAL_PARTITION},
|
crate::{
|
||||||
|
types::{HitableList, Vec3},
|
||||||
|
Camera, HORIZONTAL_PARTITION, VERTICAL_PARTITION,
|
||||||
|
},
|
||||||
rayon::prelude::*,
|
rayon::prelude::*,
|
||||||
std::{
|
std::{
|
||||||
fs::File,
|
fs::File,
|
||||||
|
@ -45,14 +50,14 @@ pub trait Demo: std::marker::Sync {
|
||||||
fn render(&self, buf: &mut Vec<u8>, width: usize, height: usize, samples: u8) {
|
fn render(&self, buf: &mut Vec<u8>, width: usize, height: usize, samples: u8) {
|
||||||
let nx = width / VERTICAL_PARTITION;
|
let nx = width / VERTICAL_PARTITION;
|
||||||
let ny = height / HORIZONTAL_PARTITION;
|
let ny = height / HORIZONTAL_PARTITION;
|
||||||
|
let world = self.world();
|
||||||
|
let camera = self.camera(nx as f64 / ny as f64);
|
||||||
|
|
||||||
let buf = Arc::new(Mutex::new(buf));
|
let buf = Arc::new(Mutex::new(buf));
|
||||||
|
|
||||||
(0..VERTICAL_PARTITION).into_par_iter().for_each(move |j| {
|
(0..VERTICAL_PARTITION).into_par_iter().for_each(|j| {
|
||||||
let buf = buf.clone();
|
let buf = buf.clone();
|
||||||
(0..HORIZONTAL_PARTITION)
|
(0..HORIZONTAL_PARTITION).into_par_iter().for_each(|i| {
|
||||||
.into_par_iter()
|
|
||||||
.for_each(move |i| {
|
|
||||||
let start_y = j * ny;
|
let start_y = j * ny;
|
||||||
let start_x = i * nx;
|
let start_x = i * nx;
|
||||||
let x = width;
|
let x = width;
|
||||||
|
@ -66,7 +71,7 @@ pub trait Demo: std::marker::Sync {
|
||||||
start_y,
|
start_y,
|
||||||
buffer: vec![0; nx * ny * 4],
|
buffer: vec![0; nx * ny * 4],
|
||||||
};
|
};
|
||||||
self.render_chunk(&mut chunk, samples);
|
self.render_chunk(&mut chunk, camera.as_ref(), world.as_ref(), samples);
|
||||||
|
|
||||||
let mut buf = buf.lock().unwrap();
|
let mut buf = buf.lock().unwrap();
|
||||||
|
|
||||||
|
@ -83,7 +88,31 @@ pub trait Demo: std::marker::Sync {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, samples: u8);
|
fn world(&self) -> Option<HitableList> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
|
||||||
|
let lookfrom = Vec3::new(0.0, 0.0, 0.0);
|
||||||
|
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
||||||
|
Some(Camera::new(
|
||||||
|
lookfrom,
|
||||||
|
lookat,
|
||||||
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
|
90.0,
|
||||||
|
aspect_ratio,
|
||||||
|
0.0,
|
||||||
|
1.0,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
camera: Option<&Camera>,
|
||||||
|
world: Option<&HitableList>,
|
||||||
|
samples: u8,
|
||||||
|
);
|
||||||
|
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
|
|
||||||
|
|
|
@ -17,17 +17,9 @@ impl Demo for PositionableCamera {
|
||||||
"positionable-camera"
|
"positionable-camera"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, samples: u8) {
|
fn world(&self) -> Option<HitableList> {
|
||||||
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 radius = (std::f64::consts::PI / 4.0).cos();
|
let radius = (std::f64::consts::PI / 4.0).cos();
|
||||||
|
Some(HitableList {
|
||||||
let world = HitableList {
|
|
||||||
list: vec![
|
list: vec![
|
||||||
Box::new(Sphere::with_material(
|
Box::new(Sphere::with_material(
|
||||||
Vec3::new(-radius, 0.0, -1.0),
|
Vec3::new(-radius, 0.0, -1.0),
|
||||||
|
@ -50,15 +42,39 @@ impl Demo for PositionableCamera {
|
||||||
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.0))),
|
Box::new(Lambertian::new(Vec3::new(0.8, 0.8, 0.0))),
|
||||||
)),
|
)),
|
||||||
],
|
],
|
||||||
};
|
})
|
||||||
|
}
|
||||||
|
|
||||||
let camera = Camera::new(
|
fn camera(&self, aspect_ratio: f64) -> Option<Camera> {
|
||||||
Vec3::new(-2.0, 2.0, 1.0),
|
let lookfrom = Vec3::new(-2.0, 2.0, 1.0);
|
||||||
Vec3::new(0.0, 0.0, -1.0),
|
let lookat = Vec3::new(0.0, 0.0, -1.0);
|
||||||
|
Some(Camera::new(
|
||||||
|
lookfrom,
|
||||||
|
lookat,
|
||||||
Vec3::new(0.0, 1.0, 0.0),
|
Vec3::new(0.0, 1.0, 0.0),
|
||||||
68.0,
|
68.0,
|
||||||
nx as f64 / ny as f64,
|
aspect_ratio,
|
||||||
);
|
0.0,
|
||||||
|
1.0,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
camera: Option<&Camera>,
|
||||||
|
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 camera = camera.unwrap();
|
||||||
|
let world = world.unwrap();
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
|
@ -72,9 +88,7 @@ impl Demo for PositionableCamera {
|
||||||
let ray = camera.get_ray(u, v);
|
let ray = camera.get_ray(u, v);
|
||||||
color += calc_color(ray, &world, 0);
|
color += calc_color(ray, &world, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
color /= samples as f64;
|
color /= samples as f64;
|
||||||
|
|
||||||
// gamma 2 corrected
|
// gamma 2 corrected
|
||||||
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
buffer[offset] = (255.99 * color.r().sqrt()) as u8;
|
||||||
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
buffer[offset + 1] = (255.99 * color.g().sqrt()) as u8;
|
||||||
|
|
|
@ -12,7 +12,23 @@ impl Demo for SimpleAntialiasing {
|
||||||
fn name(&self) -> &'static str {
|
fn name(&self) -> &'static str {
|
||||||
"simple-antialiasing"
|
"simple-antialiasing"
|
||||||
}
|
}
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, samples: u8) {
|
|
||||||
|
fn world(&self) -> Option<HitableList> {
|
||||||
|
Some(HitableList {
|
||||||
|
list: vec![
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
||||||
|
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
camera: Option<&Camera>,
|
||||||
|
world: Option<&HitableList>,
|
||||||
|
samples: u8,
|
||||||
|
) {
|
||||||
let x = chunk.x;
|
let x = chunk.x;
|
||||||
let y = chunk.y;
|
let y = chunk.y;
|
||||||
let nx = chunk.nx;
|
let nx = chunk.nx;
|
||||||
|
@ -20,15 +36,8 @@ impl Demo for SimpleAntialiasing {
|
||||||
let start_x = chunk.start_x;
|
let start_x = chunk.start_x;
|
||||||
let start_y = chunk.start_y;
|
let start_y = chunk.start_y;
|
||||||
let buffer = &mut chunk.buffer;
|
let buffer = &mut chunk.buffer;
|
||||||
|
let camera = camera.unwrap();
|
||||||
|
|
||||||
let world = HitableList {
|
|
||||||
list: vec![
|
|
||||||
Box::new(Sphere::new(Vec3::new(0.0, 0.0, -1.0), 0.5)),
|
|
||||||
Box::new(Sphere::new(Vec3::new(0.0, -100.5, -1.0), 100.0)),
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
let camera: Camera = Default::default();
|
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
let mut offset = 0;
|
let mut offset = 0;
|
||||||
|
|
||||||
|
@ -40,7 +49,7 @@ impl Demo for SimpleAntialiasing {
|
||||||
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
let v = (j as f64 + rng.gen::<f64>()) / y as f64;
|
||||||
|
|
||||||
let r = camera.get_ray(u, v);
|
let r = camera.get_ray(u, v);
|
||||||
color += calc_color(r, &world);
|
color += calc_color(r, world.unwrap());
|
||||||
}
|
}
|
||||||
color /= samples as f64;
|
color /= samples as f64;
|
||||||
buffer[offset] = (255.99 * color.r()) as u8;
|
buffer[offset] = (255.99 * color.r()) as u8;
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
use crate::demos::{Chunk, Demo};
|
use crate::{
|
||||||
|
demos::{Chunk, Demo},
|
||||||
|
types::HitableList,
|
||||||
|
Camera,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct SimpleRectangle;
|
pub struct SimpleRectangle;
|
||||||
|
|
||||||
|
@ -7,7 +11,13 @@ impl Demo for SimpleRectangle {
|
||||||
"simple_rectangle"
|
"simple_rectangle"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, _samples: u8) {
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
_camera: Option<&Camera>,
|
||||||
|
_world: Option<&HitableList>,
|
||||||
|
_samples: u8,
|
||||||
|
) {
|
||||||
let x = chunk.x;
|
let x = chunk.x;
|
||||||
let y = chunk.y;
|
let y = chunk.y;
|
||||||
let nx = chunk.nx;
|
let nx = chunk.nx;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{Ray, Vec3},
|
types::{HitableList, Ray, Vec3},
|
||||||
|
Camera,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RADIUS: f64 = 0.5;
|
const RADIUS: f64 = 0.5;
|
||||||
|
@ -12,7 +13,13 @@ impl Demo for SimpleSphere {
|
||||||
"simple_sphere"
|
"simple_sphere"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, _samples: u8) {
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
_camera: Option<&Camera>,
|
||||||
|
_world: Option<&HitableList>,
|
||||||
|
_samples: u8,
|
||||||
|
) {
|
||||||
let x = chunk.x;
|
let x = chunk.x;
|
||||||
let y = chunk.y;
|
let y = chunk.y;
|
||||||
let nx = chunk.nx;
|
let nx = chunk.nx;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
demos::{Chunk, Demo},
|
demos::{Chunk, Demo},
|
||||||
types::{Ray, Vec3},
|
types::{HitableList, Ray, Vec3},
|
||||||
|
Camera,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RADIUS: f64 = 0.5;
|
const RADIUS: f64 = 0.5;
|
||||||
|
@ -11,7 +12,13 @@ impl Demo for SurfaceNormalSphere {
|
||||||
"surface_normal_sphere"
|
"surface_normal_sphere"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_chunk(&self, chunk: &mut Chunk, _samples: u8) {
|
fn render_chunk(
|
||||||
|
&self,
|
||||||
|
chunk: &mut Chunk,
|
||||||
|
_camera: Option<&Camera>,
|
||||||
|
_world: Option<&HitableList>,
|
||||||
|
_samples: u8,
|
||||||
|
) {
|
||||||
let x = chunk.x;
|
let x = chunk.x;
|
||||||
let y = chunk.y;
|
let y = chunk.y;
|
||||||
let nx = chunk.nx;
|
let nx = chunk.nx;
|
||||||
|
|
|
@ -80,6 +80,7 @@ fn main() -> Result<(), String> {
|
||||||
Some(Keycode::Num8) => active_demo = Box::new(demos::Materials),
|
Some(Keycode::Num8) => active_demo = Box::new(demos::Materials),
|
||||||
Some(Keycode::Num9) => active_demo = Box::new(demos::DielectricMaterial),
|
Some(Keycode::Num9) => active_demo = Box::new(demos::DielectricMaterial),
|
||||||
Some(Keycode::Num0) => active_demo = Box::new(demos::PositionableCamera),
|
Some(Keycode::Num0) => active_demo = Box::new(demos::PositionableCamera),
|
||||||
|
Some(Keycode::Minus) => active_demo = Box::new(demos::DefocusBlur),
|
||||||
None => unreachable!(),
|
None => unreachable!(),
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub struct HitRecord<'a> {
|
||||||
pub material: Option<&'a Box<dyn Material>>,
|
pub material: Option<&'a Box<dyn Material>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Hitable {
|
pub trait Hitable: Send + Sync {
|
||||||
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord> {
|
fn hit(&self, _ray: &Ray, _t_min: f64, _t_max: f64) -> Option<HitRecord> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use {
|
||||||
rand::Rng,
|
rand::Rng,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub trait Material {
|
pub trait Material: Send + Sync {
|
||||||
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord) -> (Vec3, Option<Ray>);
|
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord) -> (Vec3, Option<Ray>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,8 @@ impl Material for Dielectric {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Polynomial approximation to figure out reflectivity as the angle changes
|
// Christophe Schlick's Polynomial approximation to figure out reflectivity as the angle changes
|
||||||
|
// See Fresnel Equations, https://en.wikipedia.org/wiki/Fresnel_equations
|
||||||
fn schlick(cosine: f64, reflection_index: f64) -> f64 {
|
fn schlick(cosine: f64, reflection_index: f64) -> f64 {
|
||||||
let mut r0 = (1.0 - reflection_index) / (1.0 + reflection_index);
|
let mut r0 = (1.0 - reflection_index) / (1.0 + reflection_index);
|
||||||
r0 = r0 * r0;
|
r0 = r0 * r0;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user