initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
/target
|
||||
/.idea
|
||||
/rmarcher.iml
|
||||
/a.*
|
||||
Generated
+455
@@ -0,0 +1,455 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[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 = "bstr"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bed57e2090563b83ba8f83366628ce535a7584c9afa4c9fc0612a03925c6df58"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.67"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd01a6eb3daaafa260f6fc94c3a6c36390abc2080e38e3e34ced87393fb77d80"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-queue",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94af6efb46fef72616855b036a624cf27ba656ffc9be1b9a3c931cfc7749a9a9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2584f639eb95fea8c798496315b297cf81b9b58b6d30ab066a75455333cf4b12"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"memoffset",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f6cb3c7f5b8e51bc3ebb73a2327ad4abdbd119dc13223f14f961d2f38486756"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a668f699973d0f573d15749b7002a9ac9e1f9c6b220e7b165601334c173d8de"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.23.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
"scoped_threadpool",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.1.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.93"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83fb6581e8ed1f85fd45c116db8405483899489e38406156c25eb743554361d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.16.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide 0.3.7",
|
||||
]
|
||||
|
||||
[[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.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
"rand_hc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
|
||||
dependencies = [
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b0d8e0819fadc20c74ea8373106ead0600e3a67ef1fe8da56e39b9ae7275674"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ab346ac5921dc62ffa9f89b7a773907511cdfa5490c572ae9be1be33e8afa4a"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rlua"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25fa5b2c667bae0b6218361e96d365e414fe4a0fa80f476b9631aa2dea2c6881"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bstr",
|
||||
"cc",
|
||||
"libc",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmarcher"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossbeam",
|
||||
"crossbeam-channel",
|
||||
"image",
|
||||
"rand",
|
||||
"rlua",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
|
||||
dependencies = [
|
||||
"jpeg-decoder",
|
||||
"miniz_oxide 0.4.4",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c"
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "rmarcher"
|
||||
version = "0.1.0"
|
||||
authors = ["codinget"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.3"
|
||||
crossbeam-channel = "0.5.0"
|
||||
crossbeam = "0.8.0"
|
||||
image = "0.23.14"
|
||||
rlua = "0.17.0"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 3
|
||||
@@ -0,0 +1,43 @@
|
||||
use crate::structs::{Vec3, Y, X, Z};
|
||||
|
||||
pub const COLOR_CHANNELS: usize = 4;
|
||||
|
||||
pub const EPSILON: f64 = 1. / 1024.;
|
||||
pub const LIGHT_EPSILON: f64 = 1. / 512.;
|
||||
pub const MAX_DIST: f64 = 16.;
|
||||
pub const MAX_STEPS: u32 = 1024;
|
||||
|
||||
pub const DIST_FIX_CORRECTION: f64 = 0.5;
|
||||
pub const DIST_CORRECTION: f64 = 3.;
|
||||
pub const DIST_POWER: f64 = 2.;
|
||||
|
||||
pub const ANGLE_FIX_CORRECTION: f64 = 0.;
|
||||
pub const ANGLE_CORRECTION: f64 = 2.;
|
||||
pub const ANGLE_POWER: f64 = 2.;
|
||||
|
||||
//pub const IMG_WIDTH: usize = 480;
|
||||
//pub const IMG_WIDTH: usize = 1280;
|
||||
pub const IMG_WIDTH: usize = 1080;
|
||||
//pub const IMG_HEIGHT: usize = 480;
|
||||
//pub const IMG_HEIGHT: usize = 720;
|
||||
pub const IMG_HEIGHT: usize = 1080;
|
||||
pub const IMG_DIM: usize = if IMG_HEIGHT > IMG_WIDTH { IMG_HEIGHT } else { IMG_WIDTH };
|
||||
pub const IMG_SIZE: usize = IMG_WIDTH * IMG_HEIGHT;
|
||||
pub const IMG_BYTE_SIZE: usize = IMG_SIZE * 3;
|
||||
|
||||
//pub const SUPERSAMPLING: usize = 1;
|
||||
pub const SUPERSAMPLING: usize = 2;
|
||||
//pub const RAYS_PER_PIXEL: usize = 1;
|
||||
//pub const RAYS_PER_PIXEL: usize = 50;
|
||||
pub const RAYS_PER_PIXEL: usize = 500;
|
||||
//pub const MAX_BOUNCES: u32 = 1;
|
||||
//pub const MAX_BOUNCES: u32 = 4;
|
||||
//pub const MAX_BOUNCES: u32 = 8;
|
||||
pub const MAX_BOUNCES: u32 = 10;
|
||||
|
||||
pub const THREAD_COUNT: usize = 12;
|
||||
pub const SLICES_PER_THREAD: usize = 4;
|
||||
|
||||
pub const UP: Vec3 = Y;
|
||||
pub const RIGHT: Vec3 = X;
|
||||
pub const FORWARD: Vec3 = Z;
|
||||
@@ -0,0 +1,16 @@
|
||||
use crate::structs::{ColorVec, Vec3};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Light {
|
||||
pos: Vec3,
|
||||
color: ColorVec,
|
||||
}
|
||||
|
||||
impl Light {
|
||||
pub fn new(pos: Vec3, color: ColorVec) -> Light {
|
||||
Light { pos, color }
|
||||
}
|
||||
|
||||
pub fn pos(&self) -> Vec3 { self.pos }
|
||||
pub fn color(&self) -> ColorVec { self.color }
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
mod material;
|
||||
mod light;
|
||||
mod object;
|
||||
mod structs;
|
||||
mod consts;
|
||||
|
||||
use object::*;
|
||||
use crate::consts::*;
|
||||
use crate::structs::*;
|
||||
use crate::material::*;
|
||||
use image::{ColorType, ImageFormat};
|
||||
use image::codecs::png::FilterType::Sub;
|
||||
|
||||
fn default_cam() -> Cam {
|
||||
Cam::new_pointing(Y*3. - X*5., O, 0.5)
|
||||
}
|
||||
|
||||
fn default_scene() -> Scene {
|
||||
let s0 = WithMaterial::new(
|
||||
Sphere::new_xyz(0., 0., 0., 1.),
|
||||
RED
|
||||
);
|
||||
let s1 = WithMaterial::new(
|
||||
Sphere::new_xyz(0., 1., 1., 0.5),
|
||||
MIRROR
|
||||
);
|
||||
let spheres = Union::new(s0, s1);
|
||||
let floor = WithMaterial::new(
|
||||
Plane::new_xyz(0., 1., 0., 2.),
|
||||
WHITE
|
||||
);
|
||||
let backwall = WithMaterial::new(
|
||||
Plane::new_xyz(-1., 0., 0., 2.),
|
||||
BLUE
|
||||
);
|
||||
let sidewalls = WithMaterial::new(
|
||||
Union::new(
|
||||
Plane::new_xyz(0., 0., -1., 2.5),
|
||||
Plane::new_xyz(0., 0., 1., 2.5)
|
||||
),
|
||||
GREEN
|
||||
);
|
||||
let walls = Union::new(
|
||||
Union::new(floor, backwall),
|
||||
sidewalls
|
||||
);
|
||||
let scene = Union::new(spheres, walls);
|
||||
let light = WithMaterial::new(
|
||||
Plane::new_xyz(0., -1., 0., 8.),
|
||||
LIGHTSOURCE
|
||||
);
|
||||
let scene = Union::new(scene, light);
|
||||
Scene::new(scene)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// get scene and camera
|
||||
let scene = default_scene();
|
||||
let cam = default_cam();
|
||||
|
||||
// get stats on the scene we're about to render
|
||||
let total_rpp = (RAYS_PER_PIXEL * SUPERSAMPLING * SUPERSAMPLING) as u64;
|
||||
let lights = scene.get_lights().len() as u32;
|
||||
let nodes = scene.node_count();
|
||||
let min_rays = IMG_SIZE as u64 * RAYS_PER_PIXEL as u64 * SUPERSAMPLING as u64;
|
||||
let max_rays = min_rays * MAX_BOUNCES as u64 * (1+lights) as u64;
|
||||
println!("Image size: {}x{} ({} pixels)", IMG_WIDTH, IMG_HEIGHT, IMG_SIZE);
|
||||
println!("Rpp: {}, {}x supersampling ({} total rays per image pixel)", RAYS_PER_PIXEL, SUPERSAMPLING, total_rpp);
|
||||
println!("Max bounces: {}, dist: {}, steps: {}", MAX_BOUNCES, MAX_DIST, MAX_STEPS);
|
||||
println!("Lights: {}, Nodes: {}", lights, nodes);
|
||||
println!("Threads: {}, {} slices per thread", THREAD_COUNT, SLICES_PER_THREAD);
|
||||
println!("Total rays: min {} max {}", min_rays, max_rays);
|
||||
|
||||
// generate image and save it
|
||||
let data = cam.render(&scene);
|
||||
image::save_buffer_with_format(
|
||||
"a.png",
|
||||
&data,
|
||||
IMG_WIDTH as u32,
|
||||
IMG_HEIGHT as u32,
|
||||
ColorType::Rgb8,
|
||||
ImageFormat::Png
|
||||
).unwrap();
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
use crate::structs::{ColorVec, ColorMat};
|
||||
use crate::consts::COLOR_CHANNELS;
|
||||
use crate::material::SurfaceType::{Diffuse, Reflective, Stop};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub enum SurfaceType {
|
||||
Diffuse,
|
||||
Reflective,
|
||||
Stop
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Material {
|
||||
emission: ColorVec,
|
||||
reflection: ColorMat,
|
||||
surface: SurfaceType
|
||||
}
|
||||
|
||||
impl Material {
|
||||
pub const fn new(emission: ColorVec, reflection: ColorMat, surface: SurfaceType) -> Material {
|
||||
Material { emission, reflection, surface }
|
||||
}
|
||||
|
||||
pub const fn new_from_diagonal(emission: ColorVec, reflection: ColorVec, surface: SurfaceType) -> Material {
|
||||
Material { emission, reflection: ColorMat::new_from_diagonal(reflection), surface }
|
||||
}
|
||||
|
||||
pub const fn emission(&self) -> ColorVec { self.emission }
|
||||
pub const fn reflection(&self) -> ColorMat { self.reflection }
|
||||
pub const fn surface(&self) -> SurfaceType { self.surface }
|
||||
}
|
||||
|
||||
const COLOR_ZERO: ColorVec = ColorVec::new_zero();
|
||||
const COLOR_ONE: ColorVec = ColorVec::new([1.; COLOR_CHANNELS]);
|
||||
|
||||
pub const WHITE: Material = Material::new_from_diagonal(
|
||||
COLOR_ZERO,
|
||||
COLOR_ONE,
|
||||
Diffuse
|
||||
);
|
||||
pub const RED: Material = Material::new_from_diagonal(
|
||||
COLOR_ZERO,
|
||||
ColorVec::new([0.75, 0.25, 0.25, 0.]),
|
||||
Diffuse
|
||||
);
|
||||
pub const GREEN: Material = Material::new_from_diagonal(
|
||||
COLOR_ZERO,
|
||||
ColorVec::new([0.25, 0.75, 0.25, 0.]),
|
||||
Diffuse
|
||||
);
|
||||
pub const BLUE: Material = Material::new_from_diagonal(
|
||||
COLOR_ZERO,
|
||||
ColorVec::new([0.25, 0.25, 0.75, 0.]),
|
||||
Diffuse
|
||||
);
|
||||
pub const LIGHTSOURCE: Material = Material::new_from_diagonal(
|
||||
ColorVec::new([1., 1., 1., 0.]),
|
||||
COLOR_ONE,
|
||||
Diffuse
|
||||
);
|
||||
|
||||
pub const MIRROR: Material = Material::new_from_diagonal(
|
||||
COLOR_ZERO,
|
||||
ColorVec::new([0.9, 0.9, 0.9, 0.9]),
|
||||
Reflective
|
||||
);
|
||||
|
||||
pub const STRONG_LIGHTSOURCE: Material = Material::new_from_diagonal(
|
||||
ColorVec::new([5., 5., 5., 0.]),
|
||||
COLOR_ZERO,
|
||||
Stop
|
||||
);
|
||||
|
||||
pub const UV_LIGHTSOURCE: Material = Material::new_from_diagonal(
|
||||
ColorVec::new_one(3),
|
||||
ColorVec::new([0.25, 0.25, 0.25, 1.]),
|
||||
Diffuse
|
||||
);
|
||||
pub const FLUORESCENT: Material = Material::new(
|
||||
ColorVec::new([0.25; COLOR_CHANNELS]),
|
||||
ColorMat::new([
|
||||
[1., 0., 0., 0.75],
|
||||
[0., 1., 0., 0.25],
|
||||
[0., 0., 1., 0.],
|
||||
[0., 0., 0., 0.]
|
||||
]),
|
||||
Diffuse
|
||||
);
|
||||
@@ -0,0 +1,65 @@
|
||||
use crate::structs::Vec3;
|
||||
use crate::object::Obj;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Cuboid {
|
||||
center: Vec3,
|
||||
radius: Vec3
|
||||
}
|
||||
|
||||
impl Cuboid {
|
||||
pub fn new(center: Vec3, radius: Vec3) -> Cuboid {
|
||||
Cuboid { center, radius }
|
||||
}
|
||||
pub fn new_xyz(cx: f64, cy: f64, cz: f64, rx: f64, ry: f64, rz: f64) -> Cuboid {
|
||||
let center = Vec3::new(cx, cy, cz);
|
||||
let radius = Vec3::new(rx, ry, rz);
|
||||
Cuboid { center, radius }
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Cuboid {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
let dx = point.x() - self.center.x();
|
||||
let dy = point.y() - self.center.y();
|
||||
let dz = point.z() - self.center.z();
|
||||
|
||||
if dx.abs()<self.radius.x() && dy.abs()<self.radius.y() && dz.abs()<self.radius.z() {
|
||||
-f64::min(
|
||||
f64::min(
|
||||
f64::min(
|
||||
f64::abs(dx-self.radius.x()),
|
||||
f64::abs(dx+self.radius.x())
|
||||
),
|
||||
f64::abs(dy-self.radius.y())
|
||||
),
|
||||
f64::min(
|
||||
f64::min(
|
||||
f64::abs(dy+self.radius.y()),
|
||||
f64::abs(dz-self.radius.z())
|
||||
),
|
||||
f64::abs(dz+self.radius.z())
|
||||
)
|
||||
)
|
||||
} else {
|
||||
let dx0 = f64::max(0., dx.abs()-self.radius.x());
|
||||
let dy0 = f64::max(0., dy.abs()-self.radius.y());
|
||||
let dz0 = f64::max(0., dz.abs()-self.radius.z());
|
||||
|
||||
f64::sqrt(dx0*dx0 + dy0*dy0 + dz0*dz0)
|
||||
}
|
||||
}
|
||||
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
let dx = (point.x()-self.center.y())/self.radius.x();
|
||||
let dy = (point.y()-self.center.y())/self.radius.y();
|
||||
let dz = (point.z()-self.center.z())/self.radius.z();
|
||||
let (adx, ady, adz) = (dx.abs(), dy.abs(), dz.abs());
|
||||
|
||||
Vec3::new(
|
||||
if adx>=ady && adx>adz { if dx>0. { 1. } else { -1. }} else { 0. },
|
||||
if ady>=adz && ady>adx { if dy>0. { 1. } else { -1. }} else { 0. },
|
||||
if adz>=adx && adz>ady { if dz>0. { 1. } else { -1. }} else { 0. },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use crate::structs::Vec3;
|
||||
use crate::object::Obj;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Cylinder {
|
||||
center: Vec3,
|
||||
radius: f64
|
||||
}
|
||||
|
||||
impl Cylinder {
|
||||
pub const fn new(center: Vec3, radius: f64) -> Cylinder {
|
||||
Cylinder { center, radius }
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Cylinder {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
Vec3::new(self.center.x(), 0., self.center.z()).distance_to(point) - self.radius
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
point - Vec3::new(self.center.x(), 0.,self.center.z())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use crate::object::Obj;
|
||||
use crate::light::Light;
|
||||
use crate::structs::Vec3;
|
||||
use std::vec;
|
||||
use crate::material::Material;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Intersection<A: Obj, B: Obj> {
|
||||
a: A,
|
||||
b: B
|
||||
}
|
||||
|
||||
impl<A: Obj, B: Obj> Intersection<A, B> {
|
||||
pub fn new(a: A, b: B) -> Intersection<A, B> {
|
||||
Intersection { a, b }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Obj, B: Obj> Obj for Intersection<A, B> {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
f64::max(self.a.distance_to(point), self.b.distance_to(point))
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
if self.a.distance_to(point) > self.b.distance_to(point) {
|
||||
self.a.normal_at(point)
|
||||
} else {
|
||||
self.b.normal_at(point)
|
||||
}
|
||||
}
|
||||
fn material_at(&self, point: Vec3) -> Material {
|
||||
if self.a.distance_to(point) > self.b.distance_to(point) {
|
||||
self.a.material_at(point)
|
||||
} else {
|
||||
self.b.material_at(point)
|
||||
}
|
||||
}
|
||||
fn get_lights(&self) -> vec::Vec<Light> {
|
||||
let mut l = self.a.get_lights();
|
||||
l.extend(self.b.get_lights());
|
||||
l
|
||||
}
|
||||
fn node_count(&self) -> u32 {
|
||||
self.a.node_count() + self.b.node_count() + 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
use crate::consts::{EPSILON, MAX_DIST};
|
||||
use crate::structs::{Vec3, O, X, Y, Z};
|
||||
use crate::material::Material;
|
||||
use crate::material;
|
||||
use crate::light::Light;
|
||||
use std::vec::Vec;
|
||||
|
||||
mod sphere;
|
||||
mod plane;
|
||||
mod union;
|
||||
mod intersection;
|
||||
mod cuboid;
|
||||
mod cylinder;
|
||||
mod torus;
|
||||
mod waves;
|
||||
mod with_material;
|
||||
mod with_lights;
|
||||
mod transform;
|
||||
mod scene;
|
||||
|
||||
pub trait Obj: Send + Sync {
|
||||
fn distance_to(&self, _point: Vec3) -> f64 {
|
||||
MAX_DIST
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
let v = self.distance_to(point);
|
||||
let x = self.distance_to(point + X*EPSILON) - v;
|
||||
let y = self.distance_to(point + Y*EPSILON) - v;
|
||||
let z = self.distance_to(point + Z*EPSILON) - v;
|
||||
|
||||
Vec3::new(x, y, z).unit()
|
||||
}
|
||||
fn material_at(&self, _point: Vec3) -> Material {
|
||||
material::WHITE
|
||||
}
|
||||
fn get_lights(&self) -> Vec<Light> {
|
||||
Vec::new()
|
||||
}
|
||||
fn node_count(&self) -> u32 { 1 }
|
||||
}
|
||||
|
||||
impl<T: Obj> Obj for &T {
|
||||
fn distance_to(&self, point: Vec3) -> f64 { (*self).distance_to(point) }
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 { (*self).normal_at(point) }
|
||||
fn material_at(&self, point: Vec3) -> Material { (*self).material_at(point) }
|
||||
fn get_lights(&self) -> Vec<Light> { (*self).get_lights() }
|
||||
fn node_count(&self) -> u32 { (*self).node_count() }
|
||||
}
|
||||
|
||||
pub trait ObjClone: Obj {
|
||||
fn clone_obj(&self) -> Box<dyn ObjClone>;
|
||||
}
|
||||
|
||||
impl<T: 'static + Obj + Clone> ObjClone for T {
|
||||
fn clone_obj(&self) -> Box<dyn ObjClone> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
pub use sphere::Sphere;
|
||||
pub use plane::Plane;
|
||||
pub use union::Union;
|
||||
pub use intersection::Intersection;
|
||||
pub use cuboid::Cuboid;
|
||||
pub use cylinder::Cylinder;
|
||||
pub use torus::Torus;
|
||||
pub use waves::Waves;
|
||||
pub use with_material::{WithMaterial, WithDynamicMaterial};
|
||||
pub use with_lights::{WithLights, WithLight};
|
||||
pub use transform::AffineTransform;
|
||||
pub use scene::Scene;
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::structs::Vec3;
|
||||
use crate::object::Obj;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Plane {
|
||||
normal: Vec3,
|
||||
offset: f64
|
||||
}
|
||||
|
||||
impl Plane {
|
||||
pub fn new(normal: Vec3, offset: f64) -> Plane {
|
||||
let l = normal.magnitude();
|
||||
Plane { normal: normal/l, offset: offset/l }
|
||||
}
|
||||
pub fn new_xyz(x: f64, y: f64, z: f64, offset: f64) -> Plane {
|
||||
let normal = Vec3::new(x, y, z);
|
||||
Plane::new(normal, offset)
|
||||
}
|
||||
pub const unsafe fn new_raw(normal: Vec3, offset: f64) -> Plane {
|
||||
Plane { normal, offset }
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Plane {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
point*self.normal + self.offset
|
||||
}
|
||||
fn normal_at(&self, _point: Vec3) -> Vec3 {
|
||||
self.normal
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
use crate::object::{Obj, ObjClone};
|
||||
use crate::structs::Vec3;
|
||||
use crate::material::Material;
|
||||
use crate::light::Light;
|
||||
|
||||
pub struct Scene {
|
||||
scene: Box<dyn ObjClone>
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new<T: 'static + ObjClone>(scene: T) -> Scene {
|
||||
Scene { scene: Box::new(scene) }
|
||||
}
|
||||
pub fn new_from_box(scene: Box<dyn ObjClone>) -> Scene {
|
||||
Scene { scene }
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Scene {
|
||||
fn distance_to(&self, point: Vec3) -> f64 { self.scene.distance_to(point) }
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 { self.scene.normal_at(point) }
|
||||
fn material_at(&self, point: Vec3) -> Material { self.scene.material_at(point) }
|
||||
fn get_lights(&self) -> Vec<Light> { self.scene.get_lights() }
|
||||
fn node_count(&self) -> u32 { self.scene.node_count() + 1 }
|
||||
}
|
||||
|
||||
impl Clone for Scene {
|
||||
fn clone(&self) -> Scene {
|
||||
Scene { scene: self.scene.clone_obj() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::structs::Vec3;
|
||||
use crate::object::Obj;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Sphere {
|
||||
pos: Vec3,
|
||||
radius: f64
|
||||
}
|
||||
|
||||
impl Sphere {
|
||||
pub const fn new(pos: Vec3, radius: f64) -> Sphere {
|
||||
Sphere { pos, radius }
|
||||
}
|
||||
pub const fn new_xyz(x: f64, y: f64, z: f64, radius: f64) -> Sphere {
|
||||
let pos = Vec3::new(x, y, z);
|
||||
Sphere { pos, radius }
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Sphere {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
self.pos.distance_to(point) - self.radius
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
(point - self.pos).unit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
use crate::structs::Vec3;
|
||||
use crate::object::Obj;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Torus {
|
||||
center: Vec3,
|
||||
radius: f64,
|
||||
thickness: f64
|
||||
}
|
||||
|
||||
impl Torus {
|
||||
pub const fn new(center: Vec3, radius: f64, thickness: f64) -> Torus {
|
||||
Torus { center, radius, thickness }
|
||||
}
|
||||
pub const fn new_xyz(x: f64, y: f64, z: f64, radius: f64, thickness: f64) -> Torus {
|
||||
Torus::new(Vec3::new(x, y, z), radius, thickness)
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Torus {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
let (dx, dy) = (point.x()-self.center.x(), point.y()-self.center.y());
|
||||
let dh = f64::abs(f64::sqrt(dx*dx + dy*dy) - self.radius);
|
||||
let dz = point.z() - self.center.z();
|
||||
f64::sqrt(dh*dh + dz*dz) - self.thickness
|
||||
}
|
||||
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
let centered = point - self.center;
|
||||
let centered_no_z= Vec3::new(centered.x(), centered.y(), 0.);
|
||||
let closest = centered_no_z.unit() * self.radius;
|
||||
(centered - closest).unit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
use crate::object::Obj;
|
||||
use crate::structs::{Mat3, Vec3, O, I3};
|
||||
use crate::material::Material;
|
||||
use crate::light::Light;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct AffineTransform<T: Obj + Clone> {
|
||||
obj: T,
|
||||
transform: Mat3,
|
||||
transform_inv: Mat3,
|
||||
translate: Vec3
|
||||
}
|
||||
|
||||
impl<T: Obj + Clone> AffineTransform<T> {
|
||||
pub fn new(obj: T, transform: Mat3, translate: Vec3) -> AffineTransform<T> {
|
||||
AffineTransform { obj, transform, transform_inv: transform.invert(), translate }
|
||||
}
|
||||
pub fn new_linear(obj: T, transform: Mat3) -> AffineTransform<T> {
|
||||
AffineTransform { obj, transform, transform_inv: transform.invert(), translate: O }
|
||||
}
|
||||
pub fn new_translate(obj: T, translate: Vec3) -> AffineTransform<T> {
|
||||
AffineTransform { obj, transform: I3, transform_inv: I3, translate }
|
||||
}
|
||||
|
||||
fn apply_rev(&self, point: Vec3) -> Vec3 {
|
||||
self.transform_inv*point - self.translate
|
||||
}
|
||||
fn apply_fwd(&self, point: Vec3) -> Vec3 {
|
||||
self.transform*point + self.translate
|
||||
}
|
||||
}
|
||||
|
||||
pub const SWAP_XY: Mat3 = Mat3::new(
|
||||
0., 1., 0.,
|
||||
1., 0., 0.,
|
||||
0., 0., 1.,
|
||||
);
|
||||
pub const SWAP_XZ: Mat3 = Mat3::new(
|
||||
0., 0., 1.,
|
||||
0., 1., 0.,
|
||||
1., 0., 0.,
|
||||
);
|
||||
pub const SWAP_YZ: Mat3 = Mat3::new(
|
||||
1., 0., 0.,
|
||||
0., 0., 1.,
|
||||
0., 1., 0.,
|
||||
);
|
||||
|
||||
pub const fn scale_xyz(x: f64, y: f64, z: f64) -> Mat3 {
|
||||
Mat3::new(
|
||||
x , 0., 0.,
|
||||
0., y , 0.,
|
||||
0., 0., z ,
|
||||
)
|
||||
}
|
||||
pub const fn scale(k: f64) -> Mat3 { scale_xyz(k, k, k) }
|
||||
pub const fn scale_x(k: f64) -> Mat3 { scale_xyz(k, 1., 1.) }
|
||||
pub const fn scale_y(k: f64) -> Mat3 { scale_xyz(1., k, 1.) }
|
||||
pub const fn scale_z(k: f64) -> Mat3 { scale_xyz(1., 1., k) }
|
||||
|
||||
impl<T: Obj + Clone> Obj for AffineTransform<T> {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
self.obj.distance_to(self.apply_rev(point))
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
self.apply_fwd(self.obj.normal_at(self.apply_rev(point)))
|
||||
}
|
||||
fn material_at(&self, point: Vec3) -> Material {
|
||||
self.obj.material_at(self.apply_rev(point))
|
||||
}
|
||||
fn get_lights(&self) -> Vec<Light> {
|
||||
self.obj
|
||||
.get_lights()
|
||||
.into_iter()
|
||||
.map(|light| {
|
||||
Light::new(self.apply_fwd(light.pos()), light.color())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
fn node_count(&self) -> u32 {
|
||||
self.obj.node_count() + 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
use crate::object::Obj;
|
||||
use crate::light::Light;
|
||||
use crate::structs::Vec3;
|
||||
use std::vec;
|
||||
use crate::material::Material;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Union<A: Obj, B: Obj> {
|
||||
a: A,
|
||||
b: B
|
||||
}
|
||||
|
||||
impl<A: Obj, B: Obj> Union<A, B> {
|
||||
pub fn new(a: A, b: B) -> Union<A, B> {
|
||||
Union { a, b }
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Obj, B: Obj> Obj for Union<A, B> {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
f64::min(self.a.distance_to(point), self.b.distance_to(point))
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
if self.a.distance_to(point) < self.b.distance_to(point) {
|
||||
self.a.normal_at(point)
|
||||
} else {
|
||||
self.b.normal_at(point)
|
||||
}
|
||||
}
|
||||
fn material_at(&self, point: Vec3) -> Material {
|
||||
if self.a.distance_to(point) < self.b.distance_to(point) {
|
||||
self.a.material_at(point)
|
||||
} else {
|
||||
self.b.material_at(point)
|
||||
}
|
||||
}
|
||||
fn get_lights(&self) -> vec::Vec<Light> {
|
||||
let mut l = self.a.get_lights();
|
||||
l.extend(self.b.get_lights());
|
||||
l
|
||||
}
|
||||
fn node_count(&self) -> u32 {
|
||||
self.a.node_count() + self.b.node_count() + 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
use crate::object::Obj;
|
||||
use crate::structs::Vec3;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Waves {}
|
||||
|
||||
impl Waves {
|
||||
pub const fn new() -> Waves {
|
||||
Waves {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Obj for Waves {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
let dist = point.x().sin() + point.y().sin() + point.z();
|
||||
dist / f64::sqrt(3.)
|
||||
}
|
||||
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
Vec3::new(-point.x().cos(), -point.y().cos(), 1.).unit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
use crate::light::Light;
|
||||
use crate::object::Obj;
|
||||
use crate::structs::Vec3;
|
||||
use crate::material::Material;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct WithLights<T: Obj, const N: usize> {
|
||||
obj: T,
|
||||
lights: [Light; N]
|
||||
}
|
||||
|
||||
impl<T: Obj, const N: usize> WithLights<T, N> {
|
||||
pub fn new(obj: T, lights: [Light; N]) -> WithLights<T, N> {
|
||||
WithLights { obj, lights }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Obj, const N: usize> Obj for WithLights<T, N> {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
self.obj.distance_to(point)
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
self.obj.normal_at(point)
|
||||
}
|
||||
fn material_at(&self, point: Vec3) -> Material {
|
||||
self.obj.material_at(point)
|
||||
}
|
||||
fn get_lights(&self) -> Vec<Light> {
|
||||
let mut l = self.obj.get_lights();
|
||||
l.extend(&self.lights);
|
||||
l
|
||||
}
|
||||
fn node_count(&self) -> u32 {
|
||||
self.obj.node_count() + 1
|
||||
}
|
||||
}
|
||||
|
||||
pub type WithLight<T> = WithLights<T, 1>;
|
||||
|
||||
impl<T: Obj> WithLight<T> {
|
||||
pub fn new_one(obj: T, light: Light) -> WithLight<T> {
|
||||
WithLight { obj, lights: [light; 1] }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
use crate::object::Obj;
|
||||
use crate::material::Material;
|
||||
use crate::structs::Vec3;
|
||||
use crate::light::Light;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct WithMaterial<T: Obj> {
|
||||
obj: T,
|
||||
material: Material
|
||||
}
|
||||
|
||||
impl<T: Obj> WithMaterial<T> {
|
||||
pub fn new(obj: T, material: Material) -> WithMaterial<T> {
|
||||
WithMaterial { obj, material }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Obj> Obj for WithMaterial<T> {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
self.obj.distance_to(point)
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
self.obj.normal_at(point)
|
||||
}
|
||||
fn material_at(&self, point: Vec3) -> Material {
|
||||
self.material
|
||||
}
|
||||
fn get_lights(&self) -> Vec<Light> {
|
||||
self.obj.get_lights()
|
||||
}
|
||||
fn node_count(&self) -> u32 {
|
||||
self.obj.node_count() + 1
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct WithDynamicMaterial<T: Obj, F: Clone + Send + Sync + Fn(Vec3) -> Material> {
|
||||
obj: T,
|
||||
fun: F
|
||||
}
|
||||
|
||||
impl<T: Obj, F: Clone + Send + Sync + Fn(Vec3) -> Material> WithDynamicMaterial<T, F> {
|
||||
pub fn new(obj: T, fun: F) -> WithDynamicMaterial<T, F> {
|
||||
WithDynamicMaterial { obj, fun }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Obj, F: Clone + Send + Sync + Fn(Vec3) -> Material> Obj for WithDynamicMaterial<T, F> {
|
||||
fn distance_to(&self, point: Vec3) -> f64 {
|
||||
self.obj.distance_to(point)
|
||||
}
|
||||
fn normal_at(&self, point: Vec3) -> Vec3 {
|
||||
self.obj.normal_at(point)
|
||||
}
|
||||
fn material_at(&self, point: Vec3) -> Material {
|
||||
(self.fun)(point)
|
||||
}
|
||||
fn get_lights(&self) -> Vec<Light> {
|
||||
self.obj.get_lights()
|
||||
}
|
||||
fn node_count(&self) -> u32 {
|
||||
self.obj.node_count() + 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,323 @@
|
||||
use crate::structs::{Vec3, ColorVec, COLOR_ZERO, Ray, Y, X};
|
||||
use crate::object::Obj;
|
||||
use crate::material::{Material, SurfaceType};
|
||||
use crate::consts::*;
|
||||
use crate::light::Light;
|
||||
use std::f64::consts::PI;
|
||||
use rand::prelude::*;
|
||||
use crossbeam_channel::unbounded;
|
||||
use std::ops::{Mul, Add, Sub};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Cam {
|
||||
pos: Vec3,
|
||||
dir: Vec3,
|
||||
up: Vec3,
|
||||
right: Vec3
|
||||
}
|
||||
|
||||
impl Cam {
|
||||
pub fn new(pos: Vec3, dir: Vec3, fov: f64) -> Cam {
|
||||
let right = (dir ^ UP).unit() * fov;
|
||||
let up = (right ^ dir).unit() * fov;
|
||||
let dir = dir.unit();
|
||||
Cam { pos, dir, up, right }
|
||||
}
|
||||
|
||||
pub fn new_pointing(pos: Vec3, pointing: Vec3, fov: f64) -> Cam {
|
||||
Self::new(pos, pointing - pos, fov)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct RayData {
|
||||
pub pos: Vec3,
|
||||
pub dir: Vec3,
|
||||
pub normal: Vec3,
|
||||
pub material: Material,
|
||||
pub dist: f64,
|
||||
pub steps: u32
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct LightRayData<'a> {
|
||||
pub light: &'a Light,
|
||||
pub dir: Vec3,
|
||||
pub dist: f64
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
struct Slice {
|
||||
pub x: usize,
|
||||
pub y: usize,
|
||||
pub w: usize,
|
||||
pub h: usize
|
||||
}
|
||||
|
||||
type CastData = [Option<RayData>; MAX_BOUNCES as usize];
|
||||
const NONE_CAST_DATA: CastData = [None; MAX_BOUNCES as usize];
|
||||
pub type Image = [u8; IMG_BYTE_SIZE];
|
||||
|
||||
fn l2s(l: f64) -> u8 {
|
||||
(f64::powf(f64::clamp(l, 0., 1.), 1./2.2) * 255. + 0.5) as u8
|
||||
}
|
||||
|
||||
fn randab(a: f64, b: f64) -> f64 {
|
||||
random::<f64>() * (b-a) + a
|
||||
}
|
||||
|
||||
fn shoot_ray<T: Obj>(scene: &T, pos: Vec3, dir: Vec3) -> Option<RayData> {
|
||||
let dir = dir.unit();
|
||||
let mut steps = 0;
|
||||
let mut dist = 0.;
|
||||
let ray = Ray::new(pos, dir);
|
||||
while steps < MAX_STEPS && dist < MAX_DIST {
|
||||
let pos = ray.point(dist);
|
||||
let d = scene.distance_to(pos);
|
||||
if d < EPSILON {
|
||||
return Some(RayData {
|
||||
pos,
|
||||
dir,
|
||||
normal: scene.normal_at(pos),
|
||||
material: scene.material_at(pos),
|
||||
dist,
|
||||
steps
|
||||
})
|
||||
}
|
||||
dist += d;
|
||||
steps += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn shoot_ray_at<T: Obj>(scene: &T, pos: Vec3, dest: Vec3) -> bool {
|
||||
let dir = (dest - pos).unit();
|
||||
let mut steps = 0;
|
||||
let mut dist = EPSILON; //XXX 0.
|
||||
let ray = Ray::new(pos, dir);
|
||||
while steps < MAX_STEPS && dist < MAX_DIST {
|
||||
let point = ray.point(dist);
|
||||
let sd = scene.distance_to(point);
|
||||
let dd = dest.distance_to(point);
|
||||
if sd < 0. {
|
||||
return false;
|
||||
}
|
||||
if dd < sd {
|
||||
return true;
|
||||
}
|
||||
dist += if sd < EPSILON { EPSILON } else { sd };
|
||||
steps += 1;
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
impl Cam {
|
||||
pub fn render<T: Obj + Clone>(&self, scene: &T) -> Image {
|
||||
if THREAD_COUNT > 1 {
|
||||
self.render_multithreaded(scene)
|
||||
} else {
|
||||
self.render_singlethreaded(scene)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_singlethreaded<T: Obj>(&self, scene: &T) -> Image {
|
||||
let lights = scene.get_lights();
|
||||
let mut pixels = [0; IMG_BYTE_SIZE];
|
||||
for y in 0..IMG_HEIGHT {
|
||||
for x in 0..IMG_WIDTH {
|
||||
let field_pos = (x + y*IMG_WIDTH) * 3;
|
||||
self.render_single(scene, &lights, x, y, &mut pixels[field_pos..(field_pos+3)]);
|
||||
}
|
||||
}
|
||||
pixels
|
||||
}
|
||||
|
||||
pub fn render_multithreaded<T: Obj + Clone>(&self, scene: &T) -> Image {
|
||||
let mut pixels = [0; IMG_BYTE_SIZE];
|
||||
|
||||
crossbeam::scope(|scope| {
|
||||
// initialize everything for render
|
||||
let lights = scene.get_lights();
|
||||
let slice_width = IMG_WIDTH / THREAD_COUNT;
|
||||
let slice_height = IMG_HEIGHT / SLICES_PER_THREAD;
|
||||
let slice_byte_size = slice_width * slice_height * 3;
|
||||
|
||||
let (slice_tx, slice_rx) = unbounded::<Slice>();
|
||||
let (data_tx, data_rx) = unbounded();
|
||||
|
||||
// spawn a bunch of threads
|
||||
for _i in 0..THREAD_COUNT {
|
||||
let cam = self.clone();
|
||||
let scene = scene.clone();
|
||||
let lights = lights.clone();
|
||||
let slice_rx = slice_rx.clone();
|
||||
let data_tx = data_tx.clone();
|
||||
|
||||
scope.spawn(move |_| {
|
||||
for slice in slice_rx.iter() {
|
||||
let mut pixels = Vec::with_capacity(slice_byte_size);
|
||||
pixels.resize(slice_byte_size, 0 as u8);
|
||||
let data = pixels.as_mut_slice();
|
||||
|
||||
for x in 0..slice.w {
|
||||
for y in 0..slice.h {
|
||||
let i = (x + y*slice_width) * 3;
|
||||
cam.render_single(&scene, &lights, slice.x+x, slice.y+y, &mut data[i..(i+3)]);
|
||||
}
|
||||
}
|
||||
|
||||
data_tx.send((slice, pixels)).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// don't forget to close the ends of the channel we don't use
|
||||
drop(slice_rx);
|
||||
drop(data_tx);
|
||||
|
||||
// send the slice data
|
||||
let mut y = 0;
|
||||
while y < IMG_HEIGHT {
|
||||
let mut x = 0;
|
||||
while x < IMG_WIDTH {
|
||||
let w = usize::min(slice_width, IMG_WIDTH - x);
|
||||
let h = usize::min(slice_height, IMG_HEIGHT - y);
|
||||
slice_tx.send(Slice { x, y, w, h }).unwrap();
|
||||
x += slice_width;
|
||||
}
|
||||
y += slice_height;
|
||||
}
|
||||
drop(slice_tx);
|
||||
|
||||
// merge stuff as we get it
|
||||
for (slice, data) in data_rx {
|
||||
let data = data.as_slice();
|
||||
for sy in 0..slice.h {
|
||||
let py = sy + slice.y;
|
||||
for sx in 0..slice.w {
|
||||
let px = sx + slice.x;
|
||||
|
||||
let pi = (px + py * IMG_WIDTH) * 3;
|
||||
let si = (sx + sy * slice_width) * 3;
|
||||
pixels[pi + 0] = data[si + 0];
|
||||
pixels[pi + 1] = data[si + 1];
|
||||
pixels[pi + 2] = data[si + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
}).unwrap();
|
||||
|
||||
pixels
|
||||
}
|
||||
|
||||
fn render_single<T: Obj>(&self, scene: &T, lights: &Vec<Light>, x: usize, y: usize, field: &mut [u8]) -> () {
|
||||
let mut color = COLOR_ZERO;
|
||||
for xs in 0..SUPERSAMPLING {
|
||||
for ys in 0..SUPERSAMPLING {
|
||||
for _i in 0..RAYS_PER_PIXEL {
|
||||
let cast = self.cast_single(scene, x * SUPERSAMPLING + xs, y * SUPERSAMPLING + ys);
|
||||
color = color + self.color_single(scene, &lights, cast);
|
||||
}
|
||||
}
|
||||
}
|
||||
color = color / (SUPERSAMPLING * SUPERSAMPLING * RAYS_PER_PIXEL) as f64;
|
||||
|
||||
field[0] = l2s(color.v(0));
|
||||
field[1] = l2s(color.v(1));
|
||||
field[2] = l2s(color.v(2));
|
||||
}
|
||||
|
||||
fn color_single<'a, T: Obj>(&self, scene: &'a T, lights: &'a Vec<Light>, cast: CastData) -> ColorVec {
|
||||
let mut color = COLOR_ZERO;
|
||||
let mut dist: f64 = 1.;
|
||||
|
||||
for i in (0..MAX_BOUNCES).rev() {
|
||||
if let Some(ray) = cast[i as usize] {
|
||||
match ray.material.surface() {
|
||||
SurfaceType::Diffuse => {
|
||||
color = color / dist.powf(DIST_POWER);
|
||||
|
||||
color = ray.material.reflection() * color + ray.material.emission();
|
||||
for lightray in Self::get_lights(scene, lights, ray.pos + (scene.normal_at(ray.pos)- ray.dir) * LIGHT_EPSILON) {
|
||||
let angle = f64::max(0., ray.dir.angle_to(lightray.dir).abs() / PI / ANGLE_CORRECTION + 1. - ANGLE_FIX_CORRECTION);
|
||||
let dist = f64::max(1., lightray.dist / DIST_CORRECTION - DIST_FIX_CORRECTION);
|
||||
color = color + ray.material.reflection() * lightray.light.color() / angle.powf(ANGLE_POWER) / dist.powf(DIST_POWER);
|
||||
}
|
||||
},
|
||||
SurfaceType::Reflective => {
|
||||
color = ray.material.reflection() * color + ray.material.emission();
|
||||
},
|
||||
SurfaceType::Stop => {
|
||||
color = ray.material.emission();
|
||||
}
|
||||
}
|
||||
dist = f64::max(1., ray.dist / DIST_CORRECTION - DIST_FIX_CORRECTION);
|
||||
}
|
||||
}
|
||||
|
||||
color / dist.powf(DIST_POWER)
|
||||
}
|
||||
|
||||
fn cast_single<T: Obj>(&self, scene: &T, x: usize, y: usize) -> CastData {
|
||||
let mut cast = NONE_CAST_DATA;
|
||||
let mut pos = self.pos;
|
||||
let mut dir = self.dir_for(x, y);
|
||||
let mut i = 0;
|
||||
while i < MAX_BOUNCES {
|
||||
let hit = shoot_ray(scene, pos, dir);
|
||||
cast[i as usize] = hit;
|
||||
match hit {
|
||||
Some(hit) => {
|
||||
i += 1;
|
||||
pos = hit.pos - dir * EPSILON;
|
||||
match hit.material.surface() {
|
||||
SurfaceType::Stop => break,
|
||||
SurfaceType::Diffuse => {
|
||||
let r1 = randab(0., 2.*PI);
|
||||
let r2 = randab(0., 1.);
|
||||
let w = hit.normal;
|
||||
let u = ((if w.x().abs() > 0.5 { Y } else { X }) ^ w).unit();
|
||||
let v = w ^ u;
|
||||
let x = r1.cos()*r2.sqrt();
|
||||
let y = r1.sin()*r2.sqrt();
|
||||
let z = (1.-r2).sqrt();
|
||||
if w.x().abs() < 0.5 {
|
||||
dir = (u*x + v*y + w*z).unit();
|
||||
} else {
|
||||
dir = (u*x + v*y + w*z).unit();
|
||||
}
|
||||
},
|
||||
SurfaceType::Reflective => {
|
||||
dir = dir - hit.normal * (hit.normal * dir) * 2.;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
cast
|
||||
}
|
||||
|
||||
fn get_lights<'a, T: Obj>(scene: &'a T, lights: &'a Vec<Light>, pos: Vec3) -> Vec<LightRayData<'a>> {
|
||||
lights
|
||||
.into_iter()
|
||||
.filter_map(|light| {
|
||||
if shoot_ray_at(scene, pos, light.pos()) {
|
||||
Some(LightRayData {
|
||||
light,
|
||||
dir: (pos - light.pos()),
|
||||
dist: pos.distance_to(light.pos())
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn dir_for(&self, x: usize, y: usize) -> Vec3 {
|
||||
let x = (((x + (IMG_DIM - IMG_WIDTH) * SUPERSAMPLING / 2) as f64) / ((IMG_DIM * SUPERSAMPLING) as f64) - 0.5) * 2.;
|
||||
let y = (0.5 - ((y + (IMG_DIM - IMG_HEIGHT) * SUPERSAMPLING / 2) as f64) / ((IMG_DIM * SUPERSAMPLING) as f64)) * 2.;
|
||||
(self.dir + self.right * x + self.up * y).unit()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
use crate::structs::vec3::Vec3;
|
||||
use std::ops::{Add, Sub, Neg, Mul, Div};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Mat3 {
|
||||
a: f64, b: f64, c: f64,
|
||||
d: f64, e: f64, f: f64,
|
||||
g: f64, h: f64, i: f64,
|
||||
}
|
||||
|
||||
pub const I3: Mat3 = Mat3 {
|
||||
a: 1., b: 0., c: 0.,
|
||||
d: 0., e: 1., f: 0.,
|
||||
g: 0., h: 0., i: 1.,
|
||||
};
|
||||
|
||||
pub const O3: Mat3 = Mat3 {
|
||||
a: 0., b: 0., c: 0.,
|
||||
d: 0., e: 0., f: 0.,
|
||||
g: 0., h: 0., i: 0.,
|
||||
};
|
||||
|
||||
pub const COFACTORS: Mat3 = Mat3 {
|
||||
a: 1., b: -1., c: 1.,
|
||||
d: -1., e: 1., f: -1.,
|
||||
g: 1., h: -1., i: 1.,
|
||||
};
|
||||
|
||||
impl Mat3 {
|
||||
pub const fn new(a: f64, b: f64, c: f64, d: f64, e: f64, f: f64, g: f64, h: f64, i: f64) -> Mat3 {
|
||||
Mat3 {
|
||||
a, b, c,
|
||||
d, e, f,
|
||||
g, h, i,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn new_cols(a: Vec3, b: Vec3, c: Vec3) -> Mat3 {
|
||||
Mat3 {
|
||||
a: a.x(), b: b.x(), c: c.x(),
|
||||
d: a.y(), e: b.y(), f: c.y(),
|
||||
g: a.z(), h: b.z(), i: c.z(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn new_rows(a: Vec3, d: Vec3, g: Vec3) -> Mat3 {
|
||||
Mat3 {
|
||||
a: a.x(), b: a.y(), c: a.z(),
|
||||
d: d.x(), e: d.y(), f: d.z(),
|
||||
g: g.x(), h: g.y(), i: g.z(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn a(&self) -> f64 { self.a }
|
||||
pub const fn b(&self) -> f64 { self.b }
|
||||
pub const fn c(&self) -> f64 { self.c }
|
||||
pub const fn d(&self) -> f64 { self.d }
|
||||
pub const fn e(&self) -> f64 { self.e }
|
||||
pub const fn f(&self) -> f64 { self.f }
|
||||
pub const fn g(&self) -> f64 { self.g }
|
||||
pub const fn h(&self) -> f64 { self.h }
|
||||
pub const fn i(&self) -> f64 { self.i }
|
||||
|
||||
pub fn det(&self) -> f64 {
|
||||
let (a, b, c) = (self.a(), self.b(), self.c());
|
||||
let (d, e, f) = (self.d(), self.e(), self.f());
|
||||
let (g, h, i) = (self.g(), self.h(), self.i());
|
||||
|
||||
a*(e*i - f*h) - b*(d*i - f*g) + c*(d*h - e*g)
|
||||
}
|
||||
|
||||
pub const fn trans(&self) -> Mat3 {
|
||||
Mat3 {
|
||||
a: self.a, b: self.d, c: self.g,
|
||||
d: self.b, e: self.e, f: self.h,
|
||||
g: self.c, h: self.f, i: self.i,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn minor(&self) -> Mat3 {
|
||||
let (a, b, c) = (self.a(), self.b(), self.c());
|
||||
let (d, e, f) = (self.d(), self.e(), self.f());
|
||||
let (g, h, i) = (self.g(), self.h(), self.i());
|
||||
|
||||
Mat3 {
|
||||
a: e*i - f*h, b: d*i - f*g, c: d*h - e*g,
|
||||
d: b*i - c*h, e: a*i - c*g, f: a*h - b*g,
|
||||
g: b*f - c*e, h: a*f - c*d, i: a*e - b*d,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn invert(&self) -> Mat3 {
|
||||
(self.minor().member_mul(COFACTORS)).trans() / self.det()
|
||||
}
|
||||
|
||||
pub fn member_mul(&self, other: Mat3) -> Mat3 {
|
||||
Mat3 {
|
||||
a: self.a*other.a, b: self.b*other.b, c: self.c*other.c,
|
||||
d: self.d*other.d, e: self.e*other.e, f: self.f*other.f,
|
||||
g: self.g*other.g, h: self.h*other.h, i: self.i*other.i,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Mat3 {
|
||||
type Output = Mat3;
|
||||
|
||||
fn add(self, other: Mat3) -> Mat3 {
|
||||
Mat3 {
|
||||
a: self.a+other.a, b: self.b+other.b, c: self.c+other.c,
|
||||
d: self.d+other.d, e: self.e+other.e, f: self.f+other.f,
|
||||
g: self.g+other.g, h: self.h+other.h, i: self.i+other.i,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Mat3 {
|
||||
type Output = Mat3;
|
||||
|
||||
fn sub(self, other: Mat3) -> Mat3 {
|
||||
Mat3 {
|
||||
a: self.a-other.a, b: self.b-other.b, c: self.c-other.c,
|
||||
d: self.d-other.d, e: self.e-other.e, f: self.f-other.f,
|
||||
g: self.g-other.g, h: self.h-other.h, i: self.i-other.i,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Mat3> for Mat3 {
|
||||
type Output = Mat3;
|
||||
|
||||
fn mul(self, other: Mat3) -> Mat3 {
|
||||
Mat3 {
|
||||
a: self.a*other.a + self.b*other.d + self.c*other.g,
|
||||
b: self.a*other.b + self.b*other.e + self.c*other.h,
|
||||
c: self.a*other.c + self.b*other.f + self.c*other.i,
|
||||
|
||||
d: self.d*other.a + self.e*other.d + self.f*other.g,
|
||||
e: self.d*other.b + self.e*other.e + self.f*other.h,
|
||||
f: self.d*other.c + self.e*other.f + self.e*other.i,
|
||||
|
||||
g: self.g*other.a + self.h*other.d + self.i*other.g,
|
||||
h: self.g*other.b + self.h*other.e + self.i*other.h,
|
||||
i: self.g*other.c + self.h*other.f + self.i*other.i,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Mat3 {
|
||||
type Output = Mat3;
|
||||
|
||||
fn neg(self) -> Mat3 {
|
||||
Mat3 {
|
||||
a: -self.a, b: -self.b, c: -self.c,
|
||||
d: -self.d, e: -self.e, f: -self.f,
|
||||
g: -self.h, h: -self.h, i: -self.i,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Mat3 {
|
||||
type Output = Mat3;
|
||||
|
||||
fn mul(self, k: f64) -> Mat3 {
|
||||
Mat3 {
|
||||
a: self.a*k, b: self.b*k, c: self.c*k,
|
||||
d: self.d*k, e: self.e*k, f: self.f*k,
|
||||
g: self.g*k, h: self.g*k, i: self.i*k,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vec3> for Mat3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn mul(self, other: Vec3) -> Vec3 {
|
||||
let (x, y, z) = (other.x(), other.y(), other.z());
|
||||
let (a, b, c) = (self.a(), self.b(), self.c());
|
||||
let (d, e, f) = (self.d(), self.e(), self.f());
|
||||
let (g, h, i) = (self.g(), self.h(), self.i());
|
||||
|
||||
Vec3::new(
|
||||
a*x + b*y + c*z,
|
||||
d*x + e*y + f*z,
|
||||
g*x + h*y + i*z,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Mat3> for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn mul(self, other: Mat3) -> Vec3 {
|
||||
let (x, y, z) = (self.x(), self.y(), self.z());
|
||||
let (a, b, c) = (other.a(), other.b(), other.c());
|
||||
let (d, e, f) = (other.d(), other.e(), other.f());
|
||||
let (g, h, i) = (other.g(), other.h(), other.i());
|
||||
|
||||
Vec3::new(
|
||||
x*a + y*d + z*g,
|
||||
x*b + y*e + z*h,
|
||||
x*c + y*f + z*i,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f64> for Mat3 {
|
||||
type Output = Mat3;
|
||||
|
||||
fn div(self, k: f64) -> Mat3 {
|
||||
Mat3 {
|
||||
a: self.a/k, b: self.b/k, c: self.c/k,
|
||||
d: self.d/k, e: self.e/k, f: self.f/k,
|
||||
g: self.g/k, h: self.h/k, i: self.i/k,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
use crate::structs::vec_n::Vec;
|
||||
use std::ops::{Add, Sub, Neg, Mul, Div};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Mat<const N: usize> {
|
||||
v: [[f64; N]; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> Mat<N> {
|
||||
pub const fn new(v: [[f64; N]; N]) -> Mat<N> {
|
||||
Mat { v }
|
||||
}
|
||||
|
||||
pub const fn new_zero() -> Mat<N> {
|
||||
Mat { v: [[0.; N]; N] }
|
||||
}
|
||||
|
||||
pub const fn new_unit() -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
let mut i = 0;
|
||||
while i<N {
|
||||
v[i][i] = 1.;
|
||||
i += 1;
|
||||
}
|
||||
Mat { v }
|
||||
}
|
||||
|
||||
pub const fn new_one(i: usize, j: usize) -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
v[i][j] = 1.;
|
||||
Mat { v }
|
||||
}
|
||||
|
||||
pub const fn new_from_diagonal(d: Vec<N>) -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
let mut i = 0;
|
||||
while i<N {
|
||||
v[i][i] = d.v(i);
|
||||
i += 1;
|
||||
}
|
||||
Mat { v }
|
||||
}
|
||||
|
||||
pub const fn v(&self, i: usize, j: usize) -> f64 {
|
||||
self.v[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Add for Mat<N> {
|
||||
type Output = Mat<N>;
|
||||
|
||||
fn add(self, other: Mat<N>) -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
v[i][j] = self.v[i][j] + other.v[i][j];
|
||||
}
|
||||
}
|
||||
Mat { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Sub for Mat<N> {
|
||||
type Output = Mat<N>;
|
||||
|
||||
fn sub(self, other: Mat<N>) -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
v[i][j] = self.v[i][j] - other.v[i][j];
|
||||
}
|
||||
}
|
||||
Mat { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Neg for Mat<N> {
|
||||
type Output = Mat<N>;
|
||||
|
||||
fn neg(self) -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
v[i][j] = -self.v[i][j];
|
||||
}
|
||||
}
|
||||
Mat { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Mul<f64> for Mat<N> {
|
||||
type Output = Mat<N>;
|
||||
|
||||
fn mul(self, k: f64) -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
v[i][j] = self.v[i][j] * k;
|
||||
}
|
||||
}
|
||||
Mat { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Mul<Vec<N>> for Mat<N> {
|
||||
type Output = Vec<N>;
|
||||
|
||||
fn mul(self, other: Vec<N>) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
for i in 0..N {
|
||||
let mut x = 0.;
|
||||
for j in 0..N {
|
||||
x += self.v[i][j] * other.v(i);
|
||||
}
|
||||
v[i] = x;
|
||||
}
|
||||
Vec::new(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Div<f64> for Mat<N> {
|
||||
type Output = Mat<N>;
|
||||
|
||||
fn div(self, k: f64) -> Mat<N> {
|
||||
let mut v = [[0.; N]; N];
|
||||
for i in 0..N {
|
||||
for j in 0..N {
|
||||
v[i][j] = self.v[i][j] / k;
|
||||
}
|
||||
}
|
||||
Mat { v }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
use crate::consts::COLOR_CHANNELS;
|
||||
|
||||
mod vec3;
|
||||
mod mat3;
|
||||
mod vec_n;
|
||||
mod mat_n;
|
||||
|
||||
mod ray;
|
||||
mod cam;
|
||||
|
||||
pub use vec3::{Vec3, X, Y, Z, O};
|
||||
pub use mat3::{Mat3, I3, O3};
|
||||
pub use vec_n::Vec;
|
||||
pub use mat_n::Mat;
|
||||
pub use ray::Ray;
|
||||
pub use cam::{Cam, Image};
|
||||
|
||||
pub type ColorVec = Vec<COLOR_CHANNELS>;
|
||||
pub const COLOR_ZERO: ColorVec = ColorVec::new_zero();
|
||||
pub type ColorMat = Mat<COLOR_CHANNELS>;
|
||||
@@ -0,0 +1,28 @@
|
||||
use crate::structs::Vec3;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Ray {
|
||||
from: Vec3,
|
||||
dir: Vec3,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
pub fn new(from: Vec3, dir: Vec3) -> Ray {
|
||||
Ray { from, dir }
|
||||
}
|
||||
|
||||
pub fn new_from_to(from: Vec3, to: Vec3) -> Ray {
|
||||
Ray { from, dir: to - from }
|
||||
}
|
||||
|
||||
pub fn unit(&self) -> Ray {
|
||||
Ray { from: self.from, dir: self.dir.unit() }
|
||||
}
|
||||
|
||||
pub fn from(&self) -> Vec3 { self.from }
|
||||
pub fn dir(&self) -> Vec3 { self.dir }
|
||||
|
||||
pub fn point(&self, steps: f64) -> Vec3 {
|
||||
self.from + self.dir * steps
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
use std::ops::{Add, Sub, Neg, Mul, Div, BitXor};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Vec3 {
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
}
|
||||
|
||||
pub const X: Vec3 = Vec3 { x: 1., y: 0., z: 0. };
|
||||
pub const Y: Vec3 = Vec3 { x: 0., y: 1., z: 0. };
|
||||
pub const Z: Vec3 = Vec3 { x: 0., y: 0., z: 1. };
|
||||
pub const O: Vec3 = Vec3 { x: 0., y: 0., z: 0. };
|
||||
|
||||
impl Vec3 {
|
||||
pub const fn new(x: f64, y: f64, z: f64) -> Vec3 {
|
||||
Vec3 { x, y, z }
|
||||
}
|
||||
|
||||
pub const fn x(&self) -> f64 { self.x }
|
||||
pub const fn y(&self) -> f64 { self.y }
|
||||
pub const fn z(&self) -> f64 { self.z }
|
||||
|
||||
pub fn magnitude(self) -> f64 {
|
||||
f64::sqrt(self.x*self.x + self.y*self.y + self.z*self.z)
|
||||
}
|
||||
|
||||
pub fn unit(self) -> Vec3 {
|
||||
self / self.magnitude()
|
||||
}
|
||||
|
||||
pub fn distance_to(self, other: Vec3) -> f64 {
|
||||
(self - other).magnitude()
|
||||
}
|
||||
|
||||
pub fn angle_to(self, other: Vec3) -> f64 {
|
||||
((self * other) / (self.magnitude() * other.magnitude())).acos()
|
||||
}
|
||||
|
||||
pub fn member_mul(self, other: Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.x*other.x,
|
||||
y: self.y*other.y,
|
||||
z: self.z*other.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn add(self, other: Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.x + other.x,
|
||||
y: self.y + other.y,
|
||||
z: self.z + other.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sub for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn sub(self, other: Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.x - other.x,
|
||||
y: self.y - other.y,
|
||||
z: self.z - other.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Neg for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn neg(self) -> Vec3 {
|
||||
Vec3 {
|
||||
x: -self.x,
|
||||
y: -self.y,
|
||||
z: -self.z,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitXor for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn bitxor(self, other: Vec3) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.y * other.z - self.z * other.y,
|
||||
y: self.z * other.x - self.x * other.z,
|
||||
z: self.x * other.y - self.y * other.x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<Vec3> for Vec3 {
|
||||
type Output = f64;
|
||||
|
||||
fn mul(self, other: Vec3) -> f64 {
|
||||
self.x*other.x + self.y*other.y + self.z*other.z
|
||||
}
|
||||
}
|
||||
|
||||
impl Mul<f64> for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn mul(self, k: f64) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.x * k,
|
||||
y: self.y * k,
|
||||
z: self.z * k,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Div<f64> for Vec3 {
|
||||
type Output = Vec3;
|
||||
|
||||
fn div(self, k: f64) -> Vec3 {
|
||||
Vec3 {
|
||||
x: self.x / k,
|
||||
y: self.y / k,
|
||||
z: self.z / k,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
use std::ops::{Add, Sub, Neg, Mul, Div};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Vec<const N: usize> {
|
||||
v: [f64; N],
|
||||
}
|
||||
|
||||
impl<const N: usize> Vec<N> {
|
||||
pub const fn new(v: [f64; N]) -> Vec<N> {
|
||||
Vec { v }
|
||||
}
|
||||
|
||||
pub const fn new_zero() -> Vec<N> {
|
||||
Vec { v: [0.; N] }
|
||||
}
|
||||
|
||||
pub const fn new_one(i: usize) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
v[i] = 1.;
|
||||
Vec { v }
|
||||
}
|
||||
|
||||
pub const fn v(&self, i: usize) -> f64 {
|
||||
self.v[i]
|
||||
}
|
||||
|
||||
pub fn magnitude(self) -> f64 {
|
||||
let mut x = 0.;
|
||||
for i in 0..N {
|
||||
let v = self.v[i];
|
||||
x += v*v;
|
||||
}
|
||||
f64::sqrt(x)
|
||||
}
|
||||
|
||||
pub fn unit(self) -> Vec<N> {
|
||||
self / self.magnitude()
|
||||
}
|
||||
|
||||
pub fn distance_to(self, other: Vec<N>) -> f64 {
|
||||
(self - other).magnitude()
|
||||
}
|
||||
|
||||
pub fn member_mul(self, other: Vec<N>) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
for i in 0..N {
|
||||
v[i] = self.v[i] * other.v[i];
|
||||
}
|
||||
Vec { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Add for Vec<N> {
|
||||
type Output = Vec<N>;
|
||||
|
||||
fn add(self, other: Vec<N>) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
for i in 0..N {
|
||||
v[i] = self.v[i] + other.v[i];
|
||||
}
|
||||
Vec { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Sub for Vec<N> {
|
||||
type Output = Vec<N>;
|
||||
|
||||
fn sub(self, other: Vec<N>) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
for i in 0..N {
|
||||
v[i] = self.v[i] - other.v[i];
|
||||
}
|
||||
Vec { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Neg for Vec<N> {
|
||||
type Output = Vec<N>;
|
||||
|
||||
fn neg(self) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
for i in 0..N {
|
||||
v[i] = -self.v[i];
|
||||
}
|
||||
Vec { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Mul<f64> for Vec<N> {
|
||||
type Output = Vec<N>;
|
||||
|
||||
fn mul(self, k: f64) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
for i in 0..N {
|
||||
v[i] = self.v[i] * k;
|
||||
}
|
||||
Vec { v }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Div<f64> for Vec<N> {
|
||||
type Output = Vec<N>;
|
||||
|
||||
fn div(self, k: f64) -> Vec<N> {
|
||||
let mut v = [0.; N];
|
||||
for i in 0..N {
|
||||
v[i] = self.v[i] / k;
|
||||
}
|
||||
Vec { v }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user