Renderer/src/hittable.rs

192 lines
5.2 KiB
Rust
Raw Normal View History

2022-06-29 16:17:28 +02:00
use super::{Arc, Color, Material, Metal, Point3, Ray, Vec3};
2022-04-27 18:27:07 +02:00
pub struct HitRecord {
2022-07-12 17:55:16 +02:00
pub p: Point3, // hit location
pub normal: Vec3, // normal of hit location
2022-06-08 18:28:45 +02:00
pub mat_ptr: Arc<dyn Material>,
2022-07-12 17:55:16 +02:00
pub t: f64, // ray steps (distance) from camera to hit
pub front_face: bool, // whether object was hitted from front or back
2022-04-27 18:27:07 +02:00
}
pub struct Sphere {
center: Point3,
radius: f64,
2022-06-08 18:28:45 +02:00
pub mat_ptr: Arc<dyn Material>,
2022-04-27 18:27:07 +02:00
}
2022-07-05 18:02:28 +02:00
pub struct Triangle {
a: Point3,
b: Point3,
c: Point3,
2022-07-26 18:16:08 +02:00
normal: Vec3,
2022-07-05 18:02:28 +02:00
pub mat_ptr: Arc<dyn Material>,
}
2022-06-01 20:13:55 +02:00
pub trait Hittable: Sync + Send {
2022-05-04 18:21:48 +02:00
fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool;
}
impl HitRecord {
fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) {
self.front_face = true; //Vec3::dot(r.direction(), outward_normal) < 0.0;
2022-05-04 18:21:48 +02:00
if self.front_face {
self.normal = outward_normal;
} else {
2022-05-11 18:38:30 +02:00
self.normal = (-1.0) * outward_normal;
2022-05-04 18:21:48 +02:00
}
}
2022-06-08 18:28:45 +02:00
pub fn new(p: Point3, normal: Vec3, m: Arc<dyn Material>, t: f64, front_face: bool) -> Self {
2022-05-04 18:21:48 +02:00
HitRecord {
p: p,
normal: normal,
2022-06-08 18:28:45 +02:00
mat_ptr: m,
2022-05-04 18:21:48 +02:00
t: t,
front_face: front_face,
}
}
pub fn empty() -> Self {
2022-05-11 18:38:30 +02:00
Self::new(
2022-06-08 18:28:45 +02:00
Point3::null(),
Vec3::null(),
2022-06-21 18:10:52 +02:00
Arc::new(Metal::new(&Color::null(), 0.0)),
2022-05-11 18:38:30 +02:00
0.0,
false,
)
2022-05-04 18:21:48 +02:00
}
2022-04-27 18:27:07 +02:00
}
impl Sphere {
2022-06-08 18:28:45 +02:00
pub fn new(cen: Point3, r: f64, m: Arc<dyn Material>) -> Self {
2022-04-27 18:27:07 +02:00
Sphere {
center: cen,
radius: r,
2022-06-08 18:28:45 +02:00
mat_ptr: m,
2022-04-27 18:27:07 +02:00
}
}
}
impl Hittable for Sphere {
2022-05-04 18:21:48 +02:00
fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool {
2022-04-27 18:27:07 +02:00
let oc = r.origin() - self.center;
let a = r.direction().length_squared(); // gleiches Ergebnis wie Skalarprodukt
2022-05-04 18:21:48 +02:00
let half_b = Vec3::dot(oc, r.direction());
2022-04-27 18:27:07 +02:00
let c = oc.length_squared() - self.radius * self.radius;
2022-05-04 18:21:48 +02:00
let discriminant = half_b * half_b - a * c;
2022-04-27 18:27:07 +02:00
if discriminant < 0.0 {
return false;
}
let sqrtd = discriminant.sqrt();
2022-06-08 18:28:45 +02:00
let mut root = (-half_b - sqrtd) / a;
2022-06-01 16:49:39 +02:00
let normal = (r.at(root) - self.center) / self.radius;
if root < t_min || t_max < root || Vec3::dot(normal, r.direction()) > 0.0 {
2022-06-08 18:28:45 +02:00
root = (-half_b + sqrtd) / a;
let normal = (r.at(root) - self.center) / self.radius;
if root < t_min || t_max < root || Vec3::dot(normal, r.direction()) > 0.0 {
2022-04-27 18:27:07 +02:00
return false;
}
}
rec.t = root;
rec.p = r.at(rec.t);
2022-05-04 18:21:48 +02:00
let outward_normal = (rec.p - self.center) / self.radius;
rec.set_face_normal(r, outward_normal);
2022-06-08 18:28:45 +02:00
rec.mat_ptr = self.mat_ptr.clone();
2022-04-27 18:27:07 +02:00
return true;
}
}
2022-07-05 18:02:28 +02:00
impl Triangle {
2022-07-26 18:16:08 +02:00
pub fn new(a: Point3, b: Point3, c: Point3, normal: Vec3, m: Arc<dyn Material>) -> Self {
2022-07-05 18:02:28 +02:00
Triangle {
a,
b,
c,
2022-07-26 18:16:08 +02:00
normal,
2022-07-05 18:02:28 +02:00
mat_ptr: m,
}
}
}
impl Hittable for Triangle {
fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool {
2022-07-12 17:55:16 +02:00
let epsilon = 0.0000001;
2022-07-05 18:02:28 +02:00
let a_to_b = self.b - self.a;
let a_to_c = self.c - self.a;
2022-07-12 17:55:16 +02:00
let h = Vec3::cross(r.direction(), a_to_c);
2022-07-05 18:02:28 +02:00
2022-07-12 17:55:16 +02:00
let a = Vec3::dot(a_to_b, h);
2022-07-05 18:02:28 +02:00
2022-07-12 17:55:16 +02:00
if a > -epsilon && a < epsilon {
// ray is parallel to triangle
2022-07-05 18:02:28 +02:00
return false;
}
2022-07-12 17:55:16 +02:00
let f = 1.0 / a;
2022-07-05 18:02:28 +02:00
let a_to_origin = r.origin() - self.a;
2022-07-12 17:55:16 +02:00
let u = Vec3::dot(a_to_origin, h) * f;
2022-07-05 18:02:28 +02:00
2022-07-12 17:55:16 +02:00
if u < 0.0 || u > 1.0 {
//check if outside of triangle
2022-07-05 18:02:28 +02:00
return false;
}
2022-07-12 17:55:16 +02:00
let q = Vec3::cross(a_to_origin, a_to_b);
let v = Vec3::dot(r.direction(), q) * f;
if v < 0.0 || u + v > 1.0 {
// intersection outside of triangle
return false;
}
2022-07-05 18:02:28 +02:00
2022-07-12 17:55:16 +02:00
let t = Vec3::dot(a_to_c, q) * f; // where is intersection on ray?
2022-07-05 18:02:28 +02:00
2022-07-12 17:55:16 +02:00
if t <= epsilon {
// line intersection but no ray intersection
2022-07-05 18:02:28 +02:00
return false;
}
2022-07-12 17:55:16 +02:00
// ray intersection
rec.t = t;
rec.p = r.at(t);
rec.mat_ptr = self.mat_ptr.clone();
2022-07-05 18:02:28 +02:00
2022-07-26 18:16:08 +02:00
rec.normal = self.normal;
return true;
/* old normal calculation code if normal is not known
// triangle normal alg from https://stackoverflow.com/questions/19350792/calculate-normal-of-a-single-triangle-in-3d-space
2022-07-12 17:55:16 +02:00
let normal_to_camera = Vec3::new(
a_to_b.y() * a_to_c.z() - a_to_b.z() * a_to_c.y(),
a_to_b.z() * a_to_c.x() - a_to_b.x() * a_to_c.z(),
a_to_b.x() * a_to_c.y() - a_to_b.y() * a_to_c.x(),
);
let unit_normal = normal_to_camera / normal_to_camera.length();
println!("From obj: {},{},{} - calculated: {},{},{}", self.normal.x(),self.normal.y(),self.normal.z(),unit_normal.x(),unit_normal.y(),unit_normal.z());
2022-07-12 17:55:16 +02:00
if Vec3::dot(unit_normal, r.direction()) >= 0.0 {
// normal towards camera
rec.set_face_normal(r, unit_normal);
} else {
// normal not towards camera, has to be flipped
rec.set_face_normal(r, -1.0 * unit_normal);
2022-07-05 18:02:28 +02:00
}
return true;
*/
2022-07-05 18:02:28 +02:00
}
2022-07-12 17:55:16 +02:00
}