Moving to index based access of resources and instances
This commit is contained in:
parent
7db434535b
commit
8e8a3845f8
16 changed files with 287 additions and 122 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -15,7 +15,9 @@ tests/
|
|||
scenes/
|
||||
compile.sh
|
||||
output/
|
||||
*.md
|
||||
!README.md
|
||||
!INSTALL.md
|
||||
docs/
|
||||
GTAGS
|
||||
GPATH
|
||||
GRTAGS
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use crate::core::texture::{FloatTexture, UniversalTextureEvaluator};
|
|||
use crate::core::{LightIdx, MaterialIdx};
|
||||
use crate::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
use crate::utils::math::{clamp, difference_of_products, square};
|
||||
use crate::{GVec, Ptr, Float};
|
||||
use crate::{Ptr, Float};
|
||||
use enum_dispatch::enum_dispatch;
|
||||
|
||||
#[repr(C)]
|
||||
|
|
@ -243,7 +243,7 @@ impl SurfaceInteraction {
|
|||
&self,
|
||||
w: Vector3f,
|
||||
lambda: &SampledWavelengths,
|
||||
lights: &GVec<Light>,
|
||||
lights: &[Light],
|
||||
) -> SampledSpectrum {
|
||||
if self.area_light.is_none() {
|
||||
return SampledSpectrum::new(0.);
|
||||
|
|
@ -253,7 +253,7 @@ impl SurfaceInteraction {
|
|||
}
|
||||
|
||||
pub fn compute_differentials(&mut self, r: &Ray, camera: &Camera, samples_per_pixel: i32) {
|
||||
let computed = if !r.has_differentials {
|
||||
let computed = if r.has_differentials {
|
||||
let diff = r.differential;
|
||||
let dot_rx = self.common.n.dot(diff.rx_direction.into());
|
||||
let dot_ry = self.common.n.dot(diff.ry_direction.into());
|
||||
|
|
@ -341,7 +341,7 @@ impl SurfaceInteraction {
|
|||
let new_ray = Ray::spawn(&self.pi(), &self.n(), ray.time, ray.d);
|
||||
ray.o = new_ray.o;
|
||||
// Skipping other variables, since they should not change when passing through surface
|
||||
if !ray.has_differentials {
|
||||
if ray.has_differentials {
|
||||
let mut diff = ray.differential;
|
||||
diff.rx_origin += diff.rx_direction * t;
|
||||
diff.ry_origin += diff.ry_direction * t;
|
||||
|
|
|
|||
|
|
@ -37,12 +37,12 @@ unsafe impl Send for DiffuseAreaLight {}
|
|||
unsafe impl Sync for DiffuseAreaLight {}
|
||||
|
||||
impl DiffuseAreaLight {
|
||||
fn l_base(&self, n: Normal3f, wo: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
if !self.two_sided && n.dot(wo.into()) <= 0.0 {
|
||||
return SampledSpectrum::new(0.0);
|
||||
}
|
||||
self.lemit.sample(lambda) * self.scale
|
||||
}
|
||||
// fn l_base(&self, n: Normal3f, wo: Vector3f, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
// if !self.two_sided && n.dot(wo.into()) <= 0.0 {
|
||||
// return SampledSpectrum::new(0.0);
|
||||
// }
|
||||
// self.lemit.sample(lambda) * self.scale
|
||||
// }
|
||||
|
||||
fn alpha_masked(&self, intr: &Interaction) -> bool {
|
||||
if self.alpha.is_null() {
|
||||
|
|
@ -73,7 +73,12 @@ impl LightTrait for DiffuseAreaLight {
|
|||
_allow_incomplete_pdf: bool,
|
||||
) -> Option<LightLiSample> {
|
||||
let shape_ctx = ShapeSampleContext::new(ctx.pi, ctx.n, ctx.ns, 0.0);
|
||||
|
||||
let ss = self.shape.sample_from_context(&shape_ctx, u)?;
|
||||
if ss.pdf == 0.0 || (ss.intr.p() - ctx.p()).norm_squared() == 0.0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut intr = ss.intr;
|
||||
intr.set_medium_interface(self.base.medium_interface);
|
||||
let p = intr.p();
|
||||
|
|
@ -108,9 +113,10 @@ impl LightTrait for DiffuseAreaLight {
|
|||
w: Vector3f,
|
||||
lambda: &SampledWavelengths,
|
||||
) -> SampledSpectrum {
|
||||
if self.two_sided && n.dot(w.into()) < 0. {
|
||||
if !self.two_sided && n.dot(w.into()) < 0. {
|
||||
return SampledSpectrum::new(0.);
|
||||
}
|
||||
|
||||
let intr = Interaction::Surface(SurfaceInteraction::new_minimal(
|
||||
Point3fi::new_from_point(p),
|
||||
uv,
|
||||
|
|
@ -149,7 +155,8 @@ impl LightTrait for DiffuseAreaLight {
|
|||
rgb[c] = self.image.get_channel(Point2i::new(x, y), c as i32);
|
||||
}
|
||||
|
||||
l += RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero()).sample(&lambda);
|
||||
l += RGBIlluminantSpectrum::new(&self.colorspace, rgb.clamp_zero())
|
||||
.sample(&lambda);
|
||||
}
|
||||
}
|
||||
l *= self.scale / (self.image.resolution().x() * self.image.resolution().y()) as Float;
|
||||
|
|
@ -162,7 +169,7 @@ impl LightTrait for DiffuseAreaLight {
|
|||
|
||||
#[cfg(not(target_os = "cuda"))]
|
||||
fn preprocess(&mut self, _scene_bounds: &Bounds3f) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "cuda"))]
|
||||
|
|
|
|||
|
|
@ -393,7 +393,7 @@ impl BilinearPatchShape {
|
|||
let Some(normals) = shading_normals else {
|
||||
return;
|
||||
};
|
||||
let n00 = normals[1];
|
||||
let n00 = normals[0];
|
||||
let n10 = normals[1];
|
||||
let n01 = normals[2];
|
||||
let n11 = normals[3];
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ impl TriangleShape {
|
|||
isect.common.n = ng;
|
||||
isect.shading.n = ng;
|
||||
|
||||
if !self.mesh.p.is_empty() || !self.mesh.s.is_empty() {
|
||||
if !self.mesh.n.is_empty() || !self.mesh.s.is_empty() {
|
||||
self.compute_shading_geometry(&mut isect, &ti, uv, dpdu, determinant, degenerate);
|
||||
}
|
||||
isect
|
||||
|
|
@ -455,13 +455,8 @@ impl ShapeTrait for TriangleShape {
|
|||
|
||||
fn sample_from_context(&self, ctx: &ShapeSampleContext, mut u: Point2f) -> Option<ShapeSample> {
|
||||
let [p0, p1, p2] = self.get_points();
|
||||
|
||||
let (b, tri_pdf) = sample_spherical_triangle(&[p0, p1, p2], ctx.p(), u)?;
|
||||
if tri_pdf == 0. {
|
||||
return None;
|
||||
}
|
||||
|
||||
let solid_angle = self.solid_angle(ctx.p());
|
||||
|
||||
if solid_angle < Self::MIN_SPHERICAL_SAMPLE_AREA
|
||||
|| solid_angle > Self::MAX_SPHERICAL_SAMPLE_AREA
|
||||
{
|
||||
|
|
@ -497,6 +492,11 @@ impl ShapeTrait for TriangleShape {
|
|||
pdf = bilinear_pdf(u, &w);
|
||||
}
|
||||
|
||||
let (b, tri_pdf) = sample_spherical_triangle(&[p0, p1, p2], ctx.p(), u)?;
|
||||
if tri_pdf == 0. {
|
||||
return None;
|
||||
}
|
||||
|
||||
let p0_v = Vector3f::from(p0);
|
||||
let p1_v = Vector3f::from(p1);
|
||||
let p2_v = Vector3f::from(p2);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,13 @@ use crate::spectra::{
|
|||
};
|
||||
use crate::utils::Ptr;
|
||||
use crate::Float;
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
pub static DIAG_IMG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
pub static DIAG_IMG_SCALE_BITS: AtomicU32 = AtomicU32::new(0);
|
||||
pub static DIAG_IMG_PIXEL0_BITS: AtomicU32 = AtomicU32::new(0);
|
||||
pub static DIAG_IMG_RGB0_BITS: AtomicU32 = AtomicU32::new(0);
|
||||
pub static DIAG_IMG_RESULT0_BITS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
/* GPU heavy code, dont know if this will ever work the way Im doing things.
|
||||
* Leaving it here isolated, for careful handling */
|
||||
|
|
@ -49,16 +56,22 @@ impl SpectrumImageTexture {
|
|||
let wrap = WrapMode2D {
|
||||
uv: [self.wrap_mode; 2],
|
||||
};
|
||||
let pixel0 = image.bilerp_channel_with_wrap(c.st, 0, wrap);
|
||||
let rgb = if image.n_channels == 1 {
|
||||
let v = image.bilerp_channel_with_wrap(c.st, 0, wrap);
|
||||
RGB::new(v, v, v)
|
||||
RGB::new(pixel0, pixel0, pixel0)
|
||||
} else {
|
||||
RGB::new(
|
||||
image.bilerp_channel_with_wrap(c.st, 0, wrap),
|
||||
pixel0,
|
||||
image.bilerp_channel_with_wrap(c.st, 1, wrap),
|
||||
image.bilerp_channel_with_wrap(c.st, 2, wrap),
|
||||
)
|
||||
};
|
||||
let n = DIAG_IMG_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
if n < 10 {
|
||||
DIAG_IMG_SCALE_BITS.store(self.scale.to_bits(), Ordering::Relaxed);
|
||||
DIAG_IMG_PIXEL0_BITS.store(pixel0.to_bits(), Ordering::Relaxed);
|
||||
DIAG_IMG_RGB0_BITS.store((rgb[0] as f32).to_bits(), Ordering::Relaxed);
|
||||
}
|
||||
let mut rgb = rgb * self.scale;
|
||||
if self.invert {
|
||||
rgb = (RGB::new(1.0, 1.0, 1.0) - rgb);
|
||||
|
|
@ -68,11 +81,15 @@ impl SpectrumImageTexture {
|
|||
.color_space
|
||||
.get()
|
||||
.expect("color_space must not be null");
|
||||
match self.spectrum_type {
|
||||
let result = match self.spectrum_type {
|
||||
SpectrumType::Unbounded => RGBUnboundedSpectrum::new(cs, rgb).sample(lambda),
|
||||
SpectrumType::Albedo => RGBAlbedoSpectrum::new(cs, rgb.clamp(0.0, 1.0)).sample(lambda),
|
||||
_ => RGBIlluminantSpectrum::new(cs, rgb).sample(lambda),
|
||||
};
|
||||
if n < 10 {
|
||||
DIAG_IMG_RESULT0_BITS.store(result[0].to_bits(), Ordering::Relaxed);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::core::pbrt::{Float, FloatBitOps, FloatBits, ONE_MINUS_EPSILON, PI, PI
|
|||
use crate::utils::gpu_array_from_fn;
|
||||
use crate::utils::hash::{hash_buffer, mix_bits};
|
||||
use crate::utils::sobol::{SOBOL_MATRICES_32, VDC_SOBOL_MATRICES, VDC_SOBOL_MATRICES_INV};
|
||||
use crate::{gvec, gvec_with_capacity, GVec, Ptr};
|
||||
use crate::{GVec, Ptr, gvec, gvec_with_capacity};
|
||||
use core::fmt::{self, Display, Write};
|
||||
use core::iter::{Product, Sum};
|
||||
use core::mem;
|
||||
|
|
@ -79,7 +79,7 @@ where
|
|||
{
|
||||
let cd: V = d.mul_add(c, V::zero());
|
||||
let diff: V = b.mul_add(a, -cd);
|
||||
let err: V = d.mul_add(-c, cd);
|
||||
let err: V = d.mul_add(-c, cd);
|
||||
diff + err
|
||||
}
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ pub fn windowed_sinc(x: Float, radius: Float, tau: Float) -> Float {
|
|||
return 0.;
|
||||
}
|
||||
|
||||
if x < 1e-5 {
|
||||
if x.abs() < 1e-5 {
|
||||
1.0
|
||||
} else {
|
||||
sinc(x) * sinc(x / tau)
|
||||
|
|
@ -545,7 +545,7 @@ pub fn next_float_up(v: Float) -> Float {
|
|||
if v >= 0.0 {
|
||||
ui = ui.wrapping_add(1);
|
||||
} else {
|
||||
ui.wrapping_sub(1);
|
||||
ui = ui.wrapping_sub(1);
|
||||
}
|
||||
bits_to_float(ui)
|
||||
}
|
||||
|
|
@ -1505,11 +1505,7 @@ where
|
|||
}
|
||||
|
||||
let det: T = (0..N).map(|i| lum[i][i]).product();
|
||||
if parity < 0 {
|
||||
-det
|
||||
} else {
|
||||
det
|
||||
}
|
||||
if parity < 0 { -det } else { det }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1600,7 +1596,7 @@ mod tests {
|
|||
let m = SquareMatrix {
|
||||
m: [[1.0, 2.0], [2.0, 4.0]],
|
||||
}; // Determinant is 0
|
||||
assert!(m.inverse().is_ok());
|
||||
assert!(m.inverse().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use shared::Float;
|
|||
pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
||||
let media = scene.create_media();
|
||||
let textures = scene.create_textures(arena);
|
||||
let (named_materials, materials) = scene.create_materials(&textures, arena)?;
|
||||
let (named_materials, materials, _default_mtl) = scene.create_materials(&textures, arena)?;
|
||||
let (lights, al_map) = scene.create_lights(&textures, &media, arena);
|
||||
|
||||
let _have_scattering = {
|
||||
|
|
@ -30,10 +30,8 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
|||
.any(|sh| !sh.inside_medium.is_empty() || !sh.outside_medium.is_empty())
|
||||
};
|
||||
|
||||
let aggregate = scene.create_aggregate(&textures, &named_materials, &materials, al_map, &media, arena);
|
||||
|
||||
let mut all_lights = lights;
|
||||
all_lights.extend(area_lights);
|
||||
let aggregate = scene.create_aggregate(&textures, &named_materials, &materials, &al_map, &media, arena);
|
||||
let all_lights = lights;
|
||||
|
||||
let camera = scene.get_camera().unwrap();
|
||||
let _film = camera.get_film();
|
||||
|
|
@ -114,6 +112,7 @@ pub fn render_scene(scene: &BasicScene, arena: &Arena) -> Result<()> {
|
|||
sampler.clone(),
|
||||
aggregate.clone(),
|
||||
all_lights,
|
||||
materials,
|
||||
arena,
|
||||
);
|
||||
render(
|
||||
|
|
|
|||
|
|
@ -483,9 +483,8 @@ impl BasicScene {
|
|||
&self,
|
||||
textures: &NamedTextures,
|
||||
arena: &Arena,
|
||||
) -> Result<(HashMap<String, Material>, Vec<Material>)> {
|
||||
) -> Result<(HashMap<String, MaterialIdx>, Vec<Material>, MaterialIdx)> {
|
||||
let mut state = self.material_state.lock();
|
||||
|
||||
// Finish async normal map loads
|
||||
let finished: Vec<_> = state.normal_map_jobs.drain().collect();
|
||||
for (filename, job) in finished {
|
||||
|
|
@ -499,8 +498,10 @@ impl BasicScene {
|
|||
}
|
||||
}
|
||||
|
||||
// Named materials
|
||||
let mut named_materials: HashMap<String, Material> = HashMap::new();
|
||||
let mut materials: Vec<Material> = Vec::new();
|
||||
let mut named_materials: HashMap<String, MaterialIdx> = HashMap::new();
|
||||
// Value map for resolving named sub-materials (e.g. mix) during creation.
|
||||
let mut named_values: HashMap<String, Material> = HashMap::new();
|
||||
|
||||
for (name, entity) in &state.named_materials {
|
||||
if named_materials.contains_key(name) {
|
||||
|
|
@ -511,29 +512,29 @@ impl BasicScene {
|
|||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
let mat_type = entity.parameters.get_one_string("type", "")?;
|
||||
if mat_type.is_empty() {
|
||||
log::error!("{}: missing material type for '{}'", entity.loc, name);
|
||||
continue;
|
||||
}
|
||||
|
||||
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
||||
let tex_dict = TextureParameterDictionary::new(
|
||||
Arc::new(entity.parameters.clone()),
|
||||
Some(textures),
|
||||
);
|
||||
|
||||
match Material::create(
|
||||
&mat_type,
|
||||
&tex_dict,
|
||||
normal_map,
|
||||
&named_materials,
|
||||
&named_values, // value map, not index map
|
||||
entity.loc.clone(),
|
||||
arena,
|
||||
) {
|
||||
Ok(mat) => {
|
||||
named_materials.insert(name.clone(), mat);
|
||||
let idx = MaterialIdx(materials.len() as u32);
|
||||
materials.push(mat);
|
||||
named_values.insert(name.clone(), mat);
|
||||
named_materials.insert(name.clone(), idx);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
|
|
@ -546,37 +547,37 @@ impl BasicScene {
|
|||
}
|
||||
}
|
||||
|
||||
// Indexed materials
|
||||
let materials: Vec<Material> = state
|
||||
.materials
|
||||
.iter()
|
||||
.map(|entity| {
|
||||
let result: Result<Material> = (|| {
|
||||
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
||||
let tex_dict = TextureParameterDictionary::new(
|
||||
entity.parameters.clone().into(),
|
||||
Some(textures),
|
||||
);
|
||||
Material::create(
|
||||
&entity.name,
|
||||
&tex_dict,
|
||||
normal_map,
|
||||
&named_materials,
|
||||
entity.loc.clone(),
|
||||
arena,
|
||||
)
|
||||
})();
|
||||
match result {
|
||||
Ok(mat) => mat,
|
||||
Err(e) => {
|
||||
log::error!("{}: failed to create material: {}", entity.loc, e);
|
||||
crate::core::material::default_diffuse_material(arena)
|
||||
}
|
||||
// Indexed (anonymous) materials, appended after named ones.
|
||||
for entity in &state.materials {
|
||||
let result: Result<Material> = (|| {
|
||||
let normal_map = self.get_normal_map(&state, &entity.parameters)?;
|
||||
let tex_dict = TextureParameterDictionary::new(
|
||||
entity.parameters.clone().into(),
|
||||
Some(textures),
|
||||
);
|
||||
Material::create(
|
||||
&entity.name,
|
||||
&tex_dict,
|
||||
normal_map,
|
||||
&named_values,
|
||||
entity.loc.clone(),
|
||||
arena,
|
||||
)
|
||||
})();
|
||||
let mat = match result {
|
||||
Ok(mat) => mat,
|
||||
Err(e) => {
|
||||
log::error!("{}: failed to create material: {}", entity.loc, e);
|
||||
crate::core::material::default_diffuse_material(arena)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
};
|
||||
materials.push(mat);
|
||||
}
|
||||
|
||||
Ok((named_materials, materials))
|
||||
let default_mtl = MaterialIdx(materials.len() as u32);
|
||||
materials.push(crate::core::material::default_diffuse_material(arena));
|
||||
|
||||
Ok((named_materials, materials, default_mtl))
|
||||
}
|
||||
|
||||
pub fn create_media(&self) -> HashMap<String, Arc<Medium>> {
|
||||
|
|
@ -810,7 +811,8 @@ impl BasicScene {
|
|||
camera: Arc<Camera>,
|
||||
sampler: Arc<Sampler>,
|
||||
aggregate: Arc<Primitive>,
|
||||
lights: Vec<Arc<Light>>,
|
||||
lights: Vec<Light>,
|
||||
materials: Vec<Material>,
|
||||
arena: &Arena,
|
||||
) -> PathIntegrator {
|
||||
let integrator_entity = self.integrator.lock().clone().unwrap();
|
||||
|
|
@ -823,6 +825,7 @@ impl BasicScene {
|
|||
sampler,
|
||||
aggregate,
|
||||
lights,
|
||||
materials,
|
||||
PathConfig::FULL,
|
||||
arena,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use shared::core::interaction::{Interaction, InteractionTrait};
|
|||
use shared::core::light::{Light, LightTrait};
|
||||
use shared::core::primitive::{Primitive, PrimitiveTrait};
|
||||
use shared::core::shape::ShapeIntersection;
|
||||
use shared::core::LightIdx;
|
||||
use shared::lights::sampler::{LightSampler, LightSamplerTrait};
|
||||
use shared::spectra::SampledWavelengths;
|
||||
use shared::utils::sampling::power_heuristic;
|
||||
|
|
@ -13,18 +14,16 @@ use std::sync::Arc;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct IntegratorBase {
|
||||
pub aggregate: Arc<Primitive>,
|
||||
pub lights: Vec<Arc<Light>>,
|
||||
pub infinite_lights: Vec<Arc<Light>>,
|
||||
pub lights: Vec<Light>,
|
||||
pub infinite_lights: Vec<LightIdx>,
|
||||
}
|
||||
|
||||
impl IntegratorBase {
|
||||
pub fn new(aggregate: Arc<Primitive>, mut lights: Vec<Arc<Light>>) -> Self {
|
||||
pub fn new(aggregate: Arc<Primitive>, mut lights: Vec<Light>) -> Self {
|
||||
let scene_bounds = aggregate.bounds();
|
||||
|
||||
for light in &mut lights {
|
||||
Arc::get_mut(light)
|
||||
.expect("Light has multiple owners during setup")
|
||||
.preprocess(&scene_bounds);
|
||||
light.preprocess(&scene_bounds);
|
||||
}
|
||||
|
||||
println!(
|
||||
|
|
@ -36,10 +35,11 @@ impl IntegratorBase {
|
|||
println!(" light[{}]: type={:?}", i, l.light_type());
|
||||
}
|
||||
|
||||
let infinite_lights = lights
|
||||
let infinite_lights: Vec<LightIdx> = lights
|
||||
.iter()
|
||||
.filter(|light| light.light_type().is_infinite())
|
||||
.cloned()
|
||||
.enumerate()
|
||||
.filter(|(_, l)| l.light_type().is_infinite())
|
||||
.map(|(i, _)| LightIdx(i as u32))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
|
|
@ -72,7 +72,8 @@ impl IntegratorBase {
|
|||
light_sampler: Option<&LightSampler>,
|
||||
use_mis: bool,
|
||||
) {
|
||||
for light in &self.infinite_lights {
|
||||
for &idx in &self.infinite_lights {
|
||||
let light = &self.lights[idx.0 as usize];
|
||||
let le = light.le(ray, lambda);
|
||||
if le.is_black() {
|
||||
continue;
|
||||
|
|
@ -81,7 +82,7 @@ impl IntegratorBase {
|
|||
if state.depth == 0 || state.specular_bounce || !use_mis {
|
||||
state.l += state.beta * le;
|
||||
} else if let Some(sampler) = light_sampler {
|
||||
let p_l = sampler.pmf_with_context(&state.prev_ctx, light)
|
||||
let p_l = sampler.pmf_with_context(&state.prev_ctx, idx)
|
||||
* light.pdf_li(&state.prev_ctx, ray.d, true);
|
||||
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
|
||||
state.l += state.beta * w_b * le;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use shared::core::camera::Camera;
|
|||
use shared::core::film::VisibleSurface;
|
||||
use shared::core::geometry::{Point2i, Ray};
|
||||
use shared::core::light::Light;
|
||||
use shared::core::material::Material;
|
||||
use shared::core::primitive::Primitive;
|
||||
use shared::core::sampler::Sampler;
|
||||
use shared::spectra::{SampledSpectrum, SampledWavelengths};
|
||||
|
|
@ -47,7 +48,8 @@ pub trait CreateIntegrator {
|
|||
camera: Arc<Camera>,
|
||||
sampler: Arc<Sampler>,
|
||||
aggregate: Arc<Primitive>,
|
||||
lights: Vec<Arc<Light>>,
|
||||
lights: Vec<Light>,
|
||||
materials: Vec<Material>,
|
||||
config: PathConfig,
|
||||
arena: &Arena,
|
||||
) -> Result<PathIntegrator>;
|
||||
|
|
@ -59,14 +61,15 @@ impl CreateIntegrator for PathIntegrator {
|
|||
camera: Arc<Camera>,
|
||||
_sampler: Arc<Sampler>,
|
||||
aggregate: Arc<Primitive>,
|
||||
lights: Vec<Arc<Light>>,
|
||||
lights: Vec<Light>,
|
||||
materials: Vec<Material>,
|
||||
config: PathConfig,
|
||||
arena: &Arena,
|
||||
) -> Result<PathIntegrator> {
|
||||
let _max_depth = parameters.get_one_int("maxdepth", 5)?;
|
||||
let _regularize = parameters.get_one_bool("regularize", false)?;
|
||||
let light_sampler = create_light_sampler("power", &lights, arena);
|
||||
let integrator = PathIntegrator::new(aggregate, lights, camera, light_sampler, config);
|
||||
let integrator = PathIntegrator::new(aggregate, lights, camera, light_sampler, config, materials);
|
||||
Ok(integrator)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use shared::core::geometry::{Point2i, Ray, Vector3f, VectorLike};
|
|||
use shared::core::interaction::{Interaction, InteractionTrait, SurfaceInteraction};
|
||||
use shared::core::light::LightTrait;
|
||||
use shared::core::light::{Light, LightSampleContext};
|
||||
use shared::core::material::Material;
|
||||
use shared::core::primitive::Primitive;
|
||||
use shared::core::sampler::{Sampler, SamplerTrait};
|
||||
use shared::lights::sampler::LightSampler;
|
||||
|
|
@ -68,6 +69,7 @@ pub struct PathIntegrator {
|
|||
camera: Arc<Camera>,
|
||||
sampler: LightSampler,
|
||||
config: PathConfig,
|
||||
materials: Vec<Material>,
|
||||
}
|
||||
|
||||
unsafe impl Send for PathIntegrator {}
|
||||
|
|
@ -76,10 +78,11 @@ unsafe impl Sync for PathIntegrator {}
|
|||
impl PathIntegrator {
|
||||
pub fn new(
|
||||
aggregate: Arc<Primitive>,
|
||||
lights: Vec<Arc<Light>>,
|
||||
lights: Vec<Light>,
|
||||
camera: Arc<Camera>,
|
||||
sampler: LightSampler,
|
||||
config: PathConfig,
|
||||
materials: Vec<Material>,
|
||||
) -> Self {
|
||||
let base = IntegratorBase::new(aggregate, lights);
|
||||
Self {
|
||||
|
|
@ -87,6 +90,7 @@ impl PathIntegrator {
|
|||
camera,
|
||||
sampler,
|
||||
config,
|
||||
materials,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +108,9 @@ impl PathIntegrator {
|
|||
return SampledSpectrum::zero();
|
||||
};
|
||||
|
||||
let Some(ls) = sampled.light.sample_li(&ctx, sampler.get2d(), lambda, true) else {
|
||||
let light = &self.base.lights[sampled.light.0 as usize];
|
||||
|
||||
let Some(ls) = light.sample_li(&ctx, sampler.get2d(), lambda, true) else {
|
||||
return SampledSpectrum::zero();
|
||||
};
|
||||
|
||||
|
|
@ -133,7 +139,7 @@ impl PathIntegrator {
|
|||
|
||||
let p_l = sampled.p * ls.pdf;
|
||||
|
||||
if !self.config.use_mis || sampled.light.light_type().is_delta_light() {
|
||||
if !self.config.use_mis || light.light_type().is_delta_light() {
|
||||
ls.l * f / p_l
|
||||
} else {
|
||||
let p_b = bsdf.pdf(wo, wi, FArgs::default());
|
||||
|
|
@ -228,22 +234,22 @@ impl RayIntegratorTrait for PathIntegrator {
|
|||
let isect = &mut si.intr;
|
||||
|
||||
// Emission from hit surface
|
||||
let le = isect.le(-ray.d, lambda);
|
||||
let le = isect.le(-ray.d, lambda, &self.base.lights);
|
||||
if !le.is_black() {
|
||||
if state.depth == 0 || state.specular_bounce {
|
||||
state.l += state.beta * le;
|
||||
} else if self.config.use_mis
|
||||
&& !isect.area_light.is_none() {
|
||||
let light = &isect.area_light;
|
||||
let p_l = self.sampler.pmf_with_context(&state.prev_ctx, light)
|
||||
* light.pdf_li(&state.prev_ctx, ray.d, true);
|
||||
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
|
||||
state.l += state.beta * w_b * le;
|
||||
}
|
||||
} else if self.config.use_mis && !isect.area_light.is_none() {
|
||||
let idx = isect.area_light;
|
||||
let light = &self.base.lights[idx.0 as usize];
|
||||
let p_l = self.sampler.pmf_with_context(&state.prev_ctx, idx)
|
||||
* light.pdf_li(&state.prev_ctx, ray.d, true);
|
||||
let w_b = power_heuristic(1, state.prev_pdf, 1, p_l);
|
||||
state.l += state.beta * w_b * le;
|
||||
}
|
||||
}
|
||||
|
||||
// Get BSDF
|
||||
let Some(mut bsdf) = isect.get_bsdf(&ray, lambda, &self.camera, sampler) else {
|
||||
let Some(mut bsdf) = isect.get_bsdf(&ray, lambda, &self.camera, sampler, &self.materials) else {
|
||||
state.specular_bounce = true;
|
||||
isect.skip_intersection(&mut ray, t_hit);
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -248,14 +248,6 @@ pub fn evaluate_pixel_sample<T: RayIntegratorTrait>(
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -131,12 +131,26 @@ impl SpectrumImageTexture {
|
|||
|
||||
impl SpectrumTextureTrait for SpectrumImageTexture {
|
||||
fn evaluate(&self, ctx: &TextureEvalContext, lambda: &SampledWavelengths) -> SampledSpectrum {
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
static PATH_IMG_COUNT: AtomicU32 = AtomicU32::new(0);
|
||||
let pn = PATH_IMG_COUNT.fetch_add(1, Ordering::Relaxed);
|
||||
|
||||
let mut c = self.base.mapping.map(ctx);
|
||||
c.st[1] = 1. - c.st[1];
|
||||
let dst0 = Vector2f::new(c.dsdx, c.dtdx);
|
||||
let dst1 = Vector2f::new(c.dsdy, c.dtdy);
|
||||
let rgb_unclamp = self.base.scale * self.base.mipmap.filter::<RGB>(c.st, dst0, dst1);
|
||||
let raw_rgb = self.base.mipmap.filter::<RGB>(c.st, dst0, dst1);
|
||||
let rgb_unclamp = self.base.scale * raw_rgb;
|
||||
let rgb = RGB::clamp_zero(&rgb_unclamp);
|
||||
|
||||
if pn < 5 {
|
||||
eprintln!("PATH_IMG[{pn}] scale={:.6} raw_rgb[0]={:.6} rgb_after_scale[0]={:.6} \
|
||||
st={:?} dst0={:?} dst1={:?} has_cs={}",
|
||||
self.base.scale, raw_rgb[0], rgb_unclamp[0],
|
||||
c.st, dst0, dst1,
|
||||
self.base.mipmap.get_rgb_colorspace().is_some());
|
||||
}
|
||||
|
||||
if let Some(cs) = self.base.mipmap.get_rgb_colorspace() {
|
||||
match self.spectrum_type {
|
||||
SpectrumType::Unbounded => {
|
||||
|
|
@ -148,8 +162,11 @@ impl SpectrumTextureTrait for SpectrumImageTexture {
|
|||
_ => return RGBIlluminantSpectrum::new(&cs, rgb).sample(lambda),
|
||||
}
|
||||
}
|
||||
assert!(rgb[0] == rgb[1] && rgb[1] == rgb[2]);
|
||||
SampledSpectrum::new(rgb[0])
|
||||
let result = SampledSpectrum::new(rgb[0]);
|
||||
if pn < 5 {
|
||||
eprintln!("PATH_IMG[{pn}] no-cs branch result[0]={:.6}", result[0]);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
use crate::globals::get_options;
|
||||
use log::debug;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
pub static DIAG_SHADOW_UNOCCLUDED: AtomicU32 = AtomicU32::new(0);
|
||||
use rayon::prelude::*;
|
||||
use shared::core::geometry::{Bounds3f, Ray, VectorLike};
|
||||
use shared::core::interaction::{InteractionTrait, SurfaceInteraction};
|
||||
|
|
@ -113,7 +116,7 @@ impl WavefrontAggregate for CpuAggregate {
|
|||
);
|
||||
}
|
||||
|
||||
eval_q.push(MaterialEvalWorkItem {
|
||||
let item = MaterialEvalWorkItem {
|
||||
p: intr.pi(),
|
||||
n: intr.n(),
|
||||
ns: intr.shading.n,
|
||||
|
|
@ -137,7 +140,23 @@ impl WavefrontAggregate for CpuAggregate {
|
|||
dpdvs: intr.shading.dpdv,
|
||||
dndus: intr.shading.dndu,
|
||||
dndvs: intr.shading.dndv,
|
||||
});
|
||||
};
|
||||
if let Some(slot) = eval_q.push(item) {
|
||||
if slot < 10 {
|
||||
eprintln!(
|
||||
"ENQUEUE[{slot}] pixel={:?} depth={} \
|
||||
p={:?} n={:?} ns={:?} \
|
||||
dpdu={:?} dpdv={:?} \
|
||||
dpdus={:?} dpdvs={:?} \
|
||||
uv={:?} material={:?} area_light={:?} face_index={}",
|
||||
item.pixel_index, item.depth,
|
||||
item.p, item.n, item.ns,
|
||||
item.dpdu, item.dpdv,
|
||||
item.dpdus, item.dpdvs,
|
||||
item.uv, item.material, item.area_light, item.face_index,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +175,15 @@ impl WavefrontAggregate for CpuAggregate {
|
|||
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();
|
||||
let n = DIAG_SHADOW_UNOCCLUDED.fetch_add(1, Ordering::Relaxed);
|
||||
if n < 10 {
|
||||
eprintln!(
|
||||
"SHADOW_UNOCCLUDED[{n}] pixel={} l_d={:?} r_u={:?} r_l={:?} \
|
||||
denom={:.6} ld={:?}",
|
||||
pi, work.l_d, work.r_u, work.r_l,
|
||||
(work.r_u + work.r_l).average(), ld
|
||||
);
|
||||
}
|
||||
let mut l = pixel_sample_state.l.get(pi);
|
||||
l += ld;
|
||||
pixel_sample_state.l.set(pi, l);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,26 @@ use shared::utils::soa::{SoA, SoAAllocator, WorkQueue};
|
|||
use shared::wavefront::workitems::*;
|
||||
use shared::wavefront::{WavefrontAggregate, WavefrontPathIntegrator, WavefrontRenderer};
|
||||
use shared::{gvec, gvec_from_slice, GVec, Ptr, SHADOW_EPSILON};
|
||||
use shared::textures::image::{
|
||||
DIAG_IMG_COUNT, DIAG_IMG_SCALE_BITS, DIAG_IMG_PIXEL0_BITS,
|
||||
DIAG_IMG_RGB0_BITS, DIAG_IMG_RESULT0_BITS,
|
||||
};
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
static DIAG_EVAL_ENTER: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_BSDF_EMPTY: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_NON_SPECULAR_SKIP: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_SAMPLE_LIGHT_NONE: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_SAMPLE_LI_NONE: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_LS_L_BLACK: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_LS_PDF_ZERO: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_F_NONE: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_F_BLACK: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_SHADOW_PUSH: AtomicU32 = AtomicU32::new(0);
|
||||
static DIAG_NEE_D0_PRINT: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
pub struct CpuWavefrontRenderer(pub WavefrontPathIntegrator<CpuAggregate>);
|
||||
|
||||
impl Deref for CpuWavefrontRenderer {
|
||||
|
|
@ -84,7 +101,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
let cpu_aggregate = CpuAggregate::new(*aggregate);
|
||||
let cpu_aggregate = CpuAggregate::new(*aggregate, &materials);
|
||||
|
||||
let bounds = aggregate.bounds();
|
||||
for light in &mut lights {
|
||||
|
|
@ -210,6 +227,30 @@ impl CpuWavefrontRenderer {
|
|||
&self.shadow_ray_queue,
|
||||
&self.pixel_sample_state,
|
||||
);
|
||||
|
||||
if sample_index == 0 && y0 == pixel_bounds.p_min.y() {
|
||||
eprintln!("=== DIAG s=0 y0={} depth={} ===", y0, depth);
|
||||
eprintln!(" eval_enter={}", DIAG_EVAL_ENTER.load(Ordering::Relaxed));
|
||||
eprintln!(" bsdf_empty={}", DIAG_BSDF_EMPTY.load(Ordering::Relaxed));
|
||||
eprintln!(" non_specular_skip={}", DIAG_NON_SPECULAR_SKIP.load(Ordering::Relaxed));
|
||||
eprintln!(" sample_light_none={}", DIAG_SAMPLE_LIGHT_NONE.load(Ordering::Relaxed));
|
||||
eprintln!(" sample_li_none={}", DIAG_SAMPLE_LI_NONE.load(Ordering::Relaxed));
|
||||
eprintln!(" ls_l_black={}", DIAG_LS_L_BLACK.load(Ordering::Relaxed));
|
||||
eprintln!(" ls_pdf_zero={}", DIAG_LS_PDF_ZERO.load(Ordering::Relaxed));
|
||||
eprintln!(" f_none={}", DIAG_F_NONE.load(Ordering::Relaxed));
|
||||
eprintln!(" f_black={}", DIAG_F_BLACK.load(Ordering::Relaxed));
|
||||
eprintln!(" shadow_push={}", DIAG_SHADOW_PUSH.load(Ordering::Relaxed));
|
||||
eprintln!(" shadow_unoccluded={}", super::aggregate::DIAG_SHADOW_UNOCCLUDED.load(Ordering::Relaxed));
|
||||
let img_n = DIAG_IMG_COUNT.load(Ordering::Relaxed);
|
||||
if img_n > 0 {
|
||||
let scale = f32::from_bits(DIAG_IMG_SCALE_BITS.load(Ordering::Relaxed));
|
||||
let pixel0 = f32::from_bits(DIAG_IMG_PIXEL0_BITS.load(Ordering::Relaxed));
|
||||
let rgb0 = f32::from_bits(DIAG_IMG_RGB0_BITS.load(Ordering::Relaxed));
|
||||
let result0 = f32::from_bits(DIAG_IMG_RESULT0_BITS.load(Ordering::Relaxed));
|
||||
eprintln!(" img_tex_calls={} scale={:.6} pixel0={:.6} rgb0_pre_scale={:.6} result[0]={:.6}",
|
||||
img_n, scale, pixel0, rgb0, result0);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.update_film(y0, y1, &pixel_bounds);
|
||||
let batch_pixels =
|
||||
|
|
@ -219,6 +260,18 @@ impl CpuWavefrontRenderer {
|
|||
y0 = y1;
|
||||
}
|
||||
}
|
||||
eprintln!("=== NEE DIAG COUNTS ===");
|
||||
eprintln!("eval_enter={}", DIAG_EVAL_ENTER.load(Ordering::Relaxed));
|
||||
eprintln!("bsdf_empty={}", DIAG_BSDF_EMPTY.load(Ordering::Relaxed));
|
||||
eprintln!("non_specular_skip={}", DIAG_NON_SPECULAR_SKIP.load(Ordering::Relaxed));
|
||||
eprintln!("sample_light_none={}", DIAG_SAMPLE_LIGHT_NONE.load(Ordering::Relaxed));
|
||||
eprintln!("sample_li_none={}", DIAG_SAMPLE_LI_NONE.load(Ordering::Relaxed));
|
||||
eprintln!("ls_l_black={}", DIAG_LS_L_BLACK.load(Ordering::Relaxed));
|
||||
eprintln!("ls_pdf_zero={}", DIAG_LS_PDF_ZERO.load(Ordering::Relaxed));
|
||||
eprintln!("f_none={}", DIAG_F_NONE.load(Ordering::Relaxed));
|
||||
eprintln!("f_black={}", DIAG_F_BLACK.load(Ordering::Relaxed));
|
||||
eprintln!("shadow_push={}", DIAG_SHADOW_PUSH.load(Ordering::Relaxed));
|
||||
eprintln!("shadow_unoccluded={}", super::aggregate::DIAG_SHADOW_UNOCCLUDED.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
fn generate_camera_rays(
|
||||
|
|
@ -404,6 +457,21 @@ impl CpuWavefrontRenderer {
|
|||
|
||||
(0..n as usize).into_par_iter().for_each(|i| {
|
||||
let w = unsafe { queue.storage.get(i) };
|
||||
if i < 10 {
|
||||
eprintln!(
|
||||
"DEQUEUE[{i}] pixel={:?} depth={} \
|
||||
p={:?} n={:?} ns={:?} \
|
||||
dpdu={:?} dpdv={:?} \
|
||||
dpdus={:?} dpdvs={:?} \
|
||||
uv={:?} material={:?} area_light={:?} face_index={}",
|
||||
w.pixel_index, w.depth,
|
||||
w.p, w.n, w.ns,
|
||||
w.dpdu, w.dpdv,
|
||||
w.dpdus, w.dpdvs,
|
||||
w.uv, w.material, w.area_light, w.face_index,
|
||||
);
|
||||
}
|
||||
DIAG_EVAL_ENTER.fetch_add(1, Ordering::Relaxed);
|
||||
if w.material.is_none() {
|
||||
return;
|
||||
}
|
||||
|
|
@ -445,6 +513,7 @@ impl CpuWavefrontRenderer {
|
|||
}
|
||||
|
||||
if bsdf.flags().is_empty() {
|
||||
DIAG_BSDF_EMPTY.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
if regularize && w.any_non_specular_bounces {
|
||||
|
|
@ -513,6 +582,9 @@ impl CpuWavefrontRenderer {
|
|||
|
||||
// Direct lighting
|
||||
let flags = bsdf.flags();
|
||||
if !flags.is_non_specular() {
|
||||
DIAG_NON_SPECULAR_SKIP.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
if flags.is_non_specular() {
|
||||
let mut light_ctx = LightSampleContext {
|
||||
pi: w.p,
|
||||
|
|
@ -529,24 +601,33 @@ impl CpuWavefrontRenderer {
|
|||
let Some(sampled_light) =
|
||||
light_sampler.sample_with_context(&light_ctx, rs.direct.uc)
|
||||
else {
|
||||
DIAG_SAMPLE_LIGHT_NONE.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
};
|
||||
let light = &self.lights[sampled_light.light.0 as usize];
|
||||
|
||||
let Some(ls) = light.sample_li(&light_ctx, rs.direct.u, &lambda, true) else {
|
||||
DIAG_SAMPLE_LI_NONE.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
};
|
||||
|
||||
if ls.l.is_black() || ls.pdf <= 0.0 {
|
||||
if ls.l.is_black() {
|
||||
DIAG_LS_L_BLACK.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
if ls.pdf <= 0.0 {
|
||||
DIAG_LS_PDF_ZERO.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
|
||||
let wi = ls.wi;
|
||||
let Some(f) = bsdf.f(wo, wi, TransportMode::Radiance) else {
|
||||
DIAG_F_NONE.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
};
|
||||
|
||||
if f.is_black() {
|
||||
DIAG_F_BLACK.fetch_add(1, Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -569,6 +650,19 @@ impl CpuWavefrontRenderer {
|
|||
&ls.p_light.n(),
|
||||
);
|
||||
|
||||
DIAG_SHADOW_PUSH.fetch_add(1, Ordering::Relaxed);
|
||||
if w.depth == 0 {
|
||||
let n = DIAG_NEE_D0_PRINT.fetch_add(1, Ordering::Relaxed);
|
||||
if n < 10 {
|
||||
eprintln!(
|
||||
"NEE_D0[{n}] pixel={:?} ls.l={:?} ls.pdf={:.6} f={:?} \
|
||||
beta={:?} light_pdf={:.6} bsdf_pdf={:.6} \
|
||||
r_u={:?} r_l={:?} l_d={:?}",
|
||||
w.pixel_index, ls.l, ls.pdf, f, beta,
|
||||
light_pdf, bsdf_pdf, r_u, r_l, l_d
|
||||
);
|
||||
}
|
||||
}
|
||||
shadow_ray_queue.push(ShadowRayWorkItem {
|
||||
ray,
|
||||
t_max: 1.0 - SHADOW_EPSILON,
|
||||
|
|
|
|||
Loading…
Reference in a new issue