use cgmath::prelude::*; use cgmath::{dot, vec3, Vector3}; use rand::prelude::*; use std::f64::consts::PI; pub fn clamp(x: f64, min: f64, max: f64) -> f64 { if x < min { min } else if x > max { max } else { x } } pub fn print_colour(colour: &Vector3, samples: i32) { let scolour = colour.map(|x| (x / samples as f64).sqrt()); println!( "{} {} {}", (256.0 * clamp(scolour.x, 0.0, 0.999)) as i32, (256.0 * clamp(scolour.y, 0.0, 0.999)) as i32, (256.0 * clamp(scolour.z, 0.0, 0.999)) as i32 ) } pub fn random_vector3(rng: &mut ThreadRng, min: f64, max: f64) -> Vector3 { vec3( rng.gen_range(min, max), rng.gen_range(min, max), rng.gen_range(min, max), ) } pub fn random_in_unit_sphere(rng: &mut ThreadRng) -> Vector3 { loop { let p = random_vector3(rng, -1.0, 1.0); if p.magnitude2() <= 1.0 { return p; } } } pub fn random_unit_vector(rng: &mut ThreadRng) -> Vector3 { let a = rng.gen_range(0.0, 2.0 * PI); let z = rng.gen_range(-1.0, 1.0); let r = ((1.0 - z * z) as f64).sqrt(); vec3(r * a.cos(), r * a.sin(), z) } pub fn random_in_hemisphere(rng: &mut ThreadRng, normal: &Vector3) -> Vector3 { let in_unit_sphere = random_in_unit_sphere(rng); if dot(in_unit_sphere, *normal) > 0.0 { in_unit_sphere } else { -in_unit_sphere } } pub fn random_in_unit_disk(rng: &mut ThreadRng) -> Vector3 { loop { let p = vec3(rng.gen_range(-1.0, 1.0), rng.gen_range(-1.0, 1.0), 0.0); if p.magnitude2() < 1.0 { return p; } } } pub fn reflect(v: &Vector3, n: &Vector3) -> Vector3 { v - 2.0 * dot(*v, *n) * n } pub fn refract(uv: &Vector3, n: &Vector3, etai_over_etat: f64) -> Vector3 { let cos_theta = dot(-1.0 * uv, *n); let r_out_parallel = etai_over_etat * (uv + cos_theta * n); let r_out_perp = -(1.0 - r_out_parallel.magnitude2()).sqrt() * n; r_out_parallel + r_out_perp } pub fn schlick(cosine: f64, ref_idx: f64) -> f64 { let r0 = (1.0 - ref_idx) / (1.0 + ref_idx); let r0sq = r0 * r0; r0sq + (1.0 - r0) * (1.0 - cosine).powi(5) }