From 8501bb3a7a44c5624c507a1415c028cf124f4462 Mon Sep 17 00:00:00 2001 From: "max.nuding" Date: Mon, 11 Jul 2022 13:21:57 +0200 Subject: [PATCH] Added volumes --- src/constant_medium.rs | 89 +++++++++++++++++++++++++++ src/isotropic.rs | 33 ++++++++++ src/main.rs | 136 ++++++++++++++++++++++++++++++++++++++++- src/material.rs | 7 ++- 4 files changed, 260 insertions(+), 5 deletions(-) create mode 100644 src/constant_medium.rs create mode 100644 src/isotropic.rs diff --git a/src/constant_medium.rs b/src/constant_medium.rs new file mode 100644 index 0000000..6c64b11 --- /dev/null +++ b/src/constant_medium.rs @@ -0,0 +1,89 @@ +use std::sync::Arc; +use crate::{Aabb, Color, Material, Ray, Vec3}; +use crate::hittable::{HitRecord, Hittable}; +use crate::isotropic::Isotropic; +use crate::material::Scatterable; +use crate::texture::Texture; + +/* +#[derive(Clone, Copy)] +pub struct HitRecord<'material> { + pub point: Point3, + pub normal: Vec3, + pub t: f64, + pub u: f64, + pub v: f64, + pub front_face: bool, + pub material: &'material Arc +} + */ + +pub struct ConstantMedium { + boundary: H, + phase_function: Arc, + neg_inv_density: f64 +} + +impl ConstantMedium { + pub fn new(boundary: H, phase_function: Arc, density: f64) -> Self { + Self { + boundary, + phase_function, + neg_inv_density: -1.0 / density + } + } + pub fn textured(boundary: H, texture: Arc, density: f64) -> Self { + Self { + boundary, + phase_function: Arc::new(Material::Isotropic(Isotropic::from(texture))), + neg_inv_density: -1.0 / density + } + } + pub fn colored(boundary: H, color: Color, density: f64) -> Self { + Self { + boundary, + phase_function: Arc::new(Material::Isotropic(Isotropic::from(color))), + neg_inv_density: -1.0 / density + } + } +} + +implHittable for ConstantMedium{ + fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option { + let rec1 = self.boundary.hit(ray, f64::NEG_INFINITY, f64::INFINITY); + if rec1.is_none() { + return None; + } + let mut rec1 = rec1.unwrap(); + + let rec2 = self.boundary.hit(ray, rec1.t+0.0001, f64::INFINITY); + if rec2.is_none() { + return None; + } + let mut rec2 = rec2.unwrap(); + if rec1.t < t_min { rec1.t = t_min; } + if rec2.t > t_max { rec2.t = t_max; } + if rec1.t > rec2.t { return None; } + if rec1.t < t_min { rec1.t = 0.0; } + + let ray_length = ray.direction().length(); + let distance_inside_boundary = (rec2.t - rec1.t) * ray_length; + let hit_distance = self.neg_inv_density * rand::random::().ln(); + + if hit_distance > distance_inside_boundary { return None; } + let t = rec1.t + hit_distance / ray_length; + Some(HitRecord { + point: ray.at(t), + normal: Vec3::new(1.0, 0.0, 0.0), // arbitrary + t, + u: 0.0, + v: 0.0, + front_face: true, // also arbitrary + material: &self.phase_function + }) + } + + fn bounding_box(&self, time0: f64, time1: f64) -> Option { + self.boundary.bounding_box(time0, time1) + } +} diff --git a/src/isotropic.rs b/src/isotropic.rs new file mode 100644 index 0000000..e64adde --- /dev/null +++ b/src/isotropic.rs @@ -0,0 +1,33 @@ +use std::sync::Arc; +use crate::hittable::HitRecord; +use crate::{Color, Ray, Vec3}; +use crate::texture::{SolidColor, Texture}; + +pub struct Isotropic { + pub albedo: Arc +} + +impl Isotropic { + pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option, Color)> { + Some(( + Some( + Ray::new( + hit_record.point, + Vec3::random_in_unit_sphere(), + ray.time())), + self.albedo.value(hit_record.u, hit_record.v, &hit_record.point) + )) + } +} + +impl From for Isotropic { + fn from(albedo: Color) -> Self { + let texture = SolidColor::from(albedo); + Isotropic { albedo: Arc::new(texture) } + } +} +impl From> for Isotropic { + fn from(albedo: Arc) -> Self { + Isotropic { albedo } + } +} diff --git a/src/main.rs b/src/main.rs index d0a3a93..9a08765 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use rand::distributions::{Distribution, Uniform}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use crate::material::{Dielectric, DiffuseLight, Lambertian, Material, Metal}; use crate::aabb::Aabb; +use crate::constant_medium::ConstantMedium; use crate::cuboid::Cuboid; use crate::image_texture::ImageTexture; use crate::noise::NoiseTexture; @@ -36,11 +37,13 @@ mod rect; mod cuboid; mod translate; mod rotate_y; +mod constant_medium; +mod isotropic; // Image const DEFAULT_ASPECT_RATIO: f64 = 3.0 / 2.0; -const IMAGE_WIDTH: usize = 400; -const SAMPLES_PER_PIXEL: i32 = 100; +const IMAGE_WIDTH: usize = 600; +const SAMPLES_PER_PIXEL: i32 = 200; const MAX_DEPTH: i32 = 50; struct Scene { @@ -167,6 +170,132 @@ fn cornell_box() -> Scene { } } +fn cornell_box_smoke() -> Scene { + let mut world:HittableList = Vec::new(); + let red = Material::Lambertian( + Lambertian::from(Color::new(0.65, 0.05, 0.05))); + let white = Arc::new(Material::Lambertian( + Lambertian::from(Color::new(0.73, 0.73, 0.73)))); + let green = Material::Lambertian( + Lambertian::from(Color::new(0.12, 0.45, 0.15))); + let light = Material::DiffuseLight( + DiffuseLight::from(Color::new(7.0, 7.0, 7.0))); + + // Walls + world.push(Arc::new(Rect2D::new( + Plane::YZ, + 0.0, + 555.0, + 0.0, + 555.0, + 555.0, + Arc::new(green) + ))); + world.push(Arc::new(Rect2D::new( + Plane::YZ, + 0.0, + 555.0, + 0.0, + 555.0, + 0.0, + Arc::new(red) + ))); + world.push(Arc::new(Rect2D::new( + Plane::XZ, + 113.0, + 443.0, + 127.0, + 432.0, + 554.0, + Arc::new(light) + ))); + world.push(Arc::new(Rect2D::new( + Plane::XZ, + 0.0, + 555.0, + 0.0, + 555.0, + 0.0, + white.clone() + ))); + world.push(Arc::new(Rect2D::new( + Plane::XZ, + 0.0, + 555.0, + 0.0, + 555.0, + 555.0, + white.clone() + ))); + world.push(Arc::new(Rect2D::new( + Plane::XY, + 0.0, + 555.0, + 0.0, + 555.0, + 555.0, + white.clone() + ))); + + // Boxes + + let rotated1 = RotateY::new( + Cuboid::new( + Point3::new(0.0, 0.0, 0.0), + Point3::new(165.0, 330.0, 165.0), + white.clone() + ), + 15.0 + ); + let box1 = Translate::new( + rotated1, + Vec3::new(265.0, 0.0, 295.0) + ); + let medium1 = ConstantMedium::colored( + box1, + Color::new(0.0, 0.0, 0.0), + 0.01); + world.push(Arc::new(medium1)); + let rotated2 = RotateY::new( + Cuboid::new( + Point3::new(0.0, 0.0, 0.0), + Point3::new(165.0, 165.0, 165.0), + white.clone() + ), + -18.0 + ); + let box2 = Translate::new( + rotated2, + Vec3::new(130.0,0.0,65.0) + ); + let medium2 = ConstantMedium::colored( + box2, + Color::new(1.0, 1.0, 1.0), + 0.01); + world.push(Arc::new(medium2)); + + let look_from = Point3::new(278.0, 278.0, -800.0); + let look_at = Point3::new(278.0, 278.0, 0.0); + let focus_dist = 2.0; + + let cam = Camera::new( + look_from, + look_at, + Vec3::new(0.0, 1.0, 0.0), + 1.0, + 40.0, + 0.0, + focus_dist, + 0.0, + 1.0); + + Scene { + world, + cam, + background: Color::default() + } +} + fn simple_light() -> Scene { let mut world:HittableList = Vec::new(); @@ -476,7 +605,7 @@ fn random_scene() -> Scene { fn main() { // World - let scene: u8 = 5; + let scene: u8 = 6; let scene_setup = match scene { 0 => two_spheres(), 1 => two_perlin_spheres(), @@ -484,6 +613,7 @@ fn main() { 3 => sun(), 4 => simple_light(), 5 => cornell_box(), + 6 => cornell_box_smoke(), _ => random_scene(), }; diff --git a/src/material.rs b/src/material.rs index 09981f8..00f2dd5 100644 --- a/src/material.rs +++ b/src/material.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use rand::Rng; use crate::hittable::HitRecord; use crate::{Color, Point3, Ray, Vec3}; +use crate::isotropic::Isotropic; use crate::texture::{SolidColor, Texture}; pub trait Scatterable { @@ -13,7 +14,8 @@ pub enum Material { Lambertian(Lambertian), Metal(Metal), Dielectric(Dielectric), - DiffuseLight(DiffuseLight) + DiffuseLight(DiffuseLight), + Isotropic(Isotropic) } impl Default for Material { @@ -28,7 +30,8 @@ impl Scatterable for Material { Material::Lambertian(l) => l.scatter(ray, hit_record), Material::Metal(m) => m.scatter(ray, hit_record), Material::Dielectric(d) => d.scatter(ray, hit_record), - Material::DiffuseLight(l) => l.scatter(ray, hit_record) + Material::DiffuseLight(l) => l.scatter(ray, hit_record), + Material::Isotropic(i) => i.scatter(ray, hit_record) } } fn emitted(&self, u: f64, v: f64, point: &Point3) -> Color {