diff --git a/kdtree-tester-cli/src/ray_bouncer.rs b/kdtree-tester-cli/src/ray_bouncer.rs index 419cd4e..ee2d8b7 100644 --- a/kdtree-tester-cli/src/ray_bouncer.rs +++ b/kdtree-tester-cli/src/ray_bouncer.rs @@ -83,8 +83,8 @@ impl RayBouncer { .lights .iter() .filter_map(|light| { - let shadow_ray = Ray::between(point_above, light.sample(&mut rng)); - let shadow = self.checked_ray_intersect(&shadow_ray, 0.0..=1.0); + let (shadow_ray, t_range) = light.sample_shadow_ray(point_above, &mut rng); + let shadow = self.checked_ray_intersect(&shadow_ray, t_range); (!shadow.is_valid()).then_some(shadow) }) .collect::>(); diff --git a/material-tester-cli/src/main.rs b/material-tester-cli/src/main.rs index 6f4159a..6b5a126 100644 --- a/material-tester-cli/src/main.rs +++ b/material-tester-cli/src/main.rs @@ -17,7 +17,7 @@ use std::{ use time::Duration; use tracing::{ camera::{Camera, Pinhole}, - light::{Light, PointLight}, + light::{DirectionalLight, Light}, material::Material, pathtracer::Pathtracer, worker::render_parallel_iterations, @@ -172,9 +172,13 @@ fn main() { let materials = (0..spheres.len()) .map(|i| material(i as f32 * 1.0 / (spheres.len() - 1) as f32)) .collect(); - let lights = [PointLight { - center: Vec3::new(-10.0, -5.0, 1.0) * 100.0, - intensity: Vec3::ONE * 100.0 * 100.0f32.powi(2), + //let lights = [PointLight { + // center: Vec3::new(-10.0, -5.0, 1.0) * 100.0, + // intensity: Vec3::ONE * 100.0 * 100.0f32.powi(2), + //}]; + let lights = [DirectionalLight { + direction: Vec3::new(-1.0, -1.0, 1.0), + intensity: Vec3::ONE, }]; let accelerator = NoAccelerator {}; let pathtracer = Pathtracer { diff --git a/tracing/src/light.rs b/tracing/src/light.rs index 5469976..0a4ca49 100644 --- a/tracing/src/light.rs +++ b/tracing/src/light.rs @@ -1,3 +1,6 @@ +use std::ops::RangeInclusive; + +use geometry::ray::Ray; use glam::Vec3; use rand::rngs::SmallRng; use wavefront::mtl::{self}; @@ -11,9 +14,13 @@ pub struct PointLight { } impl PointLight { - pub fn emitted(&self, point: &Vec3) -> Vec3 { + fn emitted(&self, point: &Vec3) -> Vec3 { self.intensity / (self.center - point).length_squared() } + + fn sample(&self) -> (Vec3, RangeInclusive) { + (self.center, 0.0..=1.0) + } } #[derive(Clone, Debug)] @@ -23,8 +30,22 @@ pub struct SphericalLight { } impl SphericalLight { - pub fn sample(&self, rng: &mut SmallRng) -> Vec3 { - self.point.center + uniform_sample_unit_sphere(rng) * self.radius + #[inline] + fn sample(&self, rng: &mut SmallRng) -> (Vec3, RangeInclusive) { + let target = self.point.center + uniform_sample_unit_sphere(rng) * self.radius; + (target, 0.0..=1.0) + } +} + +#[derive(Clone, Debug)] +pub struct DirectionalLight { + pub direction: Vec3, + pub intensity: Vec3, +} + +impl DirectionalLight { + fn sample(&self, point: Vec3) -> (Vec3, RangeInclusive) { + (point - self.direction, 0.0f32..=f32::MAX) } } @@ -32,21 +53,26 @@ impl SphericalLight { pub enum Light { PointLight(PointLight), SphericalLight(SphericalLight), + DirectionalLight(DirectionalLight), } impl Light { + #[inline] pub fn emitted(&self, point: &Vec3) -> Vec3 { match self { Light::PointLight(light) => light.emitted(point), Light::SphericalLight(light) => light.point.emitted(point), + Light::DirectionalLight(light) => light.intensity, } } - pub fn sample(&self, rng: &mut SmallRng) -> Vec3 { - match self { - Light::PointLight(light) => light.center, + pub fn sample_shadow_ray(&self, point: Vec3, rng: &mut SmallRng) -> (Ray, RangeInclusive) { + let (target, t_range) = match self { + Light::PointLight(light) => light.sample(), Light::SphericalLight(light) => light.sample(rng), - } + Light::DirectionalLight(light) => light.sample(point), + }; + (Ray::between(point, target), t_range) } } @@ -73,3 +99,9 @@ impl From for Light { Self::SphericalLight(value) } } + +impl From for Light { + fn from(value: DirectionalLight) -> Self { + Self::DirectionalLight(value) + } +} diff --git a/tracing/src/pathtracer.rs b/tracing/src/pathtracer.rs index 7d372ce..4200cfb 100644 --- a/tracing/src/pathtracer.rs +++ b/tracing/src/pathtracer.rs @@ -73,10 +73,10 @@ where .iter() .map(|light| { // TODO: Offset should depend on incoming direction, not only surface normal. - let shadow_ray = Ray::between(point_above, light.sample(rng)); + let (shadow_ray, t_range) = light.sample_shadow_ray(point_above, rng); let intersection = self.accelerator - .intersect(&self.geometries, &shadow_ray, 0.0..=1.0); + .intersect(&self.geometries, &shadow_ray, t_range); ray_logger .log_shadow(&shadow_ray, bounce, intersection.is_some()) .unwrap();