diff --git a/shared/src/core/bsdf.rs b/shared/src/core/bsdf.rs index 7681eac..561f681 100644 --- a/shared/src/core/bsdf.rs +++ b/shared/src/core/bsdf.rs @@ -118,10 +118,7 @@ impl BSDF { } pub fn regularize(&mut self) { - if !self.is_valid() { - let bxdf = &mut self.bxdf; - bxdf.regularize(); - } + self.bxdf.regularize(); } } diff --git a/shared/src/core/material.rs b/shared/src/core/material.rs index 916cb4d..55b7e86 100644 --- a/shared/src/core/material.rs +++ b/shared/src/core/material.rs @@ -195,6 +195,13 @@ pub enum Material { Mix(MixMaterial), } +impl Material { + #[inline(never)] + pub fn is_conductor(&self) -> bool { + matches!(self, Material::Conductor(_)) + } +} + // TODO: THIS IS A HACK JUST FOR TESTING impl PartialEq for Material { fn eq(&self, other: &Self) -> bool { diff --git a/shared/src/shapes/triangle.rs b/shared/src/shapes/triangle.rs index 0d55daf..4c139e4 100644 --- a/shared/src/shapes/triangle.rs +++ b/shared/src/shapes/triangle.rs @@ -539,14 +539,14 @@ impl ShapeTrait for TriangleShape { fn intersect(&self, ray: &Ray, t_max: Option) -> Option { let [p0, p1, p2] = self.get_points(); - let tri_isect = self.intersect_triangle(ray, t_max.unwrap_or(0.), p0, p1, p2)?; + let tri_isect = self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY), p0, p1, p2)?; let intr = self.interaction_from_intersection(tri_isect, ray.time, -ray.d); Some(ShapeIntersection::new(intr, tri_isect.t)) } fn intersect_p(&self, ray: &Ray, t_max: Option) -> bool { let [p0, p1, p2] = self.get_points(); - let tri_isect = self.intersect_triangle(ray, t_max.unwrap_or(0.), p0, p1, p2); + let tri_isect = self.intersect_triangle(ray, t_max.unwrap_or(Float::INFINITY), p0, p1, p2); tri_isect.is_some() } diff --git a/shared/src/spectra/sampled.rs b/shared/src/spectra/sampled.rs index b83f50d..5d33cb3 100644 --- a/shared/src/spectra/sampled.rs +++ b/shared/src/spectra/sampled.rs @@ -338,7 +338,15 @@ impl SampledWavelengths { } } - pub fn terminate_secondary_inplace(&mut self) {} + pub fn terminate_secondary_inplace(&mut self) { + if self.secondary_terminated() { + return; + } + self.pdf[0] /= N_SPECTRUM_SAMPLES as Float; + for i in 1..N_SPECTRUM_SAMPLES { + self.pdf[i] = 0.0; + } + } pub fn sample_uniform(u: Float, lambda_min: Float, lambda_max: Float) -> Self { let mut lambda = [0.0; N_SPECTRUM_SAMPLES]; diff --git a/src/integrators/pipeline.rs b/src/integrators/pipeline.rs index eaf8877..ea3102d 100644 --- a/src/integrators/pipeline.rs +++ b/src/integrators/pipeline.rs @@ -248,6 +248,14 @@ pub fn evaluate_pixel_sample( eprintln!(" camera_sample.p_film: {:?}", camera_sample.p_film); } + eprintln!( + "PATH p=({},{}) l={:?} lambda={:?} fw={}", + pixel.x(), + pixel.y(), + l, + lambda, + camera_sample.filter_weight, + ); film.add_sample( pixel, l, diff --git a/src/wavefront/aggregate.rs b/src/wavefront/aggregate.rs index 69731ea..a8eecc1 100644 --- a/src/wavefront/aggregate.rs +++ b/src/wavefront/aggregate.rs @@ -71,7 +71,6 @@ impl WavefrontAggregate for CpuAggregate { // Medium transition if intr.material.is_null() { let mut next = r; - intr.spawn_ray(r.ray.d); next.ray = intr.spawn_ray(r.ray.d); next_ray_q.push(next); return; @@ -143,7 +142,7 @@ impl WavefrontAggregate for CpuAggregate { (0..n_rays as usize).into_par_iter().for_each(|i| { let work = unsafe { shadow_ray_q.get(i) }; - let ray = Ray::new(work.ray_o, work.ray_d, Some(work.ray_time), Ptr::null()); + let ray = Ray::new(work.ray.o, work.ray.d, Some(work.ray.time), Ptr::null()); if !self.aggregate.intersect_p(&ray, Some(work.t_max)) { let pi = work.pixel_index as usize; let ld = work.l_d / (work.r_u + work.r_l).average(); diff --git a/src/wavefront/integrator.rs b/src/wavefront/integrator.rs index 695fadc..0537900 100644 --- a/src/wavefront/integrator.rs +++ b/src/wavefront/integrator.rs @@ -4,6 +4,7 @@ use crate::lights::sampler::create_light_sampler; use crate::Arena; use crate::ParameterDictionary; use crate::PbrtProgress; +use log::debug; use rayon::prelude::*; use shared::core::bxdf::{FArgs, TransportMode}; use shared::core::camera::{Camera, CameraTrait}; @@ -18,7 +19,7 @@ use shared::core::light::{Light, LightSampleContext, LightTrait}; use shared::core::material::{MaterialEvalContext, MaterialTrait}; use shared::core::primitive::Primitive; use shared::core::sampler::{get_camera_sample, CameraSample, Sampler, SamplerTrait}; -use shared::core::texture::{TextureEvalContext, UniversalTextureEvaluator}; +use shared::core::texture::{BasicTextureEvaluator, TextureEvalContext, UniversalTextureEvaluator}; use shared::lights::sampler::{LightSampler, LightSamplerTrait}; use shared::spectra::{SampledSpectrum, SampledWavelengths}; use shared::utils::math::square; @@ -26,7 +27,7 @@ use shared::utils::sampling::power_heuristic; use shared::utils::soa::{SoA, SoAAllocator, WorkQueue}; use shared::wavefront::workitems::*; use shared::wavefront::{WavefrontAggregate, WavefrontPathIntegrator, WavefrontRenderer}; -use shared::{gvec, Ptr}; +use shared::{gvec, Ptr, SHADOW_EPSILON}; use std::ops::{Deref, DerefMut}; use std::sync::Arc; @@ -74,6 +75,8 @@ where let scanlines_per_pass = (max_samples / res_x).max(1); let max_queue_size = res_x * scanlines_per_pass; + eprintln!("wavefront got {} lights", lights.len()); + let mut infinite_lights = gvec(); for light in &lights { if light.light_type().is_infinite() { @@ -81,6 +84,8 @@ where } } + eprintln!("infinite_lights len = {}", infinite_lights.len()); + // for light in let cpu_aggregate = CpuAggregate::new(*aggregate); @@ -396,7 +401,9 @@ impl CpuWavefrontRenderer { return; }; - let tex_eval = UniversalTextureEvaluator; + let _is_cond = material.is_conductor(); + + // GetMaterialEvalContext let ctx = MaterialEvalContext { texture: TextureEvalContext { p: w.p.into(), @@ -414,8 +421,19 @@ impl CpuWavefrontRenderer { ns: w.ns, dpdus: w.dpdus, }; + let lambda = w.lambda; - let mut bsdf = material.get_bsdf(&tex_eval, &ctx, &lambda); + + let mut bsdf = if use_universal { + material.get_bsdf(&UniversalTextureEvaluator, &ctx, &lambda) + } else { + material.get_bsdf(&BasicTextureEvaluator, &ctx, &lambda) + }; + + if lambda.secondary_terminated() { + pixel_sample_state.lambda.set(pi, lambda); + } + if bsdf.flags().is_empty() { return; } @@ -446,6 +464,7 @@ impl CpuWavefrontRenderer { let q = (1.0 - rr_beta).max(0.0_f32); if rs.indirect.rr < q { beta = SampledSpectrum::new(0.0); + debug!("Path terminated with RR"); } else { beta /= 1.0 - q; } @@ -479,62 +498,73 @@ impl CpuWavefrontRenderer { // Direct lighting let flags = bsdf.flags(); if flags.is_non_specular() { - let light_ctx = LightSampleContext { + let mut light_ctx = LightSampleContext { pi: w.p, n: w.n, ns, }; - if let Some(sampled_light) = - light_sampler.sample_with_context(&light_ctx, rs.direct.uc) - { - if let Some(ls) = - sampled_light - .light - .sample_li(&light_ctx, rs.direct.u, &lambda, true) - { - if !ls.l.is_black() && ls.pdf > 0.0 { - let wi = ls.wi; - if let Some(f) = bsdf.f(wo, wi, TransportMode::Radiance) { - if !f.is_black() { - let beta = w.beta * f * wi.abs_dot(ns.into()); - let light_pdf = ls.pdf * sampled_light.p; - let bsdf_pdf = - if sampled_light.light.light_type().is_delta_light() { - 0.0 - } else { - bsdf.pdf(wo, wi, FArgs::default()) - }; - let r_u = w.r_u * bsdf_pdf; - let r_l = w.r_u * light_pdf; - let ld = beta * ls.l; - let ray_o = Ray::spawn_to_interaction( - &w.p, - &w.n, - w.time, - &ls.p_light.pi(), - &ls.p_light.n(), - ); - let t_max = (1.0 - 1e-4) - * (Point3f::from(ls.p_light.p()) - ray_o.o).norm() - / wi.norm(); - - shadow_ray_queue.push(ShadowRayWorkItem { - ray_o: ray_o.o, - ray_d: ray_o.d, - ray_time: w.time, - t_max: 1.0 - 1e-4, - lambda, - l_d: ld, - r_u, - r_l, - pixel_index: w.pixel_index, - }); - } - } - } - } + if flags.is_reflective() && !flags.is_transmissive() { + light_ctx.pi = Point3fi::new_from_point(Ray::offset_origin(&w.p, &w.n, &(-wo))); + } else if flags.is_transmissive() && flags.is_reflective() { + light_ctx.pi = Point3fi::new_from_point(Ray::offset_origin(&w.p, &w.n, &(-wo))); } + + let Some(sampled_light) = + light_sampler.sample_with_context(&light_ctx, rs.direct.uc) + else { + return; + }; + + let Some(ls) = + sampled_light + .light + .sample_li(&light_ctx, rs.direct.u, &lambda, true) + else { + return; + }; + + if ls.l.is_black() || ls.pdf <= 0.0 { + return; + } + + let wi = ls.wi; + let Some(f) = bsdf.f(wo, wi, TransportMode::Radiance) else { + return; + }; + + if f.is_black() { + return; + } + + let beta = w.beta * f * wi.abs_dot(ns.into()); + let light_pdf = ls.pdf * sampled_light.p; + let bsdf_pdf = if sampled_light.light.light_type().is_delta_light() { + 0.0 + } else { + bsdf.pdf(wo, wi, FArgs::default()) + }; + let r_u = w.r_u * bsdf_pdf; + let r_l = w.r_u * light_pdf; + let l_d = beta * ls.l; + + let ray = Ray::spawn_to_interaction( + &w.p, + &w.n, + w.time, + &ls.p_light.pi(), + &ls.p_light.n(), + ); + + shadow_ray_queue.push(ShadowRayWorkItem { + ray, + t_max: 1.0 - SHADOW_EPSILON, + lambda, + l_d, + r_u, + r_l, + pixel_index: w.pixel_index, + }); } }); } @@ -563,19 +593,19 @@ impl CpuWavefrontRenderer { let current = (depth % 2) as usize; let ray_queue = &self.ray_queues[current]; let n = ray_queue.size(); - let dimension = 6 + 7 * depth; let pixel_sample_state = &self.pixel_sample_state; let sampler_proto = &self.sampler; (0..n as usize).into_par_iter().for_each(|i| { let w = unsafe { ray_queue.storage.get(i) }; + let dimension = 6 + 7 * w.depth; let pi = w.pixel_index as usize; let p_pixel = pixel_sample_state.p_pixel.get(pi); let mut sampler = sampler_proto.clone(); sampler.start_pixel_sample(p_pixel, sample_index as i32, Some(dimension)); - self.pixel_sample_state.samples.set( + pixel_sample_state.samples.set( pi, RaySamples { direct: DirectSamples {