132 lines
3.6 KiB
Rust
132 lines
3.6 KiB
Rust
use point::Point;
|
|
use scene::Color;
|
|
use scene::Scene;
|
|
use vector::Vector3;
|
|
|
|
pub trait Intersectable {
|
|
fn intersect(&self, ray: &Ray) -> Option<f32>;
|
|
fn surface_normal(&self, hit_point: &Point) -> Vector3;
|
|
}
|
|
|
|
pub struct Sphere {
|
|
pub center: Point,
|
|
pub radius: f32,
|
|
pub color: Color,
|
|
}
|
|
|
|
impl Intersectable for Sphere {
|
|
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
|
//Create a line segment between the ray origin and the center of the sphere
|
|
let l: Vector3 = (self.center - ray.origin).into();
|
|
//Use l as a hypotenuse and find the length of the adjacent side
|
|
let adj = l.dot(&ray.direction);
|
|
//Find the length-squared of the opposite side
|
|
//This is equivalent to (but faster than) (l.length() * l.length()) - (adj * adj)
|
|
let d2 = l.dot(&l) - (adj * adj);
|
|
let radius2 = self.radius * self.radius;
|
|
if d2 > radius2 {
|
|
return None;
|
|
}
|
|
let thc = (radius2 - d2).sqrt();
|
|
let t0 = adj - thc;
|
|
let t1 = adj + thc;
|
|
|
|
if t0 < 0.0 && t1 < 0.0 {
|
|
return None;
|
|
}
|
|
|
|
let distance = if t0 < t1 { t0 } else { t1 };
|
|
Some(distance)
|
|
}
|
|
|
|
fn surface_normal(&self, hit_point: &Point) -> Vector3 {
|
|
Vector3::from(*hit_point - self.center).normalize()
|
|
}
|
|
}
|
|
|
|
pub struct Plane {
|
|
pub origin: Point,
|
|
pub normal: Vector3,
|
|
pub color: Color,
|
|
}
|
|
|
|
impl Intersectable for Plane {
|
|
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
|
let normal = &self.normal;
|
|
let denom = normal.dot(&ray.direction);
|
|
if denom > 1e-6 {
|
|
let v: Vector3 = (self.origin - ray.origin).into();
|
|
let distance = v.dot(&normal) / denom;
|
|
if distance >= 0.0 {
|
|
return Some(distance);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
fn surface_normal(&self, _: &Point) -> Vector3 {
|
|
-self.normal
|
|
}
|
|
}
|
|
|
|
pub enum Element {
|
|
Sphere(Sphere),
|
|
Plane(Plane),
|
|
}
|
|
|
|
impl Element {
|
|
pub fn color(&self) -> &Color {
|
|
match *self {
|
|
Element::Sphere(ref s) => &s.color,
|
|
Element::Plane(ref p) => &p.color,
|
|
}
|
|
}
|
|
pub fn surface_normal(&self, hit_point: &Point) -> Vector3 {
|
|
match *self {
|
|
Element::Sphere(ref s) => s.surface_normal(hit_point),
|
|
Element::Plane(ref p) => p.surface_normal(hit_point),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Intersectable for Element {
|
|
fn intersect(&self, ray: &Ray) -> Option<f32> {
|
|
match *self {
|
|
Element::Sphere(ref s) => s.intersect(ray),
|
|
Element::Plane(ref p) => p.intersect(ray),
|
|
}
|
|
}
|
|
|
|
fn surface_normal(&self, hit_point: &Point) -> Vector3 {
|
|
match *self {
|
|
Element::Sphere(ref s) => s.surface_normal(hit_point),
|
|
Element::Plane(ref p) => p.surface_normal(hit_point),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct Ray {
|
|
pub origin: Point,
|
|
pub direction: Vector3,
|
|
}
|
|
|
|
impl Ray {
|
|
pub fn create_prime(x: u32, y: u32, scene: &Scene) -> Ray {
|
|
assert!(scene.width > scene.height);
|
|
let fov_adjustment = (scene.fov.to_radians() / 2.0).tan();
|
|
let aspect_ratio = (scene.width as f32) / (scene.height as f32);
|
|
let sensor_x =
|
|
((((x as f32 + 0.5) / scene.width as f32) * 2.0 - 1.0) * aspect_ratio) * fov_adjustment;
|
|
let sensor_y = (1.0 - ((y as f32 + 0.5) / scene.height as f32) * 2.0) * fov_adjustment;
|
|
|
|
Ray {
|
|
origin: Point::zero(),
|
|
direction: Vector3 {
|
|
x: sensor_x,
|
|
y: sensor_y,
|
|
z: -1.0,
|
|
}.normalize(),
|
|
}
|
|
}
|
|
}
|