Skip to content

Commit 0fa760d

Browse files
committed
Distant plugin test fixes
1 parent e9ad277 commit 0fa760d

File tree

2 files changed

+78
-84
lines changed

2 files changed

+78
-84
lines changed

src/sensors/distant.cpp

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ section of the scene's bounding sphere.
4040

4141
MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
4242
public:
43-
MTS_IMPORT_BASE(Sensor, m_world_transform, m_needs_sample_3, m_film)
43+
MTS_IMPORT_BASE(Sensor, m_world_transform, m_film)
4444
MTS_IMPORT_TYPES(Scene)
4545

4646
DistantSensor(const Properties &props) : Base(props) {
@@ -76,8 +76,6 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
7676
0.5f + math::RayEpsilon<Float>)
7777
Log(Warn, "This sensor should be used with a reconstruction filter "
7878
"with a radius of 0.5 or lower (e.g. default box)");
79-
80-
m_needs_sample_3 = false;
8179
}
8280

8381
void set_scene(const Scene *scene) override {
@@ -88,8 +86,8 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
8886
}
8987

9088
std::pair<Ray3f, Spectrum> sample_ray(Float time, Float wavelength_sample,
91-
const Point2f &spatial_sample,
92-
const Point2f & /*direction_sample*/,
89+
const Point2f & /*film_sample*/,
90+
const Point2f &aperture_sample,
9391
Mask active) const override {
9492
MTS_MASKED_FUNCTION(ProfilerPhase::EndpointSampleRay, active);
9593
Ray3f ray;
@@ -109,7 +107,7 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
109107
// If no target point is defined, sample a target point on the
110108
// bounding sphere's cross section
111109
Point2f offset =
112-
warp::square_to_uniform_disk_concentric(spatial_sample);
110+
warp::square_to_uniform_disk_concentric(aperture_sample);
113111
Vector3f perp_offset =
114112
trafo.transform_affine(Vector3f{ offset.x(), offset.y(), 0.f });
115113
ray.o = m_bsphere.center + (perp_offset - ray.d) * m_bsphere.radius;
@@ -118,15 +116,12 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
118116
}
119117

120118
ray.update();
121-
return std::make_pair(
122-
ray, m_has_target
123-
? wav_weight
124-
: wav_weight * (math::Pi<Float> * sqr(m_bsphere.radius)));
119+
return std::make_pair(ray, wav_weight);
125120
}
126121

