Compare commits

...

10 commits

Author SHA1 Message Date
bijan-S e2dd9f7e3a Fixed bounding sphere generation 2021-07-28 22:10:01 -04:00
bijan-S cd94c7ad6f Fixed bug in plane intersection test 2021-05-05 21:48:22 -04:00
bijan2005 6d2a1407bf
Update LICENSE 2021-03-25 21:11:46 -04:00
bijan2005 736c1e416c
Add MIT License 2021-03-25 21:05:13 -04:00
bijan2005 68f158f3c9 Fix bugs 2021-03-20 18:26:23 -04:00
bijan2005 72acde6ea8 Remove Cargo.lock from .gitignore 2021-03-20 17:51:25 -04:00
bijan2005 0d3500ceb5 Refactor some method code 2021-03-20 17:48:03 -04:00
bijan2005 6ef2c65009 Finished diffuse shading calculations 2021-01-24 10:23:59 -05:00
bijan2005 20c32fc467 Add Texture struct to add extra texture information 2021-01-24 00:06:23 -05:00
bijan2005 07445dd4be Created abbreviations for common geometry types 2021-01-23 23:27:54 -05:00
14 changed files with 636 additions and 310 deletions

4
.gitignore vendored
View file

@ -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
View 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
View 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.

View file

@ -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

View file

@ -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(())
} }

View file

@ -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());
}
}

View file

@ -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 } }
} }

View file

@ -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
View 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]));
}
}

View file

@ -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 }
}
}

View file

@ -9,10 +9,10 @@ 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.
} }
@ -20,18 +20,17 @@ pub struct Sphere {
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 } }
} }

View file

@ -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,13 +198,15 @@ 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;
if (a != 0.0) {
let mut matrix_mut = matrix.clone(); 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()); 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, &center); let radius = distance(point1, &center);
(center, radius) (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));
}
*/
}

View file

@ -1,12 +1,13 @@
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()
@ -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)) .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 }
} }

View file

@ -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: Point3f, direction: Vector3f) -> Self { Ray::from_parts(origin, Unit::new_normalize(direction)) }
pub fn new(origin: Point3<f32>, direction: Vector3<f32>) -> 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 from_points(origin: Point3<f32>, points_to: Point3<f32>) -> 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
}
}
}