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/
|
||||
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
|
||||
**/*.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
|
||||
- [x] Point source struct
|
||||
- [x] Point source illuminance test
|
||||
- [ ] Hard shadows
|
||||
- [x] Hard shadows
|
||||
- [ ] Soft shadows
|
||||
- [ ] ~~Light-emitting surfaces~~
|
||||
- [ ] Indirect lighting
|
||||
|
|
21
src/main.rs
21
src/main.rs
|
@ -1,10 +1,10 @@
|
|||
extern crate nalgebra as na;
|
||||
|
||||
use std::time::Instant;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use na::*;
|
||||
use na::geometry::Point3;
|
||||
|
||||
mod camera; use camera::*;
|
||||
mod types; use types::*;
|
||||
|
@ -36,15 +36,24 @@ fn render(camera: &Camera, scene: &Scene, filename: &str) -> 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 {
|
||||
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(),
|
||||
background: Color::black()
|
||||
lights: vec![
|
||||
|
||||
],
|
||||
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 triangle; pub use triangle::*;
|
||||
mod bound; pub use bound::*;
|
||||
mod pointlight; pub use pointlight::*;
|
||||
|
||||
use na::*;
|
||||
mod point_light; pub use point_light::*;
|
||||
|
||||
use crate::types::*;
|
||||
|
||||
|
@ -19,11 +17,11 @@ pub trait Surface {
|
|||
|
||||
// Takes in a point (assumed to be on the object's surface)
|
||||
// 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)
|
||||
// and returns the color information on that point.
|
||||
fn getcolor(&self, point: Point3<f32>) -> Color;
|
||||
// and returns the texture information on that point.
|
||||
fn get_texture(&self, point: Point3f) -> Texture;
|
||||
|
||||
// Creates a bounding sphere around the object.
|
||||
fn bound(&self) -> Bound;
|
||||
|
@ -40,7 +38,7 @@ impl Object {
|
|||
let bound = surface.bound();
|
||||
Object {
|
||||
surface: Box::new(surface),
|
||||
bound: bound
|
||||
bound
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,14 +48,22 @@ impl Object {
|
|||
self.surface.intersect(ray)
|
||||
} else { None }
|
||||
}
|
||||
pub fn normal(&self, point: Point3<f32>) -> Unit<Vector3<f32>> { self.surface.normal(point) }
|
||||
pub fn getcolor(&self, point: Point3<f32>) -> Color { self.surface.getcolor(point) }
|
||||
pub fn normal(&self, point: Point3f) -> Unit3f { self.surface.normal(point) }
|
||||
pub fn get_texture(&self, point: Point3f) -> Texture { self.surface.get_texture(point) }
|
||||
}
|
||||
|
||||
pub trait Light {
|
||||
// Determine if the light is able to illuminate the point.
|
||||
// If so, return the color of the light.
|
||||
fn illuminate(&self, point: Point3<f32>, objects: &Vec<Object>) -> Option<Color>;
|
||||
fn check_shadow(&self, point: Point3f, objects: &Vec<Object>) -> bool;
|
||||
|
||||
// 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 {
|
||||
|
@ -65,17 +71,3 @@ pub struct Scene {
|
|||
pub lights: Vec<Box<dyn Light>>,
|
||||
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::geometry::Point3;
|
||||
|
||||
use crate::types::Ray;
|
||||
use crate::types::*;
|
||||
|
||||
// A bounding sphere, used for
|
||||
// intersection test optimization.
|
||||
#[derive(Debug)]
|
||||
pub struct Bound {
|
||||
pub center: Point3<f32>,
|
||||
pub center: Point3f,
|
||||
pub radius: f32,
|
||||
|
||||
// If true, then the bounding sphere is disabled.
|
||||
|
@ -24,7 +24,7 @@ impl Bound {
|
|||
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 } }
|
||||
}
|
||||
|
|
|
@ -7,24 +7,24 @@ use crate::types::*;
|
|||
use super::{Surface, bound::*};
|
||||
|
||||
pub struct Plane {
|
||||
pub center: Point3<f32>, // Plane origin (used for texture mapping).
|
||||
pub normal: Unit<Vector3<f32>>, // Precomputed plane normal.
|
||||
pub center: Point3f, // Plane origin (used for texture mapping).
|
||||
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).
|
||||
y_axis: Vector3<f32>, // Plane y-axis (The 3D direction that corresponds to the y-direction on the plane).
|
||||
x_axis: Vector3f, // Plane x-axis (The 3D direction that corresponds to the x-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.
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl Plane {
|
||||
// Creates a new plane.
|
||||
pub fn new<F: 'static>(center: Point3<f32>, x_axis: Vector3<f32>, y_axis: Vector3<f32>, texture: F) -> Self
|
||||
where F: Fn(f32, f32) -> Color
|
||||
pub fn new<F: 'static>(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self
|
||||
where F: Fn(f32, f32) -> Texture
|
||||
{
|
||||
Plane {
|
||||
center: center,
|
||||
center,
|
||||
normal: Unit::new_normalize(x_axis.cross(&y_axis)),
|
||||
x_axis: x_axis,
|
||||
y_axis: y_axis,
|
||||
|
@ -33,8 +33,8 @@ impl Plane {
|
|||
}
|
||||
|
||||
// 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
|
||||
where F: Fn(f32, f32) -> Color
|
||||
pub fn new_flip<F: 'static>(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: F) -> Self
|
||||
where F: Fn(f32, f32) -> Texture
|
||||
{
|
||||
Plane {
|
||||
center: center,
|
||||
|
@ -46,20 +46,20 @@ impl Plane {
|
|||
}
|
||||
|
||||
// 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
|
||||
{ Plane::new(center, x_axis, y_axis, move |_, _| color) }
|
||||
pub fn new_solid(center: Point3f, x_axis: Vector3f, y_axis: Vector3f, texture: Texture) -> Self
|
||||
{ Plane::new(center, x_axis, y_axis, move |_, _| texture) }
|
||||
|
||||
// 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
|
||||
{ Plane::new_flip(center, x_axis, y_axis, move |_, _| color) }
|
||||
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 |_, _| texture) }
|
||||
|
||||
|
||||
// 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) }
|
||||
|
||||
// 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) }
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ impl Surface for Plane {
|
|||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||
|
||||
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;
|
||||
|
||||
|
@ -75,9 +75,9 @@ impl Surface for Plane {
|
|||
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 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.
|
||||
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,10 +9,10 @@ use crate::types::*;
|
|||
use super::{Surface, bound::*};
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
|
@ -20,18 +20,17 @@ pub struct Sphere {
|
|||
impl Sphere {
|
||||
// Creates a new sphere.
|
||||
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 {
|
||||
center: Point3::new(x, y, z),
|
||||
radius: radius,
|
||||
center: Point3::new(x, y, z), radius,
|
||||
texture: Box::new(texture)
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a new sphere of a solid color.
|
||||
pub fn new_solid(x: f32, y: f32, z: f32, radius: f32, color: Color) -> Self
|
||||
{ Sphere::new(x, y, z, radius, move |_, _| color) }
|
||||
pub fn new_solid(x: f32, y: f32, z: f32, radius: f32, texture: Texture) -> Self
|
||||
{ Sphere::new(x, y, z, radius, move |_, _| texture) }
|
||||
}
|
||||
|
||||
impl Surface for Sphere {
|
||||
|
@ -62,14 +61,14 @@ impl Surface for Sphere {
|
|||
else { None }
|
||||
}
|
||||
|
||||
fn normal(&self, point: Point3<f32>) -> Unit<Vector3<f32>> {
|
||||
fn normal(&self, point: Point3f) -> Unit3f {
|
||||
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);
|
||||
|
||||
// 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
|
||||
// the spherical coordinates of the point.
|
||||
let x = 0.5 + normal.z.atan2(normal.x) / (2.0 * PI);
|
||||
|
@ -78,5 +77,5 @@ impl Surface for Sphere {
|
|||
(*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 v3: usize,
|
||||
|
||||
normal: Unit<Vector3<f32>>, // Precalculated normal vector.
|
||||
normal: Unit3f, // Precalculated normal vector.
|
||||
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.
|
||||
}
|
||||
|
||||
pub struct TriangleMesh {
|
||||
pub vertices: Vec<Point3<f32>>,
|
||||
pub tris: Vec<Triangle>
|
||||
pub vertices: Vec<Point3f>,
|
||||
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();
|
||||
prlg_area / 2.0
|
||||
}
|
||||
|
||||
impl Triangle {
|
||||
fn vertex1<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v1] }
|
||||
fn vertex2<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v2] }
|
||||
fn vertex3<'a>(&self, vertices: &'a Vec<Point3<f32>>) -> &'a Point3<f32> { &vertices[self.v3] }
|
||||
fn vertex1<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v1] }
|
||||
fn vertex2<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v2] }
|
||||
fn vertex3<'a>(&self, vertices: &'a Vec<Point3f>) -> &'a Point3f { &vertices[self.v3] }
|
||||
|
||||
// Conversion of barycentric coordinates to
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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 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;
|
||||
|
@ -50,7 +50,7 @@ impl Triangle {
|
|||
(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 vect3_1 = self.vertex3(vertices) - self.vertex1(vertices);
|
||||
|
||||
|
@ -74,11 +74,11 @@ impl Triangle {
|
|||
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)))
|
||||
}
|
||||
|
||||
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);
|
||||
(*self.texture)(t, u, v)
|
||||
}
|
||||
|
@ -86,48 +86,43 @@ impl Triangle {
|
|||
|
||||
#[allow(dead_code)]
|
||||
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()
|
||||
.map(|(v1, v2, v3, f)| Triangle {
|
||||
v1: v1,
|
||||
v2: v2,
|
||||
v3: v3,
|
||||
v1, v2, v3,
|
||||
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||
texture: f
|
||||
}).collect();
|
||||
TriangleMesh {
|
||||
vertices: vertices,
|
||||
tris: triangles
|
||||
vertices, 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()
|
||||
.map(|(v1, v2, v3)| Triangle {
|
||||
v1: v1,
|
||||
v2: v2,
|
||||
v3: v3,
|
||||
v1, v2, v3,
|
||||
normal: Unit::new_normalize((&vertices[v2] - &vertices[v1]).cross(&(&vertices[v3] - &vertices[v1]))),
|
||||
area: tri_area(&vertices[v1], &vertices[v2], &vertices[v3]),
|
||||
texture: Box::new(move |_, _, _| color)
|
||||
texture: Box::new(move |_, _, _| texture)
|
||||
}).collect();
|
||||
TriangleMesh {
|
||||
vertices: vertices,
|
||||
tris: triangles
|
||||
vertices,
|
||||
triangles
|
||||
}
|
||||
}
|
||||
|
||||
pub fn singleton<F: 'static>(vertex1: Point3<f32>, vertex2: Point3<f32>, vertex3: Point3<f32>, texture: F) -> Self
|
||||
where F: Fn(f32, f32, f32) -> Color
|
||||
pub fn singleton<F: 'static>(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: F) -> Self
|
||||
where F: Fn(f32, f32, f32) -> 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
|
||||
{ TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| color) }
|
||||
pub fn singleton_solid(vertex1: Point3f, vertex2: Point3f, vertex3: Point3f, texture: Texture) -> Self
|
||||
{ TriangleMesh::singleton(vertex1, vertex2, vertex3, move |_, _, _| texture) }
|
||||
|
||||
|
||||
fn closest_tri(&self, point: Point3<f32>) -> &Triangle {
|
||||
self.tris.iter()
|
||||
fn closest_tri(&self, point: Point3f) -> &Triangle {
|
||||
self.triangles.iter()
|
||||
.map(move |tri| {
|
||||
|
||||
let rel_pos = point - tri.vertex1(&self.vertices);
|
||||
|
@ -150,22 +145,46 @@ impl TriangleMesh {
|
|||
|
||||
impl Surface for TriangleMesh {
|
||||
fn intersect(&self, ray: Ray) -> Option<f32> {
|
||||
self.tris.iter()
|
||||
self.triangles.iter()
|
||||
.filter_map(|tri| tri.intersect(&self.vertices, ray))
|
||||
.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
|
||||
}
|
||||
|
||||
fn getcolor(&self, point: Point3<f32>) -> Color {
|
||||
self.closest_tri(point).getcolor(&self.vertices, point)
|
||||
fn get_texture(&self, point: Point3f) -> Texture {
|
||||
self.closest_tri(point).get_texture(&self.vertices, point)
|
||||
}
|
||||
|
||||
// Uses Welzl's algorithm to solve the bounding sphere problem
|
||||
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 b = point2 - point1;
|
||||
|
||||
|
@ -179,13 +198,15 @@ impl Surface for TriangleMesh {
|
|||
(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(),
|
||||
point2.to_homogeneous().transpose(),
|
||||
point3.to_homogeneous().transpose(),
|
||||
point4.to_homogeneous().transpose()]);
|
||||
|
||||
let a = matrix.determinant() * 2.0;
|
||||
|
||||
if (a != 0.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());
|
||||
|
@ -202,9 +223,15 @@ impl Surface for TriangleMesh {
|
|||
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 {
|
||||
match boundary.len() {
|
||||
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::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());
|
||||
|
||||
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,12 +1,13 @@
|
|||
extern crate nalgebra as na;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::f32::consts::PI;
|
||||
|
||||
use na::*;
|
||||
use na::geometry::Point3;
|
||||
|
||||
use crate::types::*;
|
||||
use crate::object::*;
|
||||
use crate::types::*;
|
||||
|
||||
fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
||||
objects.iter()
|
||||
|
@ -15,11 +16,24 @@ fn trace(ray: Ray, objects: &Vec<Object>) -> Option<(&Object, f32)> {
|
|||
.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 {
|
||||
if let Some((obj, dist)) = trace(ray, &scene.objects) {
|
||||
let point = ray.project(dist);
|
||||
let surface_color = obj.get_texture(point).color;
|
||||
|
||||
obj.getcolor(point)
|
||||
}
|
||||
else { scene.background }
|
||||
scene.lights.iter()
|
||||
.map(|light| light_point(&scene.objects, obj, point, &**light))
|
||||
.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::geometry::Point3;
|
||||
|
||||
pub type Point3f = Point3<f32>;
|
||||
pub type Vector3f = Vector3<f32>;
|
||||
pub type Unit3f = Unit<Vector3<f32>>;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Ray {
|
||||
pub origin: Point3<f32>,
|
||||
pub direction: Unit<Vector3<f32>>
|
||||
pub origin: Point3f,
|
||||
pub direction: Unit3f
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
pub fn from_parts(origin: Point3<f32>, direction: Unit<Vector3<f32>>) -> Self {
|
||||
Ray {
|
||||
origin: origin,
|
||||
direction: direction
|
||||
pub fn from_parts(origin: Point3f, direction: Unit3f) -> Self {
|
||||
Ray { origin, direction }
|
||||
}
|
||||
}
|
||||
pub fn new(origin: Point3<f32>, direction: Vector3<f32>) -> 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 new(origin: Point3f, direction: Vector3f) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
|
||||
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)]
|
||||
|
@ -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