1
0

Completed isotropic materials and constant medium/volumes

This commit is contained in:
Ishan Jain 2021-04-08 11:09:27 +05:30
parent 73e7085818
commit f9b682a12c
9 changed files with 229 additions and 3 deletions

View File

@ -0,0 +1,119 @@
use std::sync::Arc;
use rand::{prelude::SmallRng, SeedableRng};
use crate::{
demos::{Demo, ParallelHit},
hitable::{
shapes::{Cuboid, RectBuilder},
volume::ConstantMedium,
Hitable,
},
materials::{DiffuseLight, Isotropic, Lambertian, MaterialBuilder},
texture::Solid,
types::Vec3,
BvhNode, Camera,
};
pub struct CornellSmokeAndFog {}
impl Demo for CornellSmokeAndFog {
type DemoT = BvhNode<Arc<dyn ParallelHit>>;
fn name(&self) -> &'static str {
"cornell_smoke_and_fog"
}
fn world(&self) -> Self::DemoT {
let mut world: Vec<Arc<dyn ParallelHit>> = Vec::with_capacity(8);
let mut rng = rand::thread_rng();
let mut rng = SmallRng::from_rng(&mut rng).unwrap();
let red = Lambertian::new(Solid::new(Vec3::new(0.65, 0.05, 0.05)));
let white = Lambertian::new(Solid::new(Vec3::splat(0.73)));
let green = Lambertian::new(Solid::new(Vec3::new(0.12, 0.45, 0.15)));
let light = DiffuseLight::new(Solid::new(Vec3::splat(7.0)));
world.push(Arc::new(
RectBuilder
.y(0.0..=555.0)
.z(0.0..=555.0)
.x(555.0)
.material(green),
));
world.push(Arc::new(
RectBuilder
.y(0.0..=555.0)
.z(0.0..=555.0)
.x(0.0)
.material(red),
));
world.push(Arc::new(
RectBuilder
.x(113.0..=443.0)
.z(127.0..=432.0)
.y(554.0)
.material(light),
));
world.push(Arc::new(
RectBuilder
.x(0.0..=555.0)
.z(0.0..=555.0)
.y(0.0)
.material(white),
));
world.push(Arc::new(
RectBuilder
.x(0.0..=555.0)
.z(0.0..=555.0)
.y(555.0)
.material(white),
));
world.push(Arc::new(
RectBuilder
.x(0.0..=555.0)
.y(0.0..=555.0)
.z(555.0)
.material(white),
));
// Add the two boxes
world.push(Arc::new(ConstantMedium::new(
Cuboid::new(Vec3::splat(0.0), Vec3::new(165.0, 330.0, 165.0), white)
.rotate_y(15.0)
.translate(Vec3::new(265.0, 0.0, 295.0)),
Isotropic::new(Solid::new(Vec3::splat(0.0))),
0.01,
)));
world.push(Arc::new(ConstantMedium::new(
Cuboid::new(Vec3::splat(0.0), Vec3::splat(165.0), white)
.rotate_y(-18.0)
.translate(Vec3::new(130.0, 0.0, 65.0)),
Isotropic::new(Solid::new(Vec3::splat(1.0))),
0.01,
)));
BvhNode::new(&mut rng, &mut world, 0.0, 1.0)
}
fn camera(&self, aspect_ratio: f64) -> Camera {
let lookfrom = Vec3::new(278.0, 278.0, -800.0);
let lookat = Vec3::new(278.0, 278.0, 0.0);
let aperture = 0.1;
let focus_distance = 40.0;
Camera::new(
lookfrom,
lookat,
Vec3::new(0.0, 1.0, 0.0),
40.0,
aspect_ratio,
aperture,
focus_distance,
0.0,
1.0,
)
}
}

View File

@ -13,6 +13,7 @@ use std::{
}; };
mod checkered_motion_blur; mod checkered_motion_blur;
mod cornell_smoke_and_fog;
mod image_texture; mod image_texture;
mod instances; mod instances;
mod perlin_noise_ball; mod perlin_noise_ball;
@ -20,6 +21,7 @@ mod simple_light;
mod two_spheres; mod two_spheres;
pub use checkered_motion_blur::CheckeredMotionBlur; pub use checkered_motion_blur::CheckeredMotionBlur;
pub use cornell_smoke_and_fog::CornellSmokeAndFog;
pub use image_texture::ImageTextureDemo; pub use image_texture::ImageTextureDemo;
pub use instances::Instances; pub use instances::Instances;
pub use perlin_noise_ball::PerlinNoiseBall; pub use perlin_noise_ball::PerlinNoiseBall;

View File

@ -3,6 +3,7 @@ pub mod hitable_list;
mod rotate; mod rotate;
pub mod shapes; pub mod shapes;
mod translate; mod translate;
pub mod volume;
pub use bvh::*; pub use bvh::*;
pub use translate::*; pub use translate::*;

