Compare commits
10 commits
ed6e84a240
...
e2dd9f7e3a
Author | SHA1 | Date | |
---|---|---|---|
e2dd9f7e3a | |||
cd94c7ad6f | |||
6d2a1407bf | |||
736c1e416c | |||
68f158f3c9 | |||
72acde6ea8 | |||
0d3500ceb5 | |||
6ef2c65009 | |||
20c32fc467 | |||
07445dd4be |
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -3,10 +3,6 @@
|
||||||
debug/
|
debug/
|
||||||
target/
|
target/
|
||||||
|
|
||||||
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
|
||||||
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
|
||||||
Cargo.lock
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
# These are backup files generated by rustfmt
|
||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
|
|
369
Cargo.lock
generated
Normal file
369
Cargo.lock
generated
Normal file
|
@ -0,0 +1,369 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
[[package]]
|
||||||
|
name = "alga"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f823d037a7ec6ea2197046bafd4ae150e6bc36f9ca347404f46a46823fa84f2"
|
||||||
|
dependencies = [
|
||||||
|
"approx",
|
||||||
|
"num-complex",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "approx"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0e60b75072ecd4168020818c0107f2857bb6c4e64252d8d3983f6263b40a5c3"
|
||||||
|
dependencies = [
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloudabi"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-cprng"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.94"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matrixmultiply"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "916806ba0031cd542105d916a97c8572e1fa6dd79c9c51e7eb43a09ec2dd84c1"
|
||||||
|
dependencies = [
|
||||||
|
"rawpointer",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nalgebra"
|
||||||
|
version = "0.18.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aaa9fddbc34c8c35dd2108515587b8ce0cab396f17977b8c738568e4edb521a2"
|
||||||
|
dependencies = [
|
||||||
|
"alga",
|
||||||
|
"approx",
|
||||||
|
"generic-array",
|
||||||
|
"matrixmultiply",
|
||||||
|
"num-complex",
|
||||||
|
"num-rational",
|
||||||
|
"num-traits",
|
||||||
|
"rand 0.6.5",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-complex"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-integer"
|
||||||
|
version = "0.1.44"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-rational"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"num-integer",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 1.0.1",
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.7",
|
||||||
|
"libc",
|
||||||
|
"rand_chacha 0.1.1",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
"rand_hc 0.1.0",
|
||||||
|
"rand_isaac",
|
||||||
|
"rand_jitter",
|
||||||
|
"rand_os",
|
||||||
|
"rand_pcg",
|
||||||
|
"rand_xorshift",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
"rand_chacha 0.2.2",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
"rand_hc 0.2.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.7",
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_isaac"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_jitter"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_os"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071"
|
||||||
|
dependencies = [
|
||||||
|
"cloudabi",
|
||||||
|
"fuchsia-cprng",
|
||||||
|
"libc",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
"rdrand",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_pcg"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.7",
|
||||||
|
"rand_core 0.4.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xorshift"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rawpointer"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rdrand"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.3.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "render"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"nalgebra",
|
||||||
|
"rand 0.7.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.13.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.9.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Bijan Sheibani
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
|
@ -30,7 +30,7 @@ This list may be changed or extended in the future.
|
||||||
- [ ] Point light sources
|
- [ ] Point light sources
|
||||||
- [x] Point source struct
|
- [x] Point source struct
|
||||||
- [x] Point source illuminance test
|
- [x] Point source illuminance test
|
||||||
- [ ] Hard shadows
|
- [x] Hard shadows
|
||||||
- [ ] Soft shadows
|
- [ ] Soft shadows
|
||||||
- [ ] ~~Light-emitting surfaces~~
|
- [ ] ~~Light-emitting surfaces~~
|
||||||
- [ ] Indirect lighting
|
- [ ] Indirect lighting
|
||||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -1,10 +1,10 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use std::time::Instant;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
use na::geometry::Point3;
|
|
||||||
|
|
||||||
mod camera; use camera::*;
|
mod camera; use camera::*;
|
||||||
mod types; use types::*;
|
mod types; use types::*;
|
||||||
|
@ -36,15 +36,24 @@ fn render(camera: &Camera, scene: &Scene, filename: &str) -> std::io::Result<()>
|
||||||
|
|
||||||
fn main() -> std::io::Result<()> {
|
fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
let camera = Camera::new(Point3::new(0.0,0.0,0.0), Vector3::new(0.0,0.0,1.0), 1.0, 16.0 / 9.0, 2.0, 480);
|
let camera = Camera::new(Point3::new(0.0,5.0,0.0), Vector3::new(0.0,-1.0,0.0), 1.0, 16.0 / 9.0, 2.0, 720);
|
||||||
|
|
||||||
let scene = Scene {
|
let scene = Scene {
|
||||||
objects: vec![
|
objects: vec![
|
||||||
Object::new(TriangleMesh::singleton(Point3::new(-1.0, -1.0, 2.0), Point3::new(0.0, 1.0, 2.0), Point3::new(1.0, -1.0, 2.0), |t, u, v| Color::new(t, u, v)))
|
Object::new(Plane::xz(|_, _| Texture { color: Color::white(), albedo: 0.8 })),
|
||||||
|
|
||||||
],
|
],
|
||||||
lights: Vec::new(),
|
lights: vec![
|
||||||
background: Color::black()
|
|
||||||
|
],
|
||||||
|
background: Color::gray(0.5)
|
||||||
};
|
};
|
||||||
|
|
||||||
render(&camera, &scene, "out.ppm")
|
let before = Instant::now();
|
||||||
|
|
||||||
|
render(&camera, &scene, "out.ppm")?;
|
||||||
|
|
||||||
|
println!("{}", before.elapsed().as_millis());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,7 @@ mod sphere; pub use sphere::*;
|
||||||
mod plane; pub use plane::*;
|
mod plane; pub use plane::*;
|
||||||
mod triangle; pub use triangle::*;
|
mod triangle; pub use triangle::*;
|
||||||
mod bound; pub use bound::*;
|
mod bound; pub use bound::*;
|
||||||
mod pointlight; pub use pointlight::*;
|
mod point_light; pub use point_light::*;
|
||||||
|
|
||||||
use na::*;
|
|
||||||
|
|
||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
|
|
||||||
|
@ -19,11 +17,11 @@ pub trait Surface {
|
||||||
|
|
||||||
// Takes in a point (assumed to be on the object's surface)
|
// Takes in a point (assumed to be on the object's surface)
|
||||||
// and returns the normal vector off of that point.
|
// and returns the normal vector off of that point.
|
||||||
fn normal(&self, point: Point3<f32>) -> Unit<Vector3<f32>>;
|
fn normal(&self, point: Point3f) -> Unit3f;
|
||||||
|
|
||||||
// Takes in a point (assumed to be on the object's surface)
|
// Takes in a point (assumed to be on the object's surface)
|
||||||
// and returns the color information on that point.
|
// and returns the texture information on that point.
|
||||||
fn getcolor(&self, point: Point3<f32>) -> Color;
|
fn get_texture(&self, point: Point3f) -> Texture;
|
||||||
|
|
||||||
// Creates a bounding sphere around the object.
|
// Creates a bounding sphere around the object.
|
||||||
fn bound(&self) -> Bound;
|
fn bound(&self) -> Bound;
|
||||||
|
@ -40,7 +38,7 @@ impl Object {
|
||||||
let bound = surface.bound();
|
let bound = surface.bound();
|
||||||
Object {
|
Object {
|
||||||
surface: Box::new(surface),
|
surface: Box::new(surface),
|
||||||
bound: bound
|
bound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,14 +48,22 @@ impl Object {
|
||||||
self.surface.intersect(ray)
|
self.surface.intersect(ray)
|
||||||
} else { None }
|
} else { None }
|
||||||
}
|
}
|
||||||
pub fn normal(&self, point: Point3<f32>) -> Unit<Vector3<f32>> { self.surface.normal(point) }
|
pub fn normal(&self, point: Point3f) -> Unit3f { self.surface.normal(point) }
|
||||||
pub fn getcolor(&self, point: Point3<f32>) -> Color { self.surface.getcolor(point) }
|
pub fn get_texture(&self, point: Point3f) -> Texture { self.surface.get_texture(point) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Light {
|
pub trait Light {
|
||||||
// Determine if the light is able to illuminate the point.
|
// Determine if the light is able to illuminate the point.
|
||||||
// If so, return the color of the light.
|
fn check_shadow(&self, point: Point3f, objects: &Vec<Object>) -> bool;
|
||||||
fn illuminate(&self, point: Point3<f32>, objects: &Vec<Object>) -> Option<Color>;
|
|
||||||
|
// Compute color on a point.
|
||||||
|
fn get_color(&self, point: Point3f) -> Color;
|
||||||
|
|
||||||
|
// Compute intensity on a point.
|
||||||
|
fn intensity(&self, point: Point3f) -> f32;
|
||||||
|
|
||||||
|
// Return the direction from the point to the light source.
|
||||||
|
fn direction(&self, point: Point3f) -> Unit3f;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
|
@ -65,17 +71,3 @@ pub struct Scene {
|
||||||
pub lights: Vec<Box<dyn Light>>,
|
pub lights: Vec<Box<dyn Light>>,
|
||||||
pub background: Color
|
pub background: Color
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn obj_getcolor() {
|
|
||||||
let sphere = Object::new(Sphere::new_solid(0.0, 0.0, 0.0, 1.0, Color::white()));
|
|
||||||
|
|
||||||
let point = Point3::new(1.0, 0.0, 0.0);
|
|
||||||
|
|
||||||
assert_eq!(sphere.getcolor(point), Color::white());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,13 +3,13 @@ extern crate nalgebra as na;
|
||||||
// use na::distance;
|
// use na::distance;
|
||||||
use na::geometry::Point3;
|
use na::geometry::Point3;
|
||||||
|
|
||||||
use crate::types::Ray;
|
use crate::types::*;
|
||||||
|
|
||||||
// A bounding sphere, used for
|
// A bounding sphere, used for
|
||||||
// intersection test optimization.
|
// intersection test optimization.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Bound {
|
pub struct Bound {
|
||||||
pub center: Point3<f32>,
|
pub center: Point3f,
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
|
|
||||||
// If true, then the bounding sphere is disabled.
|
// If true, then the bounding sphere is disabled.
|
||||||
|
@ -24,7 +24,7 @@ impl Bound {
|
||||||
l.norm_squared() >= self.radius * self.radius
|
l.norm_squared() >= self.radius * self.radius
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn contains(&self, point: &Point3<f32>) -> bool { distance(&self.center, point) < self.radius }
|
// pub fn contains(&self, point: &Point3f) -> bool { distance(&self.center, point) < self.radius }
|
||||||
|
|
||||||
pub fn bypass() -> Self { Bound { center: Point3::origin(), radius: 0.0, bypass: true } }
|
pub fn bypass() -> Self { Bound { center: Point3::origin(), radius: 0.0, bypass: true } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,24 +7,24 @@ use crate::types::*;
|
||||||
use super::{Surface, bound::*};
|
use super::{Surface, bound::*};
|
||||||
|
|
||||||
pub struct Plane {
|
pub struct Plane {
|
||||||
pub center: Point3<f32>, // Plane origin (used for texture mapping).
|
pub center: Point3f, // Plane origin (used for texture mapping).
|
||||||
pub normal: Unit<Vector3<f32>>, // Precomputed plane normal.
|
pub normal: Unit3f, // Precomputed plane normal.
|
||||||
|
|
||||||
x_axis: Vector3<f32>, // Plane x-axis (The 3D direction that corresponds to the x-direction on the plane).
|
x_axis: Vector3f, // Plane x-axis (The 3D direction that corresponds to the x-direction on the plane).
|
||||||
y_axis: Vector3<f32>, // Plane y-axis (The 3D direction that corresponds to the y-direction on the plane).
|
y_axis: Vector3f, // Plane y-axis (The 3D direction that corresponds to the y-direction on the plane).
|
||||||
|
|
||||||
texture: Box<dyn Fn(f32, f32) -> Color> // Texture map.
|
texture: Box<dyn Fn(f32, f32) -> Texture> // Texture map.
|
||||||
// Input coordinates are defined in terms of the axes above.
|
// Input coordinates are defined in terms of the axes above.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Plane {
|
impl Plane {
|
||||||
// Creates a new plane.
|
// Creates a new plane.
|
||||||
pub fn new<F: 'static>(center: Point3<f32>, x_axis: Vector3<f32>, y_axis: Vector3<f32>, texture: F) -> Self
|
pub fn new<F: 'static>(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self
|
||||||
where F: Fn(f32, f32) -> Color
|
where F: Fn(f32, f32) -> Texture
|
||||||
{
|
{
|
||||||
Plane {
|
Plane {
|
||||||
center: center,
|
center,
|
||||||
normal: Unit::new_normalize(x_axis.cross(&y_axis)),
|
normal: Unit::new_normalize(x_axis.cross(&y_axis)),
|
||||||
x_axis: x_axis,
|
x_axis: x_axis,
|
||||||
y_axis: y_axis,
|
y_axis: y_axis,
|
||||||
|
@ -33,8 +33,8 @@ impl Plane {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new plane with the normal flipped.
|
// Creates a new plane with the normal flipped.
|
||||||
pub fn new_flip<F: 'static>(center: Point3<f32>, x_axis: Vector3<f32>, y_axis: Vector3<f32>, texture: F) -> Self
|
pub fn new_flip<F: 'static>(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self
|
||||||
where F: Fn(f32, f32) -> Color
|
where F: Fn(f32, f32) -> Texture
|
||||||
{
|
{
|
||||||
Plane {
|
Plane {
|
||||||
center: center,
|
center: center,
|
||||||
|
@ -46,20 +46,20 @@ impl Plane {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new plane of a solid color.
|
// Creates a new plane of a solid color.
|
||||||
pub fn new_solid(center: Point3<f32>, x_axis: Vector3<f32>, y_axis: Vector3<f32>, color: Color) -> Self
|
pub fn new_solid(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: Texture) -> Self
|
||||||
{ Plane::new(center, x_axis, y_axis, move |_, _| color) }
|
{ Plane::new(center, x_axis, y_axis, move |_, _| texture) }
|
||||||
|
|
||||||
// Creates a new flipped plane of a solid color.
|
// Creates a new flipped plane of a solid color.
|
||||||
pub fn new_solid_flip(center: Point3<f32>, x_axis: Vector3<f32>, y_axis: Vector3<f32>, color: Color) -> Self
|
pub fn new_solid_flip(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: Texture) -> Self
|
||||||
{ Plane::new_flip(center, x_axis, y_axis, move |_, _| color) }
|
{ Plane::new_flip(center, x_axis, y_axis, move |_, _| texture) }
|
||||||
|
|
||||||
|
|
||||||
// Creates a new XY-plane with the given texture map.
|
// Creates a new XY-plane with the given texture map.
|
||||||
pub fn xy<F: 'static + Fn(f32, f32) -> Color>(texture: F) -> Self
|
pub fn xy(texture: impl 'static + Fn(f32, f32) -> Texture) -> Self
|
||||||
{ Plane::new(Point3::origin(), Vector3::x(), Vector3::y(), texture) }
|
{ Plane::new(Point3::origin(), Vector3::x(), Vector3::y(), texture) }
|
||||||
|
|
||||||
// Creates a new XZ-plane with the given texture map.
|
// Creates a new XZ-plane with the given texture map.
|
||||||
pub fn xz<F: 'static + Fn(f32, f32) -> Color>(texture: F) -> Self
|
pub fn xz(texture: impl 'static + Fn(f32, f32) -> Texture) -> Self
|
||||||
{ Plane::new(Point3::origin(), Vector3::x(), Vector3::z(), texture) }
|
{ Plane::new(Point3::origin(), Vector3::x(), Vector3::z(), texture) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ impl Surface for Plane {
|
||||||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||||
|
|
||||||
let d = self.normal.dot(&ray.direction);
|
let d = self.normal.dot(&ray.direction);
|
||||||
if d < 1e-3 { return None; }
|
if d > -1e-3 { return None; }
|
||||||
|
|
||||||
let t = (self.center - ray.origin).dot(&*self.normal) / d;
|
let t = (self.center - ray.origin).dot(&*self.normal) / d;
|
||||||
|
|
||||||
|
@ -75,9 +75,9 @@ impl Surface for Plane {
|
||||||
else { None }
|
else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(&self, _point: Point3<f32>) -> Unit<Vector3<f32>> { self.normal }
|
fn normal(&self, _point: Point3f) -> Unit3f { self.normal }
|
||||||
|
|
||||||
fn getcolor(&self, point: Point3<f32>) -> Color {
|
fn get_texture(&self, point: Point3f) -> Texture {
|
||||||
let rel_pos = point - self.center;
|
let rel_pos = point - self.center;
|
||||||
let proj_point3 = rel_pos - (*self.normal * self.normal.dot(&rel_pos));
|
let proj_point3 = rel_pos - (*self.normal * self.normal.dot(&rel_pos));
|
||||||
|
|
||||||
|
@ -91,36 +91,3 @@ impl Surface for Plane {
|
||||||
// bounding sphere could possibly contain one.
|
// bounding sphere could possibly contain one.
|
||||||
fn bound(&self) -> Bound { Bound::bypass() }
|
fn bound(&self) -> Bound { Bound::bypass() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn plane_new() {
|
|
||||||
let plane = Plane::xy(|_, _| Color::black());
|
|
||||||
|
|
||||||
assert_eq!(plane.center, Point3::new(0.0, 0.0, 0.0));
|
|
||||||
assert_eq!(plane.normal, Unit::new_unchecked(Vector3::z()));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn plane_intersect() {
|
|
||||||
const N: f32 = 5.0;
|
|
||||||
let plane = Plane::xz(|_, _| Color::black());
|
|
||||||
|
|
||||||
let ray = Ray::new(Point3::new(0.0, N, 0.0), Vector3::new(0.0, -1.0, 0.0));
|
|
||||||
|
|
||||||
assert_eq!(plane.intersect(ray), Some(N));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn plane_getcolor() {
|
|
||||||
const N: f32 = 5.0;
|
|
||||||
let plane = Plane::xz(|x, y| Color::new(x, y, 0.0));
|
|
||||||
|
|
||||||
let point = Point3::new(5.0, 7.0, 6.0);
|
|
||||||
|
|
||||||
assert_eq!(plane.getcolor(point), Color::new(5.0, 6.0, 0.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
50
src/object/point_light.rs
Normal file
50
src/object/point_light.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
|
use na::*;
|
||||||
|
|
||||||
|
use crate::types::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
pub struct PointLight {
|
||||||
|
pub pos: Point3f,
|
||||||
|
pub color: Color,
|
||||||
|
pub intensity: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl PointLight {
|
||||||
|
pub fn new(pos: Point3f, color: Color, intensity: f32) -> PointLight {
|
||||||
|
PointLight { pos, color, intensity }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Light for PointLight {
|
||||||
|
fn check_shadow(&self, point: Point3f, objects: &Vec<Object>) -> bool {
|
||||||
|
let max_d = distance(&self.pos, &point);
|
||||||
|
objects.iter()
|
||||||
|
.filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point)))
|
||||||
|
.all(|d| d - max_d > -1e-3 )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_color(&self, _point: Point3f) -> Color { self.color }
|
||||||
|
|
||||||
|
fn intensity(&self, _point: Point3f) -> f32 { self.intensity }
|
||||||
|
|
||||||
|
fn direction(&self, point: Point3f) -> Unit3f {
|
||||||
|
Unit::new_normalize(self.pos - point)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn point_light_check_shadow() {
|
||||||
|
let light = PointLight::new(Point3::new(0.0, 1.0, 0.0), Color::white(), 1.0);
|
||||||
|
let block = Object::new(Sphere::new_solid(0.0, 0.5, 0.0, 0.1, Texture::new(0.0, 0.0, 0.0, 0.0)));
|
||||||
|
|
||||||
|
assert!(light.check_shadow(Point3::origin(), &Vec::new()));
|
||||||
|
assert!(!light.check_shadow(Point3::origin(), &vec![block]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,36 +0,0 @@
|
||||||
extern crate nalgebra as na;
|
|
||||||
|
|
||||||
use na::*;
|
|
||||||
use na::geometry::Point3;
|
|
||||||
|
|
||||||
use crate::types::*;
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub struct PointLight {
|
|
||||||
pub pos: Point3<f32>,
|
|
||||||
pub color: Color
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PointLight {
|
|
||||||
pub fn new(pos: Point3<f32>, color: Color) -> PointLight {
|
|
||||||
PointLight {
|
|
||||||
pos: pos,
|
|
||||||
color: color
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_point(&self, point: Point3<f32>, objects: &Vec<Object>) -> bool {
|
|
||||||
let max_d = distance(&self.pos, &point);
|
|
||||||
objects.iter()
|
|
||||||
.filter_map(|obj| obj.intersect(Ray::from_points(self.pos, point)))
|
|
||||||
.all(|d| d > max_d)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Light for PointLight {
|
|
||||||
fn illuminate(&self, point: Point3<f32>, objects: &Vec<Object>) -> Option<Color> {
|
|
||||||
if self.check_point(point, objects) {
|
|
||||||
Some(self.color)
|
|
||||||
} else { None }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -9,29 +9,28 @@ use crate::types::*;
|
||||||
use super::{Surface, bound::*};
|
use super::{Surface, bound::*};
|
||||||
|
|
||||||
pub struct Sphere {
|
pub struct Sphere {
|
||||||
pub center: Point3<f32>, // Center point of the sphere.
|
pub center: Point3f, // Center point of the sphere.
|
||||||
pub radius: f32, // Radius of the sphere.
|
pub radius: f32, // Radius of the sphere.
|
||||||
|
|
||||||
texture: Box<dyn Fn(f32, f32) -> Color> // Texture map.
|
texture: Box<dyn Fn(f32, f32) -> Texture> // Texture map.
|
||||||
// Uses spherical coordinates (normalized from 0-1) as input.
|
// Uses spherical coordinates (normalized from 0-1) as input.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Sphere {
|
impl Sphere {
|
||||||
// Creates a new sphere.
|
// Creates a new sphere.
|
||||||
pub fn new<F: 'static>(x: f32, y: f32, z: f32, radius: f32, texture: F) -> Self
|
pub fn new<F: 'static>(x: f32, y: f32, z: f32, radius: f32, texture: F) -> Self
|
||||||
where F: Fn(f32, f32) -> Color
|
where F: Fn(f32, f32) -> Texture
|
||||||
{
|
{
|
||||||
Sphere {
|
Sphere {
|
||||||
center: Point3::new(x, y, z),
|
center: Point3::new(x, y, z), radius,
|
||||||
radius: radius,
|
|
||||||
texture: Box::new(texture)
|
texture: Box::new(texture)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new sphere of a solid color.
|
// Creates a new sphere of a solid color.
|
||||||
pub fn new_solid(x: f32, y: f32, z: f32, radius: f32, color: Color) -> Self
|
pub fn new_solid(x: f32, y: f32, z: f32, radius: f32, texture: Texture) -> Self
|
||||||
{ Sphere::new(x, y, z, radius, move |_, _| color) }
|
{ Sphere::new(x, y, z, radius, move |_, _| texture) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Surface for Sphere {
|
impl Surface for Sphere {
|
||||||
|
@ -62,14 +61,14 @@ impl Surface for Sphere {
|
||||||
else { None }
|
else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(&self, point: Point3<f32>) -> Unit<Vector3<f32>> {
|
fn normal(&self, point: Point3f) -> Unit3f {
|
||||||
Unit::new_normalize(point - self.center)
|
Unit::new_normalize(point - self.center)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getcolor(&self, point: Point3<f32>) -> Color {
|
fn get_texture(&self, point: Point3f) -> Texture {
|
||||||
let normal = self.normal(point);
|
let normal = self.normal(point);
|
||||||
|
|
||||||
// In this particular case, the normal is simular to a point on a unit sphere
|
// In this particular case, the normal is similar to a point on a unit sphere
|
||||||
// centred around the origin. We can thus use the normal coordinates to compute
|
// centred around the origin. We can thus use the normal coordinates to compute
|
||||||
// the spherical coordinates of the point.
|
// the spherical coordinates of the point.
|
||||||
let x = 0.5 + normal.z.atan2(normal.x) / (2.0 * PI);
|
let x = 0.5 + normal.z.atan2(normal.x) / (2.0 * PI);
|
||||||
|
@ -78,5 +77,5 @@ impl Surface for Sphere {
|
||||||
(*self.texture)(x, y)
|
(*self.texture)(x, y)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bound(&self) -> Bound { Bound::bypass() }
|
fn bound(&self) -> Bound { Bound { center: self.center, radius: self.radius, bypass: false } }
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,36 +13,36 @@ pub struct Triangle {
|
||||||
pub v2: usize,
|
pub v2: usize,
|
||||||
pub v3: usize,
|
pub v3: usize,
|
||||||
|
|
||||||
normal: Unit<Vector3<f32>>, // Precalculated normal vector.
|
normal: Unit3f, // Precalculated normal vector.
|
||||||
area: f32, // Precalculated area for barycentric calculations.
|
area: f32, // Precalculated area for barycentric calculations.
|
||||||
|
|
||||||
texture: Box<dyn Fn(f32, f32, f32) -> Color> // Texture map.
|
texture: Box<dyn Fn(f32, f32, f32) -> Texture> // Texture map.
|
||||||
// Uses barycentric coordinates as input.
|
// Uses barycentric coordinates as input.
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TriangleMesh {
|
pub struct TriangleMesh {
|
||||||
pub vertices: Vec<Point3<f32>>,
|
pub vertices: Vec<Point3f>,
|
||||||
pub tris: Vec<Triangle>
|
pub triangles: Vec<Triangle>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tri_area(a: &Point3<f32>, b: &Point3<f32>, c: &Point3<f32>) -> f32 {
|
fn tri_area(a: &Point3f, b: &Point3f, c: &Point3f) -> f32 {
|
||||||
let prlg_area: f32 = (b - a).cross(&(c - a)).norm();
|
let prlg_area: f32 = (b - a).cross(&(c - a)).norm();
|
||||||
prlg_area / 2.0
|
prlg_area / 2.0
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Triangle {
|
impl Triangle {
|
||||||
fn vertex1<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v1] }
|
fn vertex1<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v1] }
|
||||||
fn vertex2<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v2] }
|
fn vertex2<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v2] }
|
||||||
fn vertex3<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v3] }
|
fn vertex3<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v3] }
|
||||||
|
|
||||||
// Conversion of barycentric coordinates to
|
// Conversion of barycentric coordinates to
|
||||||
// a point on the triangle.
|
// a point on the triangle.
|
||||||
fn from_bary(&self, vertices: &Vec<Point3<f32>>, t: f32, u: f32, v: f32) -> Point3<f32> {
|
fn from_bary(&self, vertices: &Vec<Point3f>, t: f32, u: f32, v: f32) -> Point3f {
|
||||||
Point::from(t * self.vertex1(vertices).coords + u * self.vertex2(vertices).coords + v * self.vertex3(vertices).coords)
|
Point::from(t * self.vertex1(vertices).coords + u * self.vertex2(vertices).coords + v * self.vertex3(vertices).coords)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Conversion of a point to barycentric coordinates.
|
// Conversion of a point to barycentric coordinates.
|
||||||
fn to_bary(&self, vertices: &Vec<Point3<f32>>, point: Point3<f32>) -> (f32, f32, f32) {
|
fn to_bary(&self, vertices: &Vec<Point3f>, point: Point3f) -> (f32, f32, f32) {
|
||||||
let t = tri_area(self.vertex2(vertices), self.vertex3(vertices), &point) / self.area;
|
let t = tri_area(self.vertex2(vertices), self.vertex3(vertices), &point) / self.area;
|
||||||
let u = tri_area(self.vertex1(vertices), self.vertex3(vertices), &point) / self.area;
|
let u = tri_area(self.vertex1(vertices), self.vertex3(vertices), &point) / self.area;
|
||||||
let v = tri_area(self.vertex1(vertices), self.vertex2(vertices), &point) / self.area;
|
let v = tri_area(self.vertex1(vertices), self.vertex2(vertices), &point) / self.area;
|
||||||
|
@ -50,7 +50,7 @@ impl Triangle {
|
||||||
(t, u, v)
|
(t, u, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersect_(&self, vertices: &Vec<Point3<f32>>, ray: Ray) -> Option<(f32, f32, f32)> {
|
fn intersect_(&self, vertices: &Vec<Point3f>, ray: Ray) -> Option<(f32, f32, f32)> {
|
||||||
let vect2_1 = self.vertex2(vertices) - self.vertex1(vertices);
|
let vect2_1 = self.vertex2(vertices) - self.vertex1(vertices);
|
||||||
let vect3_1 = self.vertex3(vertices) - self.vertex1(vertices);
|
let vect3_1 = self.vertex3(vertices) - self.vertex1(vertices);
|
||||||
|
|
||||||
|
@ -74,11 +74,11 @@ impl Triangle {
|
||||||
Some((t, u, v))
|
Some((t, u, v))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn intersect(&self, vertices: &Vec<Point3<f32>>, ray: Ray) -> Option<f32> {
|
fn intersect(&self, vertices: &Vec<Point3f>, ray: Ray) -> Option<f32> {
|
||||||
self.intersect_(vertices, ray).map(|(t, u, v)| distance(&ray.origin, &self.from_bary(vertices, t, u, v)))
|
self.intersect_(vertices, ray).map(|(t, u, v)| distance(&ray.origin, &self.from_bary(vertices, t, u, v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getcolor(&self, vertices: &Vec<Point3<f32>>, point: Point3<f32>) -> Color {
|
fn get_texture(&self, vertices: &Vec<Point3f>, point: Point3f) -> Texture {
|
||||||
let (t, u, v) = self.to_bary(vertices, point);
|
let (t, u, v) = self.to_bary(vertices, point);
|
||||||
(*self.texture)(t, u, v)
|
(*self.texture)(t, u, v)
|
||||||
}
|
}
|
||||||
|
@ -86,48 +86,43 @@ impl Triangle {
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl TriangleMesh {
|
impl TriangleMesh {
|
||||||
pub fn new(vertices: Vec<Point3<f32>>, tris: Vec<(usize, usize, usize, Box<dyn Fn(f32, f32, f32) -> Color>)>) -> Self {
|
pub fn new(vertices: Vec<Point3f>, tris: Vec<(usize, usize, usize, Box<dyn Fn(f32, f32, f32) -> Texture>)>) -> Self {
|
||||||
let triangles = tris.into_iter()
|
let triangles = tris.into_iter()
|
||||||
.map(|(v1, v2, v3, f)| Triangle {
|
.map(|(v1, v2, v3, f)| Triangle {
|
||||||
v1: v1,
|
v1, v2, v3,
|
||||||
v2: v2,
|
|
||||||
v3: v3,
|
|
||||||
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
||||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||||
texture: f
|
texture: f
|
||||||
}).collect();
|
}).collect();
|
||||||
TriangleMesh {
|
TriangleMesh {
|
||||||
vertices: vertices,
|
vertices, triangles
|
||||||
tris: triangles
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_solid(vertices: Vec<Point3<f32>>, tris: Vec<(usize, usize, usize)>, color: Color) -> Self {
|
pub fn new_solid(vertices: Vec<Point3f>, tris: Vec<(usize, usize, usize)>, texture: Texture) -> Self {
|
||||||
let triangles = tris.into_iter()
|
let triangles = tris.into_iter()
|
||||||
.map(|(v1, v2, v3)| Triangle {
|
.map(|(v1, v2, v3)| Triangle {
|
||||||
v1: v1,
|
v1, v2, v3,
|
||||||
v2: v2,
|
|
||||||
v3: v3,
|
|
||||||
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
||||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||||
texture: Box::new(move |_, _, _| color)
|
texture: Box::new(move |_, _, _| texture)
|
||||||
}).collect();
|
}).collect();
|
||||||
TriangleMesh {
|
TriangleMesh {
|
||||||
vertices: vertices,
|
vertices,
|
||||||
tris: triangles
|
triangles
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn singleton<F: 'static>(vertex1: Point3<f32>, vertex2: Point3<f32>, vertex3: Point3<f32>, texture: F) -> Self
|
pub fn singleton<F: 'static>(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: F) -> Self
|
||||||
where F: Fn(f32, f32, f32) -> Color
|
where F: Fn(f32, f32, f32) -> Texture
|
||||||
{ TriangleMesh::new(vec![vertex1, vertex2, vertex3], vec![(0, 1, 2, Box::new(texture))]) }
|
{ TriangleMesh::new(vec![vertex1, vertex2, vertex3], vec![(0, 1, 2, Box::new(texture))]) }
|
||||||
|
|
||||||
pub fn singleton_solid(vertex1: Point3<f32>, vertex2: Point3<f32>, vertex3: Point3<f32>, color: Color) -> Self
|
pub fn singleton_solid(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: Texture) -> Self
|
||||||
{ TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| color) }
|
{ TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| texture) }
|
||||||
|
|
||||||
|
|
||||||
fn closest_tri(&self, point: Point3<f32>) -> &Triangle {
|
fn closest_tri(&self, point: Point3f) -> &Triangle {
|
||||||
self.tris.iter()
|
self.triangles.iter()
|
||||||
.map(move |tri| {
|
.map(move |tri| {
|
||||||
|
|
||||||
let rel_pos = point - tri.vertex1(&self.vertices);
|
let rel_pos = point - tri.vertex1(&self.vertices);
|
||||||
|
@ -150,22 +145,46 @@ impl TriangleMesh {
|
||||||
|
|
||||||
impl Surface for TriangleMesh {
|
impl Surface for TriangleMesh {
|
||||||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||||
self.tris.iter()
|
self.triangles.iter()
|
||||||
.filter_map(|tri| tri.intersect(&self.vertices, ray))
|
.filter_map(|tri| tri.intersect(&self.vertices, ray))
|
||||||
.min_by(|a, b| a.partial_cmp(&b).unwrap_or(Ordering::Equal))
|
.min_by(|a, b| a.partial_cmp(&b).unwrap_or(Ordering::Equal))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normal(&self, point: Point3<f32>) -> Unit<Vector3<f32>> {
|
fn normal(&self, point: Point3f) -> Unit3f {
|
||||||
self.closest_tri(point).normal
|
self.closest_tri(point).normal
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getcolor(&self, point: Point3<f32>) -> Color {
|
fn get_texture(&self, point: Point3f) -> Texture {
|
||||||
self.closest_tri(point).getcolor(&self.vertices, point)
|
self.closest_tri(point).get_texture(&self.vertices, point)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses Welzl's algorithm to solve the bounding sphere problem
|
// Uses Welzl's algorithm to solve the bounding sphere problem
|
||||||
fn bound(&self) -> Bound {
|
fn bound(&self) -> Bound {
|
||||||
fn triangle_sphere(point1: &Point3<f32>, point2: &Point3<f32>, point3: &Point3<f32>) -> (Point3<f32>, f32) {
|
fn smallest_sphere_plane(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) {
|
||||||
|
if points.len() == 0 || boundary.len() == 3 {
|
||||||
|
match boundary.len() {
|
||||||
|
0 => (Point3::new(0.0, 0.0, 0.0), 0.0),
|
||||||
|
1 => (*boundary[0], 0.0),
|
||||||
|
2 => { let half_span = 0.5 * (boundary[1] - boundary[0]);
|
||||||
|
(*boundary[0] + half_span, half_span.norm()) },
|
||||||
|
3 => triangle_sphere(boundary[0], boundary[1], boundary[2]),
|
||||||
|
_ => unreachable!()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let removed = points[0];
|
||||||
|
let points = Vec::from(&points[1..]);
|
||||||
|
|
||||||
|
let bound = smallest_sphere(points.clone(), boundary.clone());
|
||||||
|
if distance(&bound.0, removed) < bound.1 { return bound; }
|
||||||
|
|
||||||
|
let mut boundary = boundary.clone();
|
||||||
|
boundary.push(removed);
|
||||||
|
|
||||||
|
smallest_sphere_plane(points, boundary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triangle_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f) -> (Point3f, f32) {
|
||||||
let a = point3 - point1;
|
let a = point3 - point1;
|
||||||
let b = point2 - point1;
|
let b = point2 - point1;
|
||||||
|
|
||||||
|
@ -179,32 +198,40 @@ impl Surface for TriangleMesh {
|
||||||
(point1 + to_center, radius)
|
(point1 + to_center, radius)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tetrahedron_sphere(point1: &Point3<f32>, point2: &Point3<f32>, point3: &Point3<f32>, point4: &Point3<f32>) -> (Point3<f32>, f32) {
|
fn tetrahedron_sphere(point1: &Point3f, point2: &Point3f, point3: &Point3f, point4: &Point3f) -> (Point3f, f32) {
|
||||||
let matrix = Matrix4::from_rows(&[point1.to_homogeneous().transpose(),
|
let matrix = Matrix4::from_rows(&[point1.to_homogeneous().transpose(),
|
||||||
point2.to_homogeneous().transpose(),
|
point2.to_homogeneous().transpose(),
|
||||||
point3.to_homogeneous().transpose(),
|
point3.to_homogeneous().transpose(),
|
||||||
point4.to_homogeneous().transpose()]);
|
point4.to_homogeneous().transpose()]);
|
||||||
|
|
||||||
let a = matrix.determinant() * 2.0;
|
let a = matrix.determinant() * 2.0;
|
||||||
let mut matrix_mut = matrix.clone();
|
|
||||||
|
|
||||||
let squares = Vector4::new(point1.coords.norm_squared(), point2.coords.norm_squared(), point3.coords.norm_squared(), point4.coords.norm_squared());
|
if (a != 0.0) {
|
||||||
matrix_mut.set_column(0, &squares);
|
let mut matrix_mut = matrix.clone();
|
||||||
let center_x = matrix_mut.determinant();
|
|
||||||
|
|
||||||
matrix_mut.set_column(1, &matrix.index((.., 0)));
|
let squares = Vector4::new(point1.coords.norm_squared(), point2.coords.norm_squared(), point3.coords.norm_squared(), point4.coords.norm_squared());
|
||||||
let center_y = -matrix_mut.determinant();
|
matrix_mut.set_column(0, &squares);
|
||||||
|
let center_x = matrix_mut.determinant();
|
||||||
|
|
||||||
matrix_mut.set_column(2, &matrix.index((.., 1)));
|
matrix_mut.set_column(1, &matrix.index((.., 0)));
|
||||||
let center_z = matrix_mut.determinant();
|
let center_y = -matrix_mut.determinant();
|
||||||
|
|
||||||
let center = Point3::new(center_x / a, center_y / a, center_z / a);
|
matrix_mut.set_column(2, &matrix.index((.., 1)));
|
||||||
let radius = distance(point1, ¢er);
|
let center_z = matrix_mut.determinant();
|
||||||
|
|
||||||
(center, radius)
|
let center = Point3::new(center_x / a, center_y / a, center_z / a);
|
||||||
|
let radius = distance(point1, ¢er);
|
||||||
|
|
||||||
|
(center, radius)
|
||||||
|
} else {
|
||||||
|
let points = vec![point1, point2, point3, point4];
|
||||||
|
let boundary = Vec::new();
|
||||||
|
|
||||||
|
smallest_sphere_plane(points, boundary)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn smallest_sphere(points: Vec<&Point3<f32>>, boundary: Vec<&Point3<f32>>) -> (Point3<f32>, f32) {
|
fn smallest_sphere(points: Vec<&Point3f>, boundary: Vec<&Point3f>) -> (Point3f, f32) {
|
||||||
if points.len() == 0 || boundary.len() == 4 {
|
if points.len() == 0 || boundary.len() == 4 {
|
||||||
match boundary.len() {
|
match boundary.len() {
|
||||||
0 => (Point3::new(0.0, 0.0, 0.0), 0.0),
|
0 => (Point3::new(0.0, 0.0, 0.0), 0.0),
|
||||||
|
@ -233,110 +260,11 @@ impl Surface for TriangleMesh {
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
|
||||||
let mut points: Vec<&Point3<f32>> = self.vertices.iter().collect();
|
let mut points: Vec<&Point3f> = self.vertices.iter().collect();
|
||||||
points.shuffle(&mut thread_rng());
|
points.shuffle(&mut thread_rng());
|
||||||
|
|
||||||
let (center, radius) = smallest_sphere(points, Vec::new());
|
let (center, radius) = smallest_sphere(points, Vec::new());
|
||||||
|
|
||||||
Bound { center: center, radius: radius + 1e-3, bypass: false }
|
Bound { center, radius: radius + 1e-3, bypass: false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn roundcolor(color: Color) -> Color {
|
|
||||||
Color::new((color.red * 100.0).round() / 100.0, (color.green * 100.0).round() / 100.0, (color.blue * 100.0).round() / 100.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_intersect() {
|
|
||||||
let triangle = TriangleMesh::singleton_solid(Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 1.0, 0.0), Point3::new(0.0, 0.0, 1.0), Color::black());
|
|
||||||
|
|
||||||
let ray = Ray::new(Point3::new(0.5, 5.0, 0.3), Vector3::new(0.0, -1.0, 0.0));
|
|
||||||
|
|
||||||
let (t, u, v) = triangle.tris[0].intersect_(&triangle.vertices, ray).unwrap();
|
|
||||||
|
|
||||||
println!("{},{},{}", t, u, v);
|
|
||||||
|
|
||||||
assert!(t >= 0.0 && t <= 1.0);
|
|
||||||
assert!(u >= 0.0 && u <= 1.0);
|
|
||||||
assert!(v >= 0.0 && v <= 1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_getcolor() {
|
|
||||||
let triangle = TriangleMesh::singleton(Point3::new(0.0, 0.0, 0.0), Point3::new(1.0, 1.0, 0.0), Point3::new(0.0, 0.0, 1.0), |t, u, v| Color::new(t, u, v));
|
|
||||||
|
|
||||||
let t = 0.4;
|
|
||||||
let u = 0.1;
|
|
||||||
let v = 1.0 - t - u;
|
|
||||||
|
|
||||||
let point = triangle.tris[0].from_bary(&triangle.vertices, t, u, v);
|
|
||||||
|
|
||||||
assert_eq!(roundcolor(triangle.getcolor(point)), roundcolor(Color::new(t, u, v)));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_bounds() {
|
|
||||||
let point1 = Point3::new(0.0, 0.0, 0.0);
|
|
||||||
let point2 = Point3::new(1.0, 0.0, 0.0);
|
|
||||||
let point3 = Point3::new(0.0, 1.0, 0.0);
|
|
||||||
|
|
||||||
let triangle = TriangleMesh::singleton_solid(point1, point2, point3, Color::black());
|
|
||||||
|
|
||||||
let bound = triangle.bound();
|
|
||||||
|
|
||||||
println!("{:?}", bound);
|
|
||||||
|
|
||||||
assert!(bound.contains(&point1));
|
|
||||||
assert!(bound.contains(&point2));
|
|
||||||
assert!(bound.contains(&point3));
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
#[test]
|
|
||||||
fn triangle_tobound() {
|
|
||||||
let point1 = Point3::new(-3.0, 4.0, -6.0);
|
|
||||||
let point2 = Point3::new(5.0, -2.0, -7.0);
|
|
||||||
let point3 = Point3::new(9.0, -7.0, 3.0);
|
|
||||||
|
|
||||||
let (center, radius) = triangle_sphere(&point1, &point2, &point3);
|
|
||||||
let bound = Bound { center: center, radius: radius + 0.01, bypass: false };
|
|
||||||
|
|
||||||
println!("{:?}", bound);
|
|
||||||
|
|
||||||
println!("{}\n{}\n{}", distance(&bound.center, &point1),
|
|
||||||
distance(&bound.center, &point2),
|
|
||||||
distance(&bound.center, &point3));
|
|
||||||
|
|
||||||
assert!(bound.contains(&point1));
|
|
||||||
assert!(bound.contains(&point2));
|
|
||||||
assert!(bound.contains(&point3));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn triangle_tetrabound() {
|
|
||||||
let point1 = Point3::new(8.0, -2.0, -5.0);
|
|
||||||
let point2 = Point3::new(-3.0, 4.0, -6.0);
|
|
||||||
let point3 = Point3::new(-3.0, -9.0, 3.0);
|
|
||||||
let point4 = Point3::new(-6.0, 5.0, -9.0);
|
|
||||||
|
|
||||||
let (center, radius) = tetrahedron_sphere(&point1, &point2, &point3, &point4);
|
|
||||||
|
|
||||||
let bound = Bound { center: center, radius: radius + 0.01, bypass: false };
|
|
||||||
|
|
||||||
println!("{:?}", bound);
|
|
||||||
|
|
||||||
println!("{}\n{}\n{}\n{}", distance(&bound.center, &point1),
|
|
||||||
distance(&bound.center, &point2),
|
|
||||||
distance(&bound.center, &point3),
|
|
||||||
distance(&bound.center, &point4));
|
|
||||||
|
|
||||||
assert!(bound.contains(&point1));
|
|
||||||
assert!(bound.contains(&point2));
|
|
||||||
assert!(bound.contains(&point3));
|
|
||||||
assert!(bound.contains(&point4));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,25 +1,39 @@
|
||||||
extern crate nalgebra as na;
|
extern crate nalgebra as na;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
use na::*;
|
use na::*;
|
||||||
use na::geometry::Point3;
|
use na::geometry::Point3;
|
||||||
|
|
||||||
use crate::types::*;
|
|
||||||
use crate::object::*;
|
use crate::object::*;
|
||||||
|
use crate::types::*;
|
||||||
|
|
||||||
fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
||||||
objects.iter()
|
objects.iter()
|
||||||
.filter_map(|obj| obj.intersect(ray)
|
.filter_map(|obj| obj.intersect(ray)
|
||||||
.map(|x| (obj, x)))
|
.map(|x| (obj, x)))
|
||||||
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
.min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Equal))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn light_point(objects: &Vec<Object>, obj: &Object, point: Point3f, light: &dyn Light) -> Color {
|
||||||
|
if light.check_shadow(point, objects) {
|
||||||
|
let texture = obj.get_texture(point);
|
||||||
|
|
||||||
|
light.get_color(point) * (texture.albedo / PI) * light.intensity(point) * obj.normal(point).dot(&*light.direction(point))
|
||||||
|
} else {
|
||||||
|
// Point is in shadow
|
||||||
|
Color::black()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cast_ray(ray: Ray, scene: &Scene) -> Color {
|
pub fn cast_ray(ray: Ray, scene: &Scene) -> Color {
|
||||||
if let Some((obj, dist)) = trace(ray, &scene.objects) {
|
if let Some((obj, dist)) = trace(ray, &scene.objects) {
|
||||||
let point = ray.project(dist);
|
let point = ray.project(dist);
|
||||||
|
let surface_color = obj.get_texture(point).color;
|
||||||
|
|
||||||
obj.getcolor(point)
|
scene.lights.iter()
|
||||||
}
|
.map(|light| light_point(&scene.objects, obj, point, &**light))
|
||||||
else { scene.background }
|
.fold(Color::black(), |acc, c| acc + c) * surface_color
|
||||||
|
} else { scene.background }
|
||||||
}
|
}
|
||||||
|
|
37
src/types.rs
37
src/types.rs
|
@ -5,23 +5,24 @@ use std::ops::{Add, Mul};
|
||||||
use na::*;
|
use na::*;
|
||||||
use na::geometry::Point3;
|
use na::geometry::Point3;
|
||||||
|
|
||||||
|
pub type Point3f = Point3<f32>;
|
||||||
|
pub type Vector3f = Vector3<f32>;
|
||||||
|
pub type Unit3f = Unit<Vector3<f32>>;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub struct Ray {
|
pub struct Ray {
|
||||||
pub origin: Point3<f32>,
|
pub origin: Point3f,
|
||||||
pub direction: Unit<Vector3<f32>>
|
pub direction: Unit3f
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ray {
|
impl Ray {
|
||||||
pub fn from_parts(origin: Point3<f32>, direction: Unit<Vector3<f32>>) -> Self {
|
pub fn from_parts(origin: Point3f, direction: Unit3f) -> Self {
|
||||||
Ray {
|
Ray { origin, direction }
|
||||||
origin: origin,
|
|
||||||
direction: direction
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub fn new(origin: Point3<f32>, direction: Vector3<f32>) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
|
pub fn new(origin: Point3f, direction: Vector3f) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
|
||||||
pub fn from_points(origin: Point3<f32>, points_to: Point3<f32>) -> Self { Ray::new(origin, points_to - origin) }
|
pub fn from_points(origin: Point3f, points_to: Point3f) -> Self { Ray::new(origin, points_to - origin) }
|
||||||
|
|
||||||
pub fn project(&self, t: f32) -> Point3<f32> { self.origin + t * self.direction.into_inner() }
|
pub fn project(&self, t: f32) -> Point3f { self.origin + t * self.direction.into_inner() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
@ -93,3 +94,19 @@ impl Mul<f32> for Color {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct Texture {
|
||||||
|
pub color: Color,
|
||||||
|
pub albedo: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
impl Texture {
|
||||||
|
pub fn new(red: f32, green: f32, blue: f32, albedo: f32) -> Self {
|
||||||
|
Texture {
|
||||||
|
color: Color::new(red, green, blue),
|
||||||
|
albedo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue