Compare commits
12 Commits
35643fef39
...
master
Author | SHA1 | Date | |
---|---|---|---|
dc19f09da9 | |||
ab349b1d7e | |||
cee009f5a8 | |||
526e92f582
|
|||
a0eda53be1 | |||
a63e001edd | |||
a552c4721f | |||
8501bb3a7a | |||
30b972ce5c | |||
560b921b3a | |||
c90b185b8d | |||
c6b0fb59ae |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@
|
|||||||
.idea
|
.idea
|
||||||
*.iml
|
*.iml
|
||||||
.vscode/
|
.vscode/
|
||||||
|
*.png
|
||||||
|
27
Cargo.lock
generated
27
Cargo.lock
generated
@ -14,6 +14,17 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ahash"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"once_cell",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@ -324,6 +335,7 @@ dependencies = [
|
|||||||
"image",
|
"image",
|
||||||
"rand",
|
"rand",
|
||||||
"rayon",
|
"rayon",
|
||||||
|
"tobj",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -332,6 +344,21 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tobj"
|
||||||
|
version = "3.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "deacee3abcc4fd8ff3f0f7c08d4583ab51753ed1d5a3acacd6d5773f640c27d6"
|
||||||
|
dependencies = [
|
||||||
|
"ahash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
|
@ -10,6 +10,7 @@ include = ["/src"]
|
|||||||
image = { version = "0.24.2", default-features = false, features=["png", "jpeg", "jpeg_rayon"] }
|
image = { version = "0.24.2", default-features = false, features=["png", "jpeg", "jpeg_rayon"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
rayon = "1.5.3"
|
rayon = "1.5.3"
|
||||||
|
tobj = "3.2.3"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = 1
|
debug = 1
|
||||||
|
@ -20,9 +20,7 @@ impl Default for Aabb {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Aabb {
|
impl Aabb {
|
||||||
pub fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> bool {
|
pub fn hit(&self, ray: &Ray, mut t_min: f64, mut t_max: f64) -> bool {
|
||||||
let mut t_min = t_min;
|
|
||||||
let mut t_max = t_max;
|
|
||||||
for a in 0..3 {
|
for a in 0..3 {
|
||||||
let inv_d = 1.0 / ray.direction()[a];
|
let inv_d = 1.0 / ray.direction()[a];
|
||||||
let mut t0 = (self.minimum[a] - ray.origin()[a]) * inv_d;
|
let mut t0 = (self.minimum[a] - ray.origin()[a]) * inv_d;
|
||||||
@ -42,11 +40,11 @@ impl Aabb {
|
|||||||
let minimum = Point3::new(
|
let minimum = Point3::new(
|
||||||
self.minimum.x().min(other.minimum.x()),
|
self.minimum.x().min(other.minimum.x()),
|
||||||
self.minimum.y().min(other.minimum.y()),
|
self.minimum.y().min(other.minimum.y()),
|
||||||
self.minimum.y().min(other.minimum.z()));
|
self.minimum.z().min(other.minimum.z()));
|
||||||
let maximum = Point3::new(
|
let maximum = Point3::new(
|
||||||
self.maximum.x().max(other.maximum.x()),
|
self.maximum.x().max(other.maximum.x()),
|
||||||
self.maximum.y().max(other.maximum.y()),
|
self.maximum.y().max(other.maximum.y()),
|
||||||
self.maximum.y().max(other.maximum.z()));
|
self.maximum.z().max(other.maximum.z()));
|
||||||
Aabb {minimum, maximum}
|
Aabb {minimum, maximum}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -83,10 +83,7 @@ impl Hittable for BVH {
|
|||||||
let hit_left = left.hit(ray, t_min, t_max);
|
let hit_left = left.hit(ray, t_min, t_max);
|
||||||
if let Some(hl) = &hit_left { t_max = hl.t };
|
if let Some(hl) = &hit_left { t_max = hl.t };
|
||||||
let hit_right = right.hit(ray, t_min, t_max);
|
let hit_right = right.hit(ray, t_min, t_max);
|
||||||
match hit_left {
|
if hit_right.is_some() { hit_right } else { hit_left }
|
||||||
Some(hit_left) => Some(hit_left),
|
|
||||||
_ => hit_right
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
88
src/constant_medium.rs
Normal file
88
src/constant_medium.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use crate::{Aabb, Color, Material, Ray, Vec3};
|
||||||
|
use crate::hittable::{HitRecord, Hittable};
|
||||||
|
use crate::isotropic::Isotropic;
|
||||||
|
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<Material>
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
pub struct ConstantMedium<H: Hittable> {
|
||||||
|
boundary: H,
|
||||||
|
phase_function: Arc<Material>,
|
||||||
|
neg_inv_density: f64
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: Hittable,> ConstantMedium<H> {
|
||||||
|
pub fn new(boundary: H, phase_function: Arc<Material>, density: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
boundary,
|
||||||
|
phase_function,
|
||||||
|
neg_inv_density: -1.0 / density
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn textured(boundary: H, texture: Arc<dyn Texture>, 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: Hittable>Hittable for ConstantMedium<H>{
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
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::<f64>().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<Aabb> {
|
||||||
|
self.boundary.bounding_box(time0, time1)
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,4 @@
|
|||||||
/*
|
use image::{RgbImage};
|
||||||
let img = image::open("tests/images/jpg/progressive/cat.jpg").unwrap();
|
|
||||||
let rgnbi = img.into_rgb8();
|
|
||||||
rgnbi.get_pixel(0, 0);
|
|
||||||
rgnbi.
|
|
||||||
|
|
||||||
*/
|
|
||||||
use image::{Pixel, RgbImage};
|
|
||||||
use crate::{Color, Point3};
|
use crate::{Color, Point3};
|
||||||
use crate::texture::Texture;
|
use crate::texture::Texture;
|
||||||
|
|
||||||
|
33
src/isotropic.rs
Normal file
33
src/isotropic.rs
Normal file
@ -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<dyn Texture>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Isotropic {
|
||||||
|
pub fn scatter(&self, ray: &Ray, hit_record: &HitRecord) -> Option<(Option<Ray>, 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<Color> for Isotropic {
|
||||||
|
fn from(albedo: Color) -> Self {
|
||||||
|
let texture = SolidColor::from(albedo);
|
||||||
|
Isotropic { albedo: Arc::new(texture) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<Arc<dyn Texture>> for Isotropic {
|
||||||
|
fn from(albedo: Arc<dyn Texture>) -> Self {
|
||||||
|
Isotropic { albedo }
|
||||||
|
}
|
||||||
|
}
|
392
src/main.rs
392
src/main.rs
@ -11,14 +11,18 @@ use rand::distributions::{Distribution, Uniform};
|
|||||||
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IntoParallelIterator, ParallelIterator};
|
||||||
use crate::material::{Dielectric, DiffuseLight, Lambertian, Material, Metal};
|
use crate::material::{Dielectric, DiffuseLight, Lambertian, Material, Metal};
|
||||||
use crate::aabb::Aabb;
|
use crate::aabb::Aabb;
|
||||||
|
use crate::bvh::BVH;
|
||||||
|
use crate::constant_medium::ConstantMedium;
|
||||||
use crate::cuboid::Cuboid;
|
use crate::cuboid::Cuboid;
|
||||||
use crate::image_texture::ImageTexture;
|
use crate::image_texture::ImageTexture;
|
||||||
use crate::noise::NoiseTexture;
|
use crate::noise::NoiseTexture;
|
||||||
|
use crate::obj::obj_to_hitable;
|
||||||
use crate::perlin::Perlin;
|
use crate::perlin::Perlin;
|
||||||
use crate::texture::CheckerTexture;
|
use crate::texture::CheckerTexture;
|
||||||
use crate::rect::{Plane, Rect2D};
|
use crate::rect::{Plane, Rect2D};
|
||||||
use crate::rotate_y::RotateY;
|
use crate::rotate_y::RotateY;
|
||||||
use crate::translate::Translate;
|
use crate::translate::Translate;
|
||||||
|
use crate::triangle::Triangle;
|
||||||
|
|
||||||
mod vec3;
|
mod vec3;
|
||||||
mod ray;
|
mod ray;
|
||||||
@ -36,10 +40,14 @@ mod rect;
|
|||||||
mod cuboid;
|
mod cuboid;
|
||||||
mod translate;
|
mod translate;
|
||||||
mod rotate_y;
|
mod rotate_y;
|
||||||
|
mod constant_medium;
|
||||||
|
mod isotropic;
|
||||||
|
mod obj;
|
||||||
|
mod triangle;
|
||||||
|
|
||||||
// Image
|
// Image
|
||||||
const DEFAULT_ASPECT_RATIO: f64 = 3.0 / 2.0;
|
const DEFAULT_ASPECT_RATIO: f64 = 3.0 / 2.0;
|
||||||
const IMAGE_WIDTH: usize = 400;
|
const IMAGE_WIDTH: usize = 300;
|
||||||
const SAMPLES_PER_PIXEL: i32 = 100;
|
const SAMPLES_PER_PIXEL: i32 = 100;
|
||||||
const MAX_DEPTH: i32 = 50;
|
const MAX_DEPTH: i32 = 50;
|
||||||
|
|
||||||
@ -49,6 +57,66 @@ struct Scene {
|
|||||||
pub background: Color
|
pub background: Color
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn obj(path: &str) -> Scene {
|
||||||
|
let object = obj_to_hitable(&std::path::Path::new(path));
|
||||||
|
print!("Object hitbox: {:#?}", object.bounding_box(0.0, 1.0));
|
||||||
|
|
||||||
|
let difflight = Arc::new(Material::white_light(4.0));
|
||||||
|
|
||||||
|
let world: HittableList = vec![
|
||||||
|
object,
|
||||||
|
Arc::new(Rect2D::new(
|
||||||
|
Plane::XZ,
|
||||||
|
-2.0,
|
||||||
|
2.0,
|
||||||
|
-2.0,
|
||||||
|
2.0,
|
||||||
|
10.0,
|
||||||
|
difflight.clone()
|
||||||
|
)),
|
||||||
|
Arc::new(Sphere::new(
|
||||||
|
Point3::new(-5.0, 5.0, 0.0),
|
||||||
|
1.0,
|
||||||
|
difflight.clone()
|
||||||
|
)),
|
||||||
|
Arc::new(Sphere::new(
|
||||||
|
Point3::new(0.6, 3.5, -0.6),
|
||||||
|
0.4,
|
||||||
|
Arc::new(Material::Metal(Metal::new(Color::new(1.0, 0.0, 0.0), 0.2)))
|
||||||
|
)),
|
||||||
|
Arc::new(Sphere::new(
|
||||||
|
Point3::new(1.7, 0.4, 1.7),
|
||||||
|
0.4,
|
||||||
|
Arc::new(Material::solid(0.2, 0.2, 0.8))
|
||||||
|
)),
|
||||||
|
Arc::new(Sphere::new(
|
||||||
|
Point3::new(0.0, 300.0, 0.0),
|
||||||
|
300.0,
|
||||||
|
Arc::new(Material::Lambertian(Lambertian::textured(Arc::new(NoiseTexture { noise: Perlin::new(), scale: 0.1 }))))
|
||||||
|
))];
|
||||||
|
|
||||||
|
let look_from = Point3::new(8.0, 10.0, 8.0);
|
||||||
|
let look_at = Point3::new(0.0, 1.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,
|
||||||
|
30.0,
|
||||||
|
0.0,
|
||||||
|
focus_dist,
|
||||||
|
0.0,
|
||||||
|
1.0);
|
||||||
|
|
||||||
|
Scene {
|
||||||
|
world,
|
||||||
|
cam,
|
||||||
|
background: Color::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn cornell_box() -> Scene {
|
fn cornell_box() -> Scene {
|
||||||
let mut world:HittableList = Vec::new();
|
let mut world:HittableList = Vec::new();
|
||||||
let red = Material::Lambertian(
|
let red = Material::Lambertian(
|
||||||
@ -117,6 +185,7 @@ fn cornell_box() -> Scene {
|
|||||||
)));
|
)));
|
||||||
|
|
||||||
// Boxes
|
// Boxes
|
||||||
|
|
||||||
let rotated1 = RotateY::new(
|
let rotated1 = RotateY::new(
|
||||||
Cuboid::new(
|
Cuboid::new(
|
||||||
Point3::new(0.0, 0.0, 0.0),
|
Point3::new(0.0, 0.0, 0.0),
|
||||||
@ -144,6 +213,131 @@ fn cornell_box() -> Scene {
|
|||||||
);
|
);
|
||||||
world.push(Arc::new(box2));
|
world.push(Arc::new(box2));
|
||||||
|
|
||||||
|
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 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_from = Point3::new(278.0, 278.0, -800.0);
|
||||||
let look_at = Point3::new(278.0, 278.0, 0.0);
|
let look_at = Point3::new(278.0, 278.0, 0.0);
|
||||||
@ -474,9 +668,176 @@ fn random_scene() -> Scene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next_week_final() -> Scene {
|
||||||
|
let ground = Arc::new(
|
||||||
|
Material::Lambertian(Lambertian::from(Color::new(0.48, 0.84, 0.53))));
|
||||||
|
let boxes_per_side = 20;
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let box_range = Uniform::from(1.0..101.0);
|
||||||
|
let mut boxes1: HittableList = Vec::with_capacity(boxes_per_side*boxes_per_side);
|
||||||
|
for i in 0..boxes_per_side {
|
||||||
|
for j in 0..boxes_per_side {
|
||||||
|
let w = 100.0;
|
||||||
|
let x0 = -1000.0 + (i as f64)*w;
|
||||||
|
let z0 = -1000.0 + (j as f64)*w;
|
||||||
|
let y0 = 0.0;
|
||||||
|
let x1 = x0 + w;
|
||||||
|
let y1 = box_range.sample(&mut rng);
|
||||||
|
let z1 = z0 + w;
|
||||||
|
boxes1.push(Arc::new(Cuboid::new(
|
||||||
|
Vec3::new(x0, y0, z0),
|
||||||
|
Vec3::new(x1, y1, z1),
|
||||||
|
ground.clone()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut world: HittableList = Vec::new();
|
||||||
|
world.push(Arc::new(BVH::new(boxes1, 0.0, 1.0)));
|
||||||
|
|
||||||
|
let light = Arc::new(
|
||||||
|
Material::DiffuseLight(DiffuseLight::from(Color::new(7.0, 7.0, 7.0))));
|
||||||
|
world.push(Arc::new(Rect2D::new(
|
||||||
|
Plane::XZ,
|
||||||
|
123.0,
|
||||||
|
423.0,
|
||||||
|
147.0,
|
||||||
|
412.0,
|
||||||
|
554.0,
|
||||||
|
light
|
||||||
|
)));
|
||||||
|
|
||||||
|
// Moving Sphere
|
||||||
|
let moving_sphere_material = Arc::new(
|
||||||
|
Material::Lambertian(Lambertian::from(Color::new(0.7, 0.3, 0.1))));
|
||||||
|
let center1 = Point3::new(400.0, 400.0, 200.0);
|
||||||
|
world.push(Arc::new(MovableSphere::new(
|
||||||
|
center1,
|
||||||
|
center1 + Vec3::new(30.0, 0.0, 0.0),
|
||||||
|
50.0,
|
||||||
|
moving_sphere_material,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
)));
|
||||||
|
|
||||||
|
let glass = Arc::new(Material::Dielectric(Dielectric::new(1.5)));
|
||||||
|
|
||||||
|
// Glass
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Point3::new(260.0, 150.0, 45.0),
|
||||||
|
50.0,
|
||||||
|
glass.clone()
|
||||||
|
)));
|
||||||
|
|
||||||
|
// Metal sphere
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Point3::new(0.0, 150.0, 145.0),
|
||||||
|
50.0,
|
||||||
|
Arc::new(
|
||||||
|
Material::Metal(Metal::new(Color::new(0.8, 0.8, 0.9), 1.0)))
|
||||||
|
)));
|
||||||
|
let boundary = Sphere::new(
|
||||||
|
Point3::new(360.0, 150.0, 145.0),
|
||||||
|
70.0,
|
||||||
|
glass.clone()
|
||||||
|
);
|
||||||
|
let boundary_clone = Sphere::new(
|
||||||
|
Point3::new(360.0, 150.0, 145.0),
|
||||||
|
70.0,
|
||||||
|
glass.clone()
|
||||||
|
);
|
||||||
|
world.push(Arc::new(boundary_clone));
|
||||||
|
world.push(Arc::new(
|
||||||
|
ConstantMedium::colored(
|
||||||
|
boundary,
|
||||||
|
Color::new(0.2, 0.4, 0.9),
|
||||||
|
0.2)));
|
||||||
|
let boundary = Sphere::new(
|
||||||
|
Point3::new(0.0, 0.0, 0.0),
|
||||||
|
5000.0,
|
||||||
|
glass.clone()
|
||||||
|
);
|
||||||
|
world.push(Arc::new(
|
||||||
|
ConstantMedium::colored(
|
||||||
|
boundary,
|
||||||
|
Color::new(1.0, 1.0, 1.0),
|
||||||
|
0.0001)));
|
||||||
|
|
||||||
|
// Earth
|
||||||
|
let earth_material = Arc::new(Material::Lambertian(
|
||||||
|
Lambertian::textured(
|
||||||
|
Arc::new(ImageTexture::new("textures/earthmap.jpg")))));
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Point3::new(400.0, 200.0, 400.0),
|
||||||
|
100.0,
|
||||||
|
earth_material
|
||||||
|
)));
|
||||||
|
|
||||||
|
// Gray sphere
|
||||||
|
let pertext = Arc::new(Material::Lambertian(Lambertian::textured(
|
||||||
|
Arc::new(NoiseTexture {
|
||||||
|
noise: Perlin::new(),
|
||||||
|
scale: 0.1
|
||||||
|
}))));
|
||||||
|
world.push(Arc::new(Sphere::new(
|
||||||
|
Point3::new(220.0, 280.0, 300.0),
|
||||||
|
80.0,
|
||||||
|
pertext
|
||||||
|
)));
|
||||||
|
|
||||||
|
let ns = 1000;
|
||||||
|
let mut boxes2: HittableList = Vec::with_capacity(ns);
|
||||||
|
let white = Arc::new(Material::Lambertian(
|
||||||
|
Lambertian::from(Color::new(0.73, 0.73, 0.73))));
|
||||||
|
|
||||||
|
|
||||||
|
for _i in 0..ns {
|
||||||
|
let sphere = Sphere::new(
|
||||||
|
Point3::random(0.0, 165.0),
|
||||||
|
10.0,
|
||||||
|
white.clone()
|
||||||
|
);
|
||||||
|
boxes2.push(Arc::new(sphere));
|
||||||
|
}
|
||||||
|
world.push(
|
||||||
|
Arc::new(
|
||||||
|
Translate::new(
|
||||||
|
RotateY::new(
|
||||||
|
BVH::new(boxes2, 0.0, 1.0),
|
||||||
|
15.0
|
||||||
|
),
|
||||||
|
Vec3::new(-100.0, 270.0, 395.0)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
let look_from = Point3::new(478.0, 278.0, -600.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 main() {
|
fn main() {
|
||||||
|
rayon::ThreadPoolBuilder::new().num_threads(1).build_global().unwrap(); // Enable, to reduce load
|
||||||
// World
|
// World
|
||||||
let scene: u8 = 5;
|
let scene: u8 = 8;
|
||||||
let scene_setup = match scene {
|
let scene_setup = match scene {
|
||||||
0 => two_spheres(),
|
0 => two_spheres(),
|
||||||
1 => two_perlin_spheres(),
|
1 => two_perlin_spheres(),
|
||||||
@ -484,6 +845,9 @@ fn main() {
|
|||||||
3 => sun(),
|
3 => sun(),
|
||||||
4 => simple_light(),
|
4 => simple_light(),
|
||||||
5 => cornell_box(),
|
5 => cornell_box(),
|
||||||
|
6 => cornell_box_smoke(),
|
||||||
|
7 => next_week_final(),
|
||||||
|
8 => obj("teapot.obj"),
|
||||||
_ => random_scene(),
|
_ => random_scene(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -493,6 +857,7 @@ fn main() {
|
|||||||
let image_height: usize = (IMAGE_WIDTH as f64 / scene_setup.cam.aspect_ratio()) as usize;
|
let image_height: usize = (IMAGE_WIDTH as f64 / scene_setup.cam.aspect_ratio()) as usize;
|
||||||
let mut pixels = vec![0; IMAGE_WIDTH * image_height * 3];
|
let mut pixels = vec![0; IMAGE_WIDTH * image_height * 3];
|
||||||
let bands: Vec<(usize, &mut [u8])> = pixels.chunks_mut(3).enumerate().collect();
|
let bands: Vec<(usize, &mut [u8])> = pixels.chunks_mut(3).enumerate().collect();
|
||||||
|
|
||||||
let count = Mutex::new(0);
|
let count = Mutex::new(0);
|
||||||
bands.into_par_iter().for_each(|(i, pixel)| {
|
bands.into_par_iter().for_each(|(i, pixel)| {
|
||||||
let row = image_height - (i / IMAGE_WIDTH) - 1;
|
let row = image_height - (i / IMAGE_WIDTH) - 1;
|
||||||
@ -523,26 +888,3 @@ fn main() {
|
|||||||
PNG::write("imc.png", &pixels, IMAGE_WIDTH, image_height).expect("Error writing image: {}");
|
PNG::write("imc.png", &pixels, IMAGE_WIDTH, image_height).expect("Error writing image: {}");
|
||||||
eprintln!("\nDone. Time: {}ms", start.elapsed().as_millis());
|
eprintln!("\nDone. Time: {}ms", start.elapsed().as_millis());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn world_bounding_box(time0: f64, time1: f64, world: &HittableList) -> Option<Aabb> {
|
|
||||||
if world.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut is_first = true;
|
|
||||||
let mut output_box: Aabb = Aabb {
|
|
||||||
minimum: Point3::default(),
|
|
||||||
maximum: Point3::default()
|
|
||||||
};
|
|
||||||
for object in world {
|
|
||||||
if let Some(bb) = object.bounding_box(time0, time1) {
|
|
||||||
output_box = match is_first {
|
|
||||||
true => bb,
|
|
||||||
false => output_box.surrounding(&bb)
|
|
||||||
};
|
|
||||||
is_first = false;
|
|
||||||
} else {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(output_box)
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,7 @@ use std::sync::Arc;
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use crate::hittable::HitRecord;
|
use crate::hittable::HitRecord;
|
||||||
use crate::{Color, Point3, Ray, Vec3};
|
use crate::{Color, Point3, Ray, Vec3};
|
||||||
|
use crate::isotropic::Isotropic;
|
||||||
use crate::texture::{SolidColor, Texture};
|
use crate::texture::{SolidColor, Texture};
|
||||||
|
|
||||||
pub trait Scatterable {
|
pub trait Scatterable {
|
||||||
@ -13,12 +14,25 @@ pub enum Material {
|
|||||||
Lambertian(Lambertian),
|
Lambertian(Lambertian),
|
||||||
Metal(Metal),
|
Metal(Metal),
|
||||||
Dielectric(Dielectric),
|
Dielectric(Dielectric),
|
||||||
DiffuseLight(DiffuseLight)
|
DiffuseLight(DiffuseLight),
|
||||||
|
Isotropic(Isotropic)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Material {
|
impl Default for Material {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Material::Lambertian(Lambertian::from(Color::default()))
|
Material::solid(0.8, 0.8, 0.8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material {
|
||||||
|
pub fn solid(r: f64, g: f64, b: f64) -> Self {
|
||||||
|
Material::Lambertian(Lambertian::from(Color::new(r, g, b)))
|
||||||
|
}
|
||||||
|
pub fn light(r: f64, g: f64, b: f64) -> Self {
|
||||||
|
Material::DiffuseLight(DiffuseLight::from(Color::new(r, g, b)))
|
||||||
|
}
|
||||||
|
pub fn white_light(brightness: f64) -> Self {
|
||||||
|
Material::DiffuseLight(DiffuseLight::from(Color::new(brightness, brightness, brightness)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +42,8 @@ impl Scatterable for Material {
|
|||||||
Material::Lambertian(l) => l.scatter(ray, hit_record),
|
Material::Lambertian(l) => l.scatter(ray, hit_record),
|
||||||
Material::Metal(m) => m.scatter(ray, hit_record),
|
Material::Metal(m) => m.scatter(ray, hit_record),
|
||||||
Material::Dielectric(d) => d.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 {
|
fn emitted(&self, u: f64, v: f64, point: &Point3) -> Color {
|
||||||
|
51
src/obj.rs
Normal file
51
src/obj.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use tobj::LoadOptions;
|
||||||
|
use crate::{BVH, Color, Dielectric, HittableList, HittableObject, Lambertian, Material, Point3, Vec3};
|
||||||
|
use crate::hittable::Hittable;
|
||||||
|
use crate::triangle::Triangle;
|
||||||
|
|
||||||
|
pub fn obj_to_hitable(path: &Path) -> HittableObject {
|
||||||
|
let mut lo = LoadOptions::default();
|
||||||
|
lo.triangulate = true;
|
||||||
|
let (models, materials) = tobj::load_obj(path, &lo)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let default_mat: Arc<Material> = Arc::new(
|
||||||
|
Material::Dielectric(
|
||||||
|
Dielectric::new(1.5)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let mut triangles: HittableList = Vec::with_capacity(models.len());
|
||||||
|
for model in models {
|
||||||
|
let mesh = model.mesh;
|
||||||
|
for f in 0..mesh.indices.len() / 3 {
|
||||||
|
let i0 = mesh.indices[3 * f] as usize;
|
||||||
|
let i1 = mesh.indices[3 * f + 1] as usize;
|
||||||
|
let i2 = mesh.indices[3 * f + 2] as usize;
|
||||||
|
let v0 = Point3::new(
|
||||||
|
mesh.positions[i0 * 3] as f64,
|
||||||
|
mesh.positions[i0 * 3 + 1] as f64,
|
||||||
|
mesh.positions[i0 * 3 + 2] as f64);
|
||||||
|
let v1 = Point3::new(
|
||||||
|
mesh.positions[i1 * 3] as f64,
|
||||||
|
mesh.positions[i1 * 3 + 1] as f64,
|
||||||
|
mesh.positions[i1 * 3 + 2] as f64);
|
||||||
|
let v2 = Point3::new(
|
||||||
|
mesh.positions[i2 * 3] as f64,
|
||||||
|
mesh.positions[i2 * 3 + 1] as f64,
|
||||||
|
mesh.positions[i2 * 3 + 2] as f64);
|
||||||
|
let triangle = if mesh.normals.len() <= i0 * 3 + 2 {
|
||||||
|
Triangle::without_normal(v0, v1, v2, default_mat.clone())
|
||||||
|
} else {
|
||||||
|
let normal = Vec3::new(
|
||||||
|
mesh.normals[i0 * 3] as f64,
|
||||||
|
mesh.normals[i0 * 3 + 1] as f64,
|
||||||
|
mesh.normals[i0 * 3 + 2] as f64);
|
||||||
|
Triangle::new(v0, v1, v2, normal, default_mat.clone())
|
||||||
|
};
|
||||||
|
triangles.push(Arc::new(triangle));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Arc::new(BVH::new(triangles, 0.0, 1.0))
|
||||||
|
}
|
86
src/rotate_y.rs
Normal file
86
src/rotate_y.rs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
use crate::hittable::{HitRecord, Hittable};
|
||||||
|
use crate::{Aabb, Point3, Ray, Vec3};
|
||||||
|
|
||||||
|
pub struct RotateY<H: Hittable> {
|
||||||
|
hittable: H,
|
||||||
|
sin_theta: f64,
|
||||||
|
cos_theta: f64,
|
||||||
|
bounding_box: Option<Aabb>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H: Hittable> RotateY<H> {
|
||||||
|
pub fn new(hittable: H, degrees: f64) -> Self {
|
||||||
|
let radians = degrees.to_radians();
|
||||||
|
let sin_theta = radians.sin();
|
||||||
|
let cos_theta = radians.cos();
|
||||||
|
let mut bounding_box = hittable.bounding_box(0.0, 1.0);
|
||||||
|
|
||||||
|
if let Some(bbox) = bounding_box {
|
||||||
|
let mut min = Point3::new(f64::INFINITY, f64::INFINITY, f64::INFINITY);
|
||||||
|
let mut max = Point3::new(f64::NEG_INFINITY, f64::NEG_INFINITY, f64::NEG_INFINITY);
|
||||||
|
for i in 0..2 {
|
||||||
|
for j in 0..2 {
|
||||||
|
for k in 0..2 {
|
||||||
|
let x = (i as f64)*bbox.maximum.x() + ((1 - i) as f64)*bbox.minimum.x();
|
||||||
|
let y = (i as f64)*bbox.maximum.y() + ((1 - j) as f64)*bbox.minimum.y();
|
||||||
|
let z = (i as f64)*bbox.maximum.z() + ((1 - k) as f64)*bbox.minimum.z();
|
||||||
|
|
||||||
|
let x = cos_theta * x + sin_theta * z;
|
||||||
|
let z = -sin_theta * x + cos_theta * z;
|
||||||
|
|
||||||
|
let tester = Vec3::new(x, y, z);
|
||||||
|
for c in 0..3 {
|
||||||
|
min[c] = min[c].min(tester[c]);
|
||||||
|
max[c] = max[c].max(tester[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bounding_box = Some(Aabb {
|
||||||
|
minimum: min,
|
||||||
|
maximum: max
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Self { hittable, sin_theta, cos_theta, bounding_box }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<H: Hittable> Hittable for RotateY<H> {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
let mut origin = ray.origin();
|
||||||
|
let mut direction = ray.direction();
|
||||||
|
|
||||||
|
origin[0] = self.cos_theta * ray.origin()[0] - self.sin_theta*ray.origin()[2];
|
||||||
|
origin[2] = self.sin_theta * ray.origin()[0] + self.cos_theta*ray.origin()[2];
|
||||||
|
|
||||||
|
direction[0] = self.cos_theta * ray.direction()[0] - self.sin_theta*ray.direction()[2];
|
||||||
|
direction[2] = self.sin_theta * ray.direction()[0] + self.cos_theta*ray.direction()[2];
|
||||||
|
let rotated = Ray::new(origin, direction, ray.time());
|
||||||
|
match self.hittable.hit(&rotated, t_min, t_max) {
|
||||||
|
Some(rec) => {
|
||||||
|
let mut p = rec.point;
|
||||||
|
let mut normal = rec.normal;
|
||||||
|
p[0] = self.cos_theta*rec.point[0] + self.sin_theta * rec.point[2];
|
||||||
|
p[2] = -self.sin_theta*rec.point[0] + self.cos_theta * rec.point[2];
|
||||||
|
|
||||||
|
normal[0] = self.cos_theta*rec.normal[0] + self.sin_theta * rec.normal[2];
|
||||||
|
normal[2] = -self.sin_theta*rec.normal[0] + self.cos_theta * rec.normal[2];
|
||||||
|
let mut new_rec = HitRecord {
|
||||||
|
point: p,
|
||||||
|
normal,
|
||||||
|
t: rec.t,
|
||||||
|
u: rec.u,
|
||||||
|
v: rec.v,
|
||||||
|
front_face: rec.front_face,
|
||||||
|
material: rec.material,
|
||||||
|
};
|
||||||
|
new_rec.normalized(&rotated);
|
||||||
|
Some(new_rec)
|
||||||
|
},
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _time0: f64, _time1: f64) -> Option<Aabb> {
|
||||||
|
self.bounding_box
|
||||||
|
}
|
||||||
|
}
|
@ -18,31 +18,17 @@ impl<H: Hittable> Hittable for Translate<H> {
|
|||||||
ray.origin() - self.offset,
|
ray.origin() - self.offset,
|
||||||
ray.direction(),
|
ray.direction(),
|
||||||
ray.time());
|
ray.time());
|
||||||
match &self.hittable.hit(&moved_ray, t_min, t_max) {
|
self.hittable.hit(&moved_ray, t_min, t_max).map(|mut hr| {
|
||||||
Some(hit_record) => {
|
hr.point += self.offset;
|
||||||
let mut hr = HitRecord {
|
hr
|
||||||
point: hit_record.point,
|
})
|
||||||
material: hit_record.material,
|
|
||||||
front_face: hit_record.front_face, // Maybe need to calc normal and front_face again?
|
|
||||||
normal: hit_record.normal,
|
|
||||||
t: hit_record.t,
|
|
||||||
u: hit_record.u,
|
|
||||||
v: hit_record.v
|
|
||||||
};
|
|
||||||
hr.normalized(ray);
|
|
||||||
Some(hr)
|
|
||||||
}
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bounding_box(&self, time0: f64, time1: f64) -> Option<Aabb> {
|
fn bounding_box(&self, time0: f64, time1: f64) -> Option<Aabb> {
|
||||||
match &self.hittable.bounding_box(time0, time1) {
|
self.hittable.bounding_box(time0, time1).map(|mut b| {
|
||||||
Some(bounding_box) => Some(Aabb {
|
b.minimum += self.offset;
|
||||||
minimum: bounding_box.minimum + self.offset,
|
b.maximum += self.offset;
|
||||||
maximum: bounding_box.maximum + self.offset,
|
b
|
||||||
}),
|
})
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
src/triangle.rs
Normal file
78
src/triangle.rs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use crate::hittable::{HitRecord, Hittable};
|
||||||
|
use crate::{Aabb, Material, Point3, Ray, Vec3};
|
||||||
|
//https://github.com/perliedman/raytracing-in-one-weekend/blob/master/src/geometry/triangle.rs
|
||||||
|
|
||||||
|
pub struct Triangle {
|
||||||
|
a: Point3,
|
||||||
|
b: Point3,
|
||||||
|
c: Point3,
|
||||||
|
normal: Vec3,
|
||||||
|
material: Arc<Material>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Triangle {
|
||||||
|
pub fn new(a: Point3, b: Point3, c: Point3, normal: Vec3, material: Arc<Material>, ) -> Self {
|
||||||
|
Self { a, b, c, normal, material }
|
||||||
|
}
|
||||||
|
pub fn without_normal(a: Point3, b: Point3, c: Point3, material: Arc<Material>, ) -> Self {
|
||||||
|
let normal = (b - a).cross(&(c - a));
|
||||||
|
Self { a, b, c, normal, material }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hittable for Triangle {
|
||||||
|
fn hit(&self, ray: &Ray, t_min: f64, t_max: f64) -> Option<HitRecord> {
|
||||||
|
let ab = self.b - self.a;
|
||||||
|
let ac = self.c - self.a;
|
||||||
|
let pvec = ray.direction().cross(&ac);
|
||||||
|
let det = ab.dot(&pvec);
|
||||||
|
if det.abs() < 1e-4 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let inv_det = 1.0 / det;
|
||||||
|
let tvec = ray.origin() - self.a;
|
||||||
|
let u = tvec.dot(&pvec) * inv_det;
|
||||||
|
if u < 0.0 || u > 1.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let qvec = tvec.cross(&ab);
|
||||||
|
let v = ray.direction().dot(&qvec) * inv_det;
|
||||||
|
if v < 0.0 || u + v > 1.0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
match ac.dot(&qvec) * inv_det {
|
||||||
|
t if t < t_min || t > t_max => None,
|
||||||
|
t => {
|
||||||
|
let mut rec = HitRecord {
|
||||||
|
point: ray.at(t),
|
||||||
|
normal: self.normal,
|
||||||
|
t,
|
||||||
|
u,
|
||||||
|
v,
|
||||||
|
front_face: true,
|
||||||
|
material: &self.material
|
||||||
|
};
|
||||||
|
rec.normalized(ray);
|
||||||
|
Some(rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bounding_box(&self, _time0: f64, _time1: f64) -> Option<Aabb> {
|
||||||
|
Some(Aabb {
|
||||||
|
minimum: Point3::new(
|
||||||
|
self.a.x().min(self.b.x()).min(self.c.x()),
|
||||||
|
self.a.y().min(self.b.y()).min(self.c.y()),
|
||||||
|
self.a.z().min(self.b.z()).min(self.c.z()),
|
||||||
|
),
|
||||||
|
maximum: Point3::new(
|
||||||
|
self.a.x().max(self.b.x()).max(self.c.x()),
|
||||||
|
self.a.y().max(self.b.y()).max(self.c.y()),
|
||||||
|
self.a.z().max(self.b.z()).max(self.c.z()),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
19814
teapot.obj
Normal file
19814
teapot.obj
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user