├── .github └── workflows │ └── rust.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── screenshots ├── 10 fuzziness.png ├── 11 more balls.png ├── 12 even more balls.png ├── 12 part 2.png ├── 12 part 3.png ├── 12 part 4.png ├── 13 dielectrics.PNG ├── 14 final_image.PNG ├── 2.png ├── 3.png ├── 4.png ├── 4_antialias.png ├── 5.png ├── 6 gamma corrected.png ├── 7 shadow acne removed.png ├── 8 color.png ├── 9 metal.png └── Screen Shot 2018-03-17 at 8.36.33 PM.png └── src ├── aabb.rs ├── bvh.rs ├── camera.rs ├── helpers.rs ├── hittable.rs ├── main.rs ├── material.rs ├── moving_sphere.rs ├── ray.rs ├── sphere.rs ├── texture.rs └── world.rs /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build_ubuntu: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: install dependencies 13 | run: sudo apt-get install xorg-dev 14 | - name: Build 15 | run: rustc --version; cargo build 16 | 17 | build_MacOS: 18 | 19 | runs-on: macOS-latest 20 | 21 | steps: 22 | - uses: actions/checkout@v1 23 | - name: install_dependencies 24 | run: curl https://sh.rustup.rs -sSf | sh -s -- -y; 25 | - name: build 26 | run: export PATH="$HOME/.cargo/bin:$PATH"; cargo build 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target/ 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "aho-corasick" 13 | version = "0.7.18" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" 16 | dependencies = [ 17 | "memchr", 18 | ] 19 | 20 | [[package]] 21 | name = "ansi_term" 22 | version = "0.12.1" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" 25 | dependencies = [ 26 | "winapi", 27 | ] 28 | 29 | [[package]] 30 | name = "atty" 31 | version = "0.2.14" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 34 | dependencies = [ 35 | "hermit-abi", 36 | "libc", 37 | "winapi", 38 | ] 39 | 40 | [[package]] 41 | name = "autocfg" 42 | version = "1.0.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" 45 | 46 | [[package]] 47 | name = "bindgen" 48 | version = "0.56.0" 49 | source = "registry+https://github.com/rust-lang/crates.io-index" 50 | checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" 51 | dependencies = [ 52 | "bitflags", 53 | "cexpr", 54 | "clang-sys", 55 | "clap", 56 | "env_logger", 57 | "lazy_static", 58 | "lazycell", 59 | "log", 60 | "peeking_take_while", 61 | "proc-macro2", 62 | "quote", 63 | "regex", 64 | "rustc-hash", 65 | "shlex", 66 | "which", 67 | ] 68 | 69 | [[package]] 70 | name = "bitflags" 71 | version = "1.3.2" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 74 | 75 | [[package]] 76 | name = "bumpalo" 77 | version = "3.9.1" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" 80 | 81 | [[package]] 82 | name = "cc" 83 | version = "1.0.72" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" 86 | 87 | [[package]] 88 | name = "cexpr" 89 | version = "0.4.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" 92 | dependencies = [ 93 | "nom 5.1.2", 94 | ] 95 | 96 | [[package]] 97 | name = "cfg-if" 98 | version = "0.1.10" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" 101 | 102 | [[package]] 103 | name = "cfg-if" 104 | version = "1.0.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 107 | 108 | [[package]] 109 | name = "clang-sys" 110 | version = "1.3.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" 113 | dependencies = [ 114 | "glob", 115 | "libc", 116 | "libloading", 117 | ] 118 | 119 | [[package]] 120 | name = "clap" 121 | version = "2.34.0" 122 | source = "registry+https://github.com/rust-lang/crates.io-index" 123 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 124 | dependencies = [ 125 | "ansi_term", 126 | "atty", 127 | "bitflags", 128 | "strsim", 129 | "textwrap", 130 | "unicode-width", 131 | "vec_map", 132 | ] 133 | 134 | [[package]] 135 | name = "cmake" 136 | version = "0.1.48" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" 139 | dependencies = [ 140 | "cc", 141 | ] 142 | 143 | [[package]] 144 | name = "crc32fast" 145 | version = "1.3.1" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" 148 | dependencies = [ 149 | "cfg-if 1.0.0", 150 | ] 151 | 152 | [[package]] 153 | name = "crossbeam-channel" 154 | version = "0.5.2" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" 157 | dependencies = [ 158 | "cfg-if 1.0.0", 159 | "crossbeam-utils", 160 | ] 161 | 162 | [[package]] 163 | name = "crossbeam-deque" 164 | version = "0.8.1" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 167 | dependencies = [ 168 | "cfg-if 1.0.0", 169 | "crossbeam-epoch", 170 | "crossbeam-utils", 171 | ] 172 | 173 | [[package]] 174 | name = "crossbeam-epoch" 175 | version = "0.9.7" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" 178 | dependencies = [ 179 | "cfg-if 1.0.0", 180 | "crossbeam-utils", 181 | "lazy_static", 182 | "memoffset", 183 | "scopeguard", 184 | ] 185 | 186 | [[package]] 187 | name = "crossbeam-utils" 188 | version = "0.8.7" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" 191 | dependencies = [ 192 | "cfg-if 1.0.0", 193 | "lazy_static", 194 | ] 195 | 196 | [[package]] 197 | name = "cty" 198 | version = "0.2.2" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35" 201 | 202 | [[package]] 203 | name = "downcast-rs" 204 | version = "1.2.0" 205 | source = "registry+https://github.com/rust-lang/crates.io-index" 206 | checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" 207 | 208 | [[package]] 209 | name = "either" 210 | version = "1.6.1" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" 213 | 214 | [[package]] 215 | name = "encoding_rs" 216 | version = "0.8.30" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" 219 | dependencies = [ 220 | "cfg-if 1.0.0", 221 | ] 222 | 223 | [[package]] 224 | name = "env_logger" 225 | version = "0.8.4" 226 | source = "registry+https://github.com/rust-lang/crates.io-index" 227 | checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" 228 | dependencies = [ 229 | "atty", 230 | "humantime", 231 | "log", 232 | "regex", 233 | "termcolor", 234 | ] 235 | 236 | [[package]] 237 | name = "fastrand" 238 | version = "1.7.0" 239 | source = "registry+https://github.com/rust-lang/crates.io-index" 240 | checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" 241 | dependencies = [ 242 | "instant", 243 | ] 244 | 245 | [[package]] 246 | name = "filetime" 247 | version = "0.2.15" 248 | source = "registry+https://github.com/rust-lang/crates.io-index" 249 | checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" 250 | dependencies = [ 251 | "cfg-if 1.0.0", 252 | "libc", 253 | "redox_syscall", 254 | "winapi", 255 | ] 256 | 257 | [[package]] 258 | name = "flate2" 259 | version = "1.0.22" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" 262 | dependencies = [ 263 | "cfg-if 1.0.0", 264 | "crc32fast", 265 | "libc", 266 | "miniz_oxide", 267 | ] 268 | 269 | [[package]] 270 | name = "fuchsia-cprng" 271 | version = "0.1.1" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" 274 | 275 | [[package]] 276 | name = "glam" 277 | version = "0.20.2" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "e4fa84eead97d5412b2a20aed4d66612a97a9e41e08eababdb9ae2bf88667490" 280 | 281 | [[package]] 282 | name = "glob" 283 | version = "0.3.0" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 286 | 287 | [[package]] 288 | name = "hermit-abi" 289 | version = "0.1.19" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 292 | dependencies = [ 293 | "libc", 294 | ] 295 | 296 | [[package]] 297 | name = "humantime" 298 | version = "2.1.0" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 301 | 302 | [[package]] 303 | name = "instant" 304 | version = "0.1.12" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 307 | dependencies = [ 308 | "cfg-if 1.0.0", 309 | ] 310 | 311 | [[package]] 312 | name = "js-sys" 313 | version = "0.3.56" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" 316 | dependencies = [ 317 | "wasm-bindgen", 318 | ] 319 | 320 | [[package]] 321 | name = "lazy_static" 322 | version = "1.4.0" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 325 | 326 | [[package]] 327 | name = "lazycell" 328 | version = "1.3.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" 331 | 332 | [[package]] 333 | name = "libc" 334 | version = "0.2.117" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" 337 | 338 | [[package]] 339 | name = "libloading" 340 | version = "0.7.3" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" 343 | dependencies = [ 344 | "cfg-if 1.0.0", 345 | "winapi", 346 | ] 347 | 348 | [[package]] 349 | name = "log" 350 | version = "0.4.14" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" 353 | dependencies = [ 354 | "cfg-if 1.0.0", 355 | ] 356 | 357 | [[package]] 358 | name = "memchr" 359 | version = "2.4.1" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" 362 | 363 | [[package]] 364 | name = "memoffset" 365 | version = "0.6.5" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 368 | dependencies = [ 369 | "autocfg", 370 | ] 371 | 372 | [[package]] 373 | name = "minifb" 374 | version = "0.20.0" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "f669be3941549a8291969021bf155ffb7bb337d4d2832d24ad7ab91c426c51a7" 377 | dependencies = [ 378 | "cc", 379 | "libc", 380 | "orbclient", 381 | "raw-window-handle 0.3.4", 382 | "tempfile", 383 | "wayland-client", 384 | "wayland-cursor", 385 | "wayland-protocols", 386 | "winapi", 387 | "x11-dl", 388 | "xkb", 389 | "xkbcommon-sys", 390 | ] 391 | 392 | [[package]] 393 | name = "minimal-lexical" 394 | version = "0.2.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 397 | 398 | [[package]] 399 | name = "miniz_oxide" 400 | version = "0.4.4" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" 403 | dependencies = [ 404 | "adler", 405 | "autocfg", 406 | ] 407 | 408 | [[package]] 409 | name = "nix" 410 | version = "0.20.0" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "fa9b4819da1bc61c0ea48b63b7bc8604064dd43013e7cc325df098d49cd7c18a" 413 | dependencies = [ 414 | "bitflags", 415 | "cc", 416 | "cfg-if 1.0.0", 417 | "libc", 418 | ] 419 | 420 | [[package]] 421 | name = "nom" 422 | version = "5.1.2" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" 425 | dependencies = [ 426 | "memchr", 427 | "version_check", 428 | ] 429 | 430 | [[package]] 431 | name = "nom" 432 | version = "7.1.0" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" 435 | dependencies = [ 436 | "memchr", 437 | "minimal-lexical", 438 | "version_check", 439 | ] 440 | 441 | [[package]] 442 | name = "num_cpus" 443 | version = "1.13.1" 444 | source = "registry+https://github.com/rust-lang/crates.io-index" 445 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 446 | dependencies = [ 447 | "hermit-abi", 448 | "libc", 449 | ] 450 | 451 | [[package]] 452 | name = "once_cell" 453 | version = "1.9.0" 454 | source = "registry+https://github.com/rust-lang/crates.io-index" 455 | checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" 456 | 457 | [[package]] 458 | name = "orbclient" 459 | version = "0.3.31" 460 | source = "registry+https://github.com/rust-lang/crates.io-index" 461 | checksum = "0c976c5018e7f1db4359616d8b31ef8ae7d9649b11803c0b38fff67fd2999fc8" 462 | dependencies = [ 463 | "libc", 464 | "raw-window-handle 0.3.4", 465 | "redox_syscall", 466 | "sdl2", 467 | "sdl2-sys", 468 | "wasm-bindgen", 469 | "web-sys", 470 | ] 471 | 472 | [[package]] 473 | name = "peeking_take_while" 474 | version = "0.1.2" 475 | source = "registry+https://github.com/rust-lang/crates.io-index" 476 | checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" 477 | 478 | [[package]] 479 | name = "pkg-config" 480 | version = "0.3.24" 481 | source = "registry+https://github.com/rust-lang/crates.io-index" 482 | checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" 483 | 484 | [[package]] 485 | name = "proc-macro2" 486 | version = "1.0.36" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" 489 | dependencies = [ 490 | "unicode-xid", 491 | ] 492 | 493 | [[package]] 494 | name = "quote" 495 | version = "1.0.15" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" 498 | dependencies = [ 499 | "proc-macro2", 500 | ] 501 | 502 | [[package]] 503 | name = "rand" 504 | version = "0.4.6" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" 507 | dependencies = [ 508 | "fuchsia-cprng", 509 | "libc", 510 | "rand_core 0.3.1", 511 | "rdrand", 512 | "winapi", 513 | ] 514 | 515 | [[package]] 516 | name = "rand_core" 517 | version = "0.3.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" 520 | dependencies = [ 521 | "rand_core 0.4.2", 522 | ] 523 | 524 | [[package]] 525 | name = "rand_core" 526 | version = "0.4.2" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" 529 | 530 | [[package]] 531 | name = "raw-window-handle" 532 | version = "0.3.4" 533 | source = "registry+https://github.com/rust-lang/crates.io-index" 534 | checksum = "e28f55143d0548dad60bb4fbdc835a3d7ac6acc3324506450c5fdd6e42903a76" 535 | dependencies = [ 536 | "libc", 537 | "raw-window-handle 0.4.2", 538 | ] 539 | 540 | [[package]] 541 | name = "raw-window-handle" 542 | version = "0.4.2" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "fba75eee94a9d5273a68c9e1e105d9cffe1ef700532325788389e5a83e2522b7" 545 | dependencies = [ 546 | "cty", 547 | ] 548 | 549 | [[package]] 550 | name = "rayon" 551 | version = "1.5.1" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" 554 | dependencies = [ 555 | "autocfg", 556 | "crossbeam-deque", 557 | "either", 558 | "rayon-core", 559 | ] 560 | 561 | [[package]] 562 | name = "rayon-core" 563 | version = "1.9.1" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" 566 | dependencies = [ 567 | "crossbeam-channel", 568 | "crossbeam-deque", 569 | "crossbeam-utils", 570 | "lazy_static", 571 | "num_cpus", 572 | ] 573 | 574 | [[package]] 575 | name = "raytracing_test" 576 | version = "0.1.0" 577 | dependencies = [ 578 | "glam", 579 | "minifb", 580 | "rand", 581 | "rayon", 582 | ] 583 | 584 | [[package]] 585 | name = "rdrand" 586 | version = "0.4.0" 587 | source = "registry+https://github.com/rust-lang/crates.io-index" 588 | checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" 589 | dependencies = [ 590 | "rand_core 0.3.1", 591 | ] 592 | 593 | [[package]] 594 | name = "redox_syscall" 595 | version = "0.2.10" 596 | source = "registry+https://github.com/rust-lang/crates.io-index" 597 | checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" 598 | dependencies = [ 599 | "bitflags", 600 | ] 601 | 602 | [[package]] 603 | name = "regex" 604 | version = "1.5.4" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" 607 | dependencies = [ 608 | "aho-corasick", 609 | "memchr", 610 | "regex-syntax", 611 | ] 612 | 613 | [[package]] 614 | name = "regex-syntax" 615 | version = "0.6.25" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" 618 | 619 | [[package]] 620 | name = "remove_dir_all" 621 | version = "0.5.3" 622 | source = "registry+https://github.com/rust-lang/crates.io-index" 623 | checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" 624 | dependencies = [ 625 | "winapi", 626 | ] 627 | 628 | [[package]] 629 | name = "rustc-hash" 630 | version = "1.1.0" 631 | source = "registry+https://github.com/rust-lang/crates.io-index" 632 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" 633 | 634 | [[package]] 635 | name = "scopeguard" 636 | version = "1.1.0" 637 | source = "registry+https://github.com/rust-lang/crates.io-index" 638 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 639 | 640 | [[package]] 641 | name = "sdl2" 642 | version = "0.34.5" 643 | source = "registry+https://github.com/rust-lang/crates.io-index" 644 | checksum = "deecbc3fa9460acff5a1e563e05cb5f31bba0aa0c214bb49a43db8159176d54b" 645 | dependencies = [ 646 | "bitflags", 647 | "lazy_static", 648 | "libc", 649 | "raw-window-handle 0.3.4", 650 | "sdl2-sys", 651 | ] 652 | 653 | [[package]] 654 | name = "sdl2-sys" 655 | version = "0.34.5" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "41a29aa21f175b5a41a6e26da572d5e5d1ee5660d35f9f9d0913e8a802098f74" 658 | dependencies = [ 659 | "cfg-if 0.1.10", 660 | "cmake", 661 | "flate2", 662 | "libc", 663 | "tar", 664 | "unidiff", 665 | "version-compare", 666 | ] 667 | 668 | [[package]] 669 | name = "shlex" 670 | version = "0.1.1" 671 | source = "registry+https://github.com/rust-lang/crates.io-index" 672 | checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" 673 | 674 | [[package]] 675 | name = "smallvec" 676 | version = "1.8.0" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" 679 | 680 | [[package]] 681 | name = "strsim" 682 | version = "0.8.0" 683 | source = "registry+https://github.com/rust-lang/crates.io-index" 684 | checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 685 | 686 | [[package]] 687 | name = "syn" 688 | version = "1.0.86" 689 | source = "registry+https://github.com/rust-lang/crates.io-index" 690 | checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" 691 | dependencies = [ 692 | "proc-macro2", 693 | "quote", 694 | "unicode-xid", 695 | ] 696 | 697 | [[package]] 698 | name = "tar" 699 | version = "0.4.38" 700 | source = "registry+https://github.com/rust-lang/crates.io-index" 701 | checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" 702 | dependencies = [ 703 | "filetime", 704 | "libc", 705 | "xattr", 706 | ] 707 | 708 | [[package]] 709 | name = "tempfile" 710 | version = "3.3.0" 711 | source = "registry+https://github.com/rust-lang/crates.io-index" 712 | checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" 713 | dependencies = [ 714 | "cfg-if 1.0.0", 715 | "fastrand", 716 | "libc", 717 | "redox_syscall", 718 | "remove_dir_all", 719 | "winapi", 720 | ] 721 | 722 | [[package]] 723 | name = "termcolor" 724 | version = "1.1.2" 725 | source = "registry+https://github.com/rust-lang/crates.io-index" 726 | checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" 727 | dependencies = [ 728 | "winapi-util", 729 | ] 730 | 731 | [[package]] 732 | name = "textwrap" 733 | version = "0.11.0" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 736 | dependencies = [ 737 | "unicode-width", 738 | ] 739 | 740 | [[package]] 741 | name = "unicode-width" 742 | version = "0.1.9" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" 745 | 746 | [[package]] 747 | name = "unicode-xid" 748 | version = "0.2.2" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" 751 | 752 | [[package]] 753 | name = "unidiff" 754 | version = "0.3.3" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "d8a62719acf1933bfdbeb73a657ecd9ecece70b405125267dd549e2e2edc232c" 757 | dependencies = [ 758 | "encoding_rs", 759 | "lazy_static", 760 | "regex", 761 | ] 762 | 763 | [[package]] 764 | name = "vec_map" 765 | version = "0.8.2" 766 | source = "registry+https://github.com/rust-lang/crates.io-index" 767 | checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" 768 | 769 | [[package]] 770 | name = "version-compare" 771 | version = "0.0.10" 772 | source = "registry+https://github.com/rust-lang/crates.io-index" 773 | checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" 774 | 775 | [[package]] 776 | name = "version_check" 777 | version = "0.9.4" 778 | source = "registry+https://github.com/rust-lang/crates.io-index" 779 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 780 | 781 | [[package]] 782 | name = "wasm-bindgen" 783 | version = "0.2.79" 784 | source = "registry+https://github.com/rust-lang/crates.io-index" 785 | checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" 786 | dependencies = [ 787 | "cfg-if 1.0.0", 788 | "wasm-bindgen-macro", 789 | ] 790 | 791 | [[package]] 792 | name = "wasm-bindgen-backend" 793 | version = "0.2.79" 794 | source = "registry+https://github.com/rust-lang/crates.io-index" 795 | checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" 796 | dependencies = [ 797 | "bumpalo", 798 | "lazy_static", 799 | "log", 800 | "proc-macro2", 801 | "quote", 802 | "syn", 803 | "wasm-bindgen-shared", 804 | ] 805 | 806 | [[package]] 807 | name = "wasm-bindgen-macro" 808 | version = "0.2.79" 809 | source = "registry+https://github.com/rust-lang/crates.io-index" 810 | checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" 811 | dependencies = [ 812 | "quote", 813 | "wasm-bindgen-macro-support", 814 | ] 815 | 816 | [[package]] 817 | name = "wasm-bindgen-macro-support" 818 | version = "0.2.79" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" 821 | dependencies = [ 822 | "proc-macro2", 823 | "quote", 824 | "syn", 825 | "wasm-bindgen-backend", 826 | "wasm-bindgen-shared", 827 | ] 828 | 829 | [[package]] 830 | name = "wasm-bindgen-shared" 831 | version = "0.2.79" 832 | source = "registry+https://github.com/rust-lang/crates.io-index" 833 | checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" 834 | 835 | [[package]] 836 | name = "wayland-client" 837 | version = "0.28.6" 838 | source = "registry+https://github.com/rust-lang/crates.io-index" 839 | checksum = "e3ab332350e502f159382201394a78e3cc12d0f04db863429260164ea40e0355" 840 | dependencies = [ 841 | "bitflags", 842 | "downcast-rs", 843 | "libc", 844 | "nix", 845 | "wayland-commons", 846 | "wayland-scanner", 847 | "wayland-sys", 848 | ] 849 | 850 | [[package]] 851 | name = "wayland-commons" 852 | version = "0.28.6" 853 | source = "registry+https://github.com/rust-lang/crates.io-index" 854 | checksum = "a21817947c7011bbd0a27e11b17b337bfd022e8544b071a2641232047966fbda" 855 | dependencies = [ 856 | "nix", 857 | "once_cell", 858 | "smallvec", 859 | "wayland-sys", 860 | ] 861 | 862 | [[package]] 863 | name = "wayland-cursor" 864 | version = "0.28.6" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "be610084edd1586d45e7bdd275fe345c7c1873598caa464c4fb835dee70fa65a" 867 | dependencies = [ 868 | "nix", 869 | "wayland-client", 870 | "xcursor", 871 | ] 872 | 873 | [[package]] 874 | name = "wayland-protocols" 875 | version = "0.28.6" 876 | source = "registry+https://github.com/rust-lang/crates.io-index" 877 | checksum = "286620ea4d803bacf61fa087a4242ee316693099ee5a140796aaba02b29f861f" 878 | dependencies = [ 879 | "bitflags", 880 | "wayland-client", 881 | "wayland-commons", 882 | "wayland-scanner", 883 | ] 884 | 885 | [[package]] 886 | name = "wayland-scanner" 887 | version = "0.28.6" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "ce923eb2deb61de332d1f356ec7b6bf37094dc5573952e1c8936db03b54c03f1" 890 | dependencies = [ 891 | "proc-macro2", 892 | "quote", 893 | "xml-rs", 894 | ] 895 | 896 | [[package]] 897 | name = "wayland-sys" 898 | version = "0.28.6" 899 | source = "registry+https://github.com/rust-lang/crates.io-index" 900 | checksum = "d841fca9aed7febf9bed2e9796c49bf58d4152ceda8ac949ebe00868d8f0feb8" 901 | dependencies = [ 902 | "pkg-config", 903 | ] 904 | 905 | [[package]] 906 | name = "web-sys" 907 | version = "0.3.56" 908 | source = "registry+https://github.com/rust-lang/crates.io-index" 909 | checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" 910 | dependencies = [ 911 | "js-sys", 912 | "wasm-bindgen", 913 | ] 914 | 915 | [[package]] 916 | name = "which" 917 | version = "3.1.1" 918 | source = "registry+https://github.com/rust-lang/crates.io-index" 919 | checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" 920 | dependencies = [ 921 | "libc", 922 | ] 923 | 924 | [[package]] 925 | name = "winapi" 926 | version = "0.3.9" 927 | source = "registry+https://github.com/rust-lang/crates.io-index" 928 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 929 | dependencies = [ 930 | "winapi-i686-pc-windows-gnu", 931 | "winapi-x86_64-pc-windows-gnu", 932 | ] 933 | 934 | [[package]] 935 | name = "winapi-i686-pc-windows-gnu" 936 | version = "0.4.0" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 939 | 940 | [[package]] 941 | name = "winapi-util" 942 | version = "0.1.5" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 945 | dependencies = [ 946 | "winapi", 947 | ] 948 | 949 | [[package]] 950 | name = "winapi-x86_64-pc-windows-gnu" 951 | version = "0.4.0" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 954 | 955 | [[package]] 956 | name = "x11-dl" 957 | version = "2.19.1" 958 | source = "registry+https://github.com/rust-lang/crates.io-index" 959 | checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" 960 | dependencies = [ 961 | "lazy_static", 962 | "libc", 963 | "pkg-config", 964 | ] 965 | 966 | [[package]] 967 | name = "xattr" 968 | version = "0.2.2" 969 | source = "registry+https://github.com/rust-lang/crates.io-index" 970 | checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" 971 | dependencies = [ 972 | "libc", 973 | ] 974 | 975 | [[package]] 976 | name = "xcursor" 977 | version = "0.3.4" 978 | source = "registry+https://github.com/rust-lang/crates.io-index" 979 | checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" 980 | dependencies = [ 981 | "nom 7.1.0", 982 | ] 983 | 984 | [[package]] 985 | name = "xkb" 986 | version = "0.2.1" 987 | source = "registry+https://github.com/rust-lang/crates.io-index" 988 | checksum = "aec02bc5de902aa579f3d2f2c522edaf40fa42963cbaffe645b058ddcc68fdb2" 989 | dependencies = [ 990 | "bitflags", 991 | "libc", 992 | "xkbcommon-sys", 993 | ] 994 | 995 | [[package]] 996 | name = "xkbcommon-sys" 997 | version = "0.7.5" 998 | source = "registry+https://github.com/rust-lang/crates.io-index" 999 | checksum = "59a001b79d45b0b4541c228a501177f2b35db976bf7ee3f7fce8fa2381554ab5" 1000 | dependencies = [ 1001 | "bindgen", 1002 | "libc", 1003 | "pkg-config", 1004 | ] 1005 | 1006 | [[package]] 1007 | name = "xml-rs" 1008 | version = "0.8.4" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" 1011 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "raytracing_test" 3 | version = "0.1.0" 4 | authors = ["Ene "] 5 | edition = "2021" 6 | 7 | [dependencies] 8 | minifb = "0.20" 9 | rand = "0.4.2" 10 | rayon = "1.5" 11 | glam = "0.20" 12 | 13 | [profile.release] 14 | incremental = true 15 | debug = true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # raytracing-rs 2 | 3 | Done after "Raytracing in one weekend" book. 4 | 5 | Build and run with the following command: 6 | ```cargo run --release``` 7 | 8 | ![alt text](https://github.com/AlexEne/raytracing-rs/blob/master/screenshots/14%20final_image.PNG) 9 | -------------------------------------------------------------------------------- /screenshots/10 fuzziness.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/10 fuzziness.png -------------------------------------------------------------------------------- /screenshots/11 more balls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/11 more balls.png -------------------------------------------------------------------------------- /screenshots/12 even more balls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/12 even more balls.png -------------------------------------------------------------------------------- /screenshots/12 part 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/12 part 2.png -------------------------------------------------------------------------------- /screenshots/12 part 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/12 part 3.png -------------------------------------------------------------------------------- /screenshots/12 part 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/12 part 4.png -------------------------------------------------------------------------------- /screenshots/13 dielectrics.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/13 dielectrics.PNG -------------------------------------------------------------------------------- /screenshots/14 final_image.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/14 final_image.PNG -------------------------------------------------------------------------------- /screenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/2.png -------------------------------------------------------------------------------- /screenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/3.png -------------------------------------------------------------------------------- /screenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/4.png -------------------------------------------------------------------------------- /screenshots/4_antialias.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/4_antialias.png -------------------------------------------------------------------------------- /screenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/5.png -------------------------------------------------------------------------------- /screenshots/6 gamma corrected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/6 gamma corrected.png -------------------------------------------------------------------------------- /screenshots/7 shadow acne removed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/7 shadow acne removed.png -------------------------------------------------------------------------------- /screenshots/8 color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/8 color.png -------------------------------------------------------------------------------- /screenshots/9 metal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/9 metal.png -------------------------------------------------------------------------------- /screenshots/Screen Shot 2018-03-17 at 8.36.33 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexEne/raytracing-rs/eb3be44395a2e26a7cd4d99c9be1ab09f19e944e/screenshots/Screen Shot 2018-03-17 at 8.36.33 PM.png -------------------------------------------------------------------------------- /src/aabb.rs: -------------------------------------------------------------------------------- 1 | use crate::ray::Ray; 2 | use glam::Vec3A; 3 | 4 | #[derive(Clone, Debug)] 5 | pub struct AABB { 6 | pub min: Vec3A, 7 | pub max: Vec3A, 8 | } 9 | 10 | impl AABB { 11 | pub fn new(min: Vec3A, max: Vec3A) -> AABB { 12 | AABB { min, max } 13 | } 14 | 15 | pub fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> bool { 16 | for axis in 0..3 { 17 | let r_origin = ray.origin()[axis]; 18 | let inv_r_dir = 1.0 / ray.dir()[axis]; 19 | 20 | let t0 = f32::min( 21 | (self.min[axis] - r_origin) * inv_r_dir, 22 | (self.max[axis] - r_origin) * inv_r_dir, 23 | ); 24 | 25 | let t1 = f32::max( 26 | (self.min[axis] - r_origin) * inv_r_dir, 27 | (self.max[axis] - r_origin) * inv_r_dir, 28 | ); 29 | 30 | let t_min = f32::max(t0, t_min); 31 | let t_max = f32::min(t1, t_max); 32 | 33 | if t_max <= t_min { 34 | return false; 35 | } 36 | } 37 | 38 | true 39 | } 40 | 41 | pub fn union(&self, other: &AABB) -> AABB { 42 | AABB { 43 | min: Vec3A::new( 44 | f32::min(self.min.x, other.min.x), 45 | f32::min(self.min.y, other.min.y), 46 | f32::min(self.min.z, other.min.z), 47 | ), 48 | max: Vec3A::new( 49 | f32::max(self.max.x, other.max.x), 50 | f32::max(self.max.y, other.max.y), 51 | f32::max(self.max.z, other.max.z), 52 | ), 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/bvh.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | use rand::{thread_rng, Rng}; 4 | 5 | use crate::{ 6 | aabb::AABB, 7 | hittable::{HitRecord, Hittable}, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub enum BvhContents { 12 | Node { left: Box, right: Box }, 13 | Leaf(Box), 14 | } 15 | 16 | #[derive(Debug)] 17 | pub struct Bvh { 18 | bounding_box: AABB, 19 | contents: BvhContents, 20 | size: usize, 21 | } 22 | 23 | impl Bvh { 24 | pub fn new(mut objects: Vec>, t0: f32, t1: f32) -> Bvh { 25 | fn axis_range(axis: usize, objs: &[Box], t0: f32, t1: f32) -> f32 { 26 | let range = objs 27 | .iter() 28 | .fold((std::f32::MAX, std::f32::MIN), |range, o| { 29 | let bb = o.bounding_box(t0, t1); 30 | let min = bb.min[axis].min(bb.max[axis]); 31 | let max = bb.min[axis].max(bb.max[axis]); 32 | 33 | (range.0.min(min), range.1.max(max)) 34 | }); 35 | 36 | range.1 - range.0 37 | } 38 | 39 | // let axis = rng.gen_range(0, 3); 40 | let axis = { 41 | let mut ranges = [ 42 | (0, axis_range(0, &objects, t0, t1)), 43 | (1, axis_range(1, &objects, t0, t1)), 44 | (2, axis_range(2, &objects, t0, t1)), 45 | ]; 46 | 47 | ranges.sort_unstable_by(|a, b| b.1.partial_cmp(&a.1).unwrap()); 48 | ranges[0].0 49 | }; 50 | 51 | objects.sort_unstable_by(|a, b| { 52 | let abb = a.bounding_box(t0, t1); 53 | let bbb = b.bounding_box(t0, t1); 54 | 55 | let av = abb.min[axis] + abb.max[axis]; 56 | let bv = bbb.min[axis] + bbb.max[axis]; 57 | av.partial_cmp(&bv).unwrap() 58 | }); 59 | 60 | match objects.len() { 61 | 0 => panic!("Must have at least 1 object to insert"), 62 | 1 => Bvh { 63 | bounding_box: objects[0].bounding_box(t0, t1), 64 | contents: BvhContents::Leaf(objects.pop().unwrap()), 65 | size: 1, 66 | }, 67 | _ => { 68 | let right = Box::new(Bvh::new( 69 | objects.drain(objects.len() / 2..).collect(), 70 | t0, 71 | t1, 72 | )); 73 | 74 | let left = Box::new(Bvh::new(objects, t0, t1)); 75 | 76 | Bvh { 77 | bounding_box: left.bounding_box.union(&right.bounding_box), 78 | size: left.size + right.size, 79 | contents: BvhContents::Node { left, right }, 80 | } 81 | } 82 | } 83 | } 84 | } 85 | 86 | impl Hittable for Bvh { 87 | fn hit(&self, ray: &crate::ray::Ray, tmin: f32, tmax: f32) -> Option { 88 | if self.bounding_box.hit(ray, tmin, tmax) { 89 | match &self.contents { 90 | BvhContents::Node { left, right } => { 91 | let hit_left = left.hit(ray, tmin, tmax); 92 | 93 | let mut tmax = tmax; 94 | if let Some(hr) = &hit_left { 95 | tmax = hr.t; 96 | } 97 | let hit_right = right.hit(ray, tmin, tmax); 98 | 99 | match (hit_left, hit_right) { 100 | (h, None) | (None, h) => h, 101 | (Some(hl), Some(hr)) => { 102 | if hl.t < hr.t { 103 | Some(hl) 104 | } else { 105 | Some(hr) 106 | } 107 | } 108 | } 109 | } 110 | BvhContents::Leaf(obj) => obj.hit(ray, tmin, tmax), 111 | } 112 | } else { 113 | None 114 | } 115 | } 116 | 117 | fn bounding_box(&self, _: f32, _: f32) -> AABB { 118 | self.bounding_box.clone() 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/camera.rs: -------------------------------------------------------------------------------- 1 | use crate::ray::Ray; 2 | use glam::Vec3A; 3 | use rand; 4 | use rand::Rng; 5 | use std; 6 | 7 | pub struct Camera { 8 | lower_left_corner: Vec3A, 9 | horizontal: Vec3A, 10 | vertical: Vec3A, 11 | origin: Vec3A, 12 | lens_radius: f32, 13 | time_0: f32, 14 | time_1: f32, 15 | u: Vec3A, 16 | v: Vec3A, 17 | w: Vec3A, 18 | } 19 | 20 | pub const PI: f32 = std::f64::consts::PI as f32; 21 | 22 | fn random_in_unit_disk() -> Vec3A { 23 | let mut rng = rand::thread_rng(); 24 | 25 | loop { 26 | let p = 2.0 * Vec3A::new(rng.gen_range(0.0, 1.0), rng.gen_range(0.0, 1.0), 0.0) 27 | - Vec3A::new(1.0, 1.0, 0.0); 28 | 29 | if Vec3A::dot(p, p) < 1.0 { 30 | return p; 31 | } 32 | } 33 | } 34 | 35 | impl Camera { 36 | pub fn new( 37 | look_from: Vec3A, 38 | look_at: Vec3A, 39 | up: Vec3A, 40 | v_fov: f32, 41 | aspect: f32, 42 | aperture: f32, 43 | focus_dist: f32, 44 | t0: f32, 45 | t1: f32, 46 | ) -> Camera { 47 | let theta = v_fov * PI / 180.0; 48 | let half_height = (theta / 2.0).tan(); 49 | let half_width = aspect * half_height; 50 | 51 | let w = (look_from - look_at).normalize(); 52 | let u = Vec3A::cross(up, w).normalize(); 53 | let v = Vec3A::cross(w, u); 54 | 55 | Camera { 56 | lower_left_corner: look_from 57 | - half_width * focus_dist * u 58 | - half_height * focus_dist * v 59 | - focus_dist * w, 60 | horizontal: 2.0 * half_width * focus_dist * u, 61 | vertical: 2.0 * half_height * focus_dist * v, 62 | origin: look_from, 63 | time_0: t0, 64 | time_1: t1, 65 | lens_radius: aperture / 2.0, 66 | w: w, 67 | u: u, 68 | v: v, 69 | } 70 | } 71 | 72 | pub fn get_ray(&self, s: f32, t: f32) -> Ray { 73 | let rd = self.lens_radius * random_in_unit_disk(); 74 | let offset = self.u * rd.x + self.v * rd.y; 75 | 76 | let mut rng = rand::thread_rng(); 77 | Ray::new( 78 | self.origin + offset, 79 | self.lower_left_corner + s * self.horizontal + t * self.vertical - self.origin - offset, 80 | rng.gen_range(self.time_0, self.time_1), 81 | ) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | use glam::Vec3A; 2 | 3 | pub fn reflect(v: Vec3A, n: Vec3A) -> Vec3A { 4 | v - 2.0 * Vec3A::dot(v, n) * n 5 | } 6 | 7 | pub fn refract(v: Vec3A, n: Vec3A, ni_over_nt: f32) -> Option { 8 | let uv = v.normalize(); 9 | let dt = Vec3A::dot(uv, n); 10 | let discriminant = 1.0 - ni_over_nt * ni_over_nt * (1.0 - dt * dt); 11 | if discriminant > 0.0 { 12 | Some(ni_over_nt * (uv - n * dt) - n * discriminant.sqrt()) 13 | } else { 14 | None 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/hittable.rs: -------------------------------------------------------------------------------- 1 | use crate::aabb::AABB; 2 | use crate::material::Material; 3 | use crate::ray::Ray; 4 | use glam::Vec3A; 5 | 6 | pub struct HitRecord { 7 | pub p: Vec3A, 8 | pub normal: Vec3A, 9 | pub t: f32, 10 | pub u: f32, 11 | pub v: f32, 12 | pub material: Option, 13 | } 14 | 15 | pub trait Hittable: Send + Sync + std::fmt::Debug { 16 | fn hit(&self, ray: &Ray, tmin: f32, tmax: f32) -> Option; 17 | fn bounding_box(&self, t0: f32, t1: f32) -> AABB; 18 | } 19 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate minifb; 2 | extern crate rand; 3 | extern crate rayon; 4 | 5 | use bvh::Bvh; 6 | use rayon::prelude::*; 7 | mod aabb; 8 | mod bvh; 9 | mod camera; 10 | mod helpers; 11 | mod hittable; 12 | mod material; 13 | mod ray; 14 | mod sphere; 15 | mod texture; 16 | mod world; 17 | 18 | use camera::Camera; 19 | use glam::Vec3A; 20 | use hittable::{HitRecord, Hittable}; 21 | use material::Material; 22 | use minifb::{Key, KeyRepeat, Window, WindowOptions}; 23 | use rand::Rng; 24 | mod moving_sphere; 25 | use ray::Ray; 26 | use sphere::Sphere; 27 | use std::{sync::Arc, thread, time}; 28 | use world::World; 29 | 30 | use crate::{ 31 | moving_sphere::MovingSphere, 32 | texture::{CheckerTexture, SolidColor, Texture}, 33 | }; 34 | 35 | const WIDTH: usize = 640; 36 | const HEIGHT: usize = 320; 37 | const SAMPLE_COUNT: usize = 5; 38 | 39 | fn color_at(ray: &Ray, bvh: &Bvh, depth: u32) -> Vec3A { 40 | if let Some(rec) = bvh.hit(ray, 0.001, std::f32::MAX) { 41 | let mut scattered = Ray::new(Vec3A::default(), Vec3A::default(), ray.time()); 42 | let mut attenuation = Vec3A::default(); 43 | let rec_c = HitRecord { 44 | p: rec.p, 45 | normal: rec.normal, 46 | t: rec.t, 47 | u: rec.u, 48 | v: rec.v, 49 | material: None, 50 | }; 51 | if let Some(material) = rec.material { 52 | if depth < 50 53 | && material::scatter(&material, ray, &rec_c, &mut attenuation, &mut scattered) 54 | { 55 | return attenuation * color_at(&scattered, bvh, depth + 1); 56 | } else { 57 | return Vec3A::new(0.0, 0.0, 0.0); 58 | } 59 | } else { 60 | panic!("No material wtf!"); 61 | } 62 | } else { 63 | let t = 0.5 * (ray.dir().y + 1.0); 64 | (1.0 - t) * Vec3A::new(1.0, 1.0, 1.0) + t * Vec3A::new(0.5, 0.7, 1.0) 65 | } 66 | } 67 | 68 | fn generate_scene(buffer: &mut Vec) { 69 | let mut rng = rand::thread_rng(); 70 | let mut world = World::default(); 71 | world.add_object(Box::new(Sphere::new( 72 | Vec3A::new(0.0, -1000.0, 0.0), 73 | 1000.0, 74 | Material::Lambertian { 75 | texture: Arc::new(Box::new(CheckerTexture::new( 76 | Vec3A::new(0.2, 0.3, 0.1), 77 | Vec3A::new(0.9, 0.9, 0.9), 78 | ))), 79 | }, 80 | ))); 81 | 82 | for a in -11..11 { 83 | for b in -11..11 { 84 | let choose_mat = rng.gen_range::(0.0, 1.0); 85 | 86 | let center = Vec3A::new( 87 | a as f32 + 0.9 * rng.gen_range(0.0, 1.0), 88 | 0.2 + 0.2 * rng.gen_range::(0.0, 1.0), 89 | b as f32 + 0.9 * rng.gen_range(0.0, 1.0), 90 | ); 91 | 92 | if (center - Vec3A::new(4.0, 0.2, 0.0)).length() > 0.9 { 93 | if choose_mat < 0.8 { 94 | let r: f32 = rng.gen_range(0.0, 1.0) * rng.gen_range(0.0, 1.0); 95 | let g: f32 = rng.gen_range(0.0, 1.0) * rng.gen_range(0.0, 1.0); 96 | let b: f32 = rng.gen_range(0.0, 1.0) * rng.gen_range(0.0, 1.0); 97 | let center2 = center + 0.4 * Vec3A::new(0.0, rng.gen_range(0.0, 0.5), 0.0); 98 | world.add_object(Box::new(MovingSphere::new( 99 | center, 100 | center2, 101 | 0.0, 102 | 1.0, 103 | 0.2, 104 | Material::Lambertian { 105 | texture: Arc::new(Box::new(SolidColor::new(Vec3A::new(r, g, b)))), 106 | }, 107 | ))); 108 | } else if choose_mat < 0.95 { 109 | let r: f32 = 0.5 * (1.0 + rng.gen_range(0.0, 1.0)); 110 | let g: f32 = 0.5 * (1.0 + rng.gen_range(0.0, 1.0)); 111 | let b: f32 = 0.5 * (1.0 + rng.gen_range(0.0, 1.0)); 112 | world.add_object(Box::new(Sphere::new( 113 | center, 114 | 0.2, 115 | Material::Metal { 116 | albedo: Vec3A::new(r, g, b), 117 | fuzz: 0.5 * rng.gen_range(0.0, 1.0), 118 | }, 119 | ))); 120 | } else { 121 | world.add_object(Box::new(Sphere::new( 122 | center, 123 | 0.2, 124 | Material::Dielectric { ref_idx: 1.5 }, 125 | ))); 126 | } 127 | } 128 | } 129 | } 130 | 131 | let texture: Arc> = 132 | Arc::new(Box::new(SolidColor::new(Vec3A::new(0.4, 0.2, 0.1)))); 133 | 134 | world.add_object(Box::new(Sphere::new( 135 | Vec3A::new(0.0, 1.0, 0.0), 136 | 1.0, 137 | Material::Dielectric { ref_idx: 1.5 }, 138 | ))); 139 | 140 | world.add_object(Box::new(Sphere::new( 141 | Vec3A::new(-4.0, 1.0, 0.0), 142 | 1.0, 143 | Material::Lambertian { texture }, 144 | ))); 145 | 146 | world.add_object(Box::new(Sphere::new( 147 | Vec3A::new(4.0, 1.0, 0.0), 148 | 1.0, 149 | Material::Metal { 150 | albedo: Vec3A::new(0.7, 0.6, 0.5), 151 | fuzz: 0.0, 152 | }, 153 | ))); 154 | 155 | let look_from = Vec3A::new(12.0, 1.0, 3.0); 156 | let look_at = Vec3A::new(1.0, 0.7, -1.0); 157 | let apperture = 0.0; 158 | let dist_to_focus = 10.0; 159 | let camera = Camera::new( 160 | look_from, 161 | look_at, 162 | Vec3A::new(0.0, 1.0, 0.0), 163 | 20.0, 164 | WIDTH as f32 / HEIGHT as f32, 165 | apperture, 166 | dist_to_focus, 167 | 0.0, 168 | 1.0, 169 | ); 170 | 171 | let bvh = world.generate_bvh(0.0, 1.0); 172 | // println!("Bvh: {:#?}", bvh); 173 | // panic!("WTF"); 174 | 175 | let start = time::Instant::now(); 176 | let chunk_size = WIDTH; 177 | 178 | //Switch from par_iter_mut() to iter_mut() to compare with the single threaded version. 179 | buffer 180 | .par_iter_mut() 181 | .chunks(chunk_size) 182 | .enumerate() 183 | .for_each(|(pos, row_data)| { 184 | for (local_pos, data) in row_data.into_iter().enumerate() { 185 | let pos = pos * chunk_size + local_pos; 186 | let x = pos % WIDTH; 187 | let y = HEIGHT - pos / WIDTH; 188 | let mut total = Vec3A::default(); 189 | let mut rng = rand::thread_rng(); 190 | for _ in 0..SAMPLE_COUNT { 191 | let rx = rng.gen_range(0.0, 1.0); 192 | let ry = rng.gen_range(0.0, 1.0); 193 | let u = (x as f32 + rx) / (WIDTH as f32); 194 | let v = (y as f32 + ry) / (HEIGHT as f32); 195 | let r = camera.get_ray(u, v); 196 | total = total + color_at(&r, &bvh, 0); 197 | } 198 | let fcolor = total / (SAMPLE_COUNT as f32); 199 | let fcolor = Vec3A::new(fcolor.x.sqrt(), fcolor.y.sqrt(), fcolor.z.sqrt()); 200 | let color_r = (fcolor.x * 255.99) as u32; 201 | let color_g = (fcolor.y * 255.99) as u32; 202 | let color_b = (fcolor.z * 255.99) as u32; 203 | *data = color_r << 16 | color_g << 8 | color_b; 204 | } 205 | }); 206 | let duration = time::Instant::now() - start; 207 | println!("Generate took: {:?}", duration); 208 | } 209 | 210 | fn main() { 211 | let mut buffer: Vec = vec![0; WIDTH * HEIGHT]; //R..G..B..R..G..B 212 | 213 | let mut window = Window::new( 214 | "Raytracing on a plane - ESC to exit", 215 | WIDTH, 216 | HEIGHT, 217 | WindowOptions::default(), 218 | ) 219 | .unwrap_or_else(|e| { 220 | panic!("{}", e); 221 | }); 222 | 223 | generate_scene(&mut buffer); 224 | 225 | while window.is_open() && !window.is_key_down(Key::Escape) { 226 | // We unwrap here as we want this code to exit if it fails. 227 | // Real applications may want to handle this in a different way 228 | if window.is_key_pressed(Key::Space, KeyRepeat::No) { 229 | generate_scene(&mut buffer); 230 | } 231 | 232 | window.update_with_buffer(&buffer, WIDTH, HEIGHT).unwrap(); 233 | 234 | thread::sleep(time::Duration::from_millis(33)); 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /src/material.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | use std::sync::Arc; 3 | 4 | use crate::helpers::*; 5 | use crate::hittable::HitRecord; 6 | use crate::ray::Ray; 7 | use crate::texture::Texture; 8 | use glam::Vec3A; 9 | use rand; 10 | use rand::Rng; 11 | 12 | #[derive(Clone, Debug)] 13 | pub enum Material { 14 | Lambertian { texture: Arc> }, 15 | Metal { albedo: Vec3A, fuzz: f32 }, 16 | Dielectric { ref_idx: f32 }, 17 | } 18 | 19 | impl Default for Material { 20 | fn default() -> Material { 21 | Material::Metal { 22 | albedo: Vec3A::new(0.8, 0.8, 0.0), 23 | fuzz: 0.3, 24 | } 25 | } 26 | } 27 | 28 | pub fn scatter( 29 | material: &Material, 30 | ray_in: &Ray, 31 | hit: &HitRecord, 32 | attenuation: &mut Vec3A, 33 | scattered: &mut Ray, 34 | ) -> bool { 35 | match &material { 36 | &Material::Lambertian { texture } => { 37 | let target = hit.p + hit.normal + random_point_in_unit_sphere(); 38 | *scattered = Ray::new(hit.p, target - hit.p, ray_in.time()); 39 | *attenuation = texture.color(hit.u, hit.v, hit.p); 40 | true 41 | } 42 | &Material::Metal { albedo, fuzz } => { 43 | let reflected = reflect(ray_in.dir(), hit.normal); 44 | *scattered = Ray::new( 45 | hit.p, 46 | reflected + *fuzz * random_point_in_unit_sphere(), 47 | ray_in.time(), 48 | ); 49 | *attenuation = *albedo; 50 | 51 | Vec3A::dot(scattered.dir(), hit.normal) > 0.0 52 | } 53 | &Material::Dielectric { ref_idx } => { 54 | let outward_normal; 55 | let reflected = reflect(ray_in.dir(), hit.normal); 56 | let ni_over_nt: f32; 57 | let cosine; 58 | let reflect_prob; 59 | 60 | *attenuation = Vec3A::new(1.0, 1.0, 1.0); 61 | 62 | if Vec3A::dot(ray_in.dir(), hit.normal) > 0.0 { 63 | outward_normal = -hit.normal; 64 | ni_over_nt = *ref_idx; 65 | cosine = ref_idx * Vec3A::dot(ray_in.dir(), hit.normal) / ray_in.dir().length(); 66 | } else { 67 | outward_normal = hit.normal; 68 | ni_over_nt = 1.0 / *ref_idx; 69 | cosine = -Vec3A::dot(ray_in.dir(), hit.normal) / ray_in.dir().length(); 70 | } 71 | let refracted = refract(ray_in.dir(), outward_normal, ni_over_nt); 72 | if refracted.is_some() { 73 | reflect_prob = schlick(cosine, *ref_idx); 74 | } else { 75 | reflect_prob = 1.0; 76 | } 77 | 78 | let mut rng = rand::thread_rng(); 79 | let random_number = rng.gen_range(0.0, 1.0); 80 | 81 | if random_number < reflect_prob { 82 | *scattered = Ray::new(hit.p, reflected, ray_in.time()); 83 | } else { 84 | *scattered = Ray::new(hit.p, refracted.unwrap(), ray_in.time()); 85 | } 86 | 87 | return true; 88 | } 89 | } 90 | } 91 | 92 | fn schlick(cosine: f32, ref_idx: f32) -> f32 { 93 | let mut r0 = (1.0 - ref_idx) / (1.0 + ref_idx); 94 | r0 = r0 * r0; 95 | r0 + (1.0 - r0) * (1.0 - cosine).powf(5.0) 96 | } 97 | 98 | fn random_point_in_unit_sphere() -> Vec3A { 99 | let mut rng = rand::thread_rng(); 100 | 101 | loop { 102 | let rand_x = rng.gen_range(-1.0, 1.0); 103 | let rand_y = rng.gen_range(-1.0, 1.0); 104 | let rand_z = rng.gen_range(-1.0, 1.0); 105 | let v = Vec3A::new(rand_x, rand_y, rand_z); 106 | if v.length_squared() < 1.0 { 107 | return v; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/moving_sphere.rs: -------------------------------------------------------------------------------- 1 | use crate::aabb::AABB; 2 | use crate::hittable::{HitRecord, Hittable}; 3 | use crate::material::Material; 4 | use crate::ray::Ray; 5 | use crate::sphere::Sphere; 6 | use glam::Vec3A; 7 | 8 | #[derive(Debug)] 9 | pub struct MovingSphere { 10 | center0: Vec3A, 11 | center1: Vec3A, 12 | t0: f32, 13 | t1: f32, 14 | radius: f32, 15 | material: Material, 16 | } 17 | 18 | impl MovingSphere { 19 | pub fn new( 20 | center0: Vec3A, 21 | center1: Vec3A, 22 | t0: f32, 23 | t1: f32, 24 | radius: f32, 25 | material: Material, 26 | ) -> MovingSphere { 27 | MovingSphere { 28 | center0, 29 | center1, 30 | t0, 31 | t1, 32 | radius, 33 | material, 34 | } 35 | } 36 | } 37 | 38 | impl MovingSphere { 39 | fn center(&self, t: f32) -> Vec3A { 40 | return self.center0 41 | + ((t - self.t0) / (self.t1 - self.t0)) * (self.center1 - self.center0); 42 | } 43 | } 44 | 45 | impl Hittable for MovingSphere { 46 | fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option { 47 | let oc = ray.origin() - self.center(ray.time()); 48 | 49 | let a = Vec3A::dot(ray.dir(), ray.dir()); 50 | let b = Vec3A::dot(oc, ray.dir()); 51 | let c = Vec3A::dot(oc, oc) - self.radius * self.radius; 52 | 53 | let discriminant = b * b - a * c; 54 | if discriminant < 0.0 { 55 | return None; 56 | } 57 | 58 | if discriminant > 0.0 { 59 | let temp = (-b - (b * b - a * c).sqrt()) / a; 60 | if temp < t_max && temp > t_min { 61 | let p = ray.point_at(temp); 62 | let (u, v) = Sphere::get_uv(&p); 63 | return Some(HitRecord { 64 | t: temp, 65 | p, 66 | normal: (p - self.center(ray.time())) / self.radius, 67 | u, 68 | v, 69 | material: Some(self.material.clone()), 70 | }); 71 | } 72 | 73 | let temp = (-b + (b * b - a * c).sqrt()) / a; 74 | if temp < t_max && temp > t_min { 75 | let p = ray.point_at(temp); 76 | let (u, v) = Sphere::get_uv(&p); 77 | return Some(HitRecord { 78 | t: temp, 79 | p, 80 | u, 81 | v, 82 | normal: (p - self.center(ray.time())) / self.radius, 83 | material: Some(self.material.clone()), 84 | }); 85 | } 86 | } 87 | 88 | None 89 | } 90 | 91 | fn bounding_box(&self, t0: f32, t1: f32) -> AABB { 92 | let r = Vec3A::new(self.radius, self.radius, self.radius); 93 | let start_aabb = AABB::new(self.center(t0) - r, self.center(t0) + r); 94 | let end_aabb = AABB::new(self.center(t1) - r, self.center(t1) + r); 95 | 96 | start_aabb.union(&end_aabb) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/ray.rs: -------------------------------------------------------------------------------- 1 | use glam::Vec3A; 2 | 3 | #[derive(Copy, Clone)] 4 | pub struct Ray { 5 | origin: Vec3A, 6 | dir: Vec3A, 7 | time: f32, 8 | } 9 | 10 | impl Ray { 11 | pub fn new(origin: Vec3A, dir: Vec3A, time: f32) -> Ray { 12 | Ray { 13 | origin, 14 | dir: dir.normalize(), 15 | time, 16 | } 17 | } 18 | 19 | pub fn origin(&self) -> Vec3A { 20 | self.origin 21 | } 22 | 23 | pub fn dir(&self) -> Vec3A { 24 | self.dir 25 | } 26 | 27 | pub fn time(&self) -> f32 { 28 | self.time 29 | } 30 | 31 | pub fn point_at(&self, t: f32) -> Vec3A { 32 | self.origin + self.dir * t 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/sphere.rs: -------------------------------------------------------------------------------- 1 | use std::f32::consts::PI; 2 | 3 | use crate::aabb::AABB; 4 | use crate::hittable::{HitRecord, Hittable}; 5 | use crate::material::Material; 6 | use crate::ray::Ray; 7 | use glam::Vec3A; 8 | 9 | #[derive(Debug)] 10 | pub struct Sphere { 11 | center: Vec3A, 12 | radius: f32, 13 | material: Material, 14 | } 15 | 16 | impl Sphere { 17 | pub fn new(center: Vec3A, radius: f32, material: Material) -> Sphere { 18 | Sphere { 19 | center: center, 20 | radius: radius, 21 | material: material, 22 | } 23 | } 24 | 25 | pub fn get_uv(p: &Vec3A) -> (f32, f32) { 26 | let theta = f32::acos(-p.y); 27 | let phi = f32::atan2(-p.z, p.x) + PI; 28 | 29 | let u = phi / (2.0 * PI); 30 | let v = theta / PI; 31 | 32 | (u, v) 33 | } 34 | } 35 | 36 | impl Hittable for Sphere { 37 | fn hit(&self, ray: &Ray, t_min: f32, t_max: f32) -> Option { 38 | let oc = ray.origin() - self.center; 39 | 40 | let a = Vec3A::dot(ray.dir(), ray.dir()); 41 | let b = Vec3A::dot(oc, ray.dir()); 42 | let c = Vec3A::dot(oc, oc) - self.radius * self.radius; 43 | 44 | let discriminant = b * b - a * c; 45 | 46 | if discriminant > 0.0 { 47 | let temp = (-b - (b * b - a * c).sqrt()) / a; 48 | if temp < t_max && temp > t_min { 49 | let p = ray.point_at(temp); 50 | let (u, v) = Sphere::get_uv(&p); 51 | return Some(HitRecord { 52 | t: temp, 53 | p, 54 | u, 55 | v, 56 | normal: (p - self.center) / self.radius, 57 | material: Some(self.material.clone()), 58 | }); 59 | } 60 | let temp = (-b + (b * b - a * c).sqrt()) / a; 61 | if temp < t_max && temp > t_min { 62 | let p = ray.point_at(temp); 63 | let (u, v) = Sphere::get_uv(&p); 64 | return Some(HitRecord { 65 | t: temp, 66 | p, 67 | u, 68 | v, 69 | normal: (p - self.center) / self.radius, 70 | material: Some(self.material.clone()), 71 | }); 72 | } 73 | } 74 | 75 | None 76 | } 77 | 78 | fn bounding_box(&self, _: f32, _: f32) -> AABB { 79 | let r = Vec3A::new(self.radius, self.radius, self.radius); 80 | AABB::new(self.center - r, self.center + r) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/texture.rs: -------------------------------------------------------------------------------- 1 | use glam::Vec3A; 2 | 3 | pub trait Texture: Send + Sync + core::fmt::Debug { 4 | fn color(&self, u: f32, v: f32, p: Vec3A) -> Vec3A; 5 | } 6 | 7 | #[derive(Debug)] 8 | pub struct SolidColor { 9 | color: Vec3A, 10 | } 11 | 12 | impl SolidColor { 13 | pub fn new(color: Vec3A) -> SolidColor { 14 | SolidColor { color } 15 | } 16 | } 17 | 18 | impl Texture for SolidColor { 19 | fn color(&self, _: f32, _: f32, _: Vec3A) -> Vec3A { 20 | self.color 21 | } 22 | } 23 | 24 | #[derive(Debug)] 25 | pub struct CheckerTexture { 26 | odd: Vec3A, 27 | even: Vec3A, 28 | } 29 | 30 | impl CheckerTexture { 31 | pub fn new(odd: Vec3A, even: Vec3A) -> CheckerTexture { 32 | CheckerTexture { odd, even } 33 | } 34 | } 35 | 36 | impl Texture for CheckerTexture { 37 | fn color(&self, u: f32, v: f32, p: Vec3A) -> Vec3A { 38 | let sines = f32::sin(20.0 * p.x) * f32::sin(10.0 * p.y) * f32::sin(10.0 * p.z); 39 | 40 | if sines < 0.0 { 41 | self.odd 42 | } else { 43 | self.even 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/world.rs: -------------------------------------------------------------------------------- 1 | use glam::Vec3A; 2 | 3 | use crate::aabb::AABB; 4 | use crate::bvh::Bvh; 5 | use crate::hittable::{HitRecord, Hittable}; 6 | use crate::ray::Ray; 7 | use std::boxed::Box; 8 | 9 | #[derive(Default, Debug)] 10 | pub struct World { 11 | objects: Vec>, 12 | } 13 | 14 | impl World { 15 | pub fn add_object(&mut self, obj: Box) { 16 | self.objects.push(obj); 17 | } 18 | 19 | pub fn generate_bvh(self, t0: f32, t1: f32) -> Bvh { 20 | Bvh::new(self.objects, t0, t1) 21 | } 22 | } 23 | 24 | impl Hittable for World { 25 | fn hit(&self, ray: &Ray, tmin: f32, tmax: f32) -> Option { 26 | let mut closest = tmax; 27 | let mut hit = None; 28 | 29 | for obj in self.objects.iter() { 30 | if let Some(hit_record) = obj.hit(ray, tmin, tmax) { 31 | if hit_record.t < closest { 32 | closest = hit_record.t; 33 | hit = Some(hit_record) 34 | } 35 | } 36 | } 37 | 38 | hit 39 | } 40 | 41 | fn bounding_box(&self, _t0: f32, _t1: f32) -> AABB { 42 | AABB::new(Vec3A::ONE, Vec3A::ONE) 43 | } 44 | } 45 | --------------------------------------------------------------------------------