diff --git a/README.md b/README.md index 69f2a64..97c38cd 100644 --- a/README.md +++ b/README.md @@ -1 +1,5 @@ -# Photon Light +# Forward ray tracing demo + +![Screenshot](screenshot.jpg) + +A simple forward ray tracing demo. “Forward” means the rays are traced starting from the light (unlike common “reverse” ray tracing where rays are traced from the camera). diff --git a/screenshot.jpg b/screenshot.jpg new file mode 100644 index 0000000..dd2f6d9 Binary files /dev/null and b/screenshot.jpg differ diff --git a/src/lib.rs b/src/lib.rs index 8fc132d..06b9d40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,7 @@ use crate::{ DEPTH_FORMAT, OUTPUT_FORMAT, lines::{LookParams, Mesh, Pipeline, Vertex}, }, - trace::{Hit, Lambertian, Reflector, Scene, Source, Sphere}, + trace::{DiscReflector, Hit, Lambertian, Reflector, Scene, Source, Sphere}, }; pub use crate::ffi::{RedrawArgs, SphericalPosition, UseNormal}; @@ -370,12 +370,12 @@ impl Core { } } } + let reflector = DiscReflector { roughness: 0.25 }; if args.reflections > 0 { let mut hits1 = hits.clone(); for _ in 0..args.reflections { let mut hits2: Vec = Vec::with_capacity(hits1.len()); for hit in &hits1 { - let reflector = Lambertian; let reflected = reflector.reflect(&mut prng, hit.normal, hit.incident.dir); let light = hit.light * hit.object.color; let ray = Ray::new(hit.incident.base, reflected); @@ -422,7 +422,6 @@ impl Core { }; assert!(normal.is_normalized()); assert!(hit.incident.dir.is_normalized()); - let reflector = Lambertian; let in_lm = light_hit.light; let out_cd = in_lm * reflector.brdf(normal, light_hit.incident.dir, -hit.incident.dir) diff --git a/src/trace.rs b/src/trace.rs index 6f55949..bf87448 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -5,6 +5,10 @@ use rand_distr::{Distribution, UnitSphere}; use crate::{camera::OrbitalCamera, ray::Ray}; +pub use reflective::DiscReflector; + +mod reflective; + #[derive(Debug, Clone)] pub struct Source { /// Horizontal position (angle), in radians from +X towards +Y. diff --git a/src/trace/reflective.rs b/src/trace/reflective.rs new file mode 100644 index 0000000..b3a365f --- /dev/null +++ b/src/trace/reflective.rs @@ -0,0 +1,46 @@ +use std::f32::consts::PI; + +use glam::Vec3; +use rand_distr::{Distribution, Uniform, UnitSphere}; + +use super::Reflector; + +#[derive(Debug, Clone)] +pub struct DiscReflector { + pub roughness: f32, +} + +impl Reflector for DiscReflector { + fn brdf(&self, normal: Vec3, incident: Vec3, reflected: Vec3) -> f32 { + let specular = incident.reflect(normal); + let convergence = reflected.dot(specular); + if convergence <= 1. - self.roughness { + return 0.; + } + let c = -incident.dot(normal) * reflected.dot(normal); + if c < 0. { + return 0.; + } + 0.5 / (self.roughness * PI * (specular.dot(normal) * reflected.dot(normal)).sqrt()) + } + + fn reflect(&self, rgen: &mut impl rand::Rng, normal: Vec3, incident: Vec3) -> Vec3 { + let specular = incident.reflect(normal); + + let dist_theta = Uniform::new(0., (1. - self.roughness).acos()).unwrap(); + let dist_sphere = UnitSphere; + + loop { + let theta: f32 = dist_theta.sample(rgen); + let (xy, z) = theta.sin_cos(); + + let spherical: Vec3 = dist_sphere.sample(rgen).into(); + let off_dir = spherical.reject_from_normalized(specular).normalize(); + + let reflected = xy * off_dir + z * specular; + if reflected.dot(normal) >= 0. { + return reflected; + } + } + } +}