254 lines
6.5 KiB
Rust
254 lines
6.5 KiB
Rust
use std::fmt::{Display, Formatter};
|
|
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
|
|
use rand::distributions::{Distribution, Uniform};
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct Vec3 { x: f64, y: f64, z: f64 }
|
|
|
|
pub type Point3 = Vec3;
|
|
pub type Color = Vec3;
|
|
|
|
impl Vec3 {
|
|
pub fn new(x: f64, y: f64, z: f64) -> Vec3 { Vec3 {x, y, z} }
|
|
pub fn x(&self) -> f64 { self.x }
|
|
pub fn y(&self) -> f64 { self.y }
|
|
pub fn z(&self) -> f64 { self.z }
|
|
pub fn length_squared(&self) -> f64 {
|
|
self.x * self.x + self.y * self.y + self.z * self.z
|
|
}
|
|
pub fn length(&self) -> f64 { self.distance(&Vec3::default()) }
|
|
pub fn unit_vector(&self) -> Vec3 {
|
|
let length = self.length();
|
|
Vec3::new(self.x / length, self.y / length, self.z / length)
|
|
}
|
|
pub fn dot(&self, v:&Vec3) -> f64 {
|
|
self.x * v.x + self.y * v.y + self.z * v.z
|
|
}
|
|
pub fn cross(&self, v:&Vec3) -> Vec3 {
|
|
Vec3::new(
|
|
self.y * v.z - self.z * v.y,
|
|
self.z * v.x - self.x * v.z,
|
|
self.x * v.y - self.y * v.x
|
|
)
|
|
}
|
|
pub fn distance(&self, other: &Vec3) -> f64 {
|
|
let dx = self.x - other.x();
|
|
let dy = self.y - other.y();
|
|
let dz = self.z - other.z();
|
|
(dx * dx + dy * dy + dz * dz).sqrt()
|
|
}
|
|
|
|
pub fn random(min: f64, max:f64) -> Self {
|
|
let between = Uniform::from(min..max);
|
|
let mut rng = rand::thread_rng();
|
|
Vec3::new(
|
|
between.sample(&mut rng),
|
|
between.sample(&mut rng),
|
|
between.sample(&mut rng))
|
|
}
|
|
pub fn random_in_unit_sphere() -> Self {
|
|
loop {
|
|
let v = Vec3::random(-1.0, 1.0);
|
|
if v.length_squared() < 1.0 {
|
|
return v;
|
|
}
|
|
}
|
|
}
|
|
pub fn random_in_hemisphere(normal: &Vec3) -> Self {
|
|
let vec = Vec3::random_in_unit_sphere();
|
|
if vec.dot(normal) > 0.0 {
|
|
vec
|
|
} else {
|
|
-vec
|
|
}
|
|
}
|
|
pub fn random_unit_vector() -> Self { Vec3::random_in_unit_sphere().unit_vector() }
|
|
pub fn near_zero(&self) -> bool {
|
|
const MAXIMUM_DISTANCE_FROM_ZERO:f64 = 1e-8;
|
|
self.x.abs() < MAXIMUM_DISTANCE_FROM_ZERO &&
|
|
self.y.abs() < MAXIMUM_DISTANCE_FROM_ZERO &&
|
|
self.z.abs() < MAXIMUM_DISTANCE_FROM_ZERO
|
|
}
|
|
pub fn reflected(&self, normal: &Vec3) -> Vec3 {
|
|
let dp = self.dot(normal);
|
|
let dp = dp * 2.0 * (*normal);
|
|
*self - dp
|
|
}
|
|
pub fn refract(&self, normal: &Vec3, etai_over_etat: f64) -> Vec3 {
|
|
let dot = (-(*self)).dot(normal);
|
|
let cos_theta = dot.min(1.0);
|
|
let out_perp = etai_over_etat * ((*self) + cos_theta * (*normal));
|
|
let inner = 1.0 - out_perp.length_squared();
|
|
let abs = inner.abs();
|
|
let r = -(abs.sqrt());
|
|
out_parallel = r * (*normal);
|
|
out_perp + out_parallel
|
|
}
|
|
pub fn refract_sort_of_works(&self, normal: &Vec3, etai_over_etat: f64) -> Vec3 {
|
|
let dot = (-(*self)).dot(normal);
|
|
let cos_theta = dot.min(1.0);
|
|
let out_perp = etai_over_etat * ((*self) + cos_theta * (*normal));
|
|
let inner = 1.0 - out_perp.length_squared();
|
|
let abs = inner.abs();
|
|
let r = -(abs.sqrt());
|
|
let out_parallel = r * (*normal);
|
|
// Why?
|
|
let mut res = out_perp + out_parallel;
|
|
res.x = -res.x;
|
|
//res.y = -res.y;
|
|
res.z = -res.z;
|
|
res
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_refract() {
|
|
let uv = Point3::new(1.0, 1.0, 0.0);
|
|
let n = Point3::new(-1.0, 0.0, 0.0);
|
|
let etai_over_etat = 1.0;
|
|
let expected = Point3::new(0.0, 1.0, 0.0);
|
|
let actual = uv.refract_orig( &n, etai_over_etat);
|
|
assert_eq!(actual, expected);
|
|
}
|
|
|
|
impl Color {
|
|
pub fn write_color(self: Color, samples_per_pixel: i32) {
|
|
let scale = 1.0 / samples_per_pixel as f64;
|
|
let r = f64::sqrt(scale * self.x);
|
|
let g = f64::sqrt(scale * self.y);
|
|
let b = f64::sqrt(scale * self.z);
|
|
let r = 256.0 * f64::clamp(r, 0.0, 0.999);
|
|
let g = 256.0 * f64::clamp(g, 0.0, 0.999);
|
|
let b = 256.0 * f64::clamp(b, 0.0, 0.999);
|
|
println!("{} {} {}", r as i32, g as i32, b as i32);
|
|
}
|
|
}
|
|
|
|
impl Default for Vec3 {
|
|
fn default() -> Self { Vec3 {x: 0.0, y: 0.0, z: 0.0} }
|
|
}
|
|
|
|
impl Display for Vec3 {
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
write!(f, "{} {} {}", self.x, self.y, self.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 AddAssign for Vec3 {
|
|
fn add_assign(&mut self, rhs: Self) {
|
|
self.x += rhs.x;
|
|
self.y += rhs.y;
|
|
self.z += rhs.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 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 Mul<Vec3> for Vec3 {
|
|
type Output = Vec3;
|
|
|
|
fn mul(self, other: Vec3) -> Vec3 {
|
|
Vec3 {
|
|
x: self.x * other.x(),
|
|
y: self.y * other.y(),
|
|
z: self.z * other.z(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<f64> for Vec3 {
|
|
type Output = Vec3;
|
|
|
|
fn mul(self, other: f64) -> Vec3 {
|
|
Vec3 {
|
|
x: self.x * other,
|
|
y: self.y * other,
|
|
z: self.z * other,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Mul<Vec3> for f64 {
|
|
type Output = Vec3;
|
|
|
|
fn mul(self, other: Vec3) -> Vec3 {
|
|
other * self
|
|
}
|
|
}
|
|
|
|
impl Div<Vec3> for Vec3 {
|
|
type Output = Vec3;
|
|
|
|
fn div(self, other: Vec3) -> Vec3 {
|
|
Vec3 {
|
|
x: self.x / other.x(),
|
|
y: self.y / other.y(),
|
|
z: self.z / other.z(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Div<f64> for Vec3 {
|
|
type Output = Vec3;
|
|
|
|
fn div(self, other: f64) -> Vec3 {
|
|
Vec3 {
|
|
x: self.x / other,
|
|
y: self.y / other,
|
|
z: self.z / other,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Vec3 {
|
|
fn eq(&self, other: &Vec3) -> bool {
|
|
self.x == other.x() && self.y == other.y() && self.z == other.z()
|
|
}
|
|
}
|