127122
std::pair<RayDifferential3f, Spectrum> sample_ray_differential(
128-
Float time, Float wavelength_sample, const Point2f &spatial_sample,
129-
const Point2f & /*direction_sample*/, Mask active) const override {
123+
Float time, Float wavelength_sample, const Point2f & /*film_sample*/,
124+
const Point2f &aperture_sample, Mask active) const override {
130125
MTS_MASKED_FUNCTION(ProfilerPhase::EndpointSampleRay, active);
131126
RayDifferential3f ray;
132127
ray.time = time;
@@ -145,7 +140,7 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
145140
// If no target point is defined, sample a target point on the
146141
// bounding sphere's cross section
147142
Point2f offset =
148-
warp::square_to_uniform_disk_concentric(spatial_sample);
143+
warp::square_to_uniform_disk_concentric(aperture_sample);
149144
Vector3f perp_offset =
150145
trafo.transform_affine(Vector3f{ offset.x(), offset.y(), 0.f });
151146
ray.o = m_bsphere.center + (perp_offset - ray.d) * m_bsphere.radius;
@@ -158,10 +153,7 @@ MTS_VARIANT class DistantSensor final : public Sensor<Float, Spectrum> {
158153
ray.has_differentials = false;
159154

160155
ray.update();
161-
return std::make_pair(
162-
ray, m_has_target
163-
? wav_weight
164-
: wav_weight * (math::Pi<Float> * sqr(m_bsphere.radius)));
156+
return std::make_pair(ray, wav_weight);
165157
}
166158

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

src/sensors/tests/test_distant.py

Lines changed: 69 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,49 +5,47 @@
55
import mitsuba
66

77

8-
def xml_sensor(direction=None, target=None, xpixel=1):
8+
def dict_sensor(direction=None, target=None, fwidth=1):
99
if direction is None:
10-
xml_direction = ""
10+
dict_direction = {}
1111
else:
12-
if type(direction) is not str:
13-
direction = ",".join([str(x) for x in direction])
14-
xml_direction = f"""<vector name="direction" value="{direction}"/>"""
12+
dict_direction = {"direction": direction}
1513

1614
if target is None:
17-
xml_target = ""
15+
dict_target = {}
1816
else:
19-
if type(target) is not str:
20-
target = ",".join([str(x) for x in target])
21-
xml_target = f"""<point name="target" value="{target}"/>"""
17+
dict_target = {"target": target}
2218

23-
xml_film = f"""<film type="hdrfilm">
24-
<integer name="width" value="{xpixel}"/>
25-
<integer name="height" value="1"/>
26-
<rfilter type="box"/>
27-
</film>"""
19+
dict_film = {
20+
"type": "hdrfilm",
21+
"width": fwidth,
22+
"height": 1,
23+
"rfilter": {"type": "box"}
24+
}
2825

29-
return f"""<sensor version="2.0.0" type="distant">
30-
{xml_direction}
31-
{xml_target}
32-
{xml_film}
33-
</sensor>"""
26+
return {
27+
"type": "distant",
28+
**dict_direction,
29+
**dict_target,
30+
"film": dict_film,
31+
}
3432

3533

36-
def make_sensor(direction=None, target=None, xpixel=1):
37-
from mitsuba.core.xml import load_string
38-
return load_string(xml_sensor(direction, target, xpixel))
34+
def make_sensor(direction=None, target=None, fwidth=1):
35+
from mitsuba.core.xml import load_dict
36+
return load_dict(dict_sensor(direction, target, fwidth))
3937

4038

4139
def test_construct(variant_scalar_rgb):
42-
from mitsuba.core.xml import load_string
40+
from mitsuba.core.xml import load_string, load_dict
4341

4442
# Construct without parameters (wrong film size)
4543
with pytest.raises(RuntimeError):
46-
sensor = load_string("""<sensor version="2.0.0" type="distant"/>""")
44+
sensor = load_dict({"type": "distant"})
4745

4846
# Construct with wrong film size
4947
with pytest.raises(RuntimeError):
50-
sensor = make_sensor(xpixel=2)
48+
sensor = make_sensor(fwidth=2)
5149

5250
# Construct with minimal parameters
5351
sensor = make_sensor()
@@ -110,16 +108,15 @@ def test_sample_ray(variant_scalar_rgb, direction, target, ray_kind):
110108

111109

112110
def make_scene(direction=[0, 0, -1], target=None):
113-
from mitsuba.core.xml import load_string
111+
from mitsuba.core.xml import load_dict
114112

115-
scene_xml = f"""
116-
<scene version="2.0.0">
117-
{xml_sensor(direction, target)}
118-
<shape type="rectangle"/>
119-
</scene>
120-
"""
113+
dict_scene = {
114+
"type": "scene",
115+
"sensor": dict_sensor(direction, target),
116+
"surface": {"type": "rectangle"}
117+
}
121118

122-
return load_string(scene_xml)
119+
return load_dict(dict_scene)
123120

124121

125122
@pytest.mark.parametrize("target", [[0, 0, 0], [0.5, 0, 1]])
@@ -143,12 +140,12 @@ def test_target(variant_scalar_rgb, target):
143140
@pytest.mark.parametrize("direction", [[0, 0, -1], [0.5, 0.5, -1]])
144141
def test_intersection(variant_scalar_rgb, direction):
145142
# Check if the sensor correctly casts rays spread uniformly in the scene
146-
direction = ek.normalize(direction)
143+
direction = list(ek.normalize(direction))
147144
scene = make_scene(direction=direction)
148145
sensor = scene.sensors()[0]
149146
sampler = sensor.sampler()
150147

151-
n_rays = 10000
148+
n_rays = 1000
152149
isect = np.empty((n_rays, 3))
153150

154151
for i in range(n_rays):
@@ -168,7 +165,7 @@ def test_intersection(variant_scalar_rgb, direction):
168165
# Average intersection locations should be (in average) centered
169166
# around (0, 0, 0)
170167
isect_valid = isect[~np.isnan(isect).all(axis=1)]
171-
assert np.allclose(isect_valid[:, :2].mean(axis=0), 0., atol=1e-2)
168+
assert np.allclose(isect_valid[:, :2].mean(axis=0), 0., atol=5e-2)
172169
assert np.allclose(isect_valid[:, 2], 0., atol=1e-5)
173170

174171
# Check number of invalid intersections
@@ -177,42 +174,47 @@ def test_intersection(variant_scalar_rgb, direction):
177174
# slanting factor (cos theta) w.r.t the sensor's direction
178175
n_invalid = np.count_nonzero(np.isnan(isect).all(axis=1))
179176
assert np.allclose(n_invalid / n_rays, 1. - 2. / np.pi *
180-
ek.dot(direction, [0, 0, -1]), atol=1e-2)
177+
ek.dot(direction, [0, 0, -1]), atol=0.1)
181178

182179

183180
@pytest.mark.parametrize("radiance", [10**x for x in range(-3, 4)])
184181
def test_render(variant_scalar_rgb, radiance):
185182
# Test render results with a simple scene
186-
from mitsuba.core.xml import load_string
187-
import numpy as np
188-
189-
scene_xml = """
190-
<scene version="2.0.0">
191-
<default name="radiance" value="1.0"/>
192-
<default name="spp" value="1"/>
193-
194-
<integrator type="path"/>
195-
196-
<sensor type="distant">
197-
<film type="hdrfilm">
198-
<integer name="width" value="1"/>
199-
<integer name="height" value="1"/>
200-
<string name="pixel_format" value="rgb"/>
201-
<rfilter type="box"/>
202-
</film>
203-
204-
<sampler type="independent">
205-
<integer name="sample_count" value="$spp"/>
206-
</sampler>
207-
</sensor>
208-
209-
<emitter type="constant">
210-
<spectrum name="radiance" value="$radiance"/>
211-
</emitter>
212-
</scene>
213-
"""
214-
215-
scene = load_string(scene_xml, spp=1, radiance=radiance)
183+
from mitsuba.core.xml import load_dict
184+
from mitsuba.core import Bitmap, Struct
185+
186+
def dict_scene(radiance=1.0, spp=1):
187+
return {
188+
"type": "scene",
189+
"shape": {
190+
"type": "rectangle",
191+
"bsdf": {"type": "conductor"},
192+
},
193+
"integrator": {"type": "path"},
194+
"sensor": {
195+
"type": "distant",
196+
"film": {
197+
"type": "hdrfilm",
198+
"width": 1,
199+
"height": 1,
200+
"pixel_format": "rgb",
201+
"rfilter": {"type": "box"},
202+
},
203+
"sampler": {
204+
"type": "independent",
205+
"sample_count": spp
206+
},
207+
},
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))
216218
sensor = scene.sensors()[0]
217219
scene.integrator().render(scene, sensor)
218220
img = sensor.film().bitmap()

0 commit comments

Comments
 (0)