From b458b99c8232a819b4a642ea08f565b3add3ef23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20R=C3=B6ger?= Date: Sun, 17 Nov 2024 23:50:05 +0100 Subject: [PATCH] feat(raytracer): add minimal phong lighting demo --- Cargo.lock | 891 +++++++++++++++++++++++++++++++++++++++- Cargo.toml | 1 + flake.nix | 4 + src/bin/rt_demo.rs | 85 +++- src/raytracer/camera.rs | 84 ++-- src/raytracer/mod.rs | 3 +- src/raytracer/plane.rs | 51 +++ src/raytracer/scene.rs | 61 ++- src/raytracer/sphere.rs | 16 + src/raytracer/types.rs | 38 +- src/raytracer/vec.rs | 9 +- 11 files changed, 1192 insertions(+), 51 deletions(-) create mode 100644 src/raytracer/plane.rs diff --git a/Cargo.lock b/Cargo.lock index 7ae4834..024de38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,24 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + [[package]] name = "approx" version = "0.5.1" @@ -11,6 +29,29 @@ dependencies = [ "num-traits", ] +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "as-any" version = "0.3.1" @@ -23,18 +64,104 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "av1-grain" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "bitstream-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[package]] +name = "built" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytemuck" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "cc" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd9de9f2205d5ef3fd67e685b0df337994ddd4495e2a28d185500d0e1edfea47" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.0" @@ -47,6 +174,98 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[package]] +name = "fdeflate" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c6f4c64c1d33a3111c4466f7365ebdcc37c5bd1ea0d62aae2e3d722aacbedb" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "futures" version = "0.3.31" @@ -136,22 +355,181 @@ dependencies = [ "slab", ] +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a9bfc1af68b1726ea47d3d5109de126281def866b33970e10fbab11b5dafab3" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e031e8e3d94711a9ccb5d6ea357439ef3dcbed361798bd4071dc4d9793fbe22f" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "jobserver" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + [[package]] name = "libc" version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" +[[package]] +name = "libfuzzer-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "lispers" version = "0.1.0" dependencies = [ "as-any", "futures", + "image", "nalgebra", "nix", ] +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "matrixmultiply" version = "0.3.9" @@ -162,12 +540,38 @@ dependencies = [ "rawpointer", ] +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "nalgebra" version = "0.33.2" @@ -195,18 +599,40 @@ dependencies = [ "syn", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nix" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "num-bigint" version = "0.4.6" @@ -226,6 +652,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -255,6 +692,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + [[package]] name = "paste" version = "1.0.15" @@ -273,6 +716,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[package]] +name = "png" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f9d46a34a05a6a57566bc2bfae066ef07585a6e3fa30fbbdff5936380623f0" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.89" @@ -282,6 +753,40 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.37" @@ -291,12 +796,118 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "rawpointer" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" + [[package]] name = "safe_arch" version = "0.7.2" @@ -306,6 +917,41 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "simba" version = "0.9.0" @@ -319,6 +965,21 @@ dependencies = [ "wide", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "slab" version = "0.4.9" @@ -328,6 +989,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + [[package]] name = "syn" version = "2.0.87" @@ -339,6 +1006,90 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "typenum" version = "1.17.0" @@ -351,6 +1102,90 @@ version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + [[package]] name = "wide" version = "0.7.28" @@ -360,3 +1195,57 @@ dependencies = [ "bytemuck", "safe_arch", ] + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-inflate" +version = "0.2.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "zune-jpeg" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16099418600b4d8f028622f73ff6e3deaabdff330fb9a2a131dea781ee8b0768" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index b35b581..d2c4c78 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,6 @@ path = "src/bin/repl.rs" [dependencies] as-any = "0.3.1" futures = "0.3.30" +image = "0.25.5" nalgebra = "0.33.2" nix = "0.29.0" diff --git a/flake.nix b/flake.nix index 067890b..548fdb0 100644 --- a/flake.nix +++ b/flake.nix @@ -75,6 +75,10 @@ type = "app"; program = "${packages.default}/bin/repl"; }; + rt_demo = { + type = "app"; + program = "${packages.default}/bin/rt_demo"; + }; default = demo; }; diff --git a/src/bin/rt_demo.rs b/src/bin/rt_demo.rs index a265547..d8c66e4 100644 --- a/src/bin/rt_demo.rs +++ b/src/bin/rt_demo.rs @@ -1,5 +1,86 @@ -use lispers::raytracer::{scene::Scene, types::Light}; +use lispers::raytracer::{ + camera::Camera, + plane::Plane, + scene::Scene, + sphere::Sphere, + types::{Color, Light, Material, Point3, Vector3}, +}; +extern crate nalgebra as na; fn main() { - let scene = Scene::new(); + let mut scene = Scene::new(); + + scene.set_ambient(Color::new(0.2, 0.2, 0.2)); + + scene.add_light(Light { + position: Point3::new(4.0, 7.0, 10.0), + color: Color::new(1.0, 1.0, 1.0), + }); + scene.add_light(Light { + position: Point3::new(-2.0, 7.0, 10.0), + color: Color::new(1.0, 1.0, 1.0), + }); + + scene.add_object(Box::new(Plane::new( + Point3::new(0.0, -1.0, 0.0), + Vector3::new(0.0, 1.0, 0.0), + Material::new( + Color::new(0.5, 0.5, 0.5), + Color::new(0.5, 0.5, 0.5), + Color::new(0.0, 0.0, 0.0), + 0.0, + 0.6, + ), + ))); + + scene.add_object(Box::new(Sphere::new( + Point3::new(-2.0, 0.0, 1.0), + 1.0, + Material::new( + Color::new(0.0, 1.0, 0.0), + Color::new(0.0, 1.0, 0.0), + Color::new(0.6, 0.6, 0.6), + 20.0, + 0.3, + ), + ))); + + scene.add_object(Box::new(Sphere::new( + Point3::new(0.2, -0.5, -0.2), + 0.5, + Material::new( + Color::new(0.0, 0.0, 1.0), + Color::new(0.0, 0.0, 1.0), + Color::new(0.6, 0.6, 0.6), + 20.0, + 0.3, + ), + ))); + + scene.add_object(Box::new(Sphere::new( + Point3::new(-0.5, 0.5, -2.0), + 1.5, + Material::new( + Color::new(1.0, 0.0, 0.0), + Color::new(1.0, 0.0, 0.0), + Color::new(0.6, 0.6, 0.6), + 20.0, + 0.3, + ), + ))); + + let camera = Camera::new( + Point3::new(0.0, 0.7, 5.0), + Point3::new(-1.0, -0.5, 0.0), + Vector3::new(0.0, 1.0, 0.0), + 60.0, + 400, + 300, + ); + + let fname = "demo-scene.png"; + match camera.render(&scene, 5, 2).save(fname) { + Ok(_) => println!("Image saved to {}", fname), + Err(e) => println!("Error saving image: {}", e), + } } diff --git a/src/raytracer/camera.rs b/src/raytracer/camera.rs index 966006d..b85e46f 100644 --- a/src/raytracer/camera.rs +++ b/src/raytracer/camera.rs @@ -1,70 +1,102 @@ use super::{ scene::Scene, - types::{Point3, Ray, Scalar, Vector3}, + types::{Color, Point3, Ray, Scalar, Vector3}, }; -// use image::Rgb32FImage; +use image::RgbImage; +/// A camera that can render a scene. pub struct Camera { + /// Position of the camera's eye. position: Point3, - up: Vector3, - right: Vector3, - upper_left: Point3, + /// The lower left point of the image plane. + lower_left: Point3, + /// The direction of the x-axis on the image plane. (length is equal to the image width) x_dir: Vector3, + /// The direction of the y-axis on the image plane. (length is equal to the image height) y_dir: Vector3, + /// The width of the image. [px] width: usize, + /// The height of the image. [px] height: usize, } impl Camera { + /// Create a new camera at `position` looking at `center` with `up` as the up vector. + /// The camera has a field of view of `fovy` degrees and an image size of `width` x `height`. pub fn new( position: Point3, - direction: Vector3, + center: Point3, up: Vector3, fovy: Scalar, width: usize, height: usize, ) -> Camera { - let aspect_ratio = width as Scalar / height as Scalar; - let fovx = fovy * aspect_ratio; - let right = direction.cross(&up).normalize(); - let x_dir = right * (fovx / 2.0).tan(); - let y_dir = -up * (fovy / 2.0).tan(); - let upper_left = position + direction - x_dir + y_dir; + let view = (center - position).normalize(); + let dist = (center - position).norm(); + let aspect = width as Scalar / height as Scalar; + + let im_height = 2.0 * dist * (fovy.to_radians() / 2.0).tan(); + let im_width = aspect * im_height; + + let x_dir = view.cross(&up).normalize() * im_width; + let y_dir = x_dir.cross(&view).normalize() * im_height; + + let lower_left = center - 0.5 * x_dir - 0.5 * y_dir; Camera { position, - up, - right, - upper_left, + lower_left, x_dir, y_dir, width, height, } } -} -impl Camera { + /// Get a ray pointing from the camera to a relative position on the image plane. + /// `x` and `y` are expected to be in the range `[0, 1]`. pub fn ray_at_relative(&self, x: Scalar, y: Scalar) -> Ray { let x_dir = self.x_dir * x; let y_dir = self.y_dir * y; Ray::new( self.position, - (self.upper_left + x_dir - y_dir - self.position).normalize(), + (self.lower_left + x_dir + y_dir - self.position).normalize(), ) } + /// Get a ray pointing from the camera to a pixel on the image plane. + /// `x` and `y` are expected to be in the range `[0, width-1]` and `[0, height-1]` respectively. pub fn ray_at(&self, x: usize, y: usize) -> Ray { let x = x as Scalar / self.width as Scalar; let y = y as Scalar / self.height as Scalar; - self.ray_at_relative(x, y) + self.ray_at_relative(x, 1.0 - y) } - // pub fn render(&self, scene: &Scene, depth: u32) -> Rgb32FImage { - // Rgb32FImage::from_fn(self.width, self.height, |x, y| { - // let ray = self.ray_at(x, y); - // let color = scene.trace(&ray, depth); - // color.into() - // }) - // } + /// Render the scene from the camera's perspective. + /// - `depth` is the maximum number of reflections to calculate. + /// - `subp` is the number of subpixels to use for antialiasing. + pub fn render(&self, scene: &Scene, depth: u32, subp: u32) -> RgbImage { + let dx = 1.0 / self.width as Scalar; + let dy = 1.0 / self.width as Scalar; + let dsx = dx / subp as Scalar; + let dsy = dy / subp as Scalar; + RgbImage::from_fn(self.width as u32, self.height as u32, |x, y| { + let x = x as Scalar * dx; + let y = y as Scalar * dy; + let mut color = Color::new(0.0, 0.0, 0.0); + for sx in 0..subp { + for sy in 0..subp { + color += scene.trace( + &self.ray_at_relative( + x + sx as Scalar * dsx, + 1.0 - (y + sy as Scalar * dsy), + ), + depth, + ); + } + } + color *= 255.0 / (subp * subp) as Scalar; + [color.x as u8, color.y as u8, color.z as u8].into() + }) + } } diff --git a/src/raytracer/mod.rs b/src/raytracer/mod.rs index 4851590..d7d66f4 100644 --- a/src/raytracer/mod.rs +++ b/src/raytracer/mod.rs @@ -1,5 +1,6 @@ pub mod camera; +pub mod plane; pub mod scene; pub mod sphere; pub mod types; -pub mod vec; +mod vec; diff --git a/src/raytracer/plane.rs b/src/raytracer/plane.rs new file mode 100644 index 0000000..5501663 --- /dev/null +++ b/src/raytracer/plane.rs @@ -0,0 +1,51 @@ +use super::types::{Intersect, Material, Point3, Vector3}; + +extern crate nalgebra as na; + +/// An infinite plane in 3D space. +pub struct Plane { + /// The position of the plane. + position: Point3, + /// The normal of the plane. + normal: Vector3, + /// The material of the plane. + material: Material, +} + +impl Plane { + /// Create a new plane. + /// - `position` is the position of the plane. + /// - `normal` is the normal of the plane. + /// - `material` is the material of the plane. + pub fn new(position: Point3, normal: Vector3, material: Material) -> Plane { + Plane { + position, + normal, + material, + } + } +} + +impl Intersect for Plane { + fn intersect<'a>( + &'a self, + ray: &super::types::Ray, + ) -> Option<( + Point3, + Vector3, + super::types::Scalar, + &'a super::types::Material, + )> { + let denom = self.normal.dot(&ray.direction); + if denom != 0.0 { + let d = self.normal.dot(&self.position.coords); + let t = (d - self.normal.dot(&ray.origin.coords)) / denom; + + if t > 1e-5 { + let point = ray.origin + ray.direction * t; + return Some((point, self.normal, t, &self.material)); + } + } + None + } +} diff --git a/src/raytracer/scene.rs b/src/raytracer/scene.rs index 3046a16..3cfe543 100644 --- a/src/raytracer/scene.rs +++ b/src/raytracer/scene.rs @@ -4,32 +4,49 @@ use super::types::Light; use super::types::Material; use super::types::Point3; use super::types::Ray; -use super::types::Scalar; use super::types::Vector3; +use super::vec::mirror; use super::vec::reflect; extern crate nalgebra as na; +/// A scene is a collection of objects and lights, and provides a method to trace a ray through the scene. pub struct Scene { + /// The ambient light of the scene + ambient: Color, + /// The objects in the scene objects: Vec>, + /// The lights in the scene lights: Vec, } impl Scene { + /// Create a new empty scene pub fn new() -> Scene { Scene { + ambient: na::Vector3::new(0.0, 0.0, 0.0), objects: Vec::new(), lights: Vec::new(), } } + /// Set the ambient light of the scene + pub fn set_ambient(&mut self, ambient: Color) { + self.ambient = ambient; + } + + /// Add an object to the scene pub fn add_object(&mut self, obj: Box) { self.objects.push(obj); } + /// Add a light to the scene pub fn add_light(&mut self, light: Light) { self.lights.push(light); } + /// Trace a ray through the scene and return the color of the ray. + /// - `ray` is the ray to be traced + /// - `depth` is the maximum recursion depth aka the number of reflections pub fn trace(&self, ray: &Ray, depth: u32) -> Color { if depth == 0 { return na::Vector3::new(0.0, 0.0, 0.0); @@ -41,13 +58,9 @@ impl Scene { .filter_map(|obj| obj.intersect(ray)) .min_by(|(_, _, t1, _), (_, _, t2, _)| t1.partial_cmp(t2).unwrap()) { - None => { - return na::Vector3::new(0.0, 0.0, 0.0); - } - Some((isect_pt, isect_norm, isect_dist, material)) => { + Some((isect_pt, isect_norm, _, material)) => { // Lighting of material at the intersection point - let color = - self.lighting(-&ray.direction, material, isect_pt, isect_norm, isect_dist); + let color = self.lighting(-&ray.direction, material, isect_pt, isect_norm); // Calculate reflections, if the material has mirror properties if material.mirror > 0.0 { @@ -61,26 +74,50 @@ impl Scene { return color; } } + _ => { + return na::Vector3::new(0.0, 0.0, 0.0); + } } } + /// Calculate Phong lighting from a `view` on a `material` at an intersection point `isect_pt` with a normal `isect_norm`. fn lighting( &self, view: Vector3, material: &Material, isect_pt: Point3, isect_norm: Vector3, - isect_dist: Scalar, ) -> Color { - let mut color: Color = na::Vector3::new(0.0, 0.0, 0.0); + // Start with ambient lighting + let mut color = material.ambient_color.component_mul(&self.ambient); for light in &self.lights { - let l = (isect_pt - light.position).normalize(); - let cos_theta = l.dot(&isect_norm); + // Cast Shadow-Ray + let shadow_ray = Ray { + origin: isect_pt, + direction: (light.position - isect_pt).normalize(), + }; + if self + .objects + .iter() + .any(|obj| obj.intersect(&shadow_ray).is_some()) + { + continue; + } + // Diffuse + let l = (light.position - isect_pt).normalize(); + let cos_theta = l.dot(&isect_norm); if cos_theta > 0.0 { - // Diffuse color += material.diffuse_color.component_mul(&light.color) * cos_theta; + + // Specular + let r = mirror(l, isect_norm); + let cos_alpha = r.dot(&view); + if cos_alpha > 0.0 { + color += material.specular_color.component_mul(&light.color) + * cos_alpha.powf(material.shininess); + } } } diff --git a/src/raytracer/sphere.rs b/src/raytracer/sphere.rs index 707a9f0..1430594 100644 --- a/src/raytracer/sphere.rs +++ b/src/raytracer/sphere.rs @@ -2,12 +2,27 @@ use super::types::{Intersect, Material, Point3, Ray, Scalar, Vector3}; extern crate nalgebra as na; +/// A sphere in 3D space pub struct Sphere { + /// Center of the sphere center: Point3, + /// Radius of the sphere radius: Scalar, + /// PHONG material of the sphere material: Material, } +impl Sphere { + /// Create a new sphere at `center` with `radius` and `material`. + pub fn new(center: Point3, radius: Scalar, material: Material) -> Sphere { + Sphere { + center, + radius, + material, + } + } +} + /// Numerical error tolerance const EPSILON: Scalar = 1e-5; @@ -35,6 +50,7 @@ impl Intersect for Sphere { if t < Scalar::MAX { let isect_pt: Point3 = ray.origin + ray.direction * t; + return Some(( isect_pt, (isect_pt - self.center) / self.radius, diff --git a/src/raytracer/types.rs b/src/raytracer/types.rs index b76da96..fb197af 100644 --- a/src/raytracer/types.rs +++ b/src/raytracer/types.rs @@ -1,57 +1,87 @@ extern crate nalgebra as na; -pub type Scalar = f32; +/// The Scalar type to use for raytracing (f32 may result in acne effects) +pub type Scalar = f64; +/// The Vector3 type to use for raytracing pub type Vector3 = na::Vector3; +/// The Point3 type to use for raytracing pub type Point3 = na::Point3; +/// The Color type to use for raytracing pub type Color = Vector3; +/// A trait indicating, that an object can be intersected by a ray pub trait Intersect { + /// Intersect the object with a ray. + /// Returns None if the ray does not intersect the object. + /// Otherwise the intersection point, a normal vector at the intersection point, + /// the distance from the ray origin to the intersection point and + /// the material of the object are returned. fn intersect<'a>(&'a self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, &'a Material)>; } +/// A point light source pub struct Light { + /// Position of the light source pub position: Point3, + /// Light color pub color: Color, } impl Light { + /// Create a new light source at position with color pub fn new(position: Point3, color: Color) -> Light { Light { position, color } } } +/// A ray with origin and direction pub struct Ray { + /// Ray origin pub origin: Point3, + /// Ray direction pub direction: Vector3, } impl Ray { + /// Create a new ray with origin and direction pub fn new(origin: Point3, direction: Vector3) -> Ray { Ray { origin, direction } } } +/// A Material used for PHONG shading pub struct Material { + /// Ambient color, aka color without direct or indirect light pub ambient_color: Color, + /// Diffuse color, aka color with direct light and reflected light pub diffuse_color: Color, + /// Specular color, aka color of the highlights from direct light sources pub specular_color: Color, - pub shinyness: Scalar, + /// A shininess factor, used to calculate the size of the highlights. `pow(angle, shininess) * specular_color = intensity` + pub shininess: Scalar, + /// A mirror factor, used to calculate the reflection of the object. `self_color * reflected_color = final_color` pub mirror: Scalar, } impl Material { + /// Create a new material with ambient, diffuse, specular color, shininess and mirror factor. + /// - `ambient_color` is the color of the object without direct or indirect light + /// - `diffuse_color` is the color of the object with direct light and reflected light + /// - `specular_color` is the color of the highlights from direct light sources + /// - `shininess` is a factor used to calculate the size of the highlights. `pow(angle, shininess) * specular_color = intensity` + /// - `mirror` is a factor used to calculate the reflection of the object. `self_color * reflected_color = final_color` pub fn new( ambient_color: Color, diffuse_color: Color, specular_color: Color, - shinyness: Scalar, + shininess: Scalar, mirror: Scalar, ) -> Material { Material { ambient_color, diffuse_color, specular_color, - shinyness, + shininess, mirror, } } diff --git a/src/raytracer/vec.rs b/src/raytracer/vec.rs index c831219..bf02643 100644 --- a/src/raytracer/vec.rs +++ b/src/raytracer/vec.rs @@ -2,13 +2,12 @@ use super::types::Vector3; extern crate nalgebra as na; +/// Reflects a vector `v` around a normal `n`. pub fn reflect(v: Vector3, n: Vector3) -> Vector3 { v - 2.0 * v.dot(&n) * n } -pub fn rotate(v: &Vector3, axis: &Vector3, angle: f32) -> Vector3 { - //let axis = na::Unit::new_normalize(axis); - //let rot = na::Rotation3::from_axis_angle(&axis, angle); - //(rot * v) - todo!() +/// Mirrors a vector `v` around a normal `n`. +pub fn mirror(v: Vector3, n: Vector3) -> Vector3 { + 2.0 * v.dot(&n) * n - v }