View File

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use crate::{ use crate::{
hitable::{HitRecord, Hitable}, hitable::{HitRecord, Hitable},
types::{Ray, Vec3}, types::{Ray, Vec3},
Aabb, Dimension, X, Y, Z, Aabb, Dimension,
}; };
pub struct Rotate<D1, D2, D3, T: Hitable> { pub struct Rotate<D1, D2, D3, T: Hitable> {
@ -28,7 +28,7 @@ where
let cos_theta = radians.cos(); let cos_theta = radians.cos();
let mut min = Vec3::splat(f64::MAX); let mut min = Vec3::splat(f64::MAX);
let mut max = Vec3::splat(-f64::MAX); let mut max = Vec3::splat(f64::MIN);
let bbox = if let Some(bbox) = object.bounding_box(0.0, 1.0) { let bbox = if let Some(bbox) = object.bounding_box(0.0, 1.0) {
for i in 0..2 { for i in 0..2 {

View File

@ -0,0 +1,63 @@
use crate::{
hitable::{HitRecord, Hitable},
types::{Ray, Vec3},
Aabb, Material,
};
pub struct ConstantMedium<A: Hitable, B: Material> {
neg_inv_density: f64,
boundary: A,
phase_function: B,
}
impl<A: Hitable, B: Material> ConstantMedium<A, B> {
pub fn new(boundary: A, phase_function: B, d: f64) -> Self {
Self {
boundary,
phase_function,
neg_inv_density: -1.0 / d,
}
}
}
impl<A: Hitable, B: Material> Hitable for ConstantMedium<A, B> {
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
let mut hit1 = self.boundary.hit(ray, f64::MIN, f64::MAX)?;
let mut hit2 = self.boundary.hit(ray, hit1.t + 0.0001, f64::MAX)?;
hit1.t = hit1.t.max(t_min);
hit2.t = hit2.t.min(t_max);
if hit1.t >= hit2.t {
return None;
};
hit1.t = hit1.t.max(0.0);
let ray_length = ray.direction.length();
let distance_inside_boundary = (hit2.t - hit1.t) * ray_length;
let hit_distance = self.neg_inv_density * rand::random::<f64>().ln();
if hit_distance > distance_inside_boundary {
return None;
}
let t = hit1.t + hit_distance / ray_length;
Some(HitRecord {
t,
p: ray.point_at_parameter(t),
material: &self.phase_function,
u: 0.0,
v: 0.0,
// Arbitrary
front_face: true,
normal: Vec3::new(1.0, 0.0, 0.0),
})
}
fn bounding_box(&self, t0: f64, t1: f64) -> Option<Aabb> {
self.boundary.bounding_box(t0, t1)
}
}

View File

@ -0,0 +1,3 @@
mod constant_medium;
pub use constant_medium::ConstantMedium;

View File

@ -66,7 +66,8 @@ 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 mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> = &demos::Instances {}; let mut active_demo: &dyn Demo<DemoT = BvhNode<Arc<dyn ParallelHit>>> =
&demos::CornellSmokeAndFog {};
let mut should_update = true; let mut should_update = true;
loop { loop {
@ -107,6 +108,10 @@ fn run(mut width: usize, mut height: usize) -> Result<(), String> {
active_demo = &demos::Instances {}; active_demo = &demos::Instances {};
should_update = true; should_update = true;
} }
Some(Keycode::Num7) => {
active_demo = &demos::CornellSmokeAndFog {};
should_update = true;
}
None => unreachable!(), None => unreachable!(),
_ => (), _ => (),
}; };

View File

@ -0,0 +1,31 @@
use rand::prelude::SmallRng;
use crate::{
hitable::HitRecord,
materials::random_point_in_unit_sphere,
types::{Ray, Vec3},
Material, Texture,
};
pub struct Isotropic<T> {
texture: T,
}
impl<T: Texture> Isotropic<T> {
pub fn new(texture: T) -> Self {
Self { texture }
}
}
impl<T: Texture + Send + Sync> Material for Isotropic<T> {
fn scatter(&self, ray: &Ray, hit_rec: &HitRecord, rng: &mut SmallRng) -> (Vec3, Option<Ray>) {
(
self.texture.value(hit_rec.u, hit_rec.v, hit_rec.p),
Some(Ray::new(
hit_rec.p,
random_point_in_unit_sphere(rng),
ray.time(),
)),
)
}
}

View File

@ -1,10 +1,12 @@
mod dielectric; mod dielectric;
mod diffuse_light; mod diffuse_light;
mod isotropic;
mod lambertian; mod lambertian;
mod metal; mod metal;
pub use dielectric::Dielectric; pub use dielectric::Dielectric;
pub use diffuse_light::DiffuseLight; pub use diffuse_light::DiffuseLight;
pub use isotropic::Isotropic;
pub use lambertian::Lambertian; pub use lambertian::Lambertian;
pub use metal::Metal; pub use metal::Metal;
use rand::{prelude::SmallRng, Rng}; use rand::{prelude::SmallRng, Rng};