feat(raytracer): add minimal implementation

This commit is contained in:
Jonas Röger 2024-11-17 14:03:05 +01:00
parent 369293598f
commit a1ccb33a6e
Signed by: jonas
GPG Key ID: 4000EB35E1AE0F07
10 changed files with 443 additions and 0 deletions

151
Cargo.lock generated
View File

@ -2,6 +2,15 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "as-any"
version = "0.3.1"
@ -20,6 +29,12 @@ version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
[[package]]
name = "bytemuck"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d"
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -133,15 +148,53 @@ version = "0.1.0"
dependencies = [
"as-any",
"futures",
"nalgebra",
"nix",
]
[[package]]
name = "matrixmultiply"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a"
dependencies = [
"autocfg",
"rawpointer",
]
[[package]]
name = "memchr"
version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "nalgebra"
version = "0.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b"
dependencies = [
"approx",
"matrixmultiply",
"nalgebra-macros",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nalgebra-macros"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "nix"
version = "0.29.0"
@ -154,6 +207,60 @@ dependencies = [
"libc",
]
[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
dependencies = [
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
dependencies = [
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "paste"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
name = "pin-project-lite"
version = "0.2.15"
@ -184,6 +291,34 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rawpointer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "safe_arch"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3460605018fdc9612bce72735cba0d27efbcd9904780d44c7e3a9948f96148a"
dependencies = [
"bytemuck",
]
[[package]]
name = "simba"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3a386a501cd104797982c15ae17aafe8b9261315b5d07e3ec803f2ea26be0fa"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]]
name = "slab"
version = "0.4.9"
@ -204,8 +339,24 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "typenum"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "wide"
version = "0.7.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b828f995bf1e9622031f8009f8481a85406ce1f4d4588ff746d872043e855690"
dependencies = [
"bytemuck",
"safe_arch",
]

View File

@ -21,4 +21,5 @@ path = "src/bin/repl.rs"
[dependencies]
as-any = "0.3.1"
futures = "0.3.30"
nalgebra = "0.33.2"
nix = "0.29.0"

5
src/bin/rt_demo.rs Normal file
View File

@ -0,0 +1,5 @@
use lispers::raytracer::{scene::Scene, types::Light};
fn main() {
let scene = Scene::new();
}

View File

@ -1,2 +1,3 @@
pub mod lisp;
pub mod parser;
pub mod raytracer;

70
src/raytracer/camera.rs Normal file
View 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
View 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
View 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
View 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
View 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
View 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!()
}