Skip to content

Commit ed00d49

Browse files
committed
Distant plugin fix and docs and test updates
1 parent 0fa760d commit ed00d49

File tree

2 files changed

+74
-52
lines changed

2 files changed

+74
-52
lines changed

src/sensors/distant.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,21 @@ Distant directional sensor (:monosp:`distant`)
3232
uniformly over the entire scene.
3333
3434
This sensor plugin implements a distant directional sensor which records
35-
radiation leaving the scene in a given direction. If the ``target`` parameter
36-
is not set, rays cast by the sensor will be distributed uniformly on the cross
37-
section of the scene's bounding sphere.
35+
radiation leaving the scene in a given direction. By default, it records the
36+
(spectral) radiant flux per unit solid angle leaving the scene in the specified
37+
direction (in unit power per unit solid angle). Rays cast by the sensor are
38+
distributed uniformly on the cross section of the scene's bounding sphere.
39+
40+
.. warning::
41+
42+
If this sensor is used with an environment map emitter, it will also record
43+
radiant flux coming from the part of emitter appearing through the scene's
44+
bounding sphere cross section. Care should be taken notably when using the
45+
`constant` or `envmap` emitters.
46+
47+
If the ``target`` parameter is set, the sensor looks at a single point and
48+
records a (spectral) radiant flux per unit surface area per unit solid angle
49+
(in unit power per unit surface area per unit solid angle).
3850
3951
*/
4052

@@ -116,7 +128,10 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
116128
}
117129

118130
ray.update();
119-
return std::make_pair(ray, wav_weight);
131+
return std::make_pair(
132+
ray, m_has_target
133+
? wav_weight
134+
: wav_weight * (math::Pi<Float> * sqr(m_bsphere.radius)));
120135
}
121136

122137
std::pair<RayDifferential3f, Spectrum> sample_ray_differential(
@@ -153,7 +168,10 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
153168
ray.has_differentials = false;
154169

155170
ray.update();
156-
return std::make_pair(ray, wav_weight);
171+
return std::make_pair(
172+
ray, m_has_target
173+
? wav_weight
174+
: wav_weight * (math::Pi<Float> * sqr(m_bsphere.radius)));
157175
}
158176

159177
/// This sensor does not occupy any particular region of space, return an

src/sensors/tests/test_distant.py

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,22 @@
66

77

88
def dict_sensor(direction=None, target=None, fwidth=1):
9-
if direction is None:
10-
dict_direction = {}
11-
else:
12-
dict_direction = {"direction": direction}
9+
result = {"type": "distant"}
1310

14-
if target is None:
15-
dict_target = {}
16-
else:
17-
dict_target = {"target": target}
11+
if direction:
12+
result["direction"] = direction
1813

19-
dict_film = {
14+
if target:
15+
result["target"] = target
16+
17+
result["film"] = {
2018
"type": "hdrfilm",
2119
"width": fwidth,
2220
"height": 1,
2321
"rfilter": {"type": "box"}
2422
}
2523

26-
return {
27-
"type": "distant",
28-
**dict_direction,
29-
**dict_target,
30-
"film": dict_film,
31-
}
24+
return result
3225

3326

3427
def make_sensor(direction=None, target=None, fwidth=1):
@@ -177,45 +170,56 @@ def test_intersection(variant_scalar_rgb, direction):
177170
ek.dot(direction, [0, 0, -1]), atol=0.1)
178171

179172

180-
@pytest.mark.parametrize("radiance", [10**x for x in range(-3, 4)])
181-
def test_render(variant_scalar_rgb, radiance):
173+
def test_render(variant_scalar_rgb):
182174
# Test render results with a simple scene
183175
from mitsuba.core.xml import load_dict
184-
from mitsuba.core import Bitmap, Struct
176+
from mitsuba.core import Bitmap, Struct, ScalarTransform4f
177+
178+
for w_e, w_o in zip(([0, 0, -1], [0, 1, -1]), ([0, 0, -1], [0, 1, -1])):
179+
l_e = 1.0 # Emitted radiance
180+
w_e = list(ek.normalize(w_e)) # Emitter direction
181+
w_o = list(ek.normalize(w_o)) # Sensor direction
182+
cos_theta_e = abs(ek.dot(w_e, [0, 0, 1]))
183+
cos_theta_o = abs(ek.dot(w_o, [0, 0, 1]))
184+
185+
scale = 0.5
186+
rho = 1.0 # Surface reflectance
187+
surface_area = 4. * scale ** 2
185188

186-
def dict_scene(radiance=1.0, spp=1):
187-
return {
188-
"type": "scene",
189-
"shape": {
189+
expected = l_e * cos_theta_e * surface_area * rho / np.pi * cos_theta_o
190+
191+
dict_scene = {
192+
"type": "scene",
193+
"shape": {
190194
"type": "rectangle",
191-
"bsdf": {"type": "conductor"},
192-
},
193-
"integrator": {"type": "path"},
194-
"sensor": {
195-
"type": "distant",
196-
"film": {
195+
"to_world": ScalarTransform4f.scale(scale),
196+
"bsdf": {"type": "diffuse", "reflectance": rho},
197+
},
198+
"emitter": {
199+
"type": "directional",
200+
"irradiance": l_e,
201+
"direction": w_e
202+
},
203+
"sensor": {
204+
"type": "distant",
205+
"direction": w_o,
206+
"film": {
197207
"type": "hdrfilm",
198-
"width": 1,
199208
"height": 1,
200-
"pixel_format": "rgb",
209+
"width": 1,
210+
"pixel_format": "luminance",
201211
"rfilter": {"type": "box"},
202-
},
203-
"sampler": {
204-
"type": "independent",
205-
"sample_count": spp
206-
},
207212
},
208-
"emitter": {
209-
"type": "constant",
210-
"radiance": {
211-
"type": "spectrum",
212-
"value": radiance
213-
}
214-
}
215-
}
216-
217-
scene = load_dict(dict_scene(spp=1, radiance=radiance))
213+
"sampler": {
214+
"type": "independent",
215+
"sample_count": 512
216+
},
217+
},
218+
"integrator": {"type": "path"}
219+
}
220+
221+
scene = load_dict(dict_scene)
218222
sensor = scene.sensors()[0]
219223
scene.integrator().render(scene, sensor)
220-
img = sensor.film().bitmap()
221-
assert np.allclose(np.array(img), radiance)
224+
img = np.array(sensor.film().bitmap()).squeeze()
225+
assert np.allclose(np.array(img), expected)

0 commit comments

Comments
 (0)