diff --git a/Cargo.lock b/Cargo.lock index c0d5dfa..31a7e18 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -214,12 +214,32 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core 0.10.1", +] + [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -299,6 +319,12 @@ dependencies = [ "syn", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "exr" version = "1.74.0" @@ -365,6 +391,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "futures-channel" version = "0.3.32" @@ -418,10 +450,24 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", ] +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasip2", + "wasip3", +] + [[package]] name = "gif" version = "0.14.2" @@ -443,6 +489,27 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.5.2" @@ -518,6 +585,12 @@ dependencies = [ "want", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "image" version = "0.25.10" @@ -558,6 +631,18 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8" +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + [[package]] name = "instant" version = "0.1.13" @@ -610,7 +695,7 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom", + "getrandom 0.3.4", "libc", ] @@ -620,6 +705,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "lebe" version = "0.5.3" @@ -897,6 +988,16 @@ dependencies = [ "log", ] +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -991,6 +1092,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" version = "0.9.4" @@ -998,7 +1105,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha", - "rand_core", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", ] [[package]] @@ -1008,7 +1126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.9.5", ] [[package]] @@ -1017,9 +1135,15 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "getrandom", + "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rav1e" version = "0.8.1" @@ -1047,7 +1171,7 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand", + "rand 0.9.4", "rand_chacha", "simd_helpers", "thiserror 2.0.18", @@ -1098,6 +1222,7 @@ dependencies = [ "log", "ops", "pretty_env_logger", + "rand 0.10.1", ] [[package]] @@ -1156,6 +1281,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.228" @@ -1381,6 +1512,12 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "v_frame" version = "0.3.9" @@ -1416,6 +1553,15 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.118" @@ -1461,6 +1607,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "weezl" version = "0.1.12" @@ -1591,6 +1771,88 @@ name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.1", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] [[package]] name = "y4m" diff --git a/Cargo.toml b/Cargo.toml index 1414d03..dc98c47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,3 +8,4 @@ image = "0.25.10" log = "0.4.29" ops = "0.6.0" pretty_env_logger = "0.5.0" +rand = "0.10.1" diff --git a/src/camera.rs b/src/camera.rs index f802efe..15782f2 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -12,6 +12,7 @@ pub struct Camera { image_width: u32, image_height: u32, anti_alias_rate: u32, + max_depth: u32, center: Vec3, pixel00_loc: Vec3, pixel_delta_u: Vec3, @@ -45,6 +46,7 @@ impl Camera { image_width: image_width, image_height: image_height, anti_alias_rate: anti_alias_rate, + max_depth: 50, center: camera_center, pixel00_loc: pixel00_loc, pixel_delta_u: pixel_delta_u, @@ -70,7 +72,7 @@ impl Camera { + (y * self.pixel_delta_v / (self.anti_alias_rate + 1) as f32); let ray_dir = pixel_loc - self.center; let r = Ray::new(self.center, ray_dir); - pixel_colour += self.ray_colour(&hittables, &r); + pixel_colour += self.ray_colour(&hittables, &r, self.max_depth); } } @@ -84,11 +86,15 @@ impl Camera { imgbuf.save("output.png").unwrap(); } - fn ray_colour(&self, hittables: &Vec, r: &Ray) -> Colour { + fn ray_colour(&self, hittables: &Vec, r: &Ray, depth: u32) -> Colour { + if depth <= 0 { + return Colour::nil(); + } + let closest = Hit::hit_list(hittables, r); if let Some(hit) = closest { - let n = hit.n(); - return 0.5 * Colour::new(n.x() + 1., n.y() + 1., n.z() + 1.); + let dir = *hit.n() + Vec3::random_unit_hemisphere(hit.n()); + return 0.5 * self.ray_colour(hittables, &Ray::new(*hit.p(), dir), depth - 1); } // background diff --git a/src/main.rs b/src/main.rs index e588d88..e3ba50d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,6 @@ fn main() { world.push(Sphere::new(Vec3::new(0., 0., -0.1), 0.01)); world.push(Sphere::new(Vec3::new(0., -100.5, -1.), 100.)); - let c = Camera::new_aa(16. / 9., 400, 2); + let c = Camera::new_aa(16. / 9., 400, 3); c.render(&world); } diff --git a/src/objects/hit.rs b/src/objects/hit.rs index e1514d2..529bd58 100644 --- a/src/objects/hit.rs +++ b/src/objects/hit.rs @@ -28,7 +28,9 @@ impl Hit { for hittable in hittables { if let Some(hit) = hittable.hit(r) { // hit happens in front of camera and is closer than closest - if *hit.t() > 0. && (closest.is_none() || closest.as_ref().unwrap().t() > hit.t()) { + if *hit.t() > 0.001 + && (closest.is_none() || closest.as_ref().unwrap().t() > hit.t()) + { closest = Some(hit); } } diff --git a/src/vec3.rs b/src/vec3.rs index d4ebd29..a1b3a15 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -4,6 +4,8 @@ use std::{ ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; +use rand::RngExt; + #[derive(Copy, Clone)] pub struct Vec3 { r: f32, @@ -26,6 +28,33 @@ impl Vec3 { } } + pub fn random() -> Vec3 { + let mut rng = rand::rng(); + Vec3 { + r: rng.random_range(-1.0..1.), + g: rng.random_range(-1.0..1.), + b: rng.random_range(-1.0..1.), + } + } + + pub fn random_unit() -> Vec3 { + loop { + let v = Vec3::random(); + if v.length_squared() <= 1. { + return v / v.length(); + } + } + } + + pub fn random_unit_hemisphere(n: &Vec3) -> Vec3 { + let v = Vec3::random_unit(); + if n.dot(&v) > 0.0 { + v + } else { + -v + } + } + pub fn x(&self) -> &f32 { &self.r } @@ -67,9 +96,26 @@ impl Vec3 { } pub fn output(self) -> image::Rgb { - let ir = (255.599 * self.r.clamp(0., 1.)) as u8; - let ig = (255.599 * self.g.clamp(0., 1.)) as u8; - let ib = (255.599 * self.b.clamp(0., 1.)) as u8; + // gamma correction + let r = if self.r > 0. { + sqrt(self.r).clamp(0., 1.) + } else { + 0. + }; + let g = if self.g > 0. { + sqrt(self.g).clamp(0., 1.) + } else { + 0. + }; + let b = if self.b > 0. { + sqrt(self.b).clamp(0.1, 1.) + } else { + 0. + }; + + let ir = (255.599 * r) as u8; + let ig = (255.599 * g) as u8; + let ib = (255.599 * b) as u8; image::Rgb([ir, ig, ib]) }