feat(raytracer): add minimal implementation
This commit is contained in:
5
src/bin/rt_demo.rs
Normal file
5
src/bin/rt_demo.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
use lispers::raytracer::{scene::Scene, types::Light};
|
||||
|
||||
fn main() {
|
||||
let scene = Scene::new();
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod lisp;
|
||||
pub mod parser;
|
||||
pub mod raytracer;
|
||||
|
||||
70
src/raytracer/camera.rs
Normal file
70
src/raytracer/camera.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
use super::{
|
||||
scene::Scene,
|
||||
types::{Point3, Ray, Scalar, Vector3},
|
||||
};
|
||||
// use image::Rgb32FImage;
|
||||
|
||||
pub struct Camera {
|
||||
position: Point3,
|
||||
up: Vector3,
|
||||
right: Vector3,
|
||||
upper_left: Point3,
|
||||
x_dir: Vector3,
|
||||
y_dir: Vector3,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn new(
|
||||
position: Point3,
|
||||
direction: Vector3,
|
||||
up: Vector3,
|
||||
fovy: Scalar,
|
||||
width: usize,
|
||||
height: usize,
|
||||
) -> Camera {
|
||||
let aspect_ratio = width as Scalar / height as Scalar;
|
||||
let fovx = fovy * aspect_ratio;
|
||||
let right = direction.cross(&up).normalize();
|
||||
let x_dir = right * (fovx / 2.0).tan();
|
||||
let y_dir = -up * (fovy / 2.0).tan();
|
||||
let upper_left = position + direction - x_dir + y_dir;
|
||||
|
||||
Camera {
|
||||
position,
|
||||
up,
|
||||
right,
|
||||
upper_left,
|
||||
x_dir,
|
||||
y_dir,
|
||||
width,
|
||||
height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Camera {
|
||||
pub fn ray_at_relative(&self, x: Scalar, y: Scalar) -> Ray {
|
||||
let x_dir = self.x_dir * x;
|
||||
let y_dir = self.y_dir * y;
|
||||
Ray::new(
|
||||
self.position,
|
||||
(self.upper_left + x_dir - y_dir - self.position).normalize(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn ray_at(&self, x: usize, y: usize) -> Ray {
|
||||
let x = x as Scalar / self.width as Scalar;
|
||||
let y = y as Scalar / self.height as Scalar;
|
||||
self.ray_at_relative(x, y)
|
||||
}
|
||||
|
||||
// pub fn render(&self, scene: &Scene, depth: u32) -> Rgb32FImage {
|
||||
// Rgb32FImage::from_fn(self.width, self.height, |x, y| {
|
||||
// let ray = self.ray_at(x, y);
|
||||
// let color = scene.trace(&ray, depth);
|
||||
// color.into()
|
||||
// })
|
||||
// }
|
||||
}
|
||||
5
src/raytracer/mod.rs
Normal file
5
src/raytracer/mod.rs
Normal file
@@ -0,0 +1,5 @@
|
||||
pub mod camera;
|
||||
pub mod scene;
|
||||
pub mod sphere;
|
||||
pub mod types;
|
||||
pub mod vec;
|
||||
89
src/raytracer/scene.rs
Normal file
89
src/raytracer/scene.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use super::types::Color;
|
||||
use super::types::Intersect;
|
||||
use super::types::Light;
|
||||
use super::types::Material;
|
||||
use super::types::Point3;
|
||||
use super::types::Ray;
|
||||
use super::types::Scalar;
|
||||
use super::types::Vector3;
|
||||
use super::vec::reflect;
|
||||
extern crate nalgebra as na;
|
||||
|
||||
pub struct Scene {
|
||||
objects: Vec<Box<dyn Intersect>>,
|
||||
lights: Vec<Light>,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn new() -> Scene {
|
||||
Scene {
|
||||
objects: Vec::new(),
|
||||
lights: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_object(&mut self, obj: Box<dyn Intersect>) {
|
||||
self.objects.push(obj);
|
||||
}
|
||||
|
||||
pub fn add_light(&mut self, light: Light) {
|
||||
self.lights.push(light);
|
||||
}
|
||||
|
||||
pub fn trace(&self, ray: &Ray, depth: u32) -> Color {
|
||||
if depth == 0 {
|
||||
return na::Vector3::new(0.0, 0.0, 0.0);
|
||||
}
|
||||
|
||||
match self
|
||||
.objects
|
||||
.iter()
|
||||
.filter_map(|obj| obj.intersect(ray))
|
||||
.min_by(|(_, _, t1, _), (_, _, t2, _)| t1.partial_cmp(t2).unwrap())
|
||||
{
|
||||
None => {
|
||||
return na::Vector3::new(0.0, 0.0, 0.0);
|
||||
}
|
||||
Some((isect_pt, isect_norm, isect_dist, material)) => {
|
||||
// Lighting of material at the intersection point
|
||||
let color =
|
||||
self.lighting(-&ray.direction, material, isect_pt, isect_norm, isect_dist);
|
||||
|
||||
// Calculate reflections, if the material has mirror properties
|
||||
if material.mirror > 0.0 {
|
||||
let new_ray = Ray {
|
||||
origin: isect_pt,
|
||||
direction: reflect(ray.direction, isect_norm),
|
||||
};
|
||||
return (1.0 - material.mirror) * color
|
||||
+ material.mirror * self.trace(&new_ray, depth - 1);
|
||||
} else {
|
||||
return color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lighting(
|
||||
&self,
|
||||
view: Vector3,
|
||||
material: &Material,
|
||||
isect_pt: Point3,
|
||||
isect_norm: Vector3,
|
||||
isect_dist: Scalar,
|
||||
) -> Color {
|
||||
let mut color: Color = na::Vector3::new(0.0, 0.0, 0.0);
|
||||
|
||||
for light in &self.lights {
|
||||
let l = (isect_pt - light.position).normalize();
|
||||
let cos_theta = l.dot(&isect_norm);
|
||||
|
||||
if cos_theta > 0.0 {
|
||||
// Diffuse
|
||||
color += material.diffuse_color.component_mul(&light.color) * cos_theta;
|
||||
}
|
||||
}
|
||||
|
||||
color
|
||||
}
|
||||
}
|
||||
49
src/raytracer/sphere.rs
Normal file
49
src/raytracer/sphere.rs
Normal file
@@ -0,0 +1,49 @@
|
||||
use super::types::{Intersect, Material, Point3, Ray, Scalar, Vector3};
|
||||
|
||||
extern crate nalgebra as na;
|
||||
|
||||
pub struct Sphere {
|
||||
center: Point3,
|
||||
radius: Scalar,
|
||||
material: Material,
|
||||
}
|
||||
|
||||
/// Numerical error tolerance
|
||||
const EPSILON: Scalar = 1e-5;
|
||||
|
||||
impl Intersect for Sphere {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, &'a Material)> {
|
||||
let co = ray.origin - self.center;
|
||||
|
||||
let a = ray.direction.dot(&ray.direction);
|
||||
let b = 2.0 * ray.direction.dot(&co);
|
||||
let c = co.dot(&co) - (self.radius * self.radius);
|
||||
let d = b * b - 4.0 * a * c;
|
||||
|
||||
if d >= 0.0 {
|
||||
let e = d.sqrt();
|
||||
let t1 = (-b - e) / (2.0 * a);
|
||||
let t2 = (-b + e) / (2.0 * a);
|
||||
let mut t = Scalar::MAX;
|
||||
|
||||
if t1 > EPSILON && t1 < t {
|
||||
t = t1;
|
||||
}
|
||||
if t2 > EPSILON && t2 < t {
|
||||
t = t2;
|
||||
}
|
||||
|
||||
if t < Scalar::MAX {
|
||||
let isect_pt: Point3 = ray.origin + ray.direction * t;
|
||||
return Some((
|
||||
isect_pt,
|
||||
(isect_pt - self.center) / self.radius,
|
||||
t,
|
||||
&self.material,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
58
src/raytracer/types.rs
Normal file
58
src/raytracer/types.rs
Normal file
@@ -0,0 +1,58 @@
|
||||
extern crate nalgebra as na;
|
||||
|
||||
pub type Scalar = f32;
|
||||
pub type Vector3 = na::Vector3<Scalar>;
|
||||
pub type Point3 = na::Point3<Scalar>;
|
||||
pub type Color = Vector3;
|
||||
|
||||
pub trait Intersect {
|
||||
fn intersect<'a>(&'a self, ray: &Ray) -> Option<(Point3, Vector3, Scalar, &'a Material)>;
|
||||
}
|
||||
|
||||
pub struct Light {
|
||||
pub position: Point3,
|
||||
pub color: Color,
|
||||
}
|
||||
|
||||
impl Light {
|
||||
pub fn new(position: Point3, color: Color) -> Light {
|
||||
Light { position, color }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Ray {
|
||||
pub origin: Point3,
|
||||
pub direction: Vector3,
|
||||
}
|
||||
|
||||
impl Ray {
|
||||
pub fn new(origin: Point3, direction: Vector3) -> Ray {
|
||||
Ray { origin, direction }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Material {
|
||||
pub ambient_color: Color,
|
||||
pub diffuse_color: Color,
|
||||
pub specular_color: Color,
|
||||
pub shinyness: Scalar,
|
||||
pub mirror: Scalar,
|
||||
}
|
||||
|
||||
impl Material {
|
||||
pub fn new(
|
||||
ambient_color: Color,
|
||||
diffuse_color: Color,
|
||||
specular_color: Color,
|
||||
shinyness: Scalar,
|
||||
mirror: Scalar,
|
||||
) -> Material {
|
||||
Material {
|
||||
ambient_color,
|
||||
diffuse_color,
|
||||
specular_color,
|
||||
shinyness,
|
||||
mirror,
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/raytracer/vec.rs
Normal file
14
src/raytracer/vec.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use super::types::Vector3;
|
||||
|
||||
extern crate nalgebra as na;
|
||||
|
||||
pub fn reflect(v: Vector3, n: Vector3) -> Vector3 {
|
||||
v - 2.0 * v.dot(&n) * n
|
||||
}
|
||||
|
||||
pub fn rotate(v: &Vector3, axis: &Vector3, angle: f32) -> Vector3 {
|
||||
//let axis = na::Unit::new_normalize(axis);
|
||||
//let rot = na::Rotation3::from_axis_angle(&axis, angle);
|
||||
//(rot * v)
|
||||
todo!()
|
||||
}
|
||||
Reference in New Issue
Block a user