Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/integrators/ptracer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ class ParticleTracerIntegrator final : public AdjointIntegrator<Float, Spectrum>
dr::gather<EmitterPtr>(scene->emitters_dr(), emitter_idx);

// Don't connect delta emitters with sensor (both position and direction)
Mask active = !has_flag(emitter->flags(), EmitterFlags::Delta);
// Unless the sensor has spatial extent (e.g. irradiancemeter)
Mask active = !has_flag(emitter->flags(), EmitterFlags::Delta) ||
sensor->needs_aperture_sample();

// 3. Emitter position sampling
Spectrum emitter_weight = dr::zeros<Spectrum>();
Expand Down Expand Up @@ -347,7 +349,9 @@ class ParticleTracerIntegrator final : public AdjointIntegrator<Float, Spectrum>
// Foreshortening term and BSDF value for that direction (for surface interactions).
Spectrum result = 0.f;
Spectrum surface_weight = 1.f;
Vector3f local_d = si.to_local(sensor_ray.d);
Mask has_frame = dr::norm(si.n) > 0.f;
Vector3f local_d = dr::zeros<Vector3f>();
local_d[has_frame] = si.to_local(sensor_ray.d);
Mask on_surface = active && (si.shape != nullptr);
if (dr::any_or<true>(on_surface)) {
/* Note that foreshortening is only missing for directly visible
Expand Down Expand Up @@ -382,7 +386,7 @@ class ParticleTracerIntegrator final : public AdjointIntegrator<Float, Spectrum>
we still don't want light coming from behind the emitter. */
Mask not_on_surface = active && (si.shape == nullptr) && (bsdf == nullptr);
if (dr::any_or<true>(not_on_surface)) {
Mask invalid_side = Frame3f::cos_theta(local_d) <= 0.f;
Mask invalid_side = has_frame && (Frame3f::cos_theta(local_d) <= 0.f);
surface_weight[not_on_surface && invalid_side] = 0.f;
}

Expand Down
34 changes: 34 additions & 0 deletions src/integrators/tests/test_ptracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,37 @@ def __init__(self, props):
mi.load_dict({
'type': 'myptracer'
})


def test08_delta_light_irradiancemeter(variants_all_ad_rgb):
scene_dict = {
'type': 'scene',
'integrator': {
'type': 'ptracer',
'max_depth': 2,
},
'sensor': {
'type': 'sphere',
'center': [0, 0, 0],
'radius': 1.0,
'sensor': {
'type': 'irradiancemeter',
'film': {
'type': 'hdrfilm',
'width': 1,
'height': 1,
'rfilter': {'type': 'box'},
}
}
},
'emitter': {
'type': 'point',
'position': [2, 0, 0]
}
}

scene = mi.load_dict(scene_dict)
sensor = scene.sensors()[0]
image = mi.render(scene, seed=0, sensor=sensor)

assert image[0][0][0].numpy() > 0.1
4 changes: 3 additions & 1 deletion src/sensors/irradiancemeter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ MI_VARIANT class IrradianceMeter final : public Sensor<Float, Spectrum> {

std::pair<DirectionSample3f, Spectrum>
sample_direction(const Interaction3f &it, const Point2f &sample, Mask active) const override {
return { m_shape->sample_direction(it, sample, active), dr::Pi<ScalarFloat> };
DirectionSample3f ds = m_shape->sample_direction(it, sample, active);
return { ds, dr::select(ds.pdf > 0.f,
dr::rcp(ds.pdf) / m_shape->surface_area(), 0.f) };
}

Float pdf_direction(const Interaction3f &it, const DirectionSample3f &ds,
Expand Down
13 changes: 9 additions & 4 deletions src/sensors/tests/test_irradiancemeter.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ def test_incoming_flux(variant_scalar_rgb, radiance, np_rng):


@pytest.mark.parametrize("radiance", [2.04, 1.0, 0.0])
def test_incoming_flux_integrator(variant_scalar_rgb, radiance):
@pytest.mark.parametrize("integrator_type", ["path", "ptracer"])
def test_incoming_flux_integrator(variant_scalar_rgb, radiance, integrator_type):
"""
We test the recorded power density of the irradiance meter, by creating a simple scene:
The irradiance meter is attached to a sphere with unit radius at the coordinate origin
Expand All @@ -129,17 +130,21 @@ def test_incoming_flux_integrator(variant_scalar_rgb, radiance):
'type': 'scene',
'sensor': sensor_shape_dict(1, mi.ScalarVector3f(0, 0, 0)),
'emitter': constant_emitter_dict(radiance),
'integrator': {'type': 'path'}
'integrator': {'type': integrator_type},
'sampler': {
'type': 'independent',
'sample_count': 4000
}
}

scene = mi.load_dict(scene_dict)
scene.integrator().render(scene, seed=0)
scene.integrator().render(scene, seed=0, spp=10000)
film = scene.sensors()[0].film()

img = film.bitmap(raw=True).convert(mi.Bitmap.PixelFormat.Y,
mi.Struct.Type.Float32, srgb_gamma=False)

assert dr.allclose(mi.TensorXf(img), (radiance * dr.pi))
assert dr.allclose(mi.TensorXf(img), (radiance * dr.pi), rtol=0.2)


def test_shape_accessors(variants_vec_rgb):
Expand Down