diff --git a/pathtracer/src/render/bidirectional/bidirectional_pathtracer.rs b/pathtracer/src/render/bidirectional/bidirectional_pathtracer.rs index 91fbf5b..8e3ca12 100644 --- a/pathtracer/src/render/bidirectional/bidirectional_pathtracer.rs +++ b/pathtracer/src/render/bidirectional/bidirectional_pathtracer.rs @@ -48,7 +48,7 @@ impl BidirectionalPathtracer { res.push_point(p); - let (new_direction, _) = sample_hemisphere(normal); + let new_direction = sample_hemisphere(normal); // Calculate the incoming light along the new ray origin = hit_pos + new_direction.as_ref() * 0.001; direction = new_direction; diff --git a/pathtracer/src/render/pathtrace/pathtracer.rs b/pathtracer/src/render/pathtrace/pathtracer.rs index 538d0fc..d300bee 100644 --- a/pathtracer/src/render/pathtrace/pathtracer.rs +++ b/pathtracer/src/render/pathtrace/pathtracer.rs @@ -110,8 +110,7 @@ impl Pathtracer { let brdf = properties.diffuse; // Pick a new direction let normal = obj.shape.normal(&hit_pos); - let (new_direction, weight) = sample_hemisphere(normal); - let cos_new_ray = new_direction.dot(&normal); + let new_direction = sample_hemisphere(normal); // Calculate the incoming light along the new ray let new_ray = Ray::new(hit_pos + new_direction.as_ref() * 0.001, new_direction); let incoming = self @@ -120,7 +119,8 @@ impl Pathtracer { self.radiance(new_ray, t, obj, limit - 1) }); // Put it all together - properties.emitted + (brdf * incoming * cos_new_ray * weight) + // The weight of the sample and the cosine of the new ray cancel each other out + properties.emitted + (brdf * incoming) } fn cast_ray(&self, ray: Ray) -> Option<(f32, &Object)> { diff --git a/pathtracer/src/render/utils.rs b/pathtracer/src/render/utils.rs index 1fdbaf6..0d37da1 100644 --- a/pathtracer/src/render/utils.rs +++ b/pathtracer/src/render/utils.rs @@ -70,9 +70,9 @@ impl RefractionInfo { } } -/// Returns a random ray in the hemisphere described by a normal unit-vector, and the probability -/// to have picked that direction. -pub fn sample_hemisphere(normal: Unit) -> (Unit, f32) { +/// Returns a random ray in the hemisphere described by a normal unit-vector +/// It is cosine-sampled, which is convenient for path-tracing. +pub fn sample_hemisphere(normal: Unit) -> Unit { let mut rng = thread_rng(); let azimuth = rng.gen::() * std::f32::consts::PI * 2.; // Cosine weighted importance sampling @@ -93,15 +93,13 @@ pub fn sample_hemisphere(normal: Unit) -> (Unit, f32) { let normal_b = normal.cross(&normal_t); // Perform the matrix calculation by hand... - let scattered = Unit::new_normalize(Vector::new( + // The probability to have picked the ray is inversely proportional to cosine of the angle with + // the normal + Unit::new_normalize(Vector::new( x * normal_b.x + y * normal.x + z * normal_t.x, x * normal_b.y + y * normal.y + z * normal_t.y, x * normal_b.z + y * normal.z + z * normal_t.z, - )); - - // The probability to have picked the ray is inversely proportional to cosine of the angle with - // the normal - (scattered, (1. / scattered.dot(&normal)).min(f32::MAX)) + )) } pub fn buffer_to_image(buffer: &[LinearColor], passes: u32, width: u32, height: u32) -> RgbImage {