diff --git a/src/lib.rs b/src/lib.rs index 048a7cb..e46923c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -234,30 +234,45 @@ impl Core { const BASE: Sphere = Sphere { position: vec3(0., 0., -BASE_R), radius: BASE_R, + color: vec3(0.1, 0.5, 0.0), }; - fn sphere(pos: Vec3) -> Sphere { + fn sphere(pos: Vec3, color: Vec3) -> Sphere { Sphere { position: pos, radius: BASE_POS.distance(pos) - BASE_R, + color, } } 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)), + sphere(vec3(0., 0., 0.1), vec3(1., 0.3, 0.0)), + sphere(vec3(0.3, 0., 0.1), vec3(0.3, 1., 0.0)), + sphere(vec3(0.1, 0.3, 0.1), vec3(0.3, 0.0, 1.)), ], }; + #[derive(Debug, Clone, Copy)] + struct LightHit<'a> { + incident: Ray, + normal: Vec3, + light: Vec3, + object: &'a Sphere, + } let mut prng = rand_pcg::Pcg64::new(42, 0); let source_rays: Vec = (0..10240).map(|_| source.make_ray(&mut prng)).collect(); let camera_rays: Vec = (0..10240).map(|_| camera.make_ray(&mut prng)).collect(); let mut source_ray_display: Vec = Vec::with_capacity(source_rays.len()); - let mut hits: Vec = Vec::with_capacity(source_rays.len()); + let mut hits: Vec = Vec::with_capacity(source_rays.len()); for ray in source_rays { + let light = Vec3::splat(1.); if let Some(hit) = scene.trace_ray(ray) { - hits.push(hit); + hits.push(LightHit { + incident: hit.incident, + normal: hit.normal, + light, + object: hit.object, + }); if args.show_hit_emission { source_ray_display.extend([ Vertex { @@ -274,7 +289,7 @@ impl Core { source_ray_display.extend([ Vertex { pos: hit.incident.base - 0.02 * hit.incident.dir, - color: vec3(0., 0., 1.), + color: light, }, Vertex { pos: hit.incident.base, @@ -300,20 +315,26 @@ impl Core { if args.reflections > 0 { let mut hits1 = hits.clone(); for _ in 0..args.reflections { - let mut hits2: Vec = Vec::with_capacity(hits1.len()); + 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); let Some(hit2) = scene.trace_ray(ray) else { continue; }; - hits2.push(hit2); + hits2.push(LightHit { + incident: hit2.incident, + normal: hit2.normal, + light: light, + object: hit2.object, + }); if args.show_indirect_hit { source_ray_display.extend([ Vertex { pos: hit2.incident.base - 0.02 * hit2.incident.dir, - color: vec3(1., 0., 1.), + color: light, }, Vertex { pos: hit2.incident.base, @@ -330,8 +351,8 @@ impl Core { let sigma2 = args.accum_sigma.powi(2); let accum_normalizator = (2. * PI * sigma2).recip(); let hits = &hits; - move |hit: Hit| -> f32 { - let mut total_cd = 0.0f32; + move |hit: Hit| -> Vec3 { + let mut total_cd = Vec3::splat(0.); for light_hit in hits { let d2 = hit.incident.base.distance_squared(light_hit.incident.base); if d2 > 9. * sigma2 { @@ -344,16 +365,18 @@ impl Core { assert!(normal.is_normalized()); assert!(hit.incident.dir.is_normalized()); let reflector = Lambertian; - let in_lm = 1.0; - let out_cd = - in_lm * reflector.brdf(normal, light_hit.incident.dir, -hit.incident.dir); + let in_lm = light_hit.light; + let out_cd = in_lm + * reflector.brdf(normal, light_hit.incident.dir, -hit.incident.dir) + * hit.object.color; let weight = accum_normalizator * (-0.5 * d2 / sigma2).exp(); total_cd += weight * out_cd; } total_cd * args.accum_scale } }; - let colormap = |light: f32| { + let colormap = |light: Vec3| { + let light = light.dot(Vec3::splat(1. / 3.)); let brightness = 3. * (1. - (1. + light).recip()); vec3(brightness, brightness - 1., brightness - 2.) .clamp(Vec3::splat(0.), Vec3::splat(1.)) @@ -397,11 +420,11 @@ impl Core { if !dir.is_finite() { dir = camera.direction(); } - let light = light_at(Hit { + let color = light_at(Hit { incident: Ray::new(pos, dir), normal, + object: obj, }); - let color = Vec3::splat(light); render::faces::Vertex { pos, color } }) .collect::>(), diff --git a/src/trace.rs b/src/trace.rs index 984f9d9..6f55949 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -76,6 +76,7 @@ impl Source { pub struct Sphere { pub position: Vec3, pub radius: f32, + pub color: Vec3, } struct Hit1 { @@ -113,26 +114,28 @@ pub struct Scene { } #[derive(Debug, Clone, Copy)] -pub struct Hit { +pub struct Hit<'a> { pub incident: Ray, pub normal: Vec3, + pub object: &'a Sphere, } impl Scene { - pub fn trace_ray(&self, ray: Ray) -> Option { + pub fn trace_ray(&self, ray: Ray) -> Option> { const EPS: f32 = -1e-3; let hit = self .objects .iter() - .filter_map(|obj| obj.trace_ray(ray)) - .filter(|h| h.dist >= EPS) - .min_by(|a, b| f32::total_cmp(&a.dist, &b.dist))?; + .filter_map(|obj| obj.trace_ray(ray).map(|hit| (obj, hit))) + .filter(|(_obj, h)| h.dist >= EPS) + .min_by(|a, b| f32::total_cmp(&a.1.dist, &b.1.dist))?; Some(Hit { incident: Ray { - base: hit.pos, + base: hit.1.pos, dir: ray.dir, }, - normal: hit.normal, + normal: hit.1.normal, + object: hit.0, }) } }