├── .gitignore ├── render ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── 4.jpg ├── 5.jpg ├── 6.jpg ├── 7.jpg ├── 8.jpg ├── 9.jpg ├── 10.jpg ├── 11.jpg ├── 12.jpg ├── 13.jpg ├── 14.jpg ├── 15.jpg ├── 16.jpg ├── 17.jpg ├── 18.jpg ├── 19.jpg ├── 20.jpg ├── 21.jpg ├── 22.jpg ├── 23.jpg ├── 24.jpg ├── 25.jpg ├── 26.jpg ├── 27.jpg ├── 28.jpg ├── 29.jpg ├── 30.jpg ├── 31.jpg ├── 32.jpg ├── 33.jpg ├── 34.jpg ├── 35.jpg ├── 36.jpg ├── 37.jpg ├── 38.jpg ├── 39.jpg ├── 40.jpg ├── 41.jpg ├── 42.jpg ├── 43.jpg ├── 44.jpg ├── 45.jpg ├── 46.jpg ├── 47.jpg ├── 48.jpg ├── 49.jpg ├── 50.jpg ├── 51.jpg ├── 52.jpg ├── 53.jpg ├── 54.jpg ├── 36-bug.jpg ├── compare.jpg └── 54-with-monte-carlo.jpg ├── texture └── earthmap.jpg ├── Cargo.toml ├── LICENSE ├── src ├── transf.rs ├── pdf.rs ├── camera.rs ├── util.rs ├── perlin.rs ├── texture.rs ├── main.rs ├── bvh.rs ├── obj.rs ├── material.rs ├── vec3.rs ├── hit.rs └── scene.rs └── README.org /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock -------------------------------------------------------------------------------- /render/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/1.jpg -------------------------------------------------------------------------------- /render/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/2.jpg -------------------------------------------------------------------------------- /render/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/3.jpg -------------------------------------------------------------------------------- /render/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/4.jpg -------------------------------------------------------------------------------- /render/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/5.jpg -------------------------------------------------------------------------------- /render/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/6.jpg -------------------------------------------------------------------------------- /render/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/7.jpg -------------------------------------------------------------------------------- /render/8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/8.jpg -------------------------------------------------------------------------------- /render/9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/9.jpg -------------------------------------------------------------------------------- /render/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/10.jpg -------------------------------------------------------------------------------- /render/11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/11.jpg -------------------------------------------------------------------------------- /render/12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/12.jpg -------------------------------------------------------------------------------- /render/13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/13.jpg -------------------------------------------------------------------------------- /render/14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/14.jpg -------------------------------------------------------------------------------- /render/15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/15.jpg -------------------------------------------------------------------------------- /render/16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/16.jpg -------------------------------------------------------------------------------- /render/17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/17.jpg -------------------------------------------------------------------------------- /render/18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/18.jpg -------------------------------------------------------------------------------- /render/19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/19.jpg -------------------------------------------------------------------------------- /render/20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/20.jpg -------------------------------------------------------------------------------- /render/21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/21.jpg -------------------------------------------------------------------------------- /render/22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/22.jpg -------------------------------------------------------------------------------- /render/23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/23.jpg -------------------------------------------------------------------------------- /render/24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/24.jpg -------------------------------------------------------------------------------- /render/25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/25.jpg -------------------------------------------------------------------------------- /render/26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/26.jpg -------------------------------------------------------------------------------- /render/27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/27.jpg -------------------------------------------------------------------------------- /render/28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/28.jpg -------------------------------------------------------------------------------- /render/29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/29.jpg -------------------------------------------------------------------------------- /render/30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/30.jpg -------------------------------------------------------------------------------- /render/31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/31.jpg -------------------------------------------------------------------------------- /render/32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/32.jpg -------------------------------------------------------------------------------- /render/33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/33.jpg -------------------------------------------------------------------------------- /render/34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/34.jpg -------------------------------------------------------------------------------- /render/35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/35.jpg -------------------------------------------------------------------------------- /render/36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/36.jpg -------------------------------------------------------------------------------- /render/37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/37.jpg -------------------------------------------------------------------------------- /render/38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/38.jpg -------------------------------------------------------------------------------- /render/39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/39.jpg -------------------------------------------------------------------------------- /render/40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/40.jpg -------------------------------------------------------------------------------- /render/41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/41.jpg -------------------------------------------------------------------------------- /render/42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/42.jpg -------------------------------------------------------------------------------- /render/43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/43.jpg -------------------------------------------------------------------------------- /render/44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/44.jpg -------------------------------------------------------------------------------- /render/45.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/45.jpg -------------------------------------------------------------------------------- /render/46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/46.jpg -------------------------------------------------------------------------------- /render/47.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/47.jpg -------------------------------------------------------------------------------- /render/48.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/48.jpg -------------------------------------------------------------------------------- /render/49.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/49.jpg -------------------------------------------------------------------------------- /render/50.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/50.jpg -------------------------------------------------------------------------------- /render/51.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/51.jpg -------------------------------------------------------------------------------- /render/52.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/52.jpg -------------------------------------------------------------------------------- /render/53.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/53.jpg -------------------------------------------------------------------------------- /render/54.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/54.jpg -------------------------------------------------------------------------------- /render/36-bug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/36-bug.jpg -------------------------------------------------------------------------------- /render/compare.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/compare.jpg -------------------------------------------------------------------------------- /texture/earthmap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/texture/earthmap.jpg -------------------------------------------------------------------------------- /render/54-with-monte-carlo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/resttime/shrimpray/HEAD/render/54-with-monte-carlo.jpg -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "shrimpray" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | rand = "0.7.2" 8 | image = "0.22.3" 9 | rayon = "1.3.0" 10 | num = "0.4" -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 resttime 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/transf.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::hit::Hittable; 4 | use crate::vec3::Vec3; 5 | 6 | pub struct FlipNormals { 7 | pub obj_ref: Arc, 8 | } 9 | 10 | impl FlipNormals { 11 | pub fn new(obj_ref: Arc) -> Self { 12 | Self { obj_ref: obj_ref } 13 | } 14 | } 15 | 16 | pub struct Translate { 17 | pub obj_ref: Arc, 18 | pub offset: Vec3, 19 | } 20 | 21 | impl Translate { 22 | pub fn new(obj_ref: Arc, offset: Vec3) -> Self { 23 | Self { 24 | offset: offset, 25 | obj_ref: obj_ref, 26 | } 27 | } 28 | } 29 | 30 | pub struct RotateY { 31 | pub obj_ref: Arc, 32 | pub sin_theta: f32, 33 | pub cos_theta: f32, 34 | } 35 | 36 | impl RotateY { 37 | pub fn new(obj_ref: Arc, angle: f32) -> Self { 38 | let radians = angle.to_radians(); 39 | let sin_theta = radians.sin(); 40 | let cos_theta = radians.cos(); 41 | Self { 42 | obj_ref: obj_ref, 43 | sin_theta: sin_theta, 44 | cos_theta: cos_theta, 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /README.org: -------------------------------------------------------------------------------- 1 | * shrimpray 2 | [[./render/39.jpg]] 3 | [[./render/compare.jpg]] 4 | 5 | #+begin_quote 6 | One of the most complex eyes in the animal kingdom can be found in 7 | species of stomatopod crustaceans (mantis shrimp), some of which have 8 | 12 different photoreceptor types, each sampling a narrow set of 9 | wavelengths ranging from deep ultraviolet to far red (300 to 720 10 | nanometers). Functionally, this chromatic complexity has presented a 11 | mystery. Why use 12 color channels when three or four are sufficient 12 | for fine color discrimination? ---Science, Vol. 343, 24 Jan 2014 13 | #+end_quote 14 | 15 | *shrimpray* is my first project in Rust. It is an implementation of a 16 | ray tracer in Rust while following [[https://raytracing.github.io/][Ray Tracing in One Weekend Series]] 17 | as a guide. The style of code attempts to be idiomatic while 18 | minimizing mutables. Thread safe primitives for parallel processing. 19 | Monte carlo is used for the rendering to converge to an accurate image 20 | faster. The mantis shrimp is a cool creature. 21 | 22 | Additional renders can be found under the [[./render][render]] folder 23 | 24 | ** Building and Running 25 | #+begin_src sh 26 | # Build 27 | cargo build --release 28 | 29 | # Render to PPM 30 | cargo run --release > scene.ppm 31 | 32 | # Convert render to JPG 33 | convert scene.ppm scene.jpg 34 | #+end_src 35 | 36 | ** License 37 | Project under [[./LICENSE][MIT License]] 38 | -------------------------------------------------------------------------------- /src/pdf.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::hit::*; 4 | use crate::util::*; 5 | use crate::vec3::*; 6 | 7 | pub trait Pdf { 8 | fn value(&self, direction: &Vec3) -> f32; 9 | fn generate(&self) -> Vec3; 10 | } 11 | 12 | pub struct CosinePdf { 13 | uvw: Onb, 14 | } 15 | 16 | impl CosinePdf { 17 | pub fn new(w: &Vec3) -> Self { 18 | let mut uvw = Onb::new(); 19 | uvw.build_from_w(w); 20 | CosinePdf { uvw: uvw } 21 | } 22 | } 23 | 24 | impl Pdf for CosinePdf { 25 | fn value(&self, direction: &Vec3) -> f32 { 26 | let cosine = dot(direction.unit(), self.uvw.w()); 27 | if cosine > 0.0 { 28 | return cosine / std::f32::consts::PI; 29 | } 30 | 0.0 31 | } 32 | fn generate(&self) -> Vec3 { 33 | self.uvw.local_vector(&random_cosine_direction()) 34 | } 35 | } 36 | 37 | pub struct HittablePdf { 38 | o: Vec3, 39 | obj_ref: Arc, 40 | } 41 | 42 | impl HittablePdf { 43 | pub fn new(p: Arc, origin: Vec3) -> Self { 44 | HittablePdf { 45 | o: origin, 46 | obj_ref: p, 47 | } 48 | } 49 | } 50 | 51 | impl Pdf for HittablePdf { 52 | fn value(&self, direction: &Vec3) -> f32 { 53 | self.obj_ref.pdf_value(&self.o, &direction) 54 | } 55 | fn generate(&self) -> Vec3 { 56 | self.obj_ref.random(&self.o) 57 | } 58 | } 59 | 60 | pub struct MixturePdf { 61 | p: Vec>, 62 | } 63 | 64 | impl MixturePdf { 65 | pub fn new(p0: Box, p1: Box) -> Self { 66 | let p = vec![p0, p1]; 67 | MixturePdf { p: p } 68 | } 69 | } 70 | 71 | impl Pdf for MixturePdf { 72 | fn value(&self, direction: &Vec3) -> f32 { 73 | 0.5 * self.p[0].value(direction) + 0.5 * self.p[1].value(direction) 74 | } 75 | fn generate(&self) -> Vec3 { 76 | if rand_float() < 0.5 { 77 | return self.p[0].generate(); 78 | } 79 | self.p[1].generate() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/camera.rs: -------------------------------------------------------------------------------- 1 | use crate::util::*; 2 | use crate::vec3::{cross, Ray, Vec3}; 3 | 4 | #[derive(Default)] 5 | #[allow(dead_code)] 6 | pub struct Camera { 7 | origin: Vec3, 8 | ll_corner: Vec3, 9 | horizontal: Vec3, 10 | vertical: Vec3, 11 | u: Vec3, 12 | v: Vec3, 13 | w: Vec3, 14 | lens_radius: f32, 15 | time0: f32, 16 | time1: f32, 17 | } 18 | 19 | impl Camera { 20 | pub fn new( 21 | lookfrom: Vec3, 22 | lookat: Vec3, 23 | vup: Vec3, 24 | vfov: f32, 25 | aspect: f32, 26 | aperture: f32, 27 | focus_dist: f32, 28 | time0: f32, 29 | time1: f32, 30 | ) -> Self { 31 | let lens_radius = aperture / 2.0; 32 | let theta = vfov * std::f32::consts::PI / 180.0; 33 | let half_height = (theta / 2.0).tan(); 34 | let half_width = aspect * half_height; 35 | let origin = lookfrom; 36 | 37 | let w = (lookfrom - lookat).unit(); 38 | let u = cross(vup, w).unit(); 39 | let v = cross(w, u); 40 | 41 | let ll_corner = 42 | origin - half_width * focus_dist * u - half_height * focus_dist * v - focus_dist * w; 43 | let horizontal = 2.0 * half_width * focus_dist * u; 44 | let vertical = 2.0 * half_height * focus_dist * v; 45 | 46 | Self { 47 | origin, 48 | ll_corner, 49 | horizontal, 50 | vertical, 51 | u, 52 | v, 53 | w, 54 | lens_radius, 55 | time0, 56 | time1, 57 | } 58 | } 59 | 60 | pub fn get_ray(&self, s: f32, t: f32) -> Ray { 61 | let rd = self.lens_radius * random_in_unit_disk(); 62 | let offset = self.u * rd.x() + self.v * rd.y(); 63 | let time = self.time0 + rand_float() * (self.time1 - self.time0); 64 | Ray::new( 65 | self.origin + offset, 66 | self.ll_corner + s * self.horizontal + t * self.vertical - self.origin - offset, 67 | time, 68 | ) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use crate::vec3::Vec3; 2 | use rand::Rng; 3 | 4 | #[inline] 5 | pub fn rand_float() -> f32 { 6 | let mut rng = rand::thread_rng(); 7 | rng.gen() 8 | } 9 | 10 | #[inline] 11 | pub fn rand_float_range(min: f32, max: f32) -> f32 { 12 | let mut rng = rand::thread_rng(); 13 | rng.gen_range(min, max) 14 | } 15 | 16 | #[inline] 17 | pub fn random_in_unit_sphere() -> Vec3 { 18 | loop { 19 | let p = 20 | 2.0 * Vec3::new(rand_float(), rand_float(), rand_float()) - Vec3::new(1.0, 1.0, 1.0); 21 | if p.mag() < 1.0 { 22 | return p; 23 | } 24 | } 25 | } 26 | 27 | #[inline] 28 | pub fn random_in_unit_disk() -> Vec3 { 29 | loop { 30 | let p = 2.0 * Vec3::new(rand_float(), rand_float(), 0.0) - Vec3::new(1.0, 1.0, 0.0); 31 | if p.mag() < 1.0 { 32 | return p; 33 | } 34 | } 35 | } 36 | 37 | #[inline] 38 | pub fn random_unit_vector() -> Vec3 { 39 | let a = rand_float() * 2.0 * std::f32::consts::PI; 40 | let z = rand_float() * 2.0 - 1.0; 41 | let r = (1.0 - z * z).sqrt(); 42 | return Vec3::new(r * a.cos(), r * a.sin(), z); 43 | } 44 | 45 | #[inline] 46 | pub fn random_cosine_direction() -> Vec3 { 47 | let r1 = rand_float(); 48 | let r2 = rand_float(); 49 | let z = (1.0 - r2).sqrt(); 50 | 51 | let phi = 2.0 * std::f32::consts::PI * r1; 52 | let x = phi.cos() * r2.sqrt(); 53 | let y = phi.sin() * r2.sqrt(); 54 | 55 | Vec3::new(x, y, z) 56 | } 57 | 58 | #[inline] 59 | pub fn random_to_sphere(radius: f32, dist_sqrd: f32) -> Vec3 { 60 | let r1 = rand_float(); 61 | let r2 = rand_float(); 62 | let z = 1.0 + r2 * ((1.0 - radius * radius / dist_sqrd).sqrt() - 1.0); 63 | 64 | let phi = 2.0 * std::f32::consts::PI * r1; 65 | let x = phi.cos() * (1.0 - z * z).sqrt(); 66 | let y = phi.sin() * (1.0 - z * z).sqrt(); 67 | 68 | Vec3::new(x, y, z) 69 | } 70 | 71 | #[inline] 72 | pub fn schlick(cosine: f32, ref_idx: f32) -> f32 { 73 | let mut r0 = (1.0 - ref_idx) / (1.0 + ref_idx); 74 | r0 = r0 * r0; 75 | 76 | r0 + (1.0 - r0) * (1.0 - cosine).powi(5) 77 | } 78 | 79 | #[inline] 80 | pub fn de_nan(c: &Vec3) -> Vec3 { 81 | let mut temp: Vec3 = *c; 82 | if temp[0] != temp[0] { 83 | temp[0] = 0.0 84 | } 85 | if temp[1] != temp[1] { 86 | temp[1] = 0.0 87 | } 88 | if temp[2] != temp[2] { 89 | temp[2] = 0.0 90 | } 91 | return temp; 92 | } 93 | -------------------------------------------------------------------------------- /src/perlin.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | 3 | use crate::util::*; 4 | use crate::vec3::{dot, Vec3}; 5 | 6 | pub struct Perlin { 7 | ranvec: Vec, 8 | perm_x: Vec, 9 | perm_y: Vec, 10 | perm_z: Vec, 11 | } 12 | 13 | impl Perlin { 14 | pub fn new() -> Self { 15 | Self { 16 | ranvec: Perlin::generate(), 17 | perm_x: Perlin::generate_perm(), 18 | perm_y: Perlin::generate_perm(), 19 | perm_z: Perlin::generate_perm(), 20 | } 21 | } 22 | 23 | pub fn noise(&self, p: &Vec3) -> f32 { 24 | let u = p.x() - p.x().floor(); 25 | let v = p.y() - p.y().floor(); 26 | let w = p.z() - p.z().floor(); 27 | 28 | let i = p.x().floor() as usize; 29 | let j = p.y().floor() as usize; 30 | let k = p.z().floor() as usize; 31 | 32 | let mut c = [[[Vec3::new(0.0, 0.0, 0.0); 2]; 2]; 2]; 33 | 34 | for di in 0..2 { 35 | for dj in 0..2 { 36 | for dk in 0..2 { 37 | let index = (self.perm_x[(i + di) & 255] 38 | ^ self.perm_y[(j + dj) & 255] 39 | ^ self.perm_z[(k + dk) & 255]) as usize; 40 | c[di][dj][dk] = self.ranvec[index]; 41 | } 42 | } 43 | } 44 | Perlin::interp(c, u, v, w) 45 | } 46 | 47 | fn generate() -> Vec { 48 | let mut v: Vec = Vec::new(); 49 | for _ in 0..256 { 50 | let x_rand = 2.0 * rand_float() - 1.0; 51 | let y_rand = 2.0 * rand_float() - 1.0; 52 | let z_rand = 2.0 * rand_float() - 1.0; 53 | v.push(Vec3::new(x_rand, y_rand, z_rand).unit()); 54 | } 55 | v 56 | } 57 | 58 | fn generate_perm() -> Vec { 59 | let mut p: Vec = (0..256).collect(); 60 | p.shuffle(&mut rand::thread_rng()); 61 | p 62 | } 63 | 64 | fn interp(c: [[[Vec3; 2]; 2]; 2], u: f32, v: f32, w: f32) -> f32 { 65 | let uu = u * u * (3.0 - 2.0 * u); 66 | let vv = v * v * (3.0 - 2.0 * v); 67 | let ww = w * w * (3.0 - 2.0 * w); 68 | let mut accum = 0.0; 69 | for i in 0..2 { 70 | for j in 0..2 { 71 | for k in 0..2 { 72 | let weight_v = Vec3::new(u - i as f32, v - j as f32, w - k as f32); 73 | accum += (i as f32 * uu + (1.0 - i as f32) * (1.0 - uu)) 74 | * (j as f32 * vv + (1.0 - j as f32) * (1.0 - vv)) 75 | * (k as f32 * ww + (1.0 - k as f32) * (1.0 - ww)) 76 | * dot(c[i][j][k], weight_v); 77 | } 78 | } 79 | } 80 | accum 81 | } 82 | 83 | pub fn turb(&self, p: &Vec3, depth: u32) -> f32 { 84 | let mut accum = 0.0; 85 | let mut weight = 1.0; 86 | let mut temp_p = Vec3::new(p.x(), p.y(), p.z()); 87 | for _ in 0..depth { 88 | accum += weight * self.noise(&temp_p); 89 | weight *= 0.5; 90 | temp_p *= 2.0; 91 | } 92 | accum.abs() 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/texture.rs: -------------------------------------------------------------------------------- 1 | use crate::perlin::Perlin; 2 | use crate::vec3::Vec3; 3 | 4 | pub trait Texture: Sync + Send { 5 | fn value(&self, u: f32, v: f32, p: &Vec3) -> Vec3; 6 | } 7 | 8 | pub struct ConstantTexture { 9 | color: Vec3, 10 | } 11 | 12 | impl ConstantTexture { 13 | pub fn new(c: Vec3) -> Self { 14 | Self { color: c } 15 | } 16 | } 17 | 18 | impl Texture for ConstantTexture { 19 | fn value(&self, _u: f32, _v: f32, _p: &Vec3) -> Vec3 { 20 | self.color 21 | } 22 | } 23 | 24 | pub struct CheckerTexture { 25 | odd: Box, 26 | even: Box, 27 | } 28 | 29 | impl CheckerTexture { 30 | pub fn new(t0: Box, t1: Box) -> Self { 31 | Self { odd: t0, even: t1 } 32 | } 33 | } 34 | 35 | impl Texture for CheckerTexture { 36 | fn value(&self, u: f32, v: f32, p: &Vec3) -> Vec3 { 37 | let sines = (10.0 * p.x()).sin() * (10.0 * p.y()).sin() * (10.0 * p.z()).sin(); 38 | if sines < 0.0 { 39 | self.odd.value(u, v, p) 40 | } else { 41 | self.even.value(u, v, p) 42 | } 43 | } 44 | } 45 | 46 | pub struct NoiseTexture { 47 | scale: f32, 48 | noise: Perlin, 49 | } 50 | 51 | impl NoiseTexture { 52 | pub fn new(scale: f32, noise: Perlin) -> Self { 53 | Self { 54 | scale: scale, 55 | noise: noise, 56 | } 57 | } 58 | } 59 | 60 | impl Texture for NoiseTexture { 61 | fn value(&self, _u: f32, _v: f32, p: &Vec3) -> Vec3 { 62 | // Vec3::new(1.0, 1.0, 1.0) 63 | // * 0.50 64 | // * (1.0 + (self.scale * p.x() + 10.0 * self.noise.turb(p, 7)).sin()) 65 | let scaled_p = Vec3::new(p.x(), p.y(), p.z()) * self.scale; 66 | Vec3::new(1.0, 1.0, 1.0) 67 | * 0.50 68 | * (1.0 + (self.scale * p.x() + 5.0 * self.noise.turb(&scaled_p, 7)).sin()) 69 | } 70 | } 71 | 72 | pub struct ImageTexture { 73 | data: Vec, 74 | nx: i32, 75 | ny: i32, 76 | } 77 | 78 | impl ImageTexture { 79 | pub fn new(pixels: Vec, a: i32, b: i32) -> Self { 80 | Self { 81 | data: pixels, 82 | nx: a, 83 | ny: b, 84 | } 85 | } 86 | } 87 | 88 | impl Texture for ImageTexture { 89 | fn value(&self, u: f32, v: f32, _p: &Vec3) -> Vec3 { 90 | let mut i = (u * self.nx as f32) as i32; 91 | let mut j = ((1.0 - v) * self.ny as f32 - 0.001) as i32; 92 | if i < 0 { 93 | i = 0; 94 | } 95 | if j < 0 { 96 | j = 0; 97 | } 98 | if i > (self.nx - 1) { 99 | i = self.nx - 1; 100 | } 101 | if j > (self.ny - 1) { 102 | j = self.ny - 1; 103 | } 104 | let r = self.data[3 * i as usize + (3 * self.nx * j) as usize] as f32 / 255.0; 105 | let g = self.data[3 * i as usize + (3 * self.nx * j + 1) as usize] as f32 / 255.0; 106 | let b = self.data[3 * i as usize + (3 * self.nx * j + 2) as usize] as f32 / 255.0; 107 | Vec3::new(r, g, b) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use rayon::prelude::*; 4 | 5 | mod vec3; 6 | use vec3::*; 7 | 8 | mod camera; 9 | 10 | mod obj; 11 | use obj::*; 12 | 13 | mod hit; 14 | use hit::*; 15 | 16 | mod bvh; 17 | 18 | mod material; 19 | use material::*; 20 | 21 | mod texture; 22 | use texture::*; 23 | 24 | mod util; 25 | use util::*; 26 | 27 | mod perlin; 28 | 29 | mod pdf; 30 | use pdf::*; 31 | 32 | mod transf; 33 | 34 | mod scene; 35 | use scene::*; 36 | 37 | fn color(r: Ray, world: &Vec>, lights: &Arc, depth: u32) -> Vec3 { 38 | if depth == 0 { 39 | return Vec3::new(0.0, 0.0, 0.0); 40 | } 41 | if let Some(hit) = world.hit(r, 0.001, std::f32::MAX) { 42 | let emitted = hit.material.emitted(&r, &hit, hit.u, hit.v, &hit.p); 43 | if let Some(s_rec) = hit.material.scatter(r, &hit) { 44 | if s_rec.is_specular { 45 | return s_rec.attenuation * color(s_rec.specular_ray, world, lights, depth - 1); 46 | } else { 47 | let plight = HittablePdf::new(lights.clone(), hit.p); 48 | let p = MixturePdf::new(Box::new(plight), s_rec.pdf.unwrap()); 49 | 50 | let scattered = Ray::new(hit.p, p.generate(), r.time()); 51 | let pdf_val = p.value(&scattered.direction()); 52 | 53 | return emitted 54 | + s_rec.attenuation 55 | * hit.material.scattering_pdf(&r, &hit, &scattered) 56 | * color(scattered, world, lights, depth - 1) 57 | / pdf_val; 58 | } 59 | } else { 60 | return emitted; 61 | } 62 | } 63 | Vec3::new(0.0, 0.0, 0.0) 64 | } 65 | 66 | fn main() { 67 | let (nx, ny, ns) = (500, 500, 1000); 68 | println!("P3"); 69 | println!("{} {}", nx, ny); 70 | println!("255"); 71 | 72 | let (cam, world) = cornell_mc(ny as f32 / nx as f32); 73 | 74 | let light_shape: Arc = Arc::new(XZRect::new( 75 | 213.0, 76 | 343.0, 77 | 227.0, 78 | 332.0, 79 | 554.0, 80 | Arc::new(DiffuseLight::new(Arc::new(ConstantTexture::new( 81 | Vec3::new(1.0, 1.0, 1.0), 82 | )))), 83 | )); 84 | 85 | let glass = Arc::new(Dielectric::new(1.5)); 86 | let glass_sphere: Arc = 87 | Arc::new(Sphere::new(Vec3::new(190.0, 90.0, 190.0), 90.0, glass)); 88 | 89 | let lights: Arc = Arc::new(vec![light_shape, glass_sphere]); 90 | 91 | for j in (0..ny).rev() { 92 | for i in 0..nx { 93 | let mut col: Vec3 = (0..ns) 94 | .into_par_iter() 95 | .map(|_| { 96 | let u: f32 = (i as f32 + rand_float()) / nx as f32; 97 | let v: f32 = (j as f32 + rand_float()) / ny as f32; 98 | let r = cam.get_ray(u, v); 99 | de_nan(&color(r, &world, &lights, 50)) 100 | }) 101 | .sum(); 102 | col /= ns as f32; 103 | col = Vec3::new(col[0].sqrt(), col[1].sqrt(), col[2].sqrt()); 104 | 105 | let ir: u32 = (256.0 * num::clamp(col[0], 0.0, 0.999)) as u32; 106 | let ig: u32 = (256.0 * num::clamp(col[1], 0.0, 0.999)) as u32; 107 | let ib: u32 = (256.0 * num::clamp(col[2], 0.0, 0.999)) as u32; 108 | println!("{} {} {}", ir, ig, ib) 109 | } 110 | } 111 | } 112 | 113 | #[cfg(test)] 114 | mod tests { 115 | use crate::util::*; 116 | 117 | #[test] 118 | fn mc() { 119 | let n = 1000000; 120 | let mut sum = 0.0; 121 | for _ in 0..n { 122 | let r1 = rand_float(); 123 | let r2 = rand_float(); 124 | let x = (2.0 * std::f32::consts::PI * r1).cos() * 2.0 * (r2 * (1.0 - r2)).sqrt(); 125 | let y = (2.0 * std::f32::consts::PI * r1).sin() * 2.0 * (r2 * (1.0 - r2)).sqrt(); 126 | let z = 1.0 - r2; 127 | sum += z * z * z / (1.0 / (2.0 * std::f32::consts::PI)); 128 | } 129 | println!("Pi/2 = {}", std::f32::consts::PI / 2.0 as f32); 130 | println!("Estimate = {}", sum / n as f32); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/bvh.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::hit::Hittable; 4 | use crate::util::*; 5 | use crate::vec3::{Ray, Vec3}; 6 | 7 | pub struct AABB { 8 | _min: Vec3, 9 | _max: Vec3, 10 | } 11 | 12 | impl AABB { 13 | pub fn new(min: Vec3, max: Vec3) -> Self { 14 | Self { 15 | _min: min, 16 | _max: max, 17 | } 18 | } 19 | pub fn min(&self) -> Vec3 { 20 | self._min 21 | } 22 | pub fn max(&self) -> Vec3 { 23 | self._max 24 | } 25 | pub fn hit(&self, r: &Ray, mut t_min: f32, mut t_max: f32) -> bool { 26 | for a in 0..3 { 27 | let inv_d = 1.0 / r.direction()[a]; 28 | let mut t0 = (self.min()[a] - r.origin()[a]) * inv_d; 29 | let mut t1 = (self.max()[a] - r.origin()[a]) * inv_d; 30 | if inv_d < 0.0 { 31 | std::mem::swap(&mut t0, &mut t1); 32 | } 33 | t_min = if t0 > t_min { t0 } else { t_min }; 34 | t_max = if t1 < t_max { t1 } else { t_max }; 35 | if t_max <= t_min { 36 | return false; 37 | } 38 | } 39 | true 40 | } 41 | } 42 | 43 | pub fn surrounding_bbox(bbox0: AABB, bbox1: AABB) -> AABB { 44 | let small = Vec3::new( 45 | bbox0.min().x().min(bbox1.min().x()), 46 | bbox0.min().y().min(bbox1.min().y()), 47 | bbox0.min().z().min(bbox1.min().z()), 48 | ); 49 | let big = Vec3::new( 50 | bbox0.max().x().max(bbox1.max().x()), 51 | bbox0.max().y().max(bbox1.max().y()), 52 | bbox0.max().z().max(bbox1.max().z()), 53 | ); 54 | AABB::new(small, big) 55 | } 56 | 57 | pub struct BvhNode { 58 | pub left: Option>, 59 | pub right: Option>, 60 | pub bbox: AABB, 61 | } 62 | 63 | impl BvhNode { 64 | pub fn new(list: &mut [Arc], time0: f32, time1: f32) -> Self { 65 | let axis = (3.0 * rand_float()) as u32; 66 | match axis { 67 | 0 => { 68 | list.sort_by(box_compare_x); 69 | } 70 | 1 => { 71 | list.sort_by(box_compare_y); 72 | } 73 | 2 => { 74 | list.sort_by(box_compare_z); 75 | } 76 | _ => { 77 | panic!("There should've been an axis"); 78 | } 79 | } 80 | 81 | let (left, right): (Arc, Arc); 82 | let len = list.len(); 83 | match len { 84 | 1 => { 85 | left = list[0].clone(); 86 | right = list[0].clone(); 87 | } 88 | 2 => { 89 | left = list[0].clone(); 90 | right = list[1].clone(); 91 | } 92 | _ => { 93 | left = Arc::new(BvhNode::new(&mut list[0..len / 2], time0, time1)); 94 | right = Arc::new(BvhNode::new(&mut list[len / 2..], time0, time1)); 95 | } 96 | } 97 | 98 | let l_bbox = left.bounding_box(time0, time1).expect("no bounding box"); 99 | let r_bbox = right.bounding_box(time0, time1).expect("no bounding box"); 100 | 101 | Self { 102 | left: Some(left), 103 | right: Some(right), 104 | bbox: surrounding_bbox(l_bbox, r_bbox), 105 | } 106 | } 107 | } 108 | 109 | pub fn box_compare_x(a: &Arc, b: &Arc) -> std::cmp::Ordering { 110 | match (a.bounding_box(0.0, 0.0), b.bounding_box(0.0, 0.0)) { 111 | (Some(lbox), Some(rbox)) => lbox.min().x().partial_cmp(&rbox.min().x()).unwrap(), 112 | (_, _) => panic!("Missing bounding box in a BvhNode contructor"), 113 | } 114 | } 115 | 116 | pub fn box_compare_y(a: &Arc, b: &Arc) -> std::cmp::Ordering { 117 | match (a.bounding_box(0.0, 0.0), b.bounding_box(0.0, 0.0)) { 118 | (Some(lbox), Some(rbox)) => lbox.min().y().partial_cmp(&rbox.min().y()).unwrap(), 119 | (_, _) => panic!("Missing bounding box in a BvhNode contructor"), 120 | } 121 | } 122 | pub fn box_compare_z(a: &Arc, b: &Arc) -> std::cmp::Ordering { 123 | match (a.bounding_box(0.0, 0.0), b.bounding_box(0.0, 0.0)) { 124 | (Some(lbox), Some(rbox)) => lbox.min().z().partial_cmp(&rbox.min().z()).unwrap(), 125 | (_, _) => panic!("Missing bounding box in a BvhNode contructor"), 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/obj.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::hit::*; 4 | use crate::material::*; 5 | use crate::texture::*; 6 | use crate::transf::*; 7 | use crate::vec3::Vec3; 8 | 9 | pub struct Sphere { 10 | pub center: Vec3, 11 | pub radius: f32, 12 | pub material: Arc, 13 | } 14 | 15 | impl Sphere { 16 | pub fn new(center: Vec3, radius: f32, material: Arc) -> Sphere { 17 | Sphere { 18 | center: center, 19 | radius: radius, 20 | material: material, 21 | } 22 | } 23 | pub fn get_sphere_uv(p: &Vec3) -> (f32, f32) { 24 | let phi = p.z().atan2(p.x()); 25 | let theta = p.y().asin(); 26 | let u = 1.0 - (phi + std::f32::consts::PI) / (2.0 * std::f32::consts::PI); 27 | let v = (theta + std::f32::consts::FRAC_PI_2) / std::f32::consts::PI; 28 | (u, v) 29 | } 30 | } 31 | 32 | pub struct MovingSphere { 33 | pub center0: Vec3, 34 | pub center1: Vec3, 35 | pub time0: f32, 36 | pub time1: f32, 37 | pub radius: f32, 38 | pub material: Arc, 39 | } 40 | 41 | impl MovingSphere { 42 | pub fn new( 43 | center0: Vec3, 44 | center1: Vec3, 45 | t0: f32, 46 | t1: f32, 47 | radius: f32, 48 | material: Arc, 49 | ) -> Self { 50 | Self { 51 | center0: center0, 52 | center1: center1, 53 | time0: t0, 54 | time1: t1, 55 | radius: radius, 56 | material: material, 57 | } 58 | } 59 | pub fn center(&self, time: f32) -> Vec3 { 60 | self.center0 61 | + ((time - self.time0) / (self.time1 - self.time0)) * (self.center1 - self.center0) 62 | } 63 | } 64 | 65 | pub struct XYRect { 66 | pub x0: f32, 67 | pub x1: f32, 68 | pub y0: f32, 69 | pub y1: f32, 70 | pub k: f32, 71 | pub material: Arc, 72 | } 73 | 74 | impl XYRect { 75 | pub fn new(x0: f32, x1: f32, y0: f32, y1: f32, k: f32, material: Arc) -> Self { 76 | Self { 77 | x0: x0, 78 | x1: x1, 79 | y0: y0, 80 | y1: y1, 81 | k: k, 82 | material: material, 83 | } 84 | } 85 | } 86 | 87 | pub struct XZRect { 88 | pub x0: f32, 89 | pub x1: f32, 90 | pub z0: f32, 91 | pub z1: f32, 92 | pub k: f32, 93 | pub material: Arc, 94 | } 95 | 96 | impl XZRect { 97 | pub fn new(x0: f32, x1: f32, z0: f32, z1: f32, k: f32, material: Arc) -> Self { 98 | Self { 99 | x0: x0, 100 | x1: x1, 101 | z0: z0, 102 | z1: z1, 103 | k: k, 104 | material: material, 105 | } 106 | } 107 | } 108 | 109 | pub struct YZRect { 110 | pub y0: f32, 111 | pub y1: f32, 112 | pub z0: f32, 113 | pub z1: f32, 114 | pub k: f32, 115 | pub material: Arc, 116 | } 117 | 118 | impl YZRect { 119 | pub fn new(y0: f32, y1: f32, z0: f32, z1: f32, k: f32, material: Arc) -> Self { 120 | Self { 121 | y0: y0, 122 | y1: y1, 123 | z0: z0, 124 | z1: z1, 125 | k: k, 126 | material: material, 127 | } 128 | } 129 | } 130 | 131 | pub struct BoxShape { 132 | pub pmin: Vec3, 133 | pub pmax: Vec3, 134 | pub faces: Vec>, 135 | } 136 | 137 | impl BoxShape { 138 | pub fn new(p0: Vec3, p1: Vec3, mat: Arc) -> Self { 139 | let mut faces: Vec> = Vec::new(); 140 | faces.push(Arc::new(XYRect::new( 141 | p0.x(), 142 | p1.x(), 143 | p0.y(), 144 | p1.y(), 145 | p1.z(), 146 | mat.clone(), 147 | ))); 148 | faces.push(Arc::new(FlipNormals::new(Arc::new(XYRect::new( 149 | p0.x(), 150 | p1.x(), 151 | p0.y(), 152 | p1.y(), 153 | p0.z(), 154 | mat.clone(), 155 | ))))); 156 | 157 | faces.push(Arc::new(XZRect::new( 158 | p0.x(), 159 | p1.x(), 160 | p0.z(), 161 | p1.z(), 162 | p1.y(), 163 | mat.clone(), 164 | ))); 165 | faces.push(Arc::new(FlipNormals::new(Arc::new(XZRect::new( 166 | p0.x(), 167 | p1.x(), 168 | p0.z(), 169 | p1.z(), 170 | p0.y(), 171 | mat.clone(), 172 | ))))); 173 | 174 | faces.push(Arc::new(YZRect::new( 175 | p0.y(), 176 | p1.y(), 177 | p0.z(), 178 | p1.z(), 179 | p1.x(), 180 | mat.clone(), 181 | ))); 182 | faces.push(Arc::new(FlipNormals::new(Arc::new(YZRect::new( 183 | p0.y(), 184 | p1.y(), 185 | p0.z(), 186 | p1.z(), 187 | p0.x(), 188 | mat.clone(), 189 | ))))); 190 | Self { 191 | pmin: p0, 192 | pmax: p1, 193 | faces: faces, 194 | } 195 | } 196 | } 197 | 198 | pub struct ConstantMedium { 199 | pub boundary: Arc, 200 | pub density: f32, 201 | pub phase_function: Arc, 202 | } 203 | 204 | impl ConstantMedium { 205 | pub fn new(b: Arc, d: f32, a: Arc) -> Self { 206 | Self { 207 | boundary: b, 208 | density: d, 209 | phase_function: Arc::new(Isotropic::new(a)), 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/material.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use crate::hit::HitRecord; 4 | use crate::pdf::*; 5 | use crate::texture::Texture; 6 | use crate::util::*; 7 | use crate::vec3::*; 8 | 9 | pub struct ScatterRecord { 10 | pub specular_ray: Ray, 11 | pub is_specular: bool, 12 | pub attenuation: Vec3, 13 | pub pdf: Option>, 14 | } 15 | 16 | impl ScatterRecord { 17 | pub fn new( 18 | specular_ray: Ray, 19 | is_specular: bool, 20 | attenuation: Vec3, 21 | pdf: Option>, 22 | ) -> Self { 23 | ScatterRecord { 24 | specular_ray: specular_ray, 25 | is_specular: is_specular, 26 | attenuation: attenuation, 27 | pdf: pdf, 28 | } 29 | } 30 | } 31 | 32 | pub trait Material: Sync + Send { 33 | fn scatter(&self, _ray_in: Ray, _hit: &HitRecord) -> Option { 34 | None 35 | } 36 | fn scattering_pdf(&self, _ray_in: &Ray, _hit: &HitRecord, _scattered: &Ray) -> f32 { 37 | 0.0 38 | } 39 | fn emitted(&self, _r_in: &Ray, _hit: &HitRecord, _u: f32, _v: f32, _p: &Vec3) -> Vec3 { 40 | Vec3::new(0.0, 0.0, 0.0) 41 | } 42 | } 43 | 44 | // Diffuse 45 | pub struct Lambertian { 46 | albedo: Arc, 47 | } 48 | 49 | impl Lambertian { 50 | pub fn new(a: Arc) -> Self { 51 | Self { albedo: a } 52 | } 53 | } 54 | 55 | impl Material for Lambertian { 56 | fn scatter(&self, _ray_in: Ray, hit: &HitRecord) -> Option { 57 | let alb = self.albedo.value(hit.u, hit.v, &hit.p); 58 | let pdf = Box::new(CosinePdf::new(&hit.normal)); 59 | 60 | Some(ScatterRecord::new(Ray::default(), false, alb, Some(pdf))) 61 | } 62 | fn scattering_pdf(&self, _: &Ray, hit: &HitRecord, scattered: &Ray) -> f32 { 63 | let cosine = dot(hit.normal, scattered.direction().unit()); 64 | if cosine < 0.0 { 65 | return 0.0; 66 | } 67 | cosine / std::f32::consts::PI 68 | } 69 | } 70 | 71 | pub struct Metal { 72 | albedo: Vec3, 73 | fuzz: f32, 74 | } 75 | 76 | impl Metal { 77 | pub fn new(a: Vec3, f: f32) -> Self { 78 | let mut clamped_f = 1.0; 79 | if f < 1.0 { 80 | clamped_f = f; 81 | } 82 | Self { 83 | albedo: a, 84 | fuzz: clamped_f, 85 | } 86 | } 87 | } 88 | 89 | impl Material for Metal { 90 | fn scatter(&self, ray_in: Ray, hit: &HitRecord) -> Option { 91 | let reflected: Vec3 = reflect(ray_in.direction().unit(), hit.normal); 92 | let scattered = Ray::new( 93 | hit.p, 94 | reflected + self.fuzz * random_in_unit_sphere(), 95 | ray_in.time(), 96 | ); 97 | let attenuation = self.albedo; 98 | 99 | Some(ScatterRecord::new(scattered, true, attenuation, None)) 100 | } 101 | } 102 | 103 | pub struct Dielectric { 104 | ref_idx: f32, 105 | } 106 | 107 | impl Dielectric { 108 | pub fn new(ri: f32) -> Self { 109 | Self { ref_idx: ri } 110 | } 111 | } 112 | 113 | impl Material for Dielectric { 114 | fn scatter(&self, ray_in: Ray, hit: &HitRecord) -> Option { 115 | let outward_normal: Vec3; 116 | let ni_over_nt: f32; 117 | let cosine: f32; 118 | 119 | if dot(ray_in.direction(), hit.normal) > 0.0 { 120 | outward_normal = -1.0 * hit.normal; 121 | ni_over_nt = self.ref_idx; 122 | cosine = self.ref_idx * dot(ray_in.direction(), hit.normal) / ray_in.direction().mag(); 123 | } else { 124 | outward_normal = hit.normal; 125 | ni_over_nt = 1.0 / self.ref_idx; 126 | cosine = -1.0 * dot(ray_in.direction(), hit.normal) / ray_in.direction().mag(); 127 | } 128 | 129 | let reflected = reflect(ray_in.direction(), hit.normal); 130 | let attenuation = Vec3::new(1.0, 1.0, 1.0); 131 | 132 | if let Some(refracted) = refract(ray_in.direction(), outward_normal, ni_over_nt) { 133 | let refract_prob = 1.0 - schlick(cosine, self.ref_idx); 134 | if rand_float() < refract_prob { 135 | let scattering = Ray::new(hit.p, refracted, ray_in.time()); 136 | return Some(ScatterRecord::new(scattering, true, attenuation, None)); 137 | } 138 | } 139 | let scattering = Ray::new(hit.p, reflected, ray_in.time()); 140 | Some(ScatterRecord::new(scattering, true, attenuation, None)) 141 | } 142 | } 143 | 144 | pub struct DiffuseLight { 145 | emit: Arc, 146 | } 147 | 148 | impl DiffuseLight { 149 | pub fn new(a: Arc) -> Self { 150 | Self { emit: a } 151 | } 152 | } 153 | 154 | impl Material for DiffuseLight { 155 | fn scatter(&self, _ray_in: Ray, _hit: &HitRecord) -> Option { 156 | None 157 | } 158 | fn emitted(&self, r_in: &Ray, hit: &HitRecord, u: f32, v: f32, p: &Vec3) -> Vec3 { 159 | if dot(hit.normal, r_in.direction()) < 0.0 { 160 | return self.emit.value(u, v, p); 161 | } 162 | Vec3::new(0.0, 0.0, 0.0) 163 | } 164 | } 165 | 166 | pub struct Isotropic { 167 | albedo: Arc, 168 | } 169 | 170 | impl Isotropic { 171 | pub fn new(a: Arc) -> Self { 172 | Self { albedo: a } 173 | } 174 | } 175 | 176 | impl Material for Isotropic { 177 | fn scatter(&self, _ray_in: Ray, hit: &HitRecord) -> Option { 178 | let scattered = Ray::new(hit.p, random_in_unit_sphere(), hit.t); 179 | let attenuation = self.albedo.value(hit.u, hit.v, &hit.p); 180 | Some(ScatterRecord::new(scattered, false, attenuation, None)) 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/vec3.rs: -------------------------------------------------------------------------------- 1 | use std::ops::*; 2 | 3 | #[derive(Copy, Clone, Default, Debug)] 4 | pub struct Vec3 { 5 | pub e0: f32, 6 | pub e1: f32, 7 | pub e2: f32, 8 | } 9 | 10 | impl Index for Vec3 { 11 | type Output = f32; 12 | fn index(&self, i: u32) -> &Self::Output { 13 | match i { 14 | 0 => &self.e0, 15 | 1 => &self.e1, 16 | 2 => &self.e2, 17 | _ => panic!(), 18 | } 19 | } 20 | } 21 | 22 | impl IndexMut for Vec3 { 23 | fn index_mut(&mut self, index: u32) -> &mut Self::Output { 24 | match index { 25 | 0 => &mut self.e0, 26 | 1 => &mut self.e1, 27 | 2 => &mut self.e2, 28 | _ => panic!(), 29 | } 30 | } 31 | } 32 | 33 | impl Add for Vec3 { 34 | type Output = Self; 35 | fn add(self, other: Self) -> Self { 36 | Self { 37 | e0: self.e0 + other.e0, 38 | e1: self.e1 + other.e1, 39 | e2: self.e2 + other.e2, 40 | } 41 | } 42 | } 43 | 44 | impl AddAssign for Vec3 { 45 | fn add_assign(&mut self, other: Self) { 46 | self.e0 += other.e0; 47 | self.e1 += other.e1; 48 | self.e2 += other.e2; 49 | } 50 | } 51 | 52 | impl Sub for Vec3 { 53 | type Output = Self; 54 | fn sub(self, other: Self) -> Self { 55 | Self { 56 | e0: self.e0 - other.e0, 57 | e1: self.e1 - other.e1, 58 | e2: self.e2 - other.e2, 59 | } 60 | } 61 | } 62 | 63 | impl Mul for Vec3 { 64 | type Output = Self; 65 | fn mul(self, rhs: Self) -> Self::Output { 66 | Self { 67 | e0: self.e0 * rhs.e0, 68 | e1: self.e1 * rhs.e1, 69 | e2: self.e2 * rhs.e2, 70 | } 71 | } 72 | } 73 | 74 | impl Mul for Vec3 { 75 | type Output = Self; 76 | fn mul(self, rhs: f32) -> Self::Output { 77 | Self { 78 | e0: self.e0 * rhs, 79 | e1: self.e1 * rhs, 80 | e2: self.e2 * rhs, 81 | } 82 | } 83 | } 84 | 85 | impl Mul for f32 { 86 | type Output = Vec3; 87 | fn mul(self, rhs: Vec3) -> Vec3 { 88 | Vec3 { 89 | e0: rhs.e0 * self, 90 | e1: rhs.e1 * self, 91 | e2: rhs.e2 * self, 92 | } 93 | } 94 | } 95 | 96 | impl MulAssign for Vec3 { 97 | fn mul_assign(&mut self, rhs: f32) { 98 | self.e0 *= rhs; 99 | self.e1 *= rhs; 100 | self.e2 *= rhs; 101 | } 102 | } 103 | 104 | impl Div for Vec3 { 105 | type Output = Self; 106 | fn div(self, rhs: f32) -> Self::Output { 107 | Vec3 { 108 | e0: self.e0 / rhs, 109 | e1: self.e1 / rhs, 110 | e2: self.e2 / rhs, 111 | } 112 | } 113 | } 114 | 115 | impl DivAssign for Vec3 { 116 | fn div_assign(&mut self, rhs: f32) { 117 | self.e0 /= rhs; 118 | self.e1 /= rhs; 119 | self.e2 /= rhs; 120 | } 121 | } 122 | 123 | impl std::iter::Sum for Vec3 { 124 | fn sum(iter: I) -> Self 125 | where 126 | I: Iterator, 127 | { 128 | let mut res = Vec3::default(); 129 | for v in iter { 130 | res += v; 131 | } 132 | res 133 | } 134 | } 135 | 136 | impl Vec3 { 137 | pub fn mag(&self) -> f32 { 138 | dot(*self, *self).sqrt() 139 | } 140 | pub fn mag_sqrd(&self) -> f32 { 141 | dot(*self, *self) 142 | } 143 | pub fn unit(&self) -> Vec3 { 144 | *self / self.mag() 145 | } 146 | pub fn new(e0: f32, e1: f32, e2: f32) -> Vec3 { 147 | Vec3 { 148 | e0: e0, 149 | e1: e1, 150 | e2: e2, 151 | } 152 | } 153 | pub fn x(&self) -> f32 { 154 | self.e0 155 | } 156 | pub fn y(&self) -> f32 { 157 | self.e1 158 | } 159 | pub fn z(&self) -> f32 { 160 | self.e2 161 | } 162 | } 163 | 164 | pub fn dot(u: Vec3, v: Vec3) -> f32 { 165 | u.e0 * v.e0 + u.e1 * v.e1 + u.e2 * v.e2 166 | } 167 | 168 | // n is a unit vector that we are reflecting 169 | pub fn reflect(v: Vec3, n: Vec3) -> Vec3 { 170 | v - 2.0 * dot(v, n) * n 171 | } 172 | 173 | pub fn cross(v1: Vec3, v2: Vec3) -> Vec3 { 174 | Vec3::new( 175 | v1.e1 * v2.e2 - v1.e2 * v2.e1, 176 | v1.e2 * v2.e0 - v1.e0 * v2.e2, 177 | v1.e0 * v2.e1 - v1.e1 * v2.e0, 178 | ) 179 | } 180 | 181 | pub fn refract(v: Vec3, n: Vec3, ni_over_nt: f32) -> Option { 182 | let uv = v.unit(); 183 | let dt = dot(uv, n); 184 | let discriminant = 1.0 - ni_over_nt * ni_over_nt * (1.0 - dt * dt); 185 | 186 | if discriminant > 0.0 { 187 | let refracted = ni_over_nt * (uv - n * dt) - n * discriminant.sqrt(); 188 | return Some(refracted); 189 | } 190 | 191 | None 192 | } 193 | 194 | #[derive(Copy, Clone, Default)] 195 | pub struct Ray { 196 | pub a: Vec3, 197 | pub b: Vec3, 198 | pub time: f32, 199 | } 200 | 201 | impl Ray { 202 | pub fn direction(&self) -> Vec3 { 203 | self.b 204 | } 205 | pub fn origin(&self) -> Vec3 { 206 | self.a 207 | } 208 | pub fn time(&self) -> f32 { 209 | self.time 210 | } 211 | pub fn point_at_parameter(&self, t: f32) -> Vec3 { 212 | self.a + t * self.b 213 | } 214 | pub fn new(a: Vec3, b: Vec3, t: f32) -> Ray { 215 | Ray { 216 | a: a, 217 | b: b, 218 | time: t, 219 | } 220 | } 221 | } 222 | 223 | pub struct Onb { 224 | pub axis: Vec, 225 | } 226 | 227 | impl Onb { 228 | pub fn new() -> Self { 229 | let uvw = vec![ 230 | Vec3::new(1.0, 0.0, 0.0), 231 | Vec3::new(0.0, 1.0, 0.0), 232 | Vec3::new(0.0, 0.0, 1.0), 233 | ]; 234 | Onb { axis: uvw } 235 | } 236 | pub fn u(&self) -> Vec3 { 237 | self.axis[0] 238 | } 239 | pub fn v(&self) -> Vec3 { 240 | self.axis[1] 241 | } 242 | pub fn w(&self) -> Vec3 { 243 | self.axis[2] 244 | } 245 | pub fn local_vector(&self, a: &Vec3) -> Vec3 { 246 | a.x() * self.u() + a.y() * self.v() + a.z() * self.w() 247 | } 248 | pub fn local_coordinates(&self, a: f32, b: f32, c: f32) -> Vec3 { 249 | a * self.u() + b * self.v() + c * self.w() 250 | } 251 | pub fn build_from_w(&mut self, n: &Vec3) { 252 | self.axis[2] = n.unit(); 253 | let a: Vec3; 254 | if self.w().x().abs() > 0.9 { 255 | a = Vec3::new(0.0, 1.0, 0.0); 256 | } else { 257 | a = Vec3::new(1.0, 0.0, 0.0); 258 | } 259 | self.axis[1] = cross(self.w(), a).unit(); 260 | self.axis[0] = cross(self.w(), self.v()); 261 | } 262 | } 263 | 264 | impl Index for Onb { 265 | type Output = Vec3; 266 | fn index(&self, i: u32) -> &Self::Output { 267 | match i { 268 | 0 => &self.axis[0], 269 | 1 => &self.axis[1], 270 | 2 => &self.axis[2], 271 | _ => panic!(), 272 | } 273 | } 274 | } 275 | 276 | impl IndexMut for Onb { 277 | fn index_mut(&mut self, index: u32) -> &mut Self::Output { 278 | match index { 279 | 0 => &mut self.axis[0], 280 | 1 => &mut self.axis[1], 281 | 2 => &mut self.axis[2], 282 | _ => panic!(), 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /src/hit.rs: -------------------------------------------------------------------------------- 1 | use rand::seq::SliceRandom; 2 | use std::sync::Arc; 3 | 4 | use crate::bvh::*; 5 | use crate::material::Material; 6 | use crate::obj::*; 7 | use crate::transf::*; 8 | use crate::util::*; 9 | use crate::vec3::*; 10 | 11 | pub struct HitRecord { 12 | pub t: f32, 13 | pub p: Vec3, 14 | pub normal: Vec3, 15 | pub u: f32, 16 | pub v: f32, 17 | pub material: Arc, 18 | } 19 | 20 | impl HitRecord { 21 | fn new( 22 | t: f32, 23 | p: Vec3, 24 | normal: Vec3, 25 | u: f32, 26 | v: f32, 27 | material: Arc, 28 | ) -> HitRecord { 29 | HitRecord { 30 | t: t, 31 | p: p, 32 | normal: normal, 33 | u: u, 34 | v: v, 35 | material: material, 36 | } 37 | } 38 | } 39 | 40 | pub trait Hittable: Sync + Send { 41 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option; 42 | fn bounding_box(&self, t0: f32, t1: f32) -> Option; 43 | fn pdf_value(&self, _o: &Vec3, _v: &Vec3) -> f32 { 44 | 0.0 45 | } 46 | fn random(&self, _o: &Vec3) -> Vec3 { 47 | Vec3::new(1.0, 0.0, 0.0) 48 | } 49 | } 50 | 51 | impl Hittable for Sphere { 52 | // Solves a quadratic equation 53 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { 54 | let oc = r.origin() - self.center; 55 | let a = dot(r.b, r.b); 56 | let b = dot(oc, r.b); 57 | let c = dot(oc, oc) - self.radius * self.radius; 58 | 59 | let discriminant = b * b - a * c; 60 | if discriminant > 0.0 { 61 | // Check smaller parameter 62 | let t = (-b - discriminant.sqrt()) / a; 63 | if t_min < t && t < t_max { 64 | let point = r.point_at_parameter(t); 65 | let normal = (point - self.center) / self.radius; 66 | let (u, v) = Sphere::get_sphere_uv(&normal); 67 | return Some(HitRecord::new( 68 | t, 69 | point, 70 | normal, 71 | u, 72 | v, 73 | Arc::clone(&self.material), 74 | )); 75 | } 76 | 77 | // Check larger parameter 78 | let t = (-b + discriminant.sqrt()) / a; 79 | if t_min < t && t < t_max { 80 | let point = r.point_at_parameter(t); 81 | let normal = (point - self.center) / self.radius; 82 | let (u, v) = Sphere::get_sphere_uv(&normal); 83 | return Some(HitRecord::new( 84 | t, 85 | point, 86 | normal, 87 | u, 88 | v, 89 | Arc::clone(&self.material), 90 | )); 91 | } 92 | } 93 | None 94 | } 95 | fn bounding_box(&self, _t0: f32, _t1: f32) -> Option { 96 | let bbox = AABB::new( 97 | self.center - Vec3::new(self.radius, self.radius, self.radius), 98 | self.center + Vec3::new(self.radius, self.radius, self.radius), 99 | ); 100 | Some(bbox) 101 | } 102 | fn pdf_value(&self, o: &Vec3, v: &Vec3) -> f32 { 103 | if let Some(_) = self.hit(Ray::new(*o, *v, 0.0), 0.001, std::f32::MAX) { 104 | let cos_theta_max = 105 | (1.0 - self.radius * self.radius / (self.center - *o).mag_sqrd()).sqrt(); 106 | let solid_angle = 2.0 * std::f32::consts::PI * (1.0 - cos_theta_max); 107 | return 1.0 / solid_angle; 108 | } 109 | 0.0 110 | } 111 | fn random(&self, o: &Vec3) -> Vec3 { 112 | let direction = self.center - *o; 113 | let dist_sqrd = direction.mag_sqrd(); 114 | let mut uvw = Onb::new(); 115 | uvw.build_from_w(&direction); 116 | uvw.local_vector(&random_to_sphere(self.radius, dist_sqrd)) 117 | } 118 | } 119 | 120 | impl Hittable for Vec> { 121 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { 122 | let mut closest_t: f32 = t_max; 123 | let mut closest_hit: Option = None; 124 | for obj in self { 125 | if let Some(hit) = obj.hit(r, t_min, closest_t) { 126 | closest_t = hit.t; 127 | closest_hit = Some(hit); 128 | } 129 | } 130 | closest_hit 131 | } 132 | fn bounding_box(&self, t0: f32, t1: f32) -> Option { 133 | if self.is_empty() { 134 | return None; 135 | } 136 | 137 | let mut bbox: AABB; 138 | if let Some(new_bbox) = self[0].bounding_box(t0, t1) { 139 | bbox = new_bbox; 140 | } else { 141 | return None; 142 | } 143 | 144 | for obj in self.iter().skip(1) { 145 | match obj.bounding_box(t0, t1) { 146 | Some(new_bbox) => bbox = surrounding_bbox(bbox, new_bbox), 147 | None => { 148 | return None; 149 | } 150 | } 151 | } 152 | Some(bbox) 153 | } 154 | fn pdf_value(&self, o: &Vec3, v: &Vec3) -> f32 { 155 | let weight = 1.0 / self.len() as f32; 156 | let mut sum = 0.0; 157 | 158 | for obj in self.iter() { 159 | sum += weight * obj.pdf_value(o, v); 160 | } 161 | sum 162 | } 163 | fn random(&self, o: &Vec3) -> Vec3 { 164 | self.choose(&mut rand::thread_rng()).unwrap().random(o) 165 | } 166 | } 167 | 168 | impl Hittable for MovingSphere { 169 | // Solves a quadratic equation 170 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { 171 | let oc = r.origin() - self.center(r.time()); 172 | let a = dot(r.b, r.b); 173 | let b = dot(oc, r.b); 174 | let c = dot(oc, oc) - self.radius * self.radius; 175 | 176 | let discriminant = b * b - a * c; 177 | if discriminant > 0.0 { 178 | // Check smaller parameter 179 | let t = (-b - discriminant.sqrt()) / a; 180 | if t_min < t && t < t_max { 181 | let point = r.point_at_parameter(t); 182 | let normal = (point - self.center(r.time())) / self.radius; 183 | let (u, v) = Sphere::get_sphere_uv(&normal); 184 | return Some(HitRecord::new( 185 | t, 186 | point, 187 | normal, 188 | u, 189 | v, 190 | Arc::clone(&self.material), 191 | )); 192 | } 193 | 194 | // Check larger parameter 195 | let t = (-b + discriminant.sqrt()) / a; 196 | if t_min < t && t < t_max { 197 | let point = r.point_at_parameter(t); 198 | let normal = (point - self.center(r.time())) / self.radius; 199 | let (u, v) = Sphere::get_sphere_uv(&normal); 200 | return Some(HitRecord::new( 201 | t, 202 | point, 203 | normal, 204 | u, 205 | v, 206 | Arc::clone(&self.material), 207 | )); 208 | } 209 | } 210 | None 211 | } 212 | fn bounding_box(&self, t0: f32, t1: f32) -> Option { 213 | let bbox0 = AABB::new( 214 | self.center(t0) - Vec3::new(self.radius, self.radius, self.radius), 215 | self.center(t0) + Vec3::new(self.radius, self.radius, self.radius), 216 | ); 217 | let bbox1 = AABB::new( 218 | self.center(t1) - Vec3::new(self.radius, self.radius, self.radius), 219 | self.center(t1) + Vec3::new(self.radius, self.radius, self.radius), 220 | ); 221 | Some(surrounding_bbox(bbox0, bbox1)) 222 | } 223 | } 224 | 225 | impl Hittable for BvhNode { 226 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { 227 | match (&self.left, &self.right) { 228 | (Some(left), Some(right)) => { 229 | if self.bbox.hit(&r, t_min, t_max) { 230 | match (left.hit(r, t_min, t_max), right.hit(r, t_min, t_max)) { 231 | (Some(lhit), Some(rhit)) => { 232 | if lhit.t < rhit.t { 233 | return Some(lhit); 234 | } else { 235 | return Some(rhit); 236 | } 237 | } 238 | (Some(lhit), None) => return Some(lhit), 239 | (None, Some(rhit)) => return Some(rhit), 240 | (None, None) => (), 241 | } 242 | } 243 | } 244 | (_, _) => (), 245 | } 246 | None 247 | } 248 | fn bounding_box(&self, _t0: f32, _t1: f32) -> Option { 249 | Some(AABB::new(self.bbox.min(), self.bbox.max())) 250 | } 251 | } 252 | 253 | impl Hittable for XYRect { 254 | fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option { 255 | let t = (self.k - r.origin().z()) / r.direction().z(); 256 | if t < t0 || t > t1 { 257 | return None; 258 | } 259 | let x = r.origin().x() + t * r.direction().x(); 260 | let y = r.origin().y() + t * r.direction().y(); 261 | if x < self.x0 || x > self.x1 || y < self.y0 || y > self.y1 { 262 | return None; 263 | } 264 | let u = (x - self.x0) / (self.x1 - self.x0); 265 | let v = (y - self.y0) / (self.y1 - self.y0); 266 | let p = r.point_at_parameter(t); 267 | let normal = Vec3::new(0.0, 0.0, 1.0); 268 | Some(HitRecord::new(t, p, normal, u, v, self.material.clone())) 269 | } 270 | fn bounding_box(&self, _t0: f32, _t1: f32) -> Option { 271 | Some(AABB::new( 272 | Vec3::new(self.x0, self.y0, self.k - 0.0001), 273 | Vec3::new(self.x1, self.y1, self.k + 0.0001), 274 | )) 275 | } 276 | } 277 | 278 | impl Hittable for XZRect { 279 | fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option { 280 | let t = (self.k - r.origin().y()) / r.direction().y(); 281 | if t < t0 || t > t1 { 282 | return None; 283 | } 284 | let x = r.origin().x() + t * r.direction().x(); 285 | let z = r.origin().z() + t * r.direction().z(); 286 | if x < self.x0 || x > self.x1 || z < self.z0 || z > self.z1 { 287 | return None; 288 | } 289 | let u = (x - self.x0) / (self.x1 - self.x0); 290 | let v = (z - self.z0) / (self.z1 - self.z0); 291 | let p = r.point_at_parameter(t); 292 | let normal = Vec3::new(0.0, 1.0, 0.0); 293 | Some(HitRecord::new(t, p, normal, u, v, self.material.clone())) 294 | } 295 | fn bounding_box(&self, _t0: f32, _t1: f32) -> Option { 296 | Some(AABB::new( 297 | Vec3::new(self.x0, self.k - 0.0001, self.z0), 298 | Vec3::new(self.x1, self.k + 0.0001, self.z1), 299 | )) 300 | } 301 | fn pdf_value(&self, o: &Vec3, v: &Vec3) -> f32 { 302 | if let Some(hit) = self.hit(Ray::new(*o, *v, 0.0), 0.001, std::f32::MAX) { 303 | let area = (self.x1 - self.x0) * (self.z1 - self.z0); 304 | let dist_sqrd = hit.t * hit.t * v.mag().powi(2); 305 | let cosine = (dot(*v, hit.normal) / v.mag()).abs(); 306 | return dist_sqrd / (cosine * area); 307 | } 308 | 0.0 309 | } 310 | fn random(&self, o: &Vec3) -> Vec3 { 311 | let random_point = Vec3::new( 312 | self.x0 + rand_float() * (self.x1 - self.x0), 313 | self.k, 314 | self.z0 + rand_float() * (self.z1 - self.z0), 315 | ); 316 | random_point - *o 317 | } 318 | } 319 | 320 | impl Hittable for YZRect { 321 | fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option { 322 | let t = (self.k - r.origin().x()) / r.direction().x(); 323 | if t < t0 || t > t1 { 324 | return None; 325 | } 326 | let y = r.origin().y() + t * r.direction().y(); 327 | let z = r.origin().z() + t * r.direction().z(); 328 | if y < self.y0 || y > self.y1 || z < self.z0 || z > self.z1 { 329 | return None; 330 | } 331 | let u = (y - self.y0) / (self.y1 - self.y0); 332 | let v = (z - self.z0) / (self.z1 - self.z0); 333 | let p = r.point_at_parameter(t); 334 | let normal = Vec3::new(1.0, 0.0, 0.0); 335 | Some(HitRecord::new(t, p, normal, u, v, self.material.clone())) 336 | } 337 | fn bounding_box(&self, _t0: f32, _t1: f32) -> Option { 338 | Some(AABB::new( 339 | Vec3::new(self.k - 0.0001, self.y0, self.z0), 340 | Vec3::new(self.k + 0.0001, self.y1, self.z1), 341 | )) 342 | } 343 | } 344 | 345 | impl Hittable for FlipNormals { 346 | fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option { 347 | if let Some(mut hit) = self.obj_ref.hit(r, t0, t1) { 348 | hit.normal *= -1.0; 349 | return Some(hit); 350 | } 351 | None 352 | } 353 | fn bounding_box(&self, t0: f32, t1: f32) -> Option { 354 | self.obj_ref.bounding_box(t0, t1) 355 | } 356 | } 357 | 358 | impl Hittable for BoxShape { 359 | fn hit(&self, r: Ray, t0: f32, t1: f32) -> Option { 360 | self.faces.hit(r, t0, t1) 361 | } 362 | fn bounding_box(&self, _t0: f32, _t1: f32) -> Option { 363 | Some(AABB::new(self.pmin, self.pmax)) 364 | } 365 | } 366 | 367 | impl Hittable for Translate { 368 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { 369 | let moved_ray = Ray::new(r.origin() - self.offset, r.direction(), r.time()); 370 | 371 | if let Some(mut hit) = self.obj_ref.hit(moved_ray, t_min, t_max) { 372 | hit.p += self.offset; 373 | return Some(hit); 374 | } 375 | None 376 | } 377 | fn bounding_box(&self, t0: f32, t1: f32) -> Option { 378 | if let Some(bbox) = self.obj_ref.bounding_box(t0, t1) { 379 | return Some(AABB::new( 380 | bbox.min() + self.offset, 381 | bbox.max() + self.offset, 382 | )); 383 | } 384 | None 385 | } 386 | } 387 | 388 | impl Hittable for RotateY { 389 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { 390 | let mut origin = r.origin(); 391 | let mut direction = r.direction(); 392 | 393 | origin[0] = self.cos_theta * r.origin()[0] - self.sin_theta * r.origin()[2]; 394 | origin[2] = self.sin_theta * r.origin()[0] + self.cos_theta * r.origin()[2]; 395 | 396 | direction[0] = self.cos_theta * r.direction()[0] - self.sin_theta * r.direction()[2]; 397 | direction[2] = self.sin_theta * r.direction()[0] + self.cos_theta * r.direction()[2]; 398 | let rotate_r = Ray::new(origin, direction, r.time()); 399 | if let Some(mut hit) = self.obj_ref.hit(rotate_r, t_min, t_max) { 400 | let mut p = hit.p; 401 | let mut normal = hit.normal; 402 | p[0] = self.cos_theta * hit.p[0] + self.sin_theta * hit.p[2]; 403 | p[2] = -self.sin_theta * hit.p[0] + self.cos_theta * hit.p[2]; 404 | normal[0] = self.cos_theta * hit.normal[0] + self.sin_theta * hit.normal[2]; 405 | normal[2] = -self.sin_theta * hit.normal[0] + self.cos_theta * hit.normal[2]; 406 | hit.p = p; 407 | hit.normal = normal; 408 | return Some(hit); 409 | } 410 | None 411 | } 412 | fn bounding_box(&self, t0: f32, t1: f32) -> Option { 413 | if let Some(bbox) = self.obj_ref.bounding_box(t0, t1) { 414 | let mut max = Vec3::new( 415 | std::f32::NEG_INFINITY, 416 | std::f32::NEG_INFINITY, 417 | std::f32::NEG_INFINITY, 418 | ); 419 | let mut min = Vec3::new(std::f32::INFINITY, std::f32::INFINITY, std::f32::INFINITY); 420 | for i in 0..2 { 421 | for j in 0..2 { 422 | for k in 0..2 { 423 | let x = i as f32 * bbox.max().x() + (1.0 - i as f32) * bbox.min().x(); 424 | let y = j as f32 * bbox.max().y() + (1.0 - j as f32) * bbox.min().y(); 425 | let z = k as f32 * bbox.max().z() + (1.0 - k as f32) * bbox.min().z(); 426 | 427 | let new_x = self.cos_theta * x + self.sin_theta * z; 428 | let new_z = -self.sin_theta * x + self.cos_theta * z; 429 | let tester = Vec3::new(new_x, y, new_z); 430 | for c in 0..3 { 431 | if tester[c as u32] > max[c] { 432 | max[c] = tester[c]; 433 | } 434 | if tester[c as u32] < max[c] { 435 | min[c] = tester[c]; 436 | } 437 | } 438 | } 439 | } 440 | } 441 | return Some(AABB::new(min, max)); 442 | } 443 | None 444 | } 445 | } 446 | 447 | impl Hittable for ConstantMedium { 448 | fn hit(&self, r: Ray, t_min: f32, t_max: f32) -> Option { 449 | if let Some(mut hit1) = self 450 | .boundary 451 | .hit(r, std::f32::NEG_INFINITY, std::f32::INFINITY) 452 | { 453 | if let Some(mut hit2) = self.boundary.hit(r, hit1.t + 0.0001, std::f32::INFINITY) { 454 | if hit1.t < t_min { 455 | hit1.t = t_min; 456 | } 457 | if hit2.t > t_max { 458 | hit2.t = t_max; 459 | } 460 | if hit1.t >= hit2.t { 461 | return None; 462 | } 463 | if hit1.t < 0.0 { 464 | hit1.t = 0.0; 465 | } 466 | let dist_in_boundary = (hit2.t - hit1.t) * r.direction().mag(); 467 | let hit_dist = -(1.0 / self.density) * rand_float().ln(); 468 | if hit_dist < dist_in_boundary { 469 | let t = hit1.t + hit_dist / r.direction().mag(); 470 | let p = r.point_at_parameter(t); 471 | return Some(HitRecord::new( 472 | t, 473 | p, 474 | Vec3::new(1.0, 0.0, 0.0), 475 | 0.0, 476 | 0.0, 477 | self.phase_function.clone(), 478 | )); 479 | } 480 | } 481 | } 482 | None 483 | } 484 | fn bounding_box(&self, t0: f32, t1: f32) -> Option { 485 | self.boundary.bounding_box(t0, t1) 486 | } 487 | } 488 | -------------------------------------------------------------------------------- /src/scene.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use image::GenericImageView; 4 | 5 | use crate::bvh::*; 6 | use crate::camera::*; 7 | use crate::hit::*; 8 | use crate::material::*; 9 | use crate::obj::*; 10 | use crate::perlin::Perlin; 11 | use crate::texture::*; 12 | use crate::transf::*; 13 | use crate::util::*; 14 | use crate::vec3::*; 15 | 16 | pub fn regular_scene() -> Vec> { 17 | let world: Vec> = vec![ 18 | Arc::new(Sphere::new( 19 | Vec3::new(0.0, 0.0, -1.0), 20 | 0.5, 21 | Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 22 | 0.1, 0.2, 0.5, 23 | ))))), 24 | )), 25 | Arc::new(Sphere::new( 26 | Vec3::new(0.0, -100.5, -1.0), 27 | 100.0, 28 | Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 29 | 0.8, 0.8, 0.0, 30 | ))))), 31 | )), 32 | Arc::new(Sphere::new( 33 | Vec3::new(1.0, 0.0, -1.0), 34 | 0.5, 35 | Arc::new(Metal::new(Vec3::new(0.8, 0.6, 0.2), 0.3)), 36 | )), 37 | Arc::new(Sphere::new( 38 | Vec3::new(-1.0, 0.0, -1.0), 39 | 0.5, 40 | Arc::new(Dielectric::new(1.5)), 41 | )), 42 | Arc::new(Sphere::new( 43 | Vec3::new(-1.0, 0.0, -1.0), 44 | -0.45, 45 | Arc::new(Dielectric::new(1.5)), 46 | )), 47 | ]; 48 | world 49 | } 50 | 51 | pub fn random_scene() -> Vec> { 52 | let mut scene: Vec> = Vec::new(); 53 | let checker = Arc::new(CheckerTexture::new( 54 | Box::new(ConstantTexture::new(Vec3::new(0.2, 0.3, 0.1))), 55 | Box::new(ConstantTexture::new(Vec3::new(0.9, 0.9, 0.9))), 56 | )); 57 | scene.push(Arc::new(Sphere::new( 58 | Vec3::new(0.0, -1000.0, -1.0), 59 | 1000.0, 60 | Arc::new(Lambertian::new(checker)), 61 | ))); 62 | 63 | for a in -11..11 { 64 | for b in -11..11 { 65 | let choose_mat = rand_float(); 66 | let center = Vec3::new( 67 | a as f32 + 0.9 * rand_float(), 68 | 0.2, 69 | b as f32 + 0.9 * rand_float(), 70 | ); 71 | if (center - Vec3::new(4.0, 0.2, 0.0)).mag() > 0.9 { 72 | if choose_mat < 0.8 { 73 | // diffuse 74 | scene.push(Arc::new(MovingSphere::new( 75 | center, 76 | center + Vec3::new(0.0, 0.5 * rand_float(), 0.0), 77 | 0.0, 78 | 1.0, 79 | 0.2, 80 | Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 81 | rand_float() * rand_float(), 82 | rand_float() * rand_float(), 83 | rand_float() * rand_float(), 84 | ))))), 85 | ))); 86 | } else if choose_mat < 0.95 { 87 | // metal 88 | scene.push(Arc::new(Sphere::new( 89 | center, 90 | 0.2, 91 | Arc::new(Metal::new( 92 | Vec3::new( 93 | 0.5 * (1.0 + rand_float()), 94 | 0.5 * (1.0 + rand_float()), 95 | 0.5 * (1.0 + rand_float()), 96 | ), 97 | 0.5 * rand_float(), 98 | )), 99 | ))); 100 | } else { 101 | // glass 102 | scene.push(Arc::new(Sphere::new( 103 | center, 104 | 0.2, 105 | Arc::new(Dielectric::new(1.5)), 106 | ))); 107 | } 108 | } 109 | } 110 | } 111 | scene.push(Arc::new(Sphere::new( 112 | Vec3::new(0.0, 1.0, 0.0), 113 | 1.0, 114 | Arc::new(Dielectric::new(1.5)), 115 | ))); 116 | scene.push(Arc::new(Sphere::new( 117 | Vec3::new(-4.0, 1.0, 0.0), 118 | 1.0, 119 | Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 120 | 0.4, 0.2, 0.1, 121 | ))))), 122 | ))); 123 | scene.push(Arc::new(Sphere::new( 124 | Vec3::new(4.0, 1.0, 0.0), 125 | 1.0, 126 | Arc::new(Metal::new(Vec3::new(0.7, 0.6, 0.5), 0.0)), 127 | ))); 128 | scene 129 | } 130 | 131 | pub fn two_spheres_scene() -> Vec> { 132 | let mut scene: Vec> = Vec::new(); 133 | let checker = Arc::new(CheckerTexture::new( 134 | Box::new(ConstantTexture::new(Vec3::new(0.2, 0.3, 0.1))), 135 | Box::new(ConstantTexture::new(Vec3::new(0.9, 0.9, 0.9))), 136 | )); 137 | let checker2 = Arc::new(CheckerTexture::new( 138 | Box::new(ConstantTexture::new(Vec3::new(0.1, 0.2, 0.3))), 139 | Box::new(ConstantTexture::new(Vec3::new(0.9, 0.9, 0.9))), 140 | )); 141 | scene.push(Arc::new(Sphere::new( 142 | Vec3::new(0.0, -10.0, 0.0), 143 | 10.0, 144 | Arc::new(Lambertian::new(checker)), 145 | ))); 146 | scene.push(Arc::new(Sphere::new( 147 | Vec3::new(0.0, 10.0, 0.0), 148 | 10.0, 149 | Arc::new(Lambertian::new(checker2)), 150 | ))); 151 | scene 152 | } 153 | 154 | pub fn two_perlin_spheres_scene() -> Vec> { 155 | let mut scene: Vec> = Vec::new(); 156 | let perlin_texture = Arc::new(NoiseTexture::new(4.0, Perlin::new())); 157 | scene.push(Arc::new(Sphere::new( 158 | Vec3::new(0.0, -1000.0, 0.0), 159 | 1000.0, 160 | Arc::new(Lambertian::new(perlin_texture.clone())), 161 | ))); 162 | scene.push(Arc::new(Sphere::new( 163 | Vec3::new(0.0, 2.0, 0.0), 164 | 2.0, 165 | Arc::new(Lambertian::new(perlin_texture)), 166 | ))); 167 | scene 168 | } 169 | 170 | pub fn earth_scene() -> Vec> { 171 | let mut scene: Vec> = Vec::new(); 172 | let perlin_texture = Arc::new(NoiseTexture::new(4.0, Perlin::new())); 173 | scene.push(Arc::new(Sphere::new( 174 | Vec3::new(0.0, -1000.0, 0.0), 175 | 1000.0, 176 | Arc::new(Lambertian::new(perlin_texture)), 177 | ))); 178 | 179 | let img = image::open("texture/earthmap.jpg").unwrap(); 180 | let (nx, ny) = img.dimensions(); 181 | let data = img.raw_pixels(); 182 | let image_texture = Arc::new(ImageTexture::new(data, nx as i32, ny as i32)); 183 | scene.push(Arc::new(Sphere::new( 184 | Vec3::new(0.0, 2.0, 0.0), 185 | 2.0, 186 | Arc::new(Lambertian::new(image_texture)), 187 | ))); 188 | scene 189 | } 190 | 191 | pub fn simple_light() -> Vec> { 192 | let perlin_texture = Arc::new(NoiseTexture::new(4.0, Perlin::new())); 193 | let mut scene: Vec> = Vec::new(); 194 | scene.push(Arc::new(Sphere::new( 195 | Vec3::new(0.0, -1000.0, 0.0), 196 | 1000.0, 197 | Arc::new(Lambertian::new(perlin_texture.clone())), 198 | ))); 199 | scene.push(Arc::new(Sphere::new( 200 | Vec3::new(0.0, 2.0, 0.0), 201 | 2.0, 202 | Arc::new(Lambertian::new(perlin_texture)), 203 | ))); 204 | 205 | let constant_texture = Arc::new(ConstantTexture::new(Vec3::new(4.0, 4.0, 4.0))); 206 | scene.push(Arc::new(Sphere::new( 207 | Vec3::new(0.0, 7.0, 0.0), 208 | 2.0, 209 | Arc::new(DiffuseLight::new(constant_texture.clone())), 210 | ))); 211 | scene.push(Arc::new(XYRect::new( 212 | 3.0, 213 | 5.0, 214 | 1.0, 215 | 3.0, 216 | -2.0, 217 | Arc::new(DiffuseLight::new(constant_texture.clone())), 218 | ))); 219 | scene 220 | } 221 | 222 | pub fn cornell_box() -> Vec> { 223 | let mut scene: Vec> = Vec::new(); 224 | 225 | let red = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 226 | 0.65, 0.05, 0.05, 227 | ))))); 228 | let white = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 229 | 0.73, 0.73, 0.73, 230 | ))))); 231 | let green = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 232 | 0.12, 0.45, 0.15, 233 | ))))); 234 | let light = Arc::new(DiffuseLight::new(Arc::new(ConstantTexture::new( 235 | Vec3::new(15.0, 15.0, 15.0), 236 | )))); 237 | 238 | scene.push(Arc::new(FlipNormals::new(Arc::new(YZRect::new( 239 | 0.0, 555.0, 0.0, 555.0, 555.0, green, 240 | ))))); 241 | scene.push(Arc::new(YZRect::new(0.0, 555.0, 0.0, 555.0, 0.0, red))); 242 | scene.push(Arc::new(XZRect::new( 243 | 213.0, 343.0, 227.0, 332.0, 554.0, light, 244 | ))); 245 | scene.push(Arc::new(FlipNormals::new(Arc::new(XZRect::new( 246 | 0.0, 247 | 555.0, 248 | 0.0, 249 | 555.0, 250 | 555.0, 251 | white.clone(), 252 | ))))); 253 | scene.push(Arc::new(XZRect::new( 254 | 0.0, 255 | 555.0, 256 | 0.0, 257 | 555.0, 258 | 0.0, 259 | white.clone(), 260 | ))); 261 | scene.push(Arc::new(FlipNormals::new(Arc::new(XYRect::new( 262 | 0.0, 263 | 555.0, 264 | 0.0, 265 | 555.0, 266 | 555.0, 267 | white.clone(), 268 | ))))); 269 | 270 | let small_box = Arc::new(BoxShape::new( 271 | Vec3::new(0.0, 0.0, 0.0), 272 | Vec3::new(165.0, 165.0, 165.0), 273 | white.clone(), 274 | )); 275 | let tall_box = Arc::new(BoxShape::new( 276 | Vec3::new(0.0, 0.0, 0.0), 277 | Vec3::new(165.0, 330.0, 165.0), 278 | white.clone(), 279 | )); 280 | scene.push(Arc::new(Translate::new( 281 | Arc::new(RotateY::new(small_box, -18.0)), 282 | Vec3::new(130.0, 0.0, 65.0), 283 | ))); 284 | scene.push(Arc::new(Translate::new( 285 | Arc::new(RotateY::new(tall_box, 15.0)), 286 | Vec3::new(265.0, 0.0, 295.0), 287 | ))); 288 | 289 | scene 290 | } 291 | 292 | pub fn cornell_smoke_scene() -> Vec> { 293 | let mut scene: Vec> = Vec::new(); 294 | let red = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 295 | 0.65, 0.05, 0.05, 296 | ))))); 297 | let white = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 298 | 0.73, 0.73, 0.73, 299 | ))))); 300 | let green = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 301 | 0.12, 0.45, 0.15, 302 | ))))); 303 | let light = Arc::new(DiffuseLight::new(Arc::new(ConstantTexture::new( 304 | Vec3::new(7.0, 7.0, 7.0), 305 | )))); 306 | 307 | scene.push(Arc::new(FlipNormals::new(Arc::new(YZRect::new( 308 | 0.0, 555.0, 0.0, 555.0, 555.0, green, 309 | ))))); 310 | scene.push(Arc::new(YZRect::new(0.0, 555.0, 0.0, 555.0, 0.0, red))); 311 | scene.push(Arc::new(XZRect::new( 312 | 113.0, 443.0, 127.0, 432.0, 554.0, light, 313 | ))); 314 | scene.push(Arc::new(FlipNormals::new(Arc::new(XZRect::new( 315 | 0.0, 316 | 555.0, 317 | 0.0, 318 | 555.0, 319 | 555.0, 320 | white.clone(), 321 | ))))); 322 | scene.push(Arc::new(XZRect::new( 323 | 0.0, 324 | 555.0, 325 | 0.0, 326 | 555.0, 327 | 0.0, 328 | white.clone(), 329 | ))); 330 | scene.push(Arc::new(FlipNormals::new(Arc::new(XYRect::new( 331 | 0.0, 332 | 555.0, 333 | 0.0, 334 | 555.0, 335 | 555.0, 336 | white.clone(), 337 | ))))); 338 | let tall_box = Arc::new(BoxShape::new( 339 | Vec3::new(0.0, 0.0, 0.0), 340 | Vec3::new(165.0, 330.0, 165.0), 341 | white.clone(), 342 | )); 343 | let tall_box = Arc::new(RotateY::new(tall_box, 15.0)); 344 | let tall_box = Arc::new(Translate::new(tall_box, Vec3::new(265.0, 0.0, 295.0))); 345 | let small_box = Arc::new(BoxShape::new( 346 | Vec3::new(0.0, 0.0, 0.0), 347 | Vec3::new(165.0, 165.0, 165.0), 348 | white.clone(), 349 | )); 350 | let small_box = Arc::new(RotateY::new(small_box, -18.0)); 351 | let small_box = Arc::new(Translate::new(small_box, Vec3::new(130.0, 0.0, 65.0))); 352 | scene.push(Arc::new(ConstantMedium::new( 353 | tall_box, 354 | 0.01, 355 | Arc::new(ConstantTexture::new(Vec3::new(0.0, 0.0, 0.0))), 356 | ))); 357 | scene.push(Arc::new(ConstantMedium::new( 358 | small_box, 359 | 0.01, 360 | Arc::new(ConstantTexture::new(Vec3::new(1.0, 1.0, 1.0))), 361 | ))); 362 | scene 363 | } 364 | 365 | pub fn final_scene() -> Vec> { 366 | // Create scene vector 367 | let mut scene: Vec> = Vec::new(); 368 | 369 | // Create and add a ground of boxes 370 | let mut boxes1: Vec> = Vec::new(); 371 | let ground = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 372 | 0.48, 0.83, 0.53, 373 | ))))); 374 | let boxes_per_side = 20; 375 | for i in 0..boxes_per_side { 376 | for j in 0..boxes_per_side { 377 | let w = 100.0; 378 | let x0 = -1000.0 + i as f32 * w; 379 | let z0 = -1000.0 + j as f32 * w; 380 | let y0 = 0.0; 381 | let x1 = x0 + w; 382 | let y1 = rand_float() * 100.0 + 1.0; 383 | let z1 = z0 + w; 384 | boxes1.push(Arc::new(BoxShape::new( 385 | Vec3::new(x0, y0, z0), 386 | Vec3::new(x1, y1, z1), 387 | ground.clone(), 388 | ))); 389 | } 390 | } 391 | scene.push(Arc::new(BvhNode::new(&mut boxes1, 0.0, 1.0))); 392 | 393 | // Create and add lighting to scene 394 | let light = Arc::new(DiffuseLight::new(Arc::new(ConstantTexture::new( 395 | Vec3::new(7.0, 7.0, 7.0), 396 | )))); 397 | scene.push(Arc::new(XZRect::new( 398 | 123.0, 423.0, 147.0, 412.0, 554.0, light, 399 | ))); 400 | 401 | // Add moving sphere 402 | let center = Vec3::new(400.0, 400.0, 200.0); 403 | scene.push(Arc::new(MovingSphere::new( 404 | center, 405 | center + Vec3::new(30.0, 0.0, 0.0), 406 | 0.0, 407 | 1.0, 408 | 50.0, 409 | Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 410 | 0.7, 0.3, 0.1, 411 | ))))), 412 | ))); 413 | 414 | // Add dielectric and metal sphere 415 | scene.push(Arc::new(Sphere::new( 416 | Vec3::new(260.0, 150.0, 45.0), 417 | 50.0, 418 | Arc::new(Dielectric::new(1.5)), 419 | ))); 420 | scene.push(Arc::new(Sphere::new( 421 | Vec3::new(0.0, 150.0, 145.0), 422 | 50.0, 423 | Arc::new(Metal::new(Vec3::new(0.8, 0.8, 0.9), 10.0)), 424 | ))); 425 | 426 | // Add boundary 427 | let mut boundary = Arc::new(Sphere::new( 428 | Vec3::new(360.0, 150.0, 145.0), 429 | 70.0, 430 | Arc::new(Dielectric::new(1.5)), 431 | )); 432 | scene.push(boundary.clone()); 433 | 434 | // Add medium in boundary 435 | scene.push(Arc::new(ConstantMedium::new( 436 | boundary.clone(), 437 | 0.2, 438 | Arc::new(ConstantTexture::new(Vec3::new(0.2, 0.4, 0.9))), 439 | ))); 440 | 441 | // Add new medium in different boundary 442 | boundary = Arc::new(Sphere::new( 443 | Vec3::new(0.0, 0.0, 0.0), 444 | 5000.0, 445 | Arc::new(Dielectric::new(1.5)), 446 | )); 447 | scene.push(Arc::new(ConstantMedium::new( 448 | boundary.clone(), 449 | 0.0001, 450 | Arc::new(ConstantTexture::new(Vec3::new(1.0, 1.0, 1.0))), 451 | ))); 452 | 453 | // Add image textured sphere 454 | let img = image::open("texture/earthmap.jpg").unwrap(); 455 | let (nx, ny) = img.dimensions(); 456 | let data = img.raw_pixels(); 457 | let image_texture = Arc::new(ImageTexture::new(data, nx as i32, ny as i32)); 458 | let image_mat = Arc::new(Lambertian::new(image_texture)); 459 | scene.push(Arc::new(Sphere::new( 460 | Vec3::new(400.0, 200.0, 400.0), 461 | 100.0, 462 | image_mat, 463 | ))); 464 | 465 | // Add perlin textured sphere 466 | let perlin_texture = Arc::new(NoiseTexture::new(0.1, Perlin::new())); 467 | scene.push(Arc::new(Sphere::new( 468 | Vec3::new(220.0, 280.0, 300.0), 469 | 80.0, 470 | Arc::new(Lambertian::new(perlin_texture.clone())), 471 | ))); 472 | 473 | // Add rotated "box" of spheres 474 | let mut box_of_spheres: Vec> = Vec::new(); 475 | let white = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 476 | 0.73, 0.73, 0.73, 477 | ))))); 478 | for _ in 0..1000 { 479 | box_of_spheres.push(Arc::new(Sphere::new( 480 | Vec3::new( 481 | rand_float() * 165.0, 482 | rand_float() * 165.0, 483 | rand_float() * 165.0, 484 | ), 485 | 10.0, 486 | white.clone(), 487 | ))); 488 | } 489 | scene.push(Arc::new(Translate::new( 490 | Arc::new(RotateY::new( 491 | Arc::new(BvhNode::new(&mut box_of_spheres, 0.0, 1.0)), 492 | 15.0, 493 | )), 494 | Vec3::new(-100.0, 270.0, 395.0), 495 | ))); 496 | 497 | // All done, return the scene! 498 | scene 499 | } 500 | 501 | pub fn cornell_mc(aspect: f32) -> (Camera, Vec>) { 502 | let mut scene: Vec> = Vec::new(); 503 | let red = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 504 | 0.65, 0.05, 0.05, 505 | ))))); 506 | let white = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 507 | 0.73, 0.73, 0.73, 508 | ))))); 509 | let green = Arc::new(Lambertian::new(Arc::new(ConstantTexture::new(Vec3::new( 510 | 0.12, 0.45, 0.15, 511 | ))))); 512 | let light = Arc::new(DiffuseLight::new(Arc::new(ConstantTexture::new( 513 | Vec3::new(15.0, 15.0, 15.0), 514 | )))); 515 | 516 | scene.push(Arc::new(FlipNormals::new(Arc::new(YZRect::new( 517 | 0.0, 555.0, 0.0, 555.0, 555.0, green, 518 | ))))); 519 | scene.push(Arc::new(YZRect::new(0.0, 555.0, 0.0, 555.0, 0.0, red))); 520 | scene.push(Arc::new(FlipNormals::new(Arc::new(XZRect::new( 521 | 213.0, 343.0, 227.0, 332.0, 554.0, light, 522 | ))))); 523 | scene.push(Arc::new(FlipNormals::new(Arc::new(XZRect::new( 524 | 0.0, 525 | 555.0, 526 | 0.0, 527 | 555.0, 528 | 555.0, 529 | white.clone(), 530 | ))))); 531 | scene.push(Arc::new(XZRect::new( 532 | 0.0, 533 | 555.0, 534 | 0.0, 535 | 555.0, 536 | 0.0, 537 | white.clone(), 538 | ))); 539 | scene.push(Arc::new(FlipNormals::new(Arc::new(XYRect::new( 540 | 0.0, 541 | 555.0, 542 | 0.0, 543 | 555.0, 544 | 555.0, 545 | white.clone(), 546 | ))))); 547 | 548 | let tall_box = Arc::new(BoxShape::new( 549 | Vec3::new(0.0, 0.0, 0.0), 550 | Vec3::new(165.0, 330.0, 165.0), 551 | white.clone(), 552 | )); 553 | scene.push(Arc::new(Translate::new( 554 | Arc::new(RotateY::new(tall_box, 15.0)), 555 | Vec3::new(265.0, 0.0, 295.0), 556 | ))); 557 | 558 | let glass = Arc::new(Dielectric::new(1.5)); 559 | scene.push(Arc::new(Sphere::new( 560 | Vec3::new(190.0, 90.0, 190.0), 561 | 90.0, 562 | glass, 563 | ))); 564 | 565 | let lookfrom = Vec3::new(278.0, 278.0, -800.0); 566 | let lookat = Vec3::new(278.0, 278.0, 0.0); 567 | let vup = Vec3::new(0.0, 1.0, 0.0); 568 | let dist_to_focus = 10.0; 569 | let aperture = 0.0; 570 | let vfov = 40.0; 571 | let t0 = 0.0; 572 | let t1 = 1.0; 573 | 574 | let cam = Camera::new( 575 | lookfrom, 576 | lookat, 577 | vup, 578 | vfov, 579 | aspect, 580 | aperture, 581 | dist_to_focus, 582 | t0, 583 | t1, 584 | ); 585 | 586 | (cam, scene) 587 | } 588 | --------------------------------------------------------------------------------