raytracers/bheisler/src/rendering.rs

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(),
}
}
}