From 66032abf7623cdb6aa90f0d7f16380f8e89f3245 Mon Sep 17 00:00:00 2001 From: Wito Wiala Date: Tue, 2 Jun 2026 21:46:11 +0100 Subject: [PATCH] Still broken --- kernels/src/intersect.rs | 4 +- shared/src/wavefront/workitems.rs | 60 ++++--- src/core/render.rs | 3 +- src/core/scene/scene.rs | 81 +-------- src/wavefront/aggregate.rs | 36 ++-- src/wavefront/integrator.rs | 279 ++++++++++++++++++++---------- src/wavefront/mod.rs | 1 + 7 files changed, 246 insertions(+), 218 deletions(-) diff --git a/kernels/src/intersect.rs b/kernels/src/intersect.rs index 479604c..88c3af8 100644 --- a/kernels/src/intersect.rs +++ b/kernels/src/intersect.rs @@ -189,7 +189,7 @@ pub mod device { // on the material, which we can refine later. let q = &*params.universal_eval_mtl_q.as_raw(); q.push(MaterialEvalWorkItem { - p: intr.p(), + p: intr.pi(), n: intr.n(), ns: intr.shading.n, dpdu: intr.shading.dpdu, @@ -258,8 +258,6 @@ pub mod device { pub n_rays: u32, } - // -- Helper functions -- - unsafe fn push_escaped( params: &IntersectClosestParams, work: &RayWorkItem, diff --git a/shared/src/wavefront/workitems.rs b/shared/src/wavefront/workitems.rs index 3becd60..7a9df51 100644 --- a/shared/src/wavefront/workitems.rs +++ b/shared/src/wavefront/workitems.rs @@ -316,8 +316,7 @@ impl SoA for HitAreaLightWorkItemSoA { #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct MaterialEvalWorkItem { - // Surface interaction - pub p: Point3f, + pub p: Point3fi, pub n: Normal3f, pub ns: Normal3f, pub dpdu: Vector3f, @@ -326,30 +325,27 @@ pub struct MaterialEvalWorkItem { pub wo: Vector3f, pub time: Float, pub face_index: i32, - - // Material pub material: Ptr, pub area_light: Ptr, - - // Medium interface pub medium_interface: MediumInterface, - - // Path state pub pixel_index: u32, pub lambda: SampledWavelengths, pub beta: SampledSpectrum, pub r_u: SampledSpectrum, - - // For next-event estimation + // pub r_l: SampledSpectrum, pub any_non_specular_bounces: bool, pub depth: u32, pub eta_scale: Float, + pub dpdus: Vector3f , + pub dpdvs: Vector3f, + pub dndus: Normal3f, + pub dndvs: Normal3f } #[repr(C)] #[derive(Clone, Copy)] pub struct MaterialEvalWorkItemSoA { - pub p: SoABuffer, + pub p: SoABuffer, pub n: SoABuffer, pub ns: SoABuffer, pub dpdu: SoABuffer, @@ -365,9 +361,15 @@ pub struct MaterialEvalWorkItemSoA { pub lambda: SoABuffer, pub beta: SoABuffer, pub r_u: SoABuffer, + // pub r_l: SoABuffer, pub any_non_specular_bounces: SoABuffer, pub depth: SoABuffer, pub eta_scale: SoABuffer, + pub dpdus: SoABuffer, + pub dpdvs: SoABuffer, + pub dndus: SoABuffer, + pub dndvs: SoABuffer + } impl SoA for MaterialEvalWorkItemSoA { @@ -391,9 +393,14 @@ impl SoA for MaterialEvalWorkItemSoA { lambda: alloc_soa_buffer(n, alloc), beta: alloc_soa_buffer(n, alloc), r_u: alloc_soa_buffer(n, alloc), + // r_l: alloc_soa_buffer(n, alloc), any_non_specular_bounces: alloc_soa_buffer(n, alloc), depth: alloc_soa_buffer(n, alloc), eta_scale: alloc_soa_buffer(n, alloc), + dpdus: alloc_soa_buffer(n, alloc), + dpdvs: alloc_soa_buffer(n, alloc), + dndus: alloc_soa_buffer(n, alloc), + dndvs: alloc_soa_buffer(n, alloc), } } @@ -415,9 +422,15 @@ impl SoA for MaterialEvalWorkItemSoA { lambda: self.lambda.get(i), beta: self.beta.get(i), r_u: self.r_u.get(i), + // r_l: self.r_l.get(i), any_non_specular_bounces: self.any_non_specular_bounces.get(i) != 0, depth: self.depth.get(i), eta_scale: self.eta_scale.get(i), + dpdus: self.dpdus.get(i), + dpdvs: self.dpdvs.get(i), + dndus: self.dndus.get(i), + dndvs: self.dndvs.get(i), + } } @@ -438,19 +451,22 @@ impl SoA for MaterialEvalWorkItemSoA { self.lambda.set(i, v.lambda); self.beta.set(i, v.beta); self.r_u.set(i, v.r_u); + // self.r_l.set(i, v.r_l); self.any_non_specular_bounces .set(i, v.any_non_specular_bounces as u8); self.depth.set(i, v.depth); self.eta_scale.set(i, v.eta_scale); + self.dpdus.set(i, v.dpdus); + self.dpdvs.set(i, v.dpdvs); + self.dndus.set(i, v.dndus); + self.dndvs.set(i, v.dndvs); } } #[repr(C)] #[derive(Clone, Copy, Debug)] pub struct ShadowRayWorkItem { - pub ray_o: Point3f, - pub ray_d: Vector3f, - pub ray_time: Float, + pub ray: Ray, pub t_max: Float, pub lambda: SampledWavelengths, pub l_d: SampledSpectrum, @@ -462,9 +478,7 @@ pub struct ShadowRayWorkItem { #[repr(C)] #[derive(Clone, Copy)] pub struct ShadowRayWorkItemSoA { - pub ray_o: SoABuffer, - pub ray_d: SoABuffer, - pub ray_time: SoABuffer, + pub ray: SoABuffer, pub t_max: SoABuffer, pub lambda: SoABuffer, pub l_d: SoABuffer, @@ -478,9 +492,7 @@ impl SoA for ShadowRayWorkItemSoA { fn allocate(n: u32, alloc: &dyn SoAAllocator) -> Self { Self { - ray_o: alloc_soa_buffer(n, alloc), - ray_d: alloc_soa_buffer(n, alloc), - ray_time: alloc_soa_buffer(n, alloc), + ray: alloc_soa_buffer(n, alloc), t_max: alloc_soa_buffer(n, alloc), lambda: alloc_soa_buffer(n, alloc), l_d: alloc_soa_buffer(n, alloc), @@ -492,9 +504,7 @@ impl SoA for ShadowRayWorkItemSoA { unsafe fn get(&self, i: usize) -> ShadowRayWorkItem { ShadowRayWorkItem { - ray_o: self.ray_o.get(i), - ray_d: self.ray_d.get(i), - ray_time: self.ray_time.get(i), + ray: self.ray.get(i), t_max: self.t_max.get(i), lambda: self.lambda.get(i), l_d: self.l_d.get(i), @@ -505,9 +515,7 @@ impl SoA for ShadowRayWorkItemSoA { } unsafe fn set(&self, i: usize, v: ShadowRayWorkItem) { - self.ray_o.set(i, v.ray_o); - self.ray_d.set(i, v.ray_d); - self.ray_time.set(i, v.ray_time); + self.ray.set(i, v.ray); self.t_max.set(i, v.t_max); self.lambda.set(i, v.lambda); self.l_d.set(i, v.l_d); diff --git a/src/core/render.rs b/src/core/render.rs index 406419b..eb6363a 100644 --- a/src/core/render.rs +++ b/src/core/render.rs @@ -106,8 +106,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> { all_lights, arena, ); - let mut renderer = CpuWavefrontRenderer(wf); - renderer.render(); + wf.render(); } else { eprintln!("RENDER: Path integrator backend"); let integrator = scene.create_integrator( diff --git a/src/core/scene/scene.rs b/src/core/scene/scene.rs index 1fbfa92..d9a15b5 100644 --- a/src/core/scene/scene.rs +++ b/src/core/scene/scene.rs @@ -15,7 +15,7 @@ use crate::lights::sampler::create_light_sampler; use crate::utils::parallel::{run_async, AsyncJob}; use crate::utils::parameters::{NamedTextures, ParameterDictionary, TextureParameterDictionary}; use crate::utils::resolve_filename; -use crate::wavefront::CpuAggregate; +use crate::wavefront::{CreateWavefront, CpuAggregate, CpuWavefrontRenderer}; use crate::{Arena, ArenaUpload, FileLoc}; use anyhow::{anyhow, Result}; use parking_lot::Mutex; @@ -725,81 +725,10 @@ impl BasicScene { aggregate: Arc, lights: Vec>, arena: &Arena, - ) -> WavefrontPathIntegrator { - let entity = self.integrator.lock().clone().unwrap(); - let max_depth = entity - .parameters - .get_one_int("maxdepth", 5) - .expect("Could not obtain depth value"); - let regularize = entity - .parameters - .get_one_bool("regularize", false) - .expect("Could not obtain regularize flag value"); - - let spp = sampler.samples_per_pixel() as u32; - let film = camera.base().film; - let pixel_bounds = film.pixel_bounds(); - let filter = Ptr::from(&film.base().filter); - let light_sampler = create_light_sampler("power", &lights, arena); - let res_x = pixel_bounds.diagonal().x() as u32; - let max_samples = 1024u32 * 1024; - let scanlines_per_pass = (max_samples / res_x).max(1); - let max_queue_size = res_x * scanlines_per_pass; - - let mut infinite_lights = gvec(); - for light in &lights { - if light.light_type().is_infinite() { - infinite_lights.push(arena.alloc(**light)); - } - } - - let cpu_aggregate = CpuAggregate::new(*aggregate); - - WavefrontPathIntegrator { - aggregate: cpu_aggregate, - camera: (*camera).clone(), - sampler: (*sampler).clone(), - max_depth: max_depth.try_into().unwrap(), - film, - filter, - samples_per_pixel: spp, - regularize, - infinite_lights, - max_queue_size, - scanlines_per_pass, - light_sampler, - ray_queues: [ - WorkQueue::new( - RayWorkItemSoA::allocate(max_queue_size, arena), - max_queue_size, - ), - WorkQueue::new( - RayWorkItemSoA::allocate(max_queue_size, arena), - max_queue_size, - ), - ], - shadow_ray_queue: WorkQueue::new( - ShadowRayWorkItemSoA::allocate(max_queue_size, arena), - max_queue_size, - ), - escaped_ray_queue: WorkQueue::new( - EscapedRayWorkItemSoA::allocate(max_queue_size, arena), - max_queue_size, - ), - hit_area_light_queue: WorkQueue::new( - HitAreaLightWorkItemSoA::allocate(max_queue_size, arena), - max_queue_size, - ), - basic_eval_material_queue: WorkQueue::new( - MaterialEvalWorkItemSoA::allocate(max_queue_size, arena), - max_queue_size, - ), - universal_eval_material_queue: WorkQueue::new( - MaterialEvalWorkItemSoA::allocate(max_queue_size, arena), - max_queue_size, - ), - pixel_sample_state: PixelSampleState::allocate(max_queue_size, arena), - } + ) -> CpuWavefrontRenderer { + let integrator_entity = self.integrator.lock().clone().unwrap(); + let params = &integrator_entity.parameters; + CpuWavefrontRenderer::create(params.clone(), camera, sampler, aggregate, lights, arena) } // Getters diff --git a/src/wavefront/aggregate.rs b/src/wavefront/aggregate.rs index df1b06d..69731ea 100644 --- a/src/wavefront/aggregate.rs +++ b/src/wavefront/aggregate.rs @@ -1,14 +1,14 @@ use crate::globals::get_options; use rayon::prelude::*; use shared::core::geometry::{Bounds3f, Ray, VectorLike}; -use shared::core::interaction::InteractionTrait; +use shared::core::interaction::{InteractionTrait, SurfaceInteraction}; use shared::core::material::MaterialTrait; use shared::core::primitive::{Primitive, PrimitiveTrait}; use shared::core::texture::BasicTextureEvaluator; use shared::core::texture::TextureEvaluator; use shared::wavefront::workitems::*; use shared::wavefront::WavefrontAggregate; -use shared::Ptr; +use shared::{Float, Ptr}; pub struct CpuAggregate { pub aggregate: Primitive, @@ -20,6 +20,13 @@ impl CpuAggregate { } } +// fn enqueue_after_intersection( +// r: RayWorkItem, ray_medium: Medium, t_max: Float, intr: SurfaceInteraction, +// mut medium_sample_queue: &MediumQueue, mut next_ray_queue: &RayQueue, +// mut hit_area_light_queue: &HitAreaLightQueue, mut basic_eval_mtl_q: &MaterialEvalQueue, +// mut universal_eval_mlt_q: MaterialEvalQueue) { +// } + impl WavefrontAggregate for CpuAggregate { fn bounds(&self) -> Bounds3f { self.aggregate.bounds() @@ -38,12 +45,12 @@ impl WavefrontAggregate for CpuAggregate { ) { let n_rays = ray_q.size().min(max_rays as u32); - // Intersect _r_'s ray with the scene and enqueue resulting work + // Intersect ray with the scene and enqueue resulting work (0..n_rays as usize).into_par_iter().for_each(|i| { let r = unsafe { ray_q.get(i) }; let Some(si) = self.aggregate.intersect(&r.ray, None) else { - // EnqueueMiss - push to escaped queue with r's path state + // EnqueueMiss escaped_ray_q.push(EscapedRayWorkItem { ray_o: r.ray.o, ray_d: r.ray.d, @@ -61,7 +68,7 @@ impl WavefrontAggregate for CpuAggregate { let intr = &si.intr; - // Medium transition — re-push as indirect ray with same path state + // Medium transition if intr.material.is_null() { let mut next = r; intr.spawn_ray(r.ray.d); @@ -90,17 +97,6 @@ impl WavefrontAggregate for CpuAggregate { } // Material eval queue dispatch - // let material = *intr.material.get().unwrap(); - // let displacement = material.get_displacement(); - // let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) - // && (displacement.is_null() - // || BasicTextureEvaluator.can_evaluate(&[displacement], &[])) - // { - // basic_eval_mtl_q - // } else { - // universal_eval_mtl_q - // }; - // let material = *intr.material.get().unwrap(); let eval_q = if material.can_evaluate_textures(&BasicTextureEvaluator) { basic_eval_mtl_q @@ -109,13 +105,13 @@ impl WavefrontAggregate for CpuAggregate { }; eval_q.push(MaterialEvalWorkItem { - p: intr.p(), + p: intr.pi(), n: intr.n(), ns: intr.shading.n, dpdu: intr.shading.dpdu, dpdv: intr.shading.dpdv, uv: intr.common.uv, - wo: -r.ray.d, + wo: intr.wo(), time: r.ray.time, face_index: intr.face_index, material: intr.material, @@ -128,6 +124,10 @@ impl WavefrontAggregate for CpuAggregate { any_non_specular_bounces: r.any_non_specular_bounces != 0, depth: r.depth, eta_scale: r.eta_scale, + dpdus: intr.shading.dpdu, + dpdvs: intr.shading.dpdv, + dndus: intr.shading.dndu, + dndvs: intr.shading.dndv, }); }); } diff --git a/src/wavefront/integrator.rs b/src/wavefront/integrator.rs index 15da42e..695fadc 100644 --- a/src/wavefront/integrator.rs +++ b/src/wavefront/integrator.rs @@ -1,5 +1,8 @@ use super::CpuAggregate; use crate::globals::get_options; +use crate::lights::sampler::create_light_sampler; +use crate::Arena; +use crate::ParameterDictionary; use crate::PbrtProgress; use rayon::prelude::*; use shared::core::bxdf::{FArgs, TransportMode}; @@ -13,6 +16,7 @@ use shared::core::geometry::{ use shared::core::interaction::InteractionTrait; 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::lights::sampler::{LightSampler, LightSamplerTrait}; @@ -22,8 +26,9 @@ 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::Ptr; +use shared::{gvec, Ptr}; use std::ops::{Deref, DerefMut}; +use std::sync::Arc; pub struct CpuWavefrontRenderer(pub WavefrontPathIntegrator); @@ -40,8 +45,95 @@ impl DerefMut for CpuWavefrontRenderer { } } -use std::sync::Mutex; -static M: Mutex<()> = Mutex::new(()); +pub trait CreateWavefront +where + Self: Sized, +{ + fn create( + parameters: ParameterDictionary, + camera: Arc, + sampler: Arc, + aggregate: Arc, + lights: Vec>, + arena: &Arena, + ) -> CpuWavefrontRenderer { + let max_depth = parameters + .get_one_int("maxdepth", 5) + .expect("Could not obtain depth value"); + let regularize = parameters + .get_one_bool("regularize", false) + .expect("Could not obtain regularize flag value"); + + let spp = sampler.samples_per_pixel() as u32; + let film = camera.base().film; + let pixel_bounds = film.pixel_bounds(); + let filter = Ptr::from(&film.base().filter); + let light_sampler = create_light_sampler("power", &lights, arena); + let res_x = pixel_bounds.diagonal().x() as u32; + let max_samples = 1024u32 * 1024; + let scanlines_per_pass = (max_samples / res_x).max(1); + let max_queue_size = res_x * scanlines_per_pass; + + let mut infinite_lights = gvec(); + for light in &lights { + if light.light_type().is_infinite() { + infinite_lights.push(arena.alloc(**light)); + } + } + + // for light in + + let cpu_aggregate = CpuAggregate::new(*aggregate); + + CpuWavefrontRenderer(WavefrontPathIntegrator { + aggregate: cpu_aggregate, + camera: (*camera).clone(), + sampler: (*sampler).clone(), + max_depth: max_depth.try_into().unwrap(), + film, + filter, + samples_per_pixel: spp, + regularize, + infinite_lights, + max_queue_size, + scanlines_per_pass, + light_sampler, + ray_queues: [ + WorkQueue::new( + RayWorkItemSoA::allocate(max_queue_size, arena), + max_queue_size, + ), + WorkQueue::new( + RayWorkItemSoA::allocate(max_queue_size, arena), + max_queue_size, + ), + ], + shadow_ray_queue: WorkQueue::new( + ShadowRayWorkItemSoA::allocate(max_queue_size, arena), + max_queue_size, + ), + escaped_ray_queue: WorkQueue::new( + EscapedRayWorkItemSoA::allocate(max_queue_size, arena), + max_queue_size, + ), + hit_area_light_queue: WorkQueue::new( + HitAreaLightWorkItemSoA::allocate(max_queue_size, arena), + max_queue_size, + ), + basic_eval_material_queue: WorkQueue::new( + MaterialEvalWorkItemSoA::allocate(max_queue_size, arena), + max_queue_size, + ), + universal_eval_material_queue: WorkQueue::new( + MaterialEvalWorkItemSoA::allocate(max_queue_size, arena), + max_queue_size, + ), + pixel_sample_state: PixelSampleState::allocate(max_queue_size, arena), + }) + } +} + +impl CreateWavefront for CpuWavefrontRenderer {} impl CpuWavefrontRenderer { pub fn render(&mut self) { @@ -125,8 +217,6 @@ impl CpuWavefrontRenderer { sample_index: u32, pixel_bounds: &Bounds2i, ) { - // For each pixel in the scanline range, generate a camera ray - // and push it to the ray queue. Initialize the PixelSampleState. let filter = self.filter; let film = self.film; let camera = &self.camera; @@ -135,60 +225,68 @@ impl CpuWavefrontRenderer { let ray_queue = &self.ray_queues[0]; let x_resolution = pixel_bounds.p_max.x() - pixel_bounds.p_min.x(); - let n_scanlines = y1 - y0; - let total = (n_scanlines * x_resolution) as usize; - (0..total).into_par_iter().for_each(|idx| { - let dx = idx as i32 % x_resolution; - let dy = idx as i32 / x_resolution; - let p_pixel = Point2i::new(pixel_bounds.p_min.x() + dx, y0 + dy); - let pixel_index = idx as u32; - let pi = idx; + // Iterate the whole queue, exactly like pbrt's ParallelFor(maxQueueSize). + // The loop index IS the pixelSampleState key; pPixel is derived from it, + // and every later kernel addresses state by this same absolute index. + (0..self.max_queue_size as usize) + .into_par_iter() + .for_each(|pixel_index| { + let p_pixel = Point2i::new( + pixel_bounds.p_min.x() + (pixel_index as i32 % x_resolution), + y0 + (pixel_index as i32 / x_resolution), + ); + pixel_sample_state.p_pixel.set(pixel_index, p_pixel); - if !pixel_bounds.contains_exclusive(p_pixel) { - return; - } + // Skipped pixels contribute nothing; their slots are simply never + // populated, and update_film filters them by the same bounds test. + if !pixel_bounds.contains_exclusive(p_pixel) { + return; + } - let mut sampler = sampler_proto.clone(); - sampler.start_pixel_sample(p_pixel, sample_index as i32, Some(0)); + let mut sampler = sampler_proto.clone(); + sampler.start_pixel_sample(p_pixel, sample_index as i32, Some(0)); - let lu = sampler.get1d(); - let lambda = film.sample_wavelengths(lu); - let camera_sample = get_camera_sample(&mut sampler, p_pixel, &filter); + let lu = sampler.get1d(); + let lambda = film.sample_wavelengths(lu); + let camera_sample = get_camera_sample(&mut sampler, p_pixel, &filter); - pixel_sample_state.l.set(pi, SampledSpectrum::new(0.0)); - pixel_sample_state.lambda.set(pi, lambda); - pixel_sample_state - .filter_weight - .set(pi, camera_sample.filter_weight); - pixel_sample_state.p_film.set(pi, camera_sample.p_film); - pixel_sample_state.p_pixel.set(pi, p_pixel); + pixel_sample_state + .l + .set(pixel_index, SampledSpectrum::new(0.0)); + pixel_sample_state.lambda.set(pixel_index, lambda); + pixel_sample_state + .filter_weight + .set(pixel_index, camera_sample.filter_weight); + pixel_sample_state + .p_film + .set(pixel_index, camera_sample.p_film); + + let Some(camera_ray) = camera.generate_ray(camera_sample, &lambda) else { + pixel_sample_state + .camera_ray_weight + .set(pixel_index, SampledSpectrum::new(0.0)); + return; + }; - let Some(camera_ray) = camera.generate_ray(camera_sample, &lambda) else { pixel_sample_state .camera_ray_weight - .set(pi, SampledSpectrum::new(0.0)); - return; - }; + .set(pixel_index, camera_ray.weight); - pixel_sample_state - .camera_ray_weight - .set(pi, camera_ray.weight); - - ray_queue.push(RayWorkItem { - ray: camera_ray.ray, - depth: 0, - pixel_index, - lambda, - beta: SampledSpectrum::new(1.0), - r_u: SampledSpectrum::new(1.0), - r_l: SampledSpectrum::new(1.0), - prev_intr_ctx: LightSampleContext::default(), - eta_scale: 1.0, - specular_bounce: 0, - any_non_specular_bounces: 0, + ray_queue.push(RayWorkItem { + ray: camera_ray.ray, + depth: 0, + pixel_index: pixel_index as u32, + lambda, + beta: SampledSpectrum::new(1.0), + r_u: SampledSpectrum::new(1.0), + r_l: SampledSpectrum::new(1.0), + prev_intr_ctx: LightSampleContext::default(), + eta_scale: 1.0, + specular_bounce: 0, + any_non_specular_bounces: 0, + }); }); - }); } /// Evaluate infinite lights. @@ -209,7 +307,7 @@ impl CpuWavefrontRenderer { let ray = Ray::new(w.ray_o, w.ray_d, None, Ptr::null()); let le = light.le(&ray, &w.lambda); if le.is_black() { - return; + continue; } if w.depth == 0 || w.specular_bounce { @@ -289,8 +387,7 @@ impl CpuWavefrontRenderer { let next_ray_queue = &self.ray_queues[next]; let regularize = self.regularize; - // (0..n as usize).into_par_iter().for_each(|i| { - for i in 0..n as usize { + (0..n as usize).into_par_iter().for_each(|i| { let w = unsafe { queue.storage.get(i) }; let pi = w.pixel_index as usize; let rs = pixel_sample_state.samples.get(pi); @@ -302,7 +399,7 @@ impl CpuWavefrontRenderer { let tex_eval = UniversalTextureEvaluator; let ctx = MaterialEvalContext { texture: TextureEvalContext { - p: w.p, + p: w.p.into(), dpdx: Vector3f::zero(), dpdy: Vector3f::zero(), n: w.n, @@ -315,14 +412,14 @@ impl CpuWavefrontRenderer { }, wo: w.wo, ns: w.ns, - dpdus: w.dpdu, + dpdus: w.dpdus, }; let lambda = w.lambda; let mut bsdf = material.get_bsdf(&tex_eval, &ctx, &lambda); if bsdf.flags().is_empty() { return; } - if self.regularize && w.any_non_specular_bounces { + if regularize && w.any_non_specular_bounces { bsdf.regularize(); } @@ -345,7 +442,7 @@ impl CpuWavefrontRenderer { } let rr_beta = (beta * eta_scale / r_u.average()).max_component_value(); - if rr_beta < 1.0 && w.depth >= 1 { + if rr_beta < 1.0 && w.depth > 1 { let q = (1.0 - rr_beta).max(0.0_f32); if rs.indirect.rr < q { beta = SampledSpectrum::new(0.0); @@ -355,10 +452,10 @@ impl CpuWavefrontRenderer { } if !beta.is_black() { - let ray = Ray::spawn(&Point3fi::new_from_point(w.p), &w.n, w.time, wi); + let ray = Ray::spawn(&w.p, &w.n, w.time, wi); let any_non_specular = !bs.is_specular() || w.any_non_specular_bounces; let ctx = LightSampleContext { - pi: Point3fi::new_from_point(w.p), + pi: w.p, n: w.n, ns, }; @@ -379,17 +476,16 @@ impl CpuWavefrontRenderer { } } - // Direct lighting — independent + // Direct lighting let flags = bsdf.flags(); if flags.is_non_specular() { let light_ctx = LightSampleContext { - pi: Point3fi::new_from_point(w.p), + pi: w.p, n: w.n, ns, }; - if let Some(sampled_light) = self - .light_sampler - .sample_with_context(&light_ctx, rs.direct.uc) + if let Some(sampled_light) = + light_sampler.sample_with_context(&light_ctx, rs.direct.uc) { if let Some(ls) = sampled_light @@ -412,20 +508,22 @@ impl CpuWavefrontRenderer { let r_l = w.r_u * light_pdf; let ld = beta * ls.l; - let ray_o = Ray::offset_origin( - &Point3fi::new_from_point(w.p), + let ray_o = Ray::spawn_to_interaction( + &w.p, &w.n, - &wi, + 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).norm() + * (Point3f::from(ls.p_light.p()) - ray_o.o).norm() / wi.norm(); - self.shadow_ray_queue.push(ShadowRayWorkItem { - ray_o, - ray_d: wi, + shadow_ray_queue.push(ShadowRayWorkItem { + ray_o: ray_o.o, + ray_d: ray_o.d, ray_time: w.time, - t_max, + t_max: 1.0 - 1e-4, lambda, l_d: ld, r_u, @@ -438,31 +536,27 @@ impl CpuWavefrontRenderer { } } } - } - // }); + }); } - fn update_film(&self, y0: i32, y1: i32, pixel_bounds: &Bounds2i) { - let x_resolution = pixel_bounds.p_max.x() - pixel_bounds.p_min.x(); - let n_scanlines = y1 - y0; - let total = (n_scanlines * x_resolution) as usize; + fn update_film(&self, _y0: i32, _y1: i32, pixel_bounds: &Bounds2i) { + (0..self.max_queue_size as usize) + .into_par_iter() + .for_each(|pixel_index| { + let p_pixel = self.pixel_sample_state.p_pixel.get(pixel_index); + if !pixel_bounds.contains_exclusive(p_pixel) { + return; + } - (0..total).into_par_iter().for_each(|idx| { - let pi = idx; - let p_pixel = self.pixel_sample_state.p_pixel.get(pi); - if !pixel_bounds.contains_exclusive(p_pixel) { - return; - } + let l = self.pixel_sample_state.l.get(pixel_index); + let camera_weight = self.pixel_sample_state.camera_ray_weight.get(pixel_index); + let weighted_l = l * camera_weight; + let lambda = self.pixel_sample_state.lambda.get(pixel_index); + let filter_weight = self.pixel_sample_state.filter_weight.get(pixel_index); - let l = self.pixel_sample_state.l.get(pi); - let camera_weight = self.pixel_sample_state.camera_ray_weight.get(pi); - let weighted_l = l * camera_weight; - let lambda = self.pixel_sample_state.lambda.get(pi); - let filter_weight = self.pixel_sample_state.filter_weight.get(pi); - - self.film - .add_sample(p_pixel, weighted_l, &lambda, None, filter_weight); - }); + self.film + .add_sample(p_pixel, weighted_l, &lambda, None, filter_weight); + }); } fn generate_ray_samples(&mut self, depth: u32, sample_index: u32) { @@ -474,7 +568,6 @@ impl CpuWavefrontRenderer { let sampler_proto = &self.sampler; (0..n as usize).into_par_iter().for_each(|i| { - let _g = M.lock().unwrap(); let w = unsafe { ray_queue.storage.get(i) }; let pi = w.pixel_index as usize; let p_pixel = pixel_sample_state.p_pixel.get(pi); diff --git a/src/wavefront/mod.rs b/src/wavefront/mod.rs index 8eb82c1..36b284c 100644 --- a/src/wavefront/mod.rs +++ b/src/wavefront/mod.rs @@ -2,3 +2,4 @@ pub mod aggregate; pub mod integrator; pub use aggregate::CpuAggregate; +pub use integrator::{CreateWavefront, CpuWavefrontRenderer};