diff --git a/src/main.rs b/src/main.rs index da50183..4491e80 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::{convert::identity, error::Error, f32::consts::PI, sync::Arc}; -use glam::{Mat4, vec3}; +use glam::{Mat4, Vec3, vec3}; use winit::{ application::ApplicationHandler, event::WindowEvent, @@ -13,7 +13,7 @@ use winit::{ use crate::{ camera::OrbitalCamera, render::lines::{LookParams, Mesh, Pipeline, Vertex}, - trace::Source, + trace::{Scene, Source, Sphere}, }; mod camera; @@ -155,20 +155,54 @@ impl MainWindow { self.pipeline .render(&mut pass, [&Mesh::new(&self.device, &contour)]); + const BASE_R: f32 = 2.; + const BASE_POS: Vec3 = vec3(0., 0., -BASE_R); + const BASE: Sphere = Sphere { + position: vec3(0., 0., -BASE_R), + radius: BASE_R, + }; + fn sphere(pos: Vec3) -> Sphere { + Sphere { + position: pos, + radius: BASE_POS.distance(pos) - BASE_R, + } + } + let scene = Scene { + objects: vec![ + BASE, + sphere(vec3(0., 0., 0.1)), + sphere(vec3(0.3, 0., 0.1)), + sphere(vec3(0.1, 0.3, 0.1)), + ], + }; + let mut prng = rand_pcg::Pcg64::new(42, 0); - let rays: Vec = (0..1000) + let rays: Vec = (0..10000) .flat_map(|_| { let ray = source.make_ray(&mut prng); - [ - Vertex { - pos: ray.base, - color: vec3(0., 0., 0.), - }, - Vertex { - pos: ray.base + 0.1 * ray.dir, - color: vec3(1., 1., 1.), - }, - ] + if let Some(ray) = scene.trace_ray(ray) { + [ + Vertex { + pos: ray.base - 0.02 * ray.dir, + color: vec3(1., 1., 1.), + }, + Vertex { + pos: ray.base, + color: vec3(0., 1., 0.), + }, + ] + } else { + [ + Vertex { + pos: ray.base, + color: vec3(1., 1., 1.), + }, + Vertex { + pos: ray.base + 0.1 * ray.dir, + color: vec3(1., 0., 0.), + }, + ] + } }) .collect(); self.pipeline diff --git a/src/ray.rs b/src/ray.rs index 265dd96..2617a75 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -1,6 +1,27 @@ use glam::Vec3; +#[derive(Debug, Clone, Copy)] pub struct Ray { pub base: Vec3, pub dir: Vec3, } + +impl Ray { + pub fn new(base: Vec3, dir: Vec3) -> Self { + Ray { base, dir } + } + + pub fn normalize(self) -> Self { + Self { + base: self.base, + dir: self.dir.normalize(), + } + } + + pub fn advance(self, amount: f32) -> Self { + Self { + base: self.base + amount * self.dir, + dir: self.dir, + } + } +} diff --git a/src/trace.rs b/src/trace.rs index 196dc67..b1945c2 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -71,3 +71,45 @@ impl Source { } } } + +#[derive(Debug, Clone)] +pub struct Sphere { + pub position: Vec3, + pub radius: f32, +} + +impl Sphere { + fn trace_ray(&self, ray: Ray) -> Option { + // let t: f32; + // let hit = ray.base + t * ray.dir; + // (hit - self.position).length() == self.radius; + let Ray { base, dir } = ray; + let rel = base - self.position; + // (rel + t⋅dir)² == r² + // rel² − r² + 2⋅t⋅rel⋅dir + t²⋅dir² = 0 + let a = dir.length_squared(); + let b2 = rel.dot(dir); + let c = rel.length_squared() - self.radius.powi(2); + let d4 = b2.powi(2) - a * c; + if d4 < 0. { + return None; + } + Some((-b2 - d4.sqrt()) / a) + } +} + +#[derive(Debug)] +pub struct Scene { + pub objects: Vec, +} + +impl Scene { + pub fn trace_ray(&self, ray: Ray) -> Option { + let dist = self + .objects + .iter() + .filter_map(|obj| obj.trace_ray(ray)) + .min_by(f32::total_cmp); + Some(ray.advance(dist?)) + } +}