Skip to content

Commit e193855

Browse files
committed
Get everything working with the latest Mitsuba master branch. Fully correct implementation of integrators, volpath, volpathmis and prbvolpath. Comparison with PBRT-V4 in a homogeneous medium bounded by the sphere shape with emission, absorption and scattering gives identical results.
TODO: * Test mesh sampling and directional PDFs are correct * Validate `heterogeneous` media results * Update reference images in `resources` submodule Caveats: * PRBVolpath gives incorrect gradients (correct sign, incorrect magnitude) for the medium density `sigma_t` and the phase function `phase`, but this exists in the original implementation as well.
1 parent 042f6af commit e193855

39 files changed

+865
-765
lines changed

include/mitsuba/core/warp.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -282,19 +282,19 @@ MI_INLINE Vector<Value, 3> cube_to_uniform_sphere(const Point<Value, 3> &sample)
282282
Value radius = dr::safe_cbrt(sample.x()),
283283
phi = 2.f * dr::Pi<Value> * sample.z(),
284284
u = dr::fmsub(2.f, sample.y(), 1.f);
285-
radius = dr::select(dr::neq(sample.x(), 0.f), radius, sample.x());
285+
radius = dr::select(sample.x() == 0.f, sample.x(), radius);
286286
auto [s, c] = dr::sincos(phi);
287-
return { radius * c * dr::safe_sqrt(1 - dr::sqr(u)), radius * s * dr::safe_sqrt(1 - dr::sqr(u)), radius * u };
287+
return { radius * c * dr::safe_sqrt(1 - dr::square(u)), radius * s * dr::safe_sqrt(1 - dr::square(u)), radius * u };
288288
}
289289

290290
/// Inverse of the mapping \ref square_to_uniform_sphere
291291
template <typename Value>
292292
MI_INLINE Point<Value, 3> uniform_sphere_to_cube(const Vector<Value, 3> &p) {
293293
Value phi = dr::atan2(p.y(), p.x()) * dr::InvTwoPi<Value>,
294-
radius = dr::safe_sqrt(dr::sqr(p.x()) + dr::sqr(p.y()) + dr::sqr(p.z())),
294+
radius = dr::safe_sqrt(dr::square(p.x()) + dr::square(p.y()) + dr::square(p.z())),
295295
u = (p.z() / radius + 1.f) / 2.f;
296296
return {
297-
dr::sqr(radius) * radius,
297+
dr::square(radius) * radius,
298298
u,
299299
dr::select(phi < 0.f, phi + 1.f, phi),
300300
};

include/mitsuba/python/docstr.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,6 +2549,8 @@ static const char *__doc_mitsuba_Endpoint_m_needs_sample_2 = R"doc()doc";
25492549

25502550
static const char *__doc_mitsuba_Endpoint_m_needs_sample_3 = R"doc()doc";
25512551

2552+
static const char *__doc_mitsuba_Endpoint_m_needs_sample_2_3d = R"doc()doc";
2553+
25522554
static const char *__doc_mitsuba_Endpoint_m_shape = R"doc()doc";
25532555

25542556
static const char *__doc_mitsuba_Endpoint_m_to_world = R"doc()doc";
@@ -4259,6 +4261,8 @@ static const char *__doc_mitsuba_MediumInteraction_sigma_s = R"doc()doc";
42594261

42604262
static const char *__doc_mitsuba_MediumInteraction_sigma_t = R"doc()doc";
42614263

4264+
static const char *__doc_mitsuba_MediumInteraction_radiance = R"doc()doc";
4265+
42624266
static const char *__doc_mitsuba_MediumInteraction_to_local =
42634267
R"doc(Convert a world-space vector into local shading coordinates (defined
42644268
by ``wi``))doc";
@@ -4291,6 +4295,18 @@ static const char *__doc_mitsuba_Medium_get_interaction_probabilities =
42914295
R"doc(Returns the real scatter event probability, and the
42924296
weights of a real and null scattering event at a given MediumInteraction mi)doc";
42934297

4298+
static const char *__doc_mitsuba_Medium_medium_probabilities_analog =
4299+
R"doc(Computes the probabilities of interacting with a medium based on the point-wise
4300+
density of each event)doc";
4301+
4302+
static const char *__doc_mitsuba_Medium_medium_probabilities_max =
4303+
R"doc(Computes the probabilities of interacting with a medium based on the maximum
4304+
density of each event multiplied by the throughput)doc";
4305+
4306+
static const char *__doc_mitsuba_Medium_medium_probabilities_mean =
4307+
R"doc(Computes the probabilities of interacting with a medium based on the mean
4308+
density of each event multiplied by the throughput)doc";
4309+
42944310
static const char *__doc_mitsuba_Medium_get_radiance = R"doc(Returns the medium's radiance used for emissive media)doc";
42954311

42964312
static const char *__doc_mitsuba_Medium_has_spectral_extinction = R"doc(Returns whether this medium has a spectrally varying extinction)doc";
@@ -4313,6 +4329,16 @@ static const char *__doc_mitsuba_Medium_m_phase_function = R"doc()doc";
43134329

43144330
static const char *__doc_mitsuba_Medium_m_sample_emitters = R"doc()doc";
43154331

4332+
static const char *__doc_mitsuba_Medium_m_medium_sampling_mode =
4333+
R"doc(Determine how free-flight distances are sampled in the medium
4334+
4335+
Takes on one of three values --- Analogue, Maximum and Mean --- which
4336+
consider the probability of interacting with the medium based on the
4337+
point-wise densities of each event, the maximum density of each event
4338+
convolved with the throughput of the path, and the mean density of
4339+
each event convolved with the throughput of the path.
4340+
)doc";
4341+
43164342
static const char *__doc_mitsuba_Medium_phase_function = R"doc(Return the phase function of this medium)doc";
43174343

43184344
static const char *__doc_mitsuba_Medium_emitter = R"doc(Return the emitter associated with this medium)doc";

include/mitsuba/render/interaction.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ struct MediumInteraction : Interaction<Float_, Spectrum_> {
564564
/// Incident direction in world frame
565565
Vector3f wi;
566566

567-
UnpolarizedSpectrum sigma_s, sigma_n, sigma_t, combined_extinction;
567+
UnpolarizedSpectrum sigma_s, sigma_n, sigma_t, radiance, combined_extinction;
568568

569569
/// mint used when sampling the given distance ``t``
570570
Float mint;
@@ -587,6 +587,7 @@ struct MediumInteraction : Interaction<Float_, Spectrum_> {
587587
sigma_s = dr::zeros<UnpolarizedSpectrum>(size);
588588
sigma_n = dr::zeros<UnpolarizedSpectrum>(size);
589589
sigma_t = dr::zeros<UnpolarizedSpectrum>(size);
590+
radiance = dr::zeros<UnpolarizedSpectrum>(size);
590591
combined_extinction = dr::zeros<UnpolarizedSpectrum>(size);
591592
mint = dr::zeros<Float>(size);
592593

@@ -615,7 +616,7 @@ struct MediumInteraction : Interaction<Float_, Spectrum_> {
615616
// =============================================================
616617

617618
DRJIT_STRUCT(MediumInteraction, t, time, wavelengths, p, n, medium,
618-
sh_frame, wi, sigma_s, sigma_n, sigma_t,
619+
sh_frame, wi, sigma_s, sigma_n, sigma_t, radiance,
619620
combined_extinction, mint)
620621
};
621622

include/mitsuba/render/medium.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,16 @@ class MI_EXPORT_LIB Medium : public Object {
4141

4242
/// Returns the radiance, the probability of a scatter event, and
4343
/// the weights associated with real and null scattering events
44-
std::pair<std::pair<UnpolarizedSpectrum, UnpolarizedSpectrum>,
44+
virtual std::pair<std::pair<UnpolarizedSpectrum, UnpolarizedSpectrum>,
4545
std::pair<UnpolarizedSpectrum, UnpolarizedSpectrum>>
4646
get_interaction_probabilities(const Spectrum &radiance,
4747
const MediumInteraction3f &mi,
4848
const Spectrum &throughput) const;
4949

5050
MI_INLINE std::pair<UnpolarizedSpectrum, UnpolarizedSpectrum>
5151
medium_probabilities_analog(const UnpolarizedSpectrum &radiance,
52-
const MediumInteraction3f &mi) const {
52+
const MediumInteraction3f &mi,
53+
const UnpolarizedSpectrum &/*throughput*/) const {
5354
auto prob_s = mi.sigma_t;
5455
auto prob_n = mi.sigma_n + dr::maximum(radiance, dr::mean(dr::abs(radiance)));
5556
return std::make_pair( prob_s, prob_n );
@@ -192,6 +193,7 @@ MI_CALL_TEMPLATE_BEGIN(Medium)
192193
DRJIT_CALL_GETTER(emitter)
193194
DRJIT_CALL_GETTER(use_emitter_sampling)
194195
DRJIT_CALL_GETTER(is_homogeneous)
196+
DRJIT_CALL_GETTER(is_emitter)
195197
DRJIT_CALL_GETTER(has_spectral_extinction)
196198
DRJIT_CALL_METHOD(get_majorant)
197199
DRJIT_CALL_METHOD(get_radiance)
@@ -203,7 +205,6 @@ MI_CALL_TEMPLATE_BEGIN(Medium)
203205
DRJIT_CALL_METHOD(medium_probabilities_analog)
204206
DRJIT_CALL_METHOD(medium_probabilities_max)
205207
DRJIT_CALL_METHOD(medium_probabilities_mean)
206-
auto is_emitter() const { return emitter() != nullptr; }
207208
MI_CALL_TEMPLATE_END(Medium)
208209

209210
//! @}

include/mitsuba/render/scene.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -647,9 +647,11 @@ typename SurfaceInteraction<Float, Spectrum>::EmitterPtr
647647
SurfaceInteraction<Float, Spectrum>::emitter(const Scene *scene, Mask active) const {
648648
if constexpr (!dr::is_jit_v<Float>) {
649649
DRJIT_MARK_USED(active);
650-
return is_valid() ? shape->emitter() : scene->environment();
650+
EmitterPtr emitter = is_valid() ? shape->emitter() : scene->environment();
651+
return (emitter != nullptr) && has_flag(emitter->flags(), EmitterFlags::Medium) ? nullptr : emitter;
651652
} else {
652653
EmitterPtr emitter = shape->emitter(active);
654+
emitter = dr::select(active & !has_flag(emitter->flags(), EmitterFlags::Medium), emitter, nullptr);
653655
if (scene && scene->environment())
654656
emitter = dr::select(is_valid(), emitter, scene->environment() & active);
655657
return emitter;
@@ -662,9 +664,9 @@ typename MediumInteraction<Float, Spectrum>::EmitterPtr
662664
MediumInteraction<Float, Spectrum>::emitter(Mask active) const {
663665
if constexpr (!dr::is_jit_v<Float>) {
664666
DRJIT_MARK_USED(active);
665-
return dr::neq(medium, nullptr) ? medium->emitter() : nullptr;
667+
return medium != nullptr ? medium->emitter() : nullptr;
666668
} else {
667-
EmitterPtr emitter = medium->emitter(active && dr::neq(medium, nullptr));
669+
EmitterPtr emitter = medium->emitter(active && medium != nullptr);
668670
return emitter;
669671
}
670672
}

include/mitsuba/render/shape.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ struct SilhouetteSample : public PositionSample<Float_, Spectrum_> {
221221
template <typename Float, typename Spectrum>
222222
class MI_EXPORT_LIB Shape : public Object {
223223
public:
224-
MI_IMPORT_TYPES(BSDF, Medium, Volume, Emitter, Sensor, MeshAttribute, Texture)
224+
MI_IMPORT_TYPES(BSDF, Medium, Volume, Emitter, Sensor, MeshAttribute, Texture, EmitterPtr)
225225

226226
// Use 32 bit indices to keep track of indices to conserve memory
227227
using ScalarIndex = uint32_t;

src/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import drjit as dr
1010
import mitsuba as mi
1111

12+
1213
re1 = re.compile(r'<built-in method (\w*) of PyCapsule object at 0x[0-9a-f]*>')
1314
re2 = re.compile(r'<bound method PyCapsule.(\w*)[^>]*>')
1415

src/core/tests/test_logger.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,12 @@ def append(self, level, text):
2929
logger.set_formatter(MyFormatter())
3030
logger.add_appender(MyAppender())
3131

32-
mi.Log(mi.LogLevel.Warn, "This is a test message")
33-
assert len(messages) == 1
34-
assert messages[0].startswith(
35-
'300: class=None, thread=main, text=test01_custom(): This is a'
36-
' test message, filename=')
32+
with mi.scoped_log_level(mi.LogLevel.Warn):
33+
mi.Log(mi.LogLevel.Warn, "This is a test message")
34+
assert len(messages) == 1
35+
assert messages[0].startswith(
36+
'300: class=None, thread=main, text=test01_custom(): This is a'
37+
' test message, filename=')
3738
finally:
3839
logger.clear_appenders()
3940
for app in appenders:

src/core/tests/test_warp.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ def test_cube_to_uniform_sphere_vec(variant_scalar_rgb):
124124
check_inverse(mi.warp.cube_to_uniform_sphere, mi.warp.uniform_sphere_to_cube, is_3d=True)
125125
check_warp_vectorization("cube_to_uniform_sphere", is_3d=True)
126126

127+
dr.set_flag(dr.JitFlag.Debug, False)
128+
127129

128130
def test_square_to_uniform_hemisphere(variant_scalar_rgb):
129131
assert(dr.allclose(mi.warp.square_to_uniform_hemisphere([0.5, 0.5]), [0, 0, 1]))

src/emitters/tests/test_volumelight.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import gc
2+
13
import pytest
24
import drjit as dr
35
import mitsuba as mi
@@ -23,6 +25,12 @@ def create_emitter_and_spectrum(s_key='d65'):
2325
emitter = mi.load_dict({
2426
"type": "obj",
2527
"filename": "resources/data/tests/obj/cbox_smallbox.obj",
28+
"interior": {
29+
"type": "homogeneous", "sigma_t": {
30+
"type": "rgb",
31+
"value": [1.0, 1.0, 1.0]
32+
}
33+
},
2634
"emitter" : { "type": "volumelight", "radiance" : spectrum_dicts[s_key] }
2735
})
2836
spectrum = mi.load_dict(spectrum_dicts[s_key])
@@ -43,7 +51,7 @@ def create_emitter_rgb():
4351
"emitter" : { "type": "volumelight", "radiance" : {
4452
"type": "rgb",
4553
"value": [1.0, 2.0, 1.0]
46-
} }
54+
}}
4755
})
4856

4957
return r, c, emitter
@@ -58,14 +66,13 @@ def test01_constructor(variant_scalar_rgb):
5866
with pytest.raises(RuntimeError):
5967
e = mi.load_dict({
6068
"type" : "volumelight",
61-
"to_world" : mi.ScalarTransform4f.translate([5, 0, 0])
69+
"to_world" : mi.ScalarTransform4f().translate([5, 0, 0])
6270
})
6371

6472

6573
@pytest.mark.parametrize("spectrum_key", spectrum_dicts.keys())
6674
def test02_eval(variants_vec_spectral, spectrum_key):
6775
# Check that eval() return the same values as the 'radiance' spectrum
68-
6976
shape, spectrum = create_emitter_and_spectrum(spectrum_key)
7077
emitter = shape.emitter()
7178

@@ -81,7 +88,6 @@ def test02_eval(variants_vec_spectral, spectrum_key):
8188
def test03_sample_ray(variants_vec_spectral, spectrum_key):
8289
# Check the correctness of the sample_ray() method
8390

84-
8591
shape, spectrum = create_emitter_and_spectrum(spectrum_key)
8692
emitter = shape.emitter()
8793

@@ -188,7 +194,7 @@ def test06_sample_direction_rgb(variants_vec_rgb):
188194

189195
# Direction sampling is conditioned on a sampled position
190196
it = dr.zeros(mi.SurfaceInteraction3f, 3)
191-
it.p = [[0.0, 0.48, 0.19, 10, -2], [0.0, 0.9018, 2.19, 2, 1.02], [0.0, 0.5, 3.291, 3.1, 9.1]] # Some positions
197+
it.p = mi.Point3f([[0.0, 0.48, 0.19, 10, -2], [0.0, 0.9018, 2.19, 2, 1.02], [0.0, 0.5, 3.291, 3.1, 9.1]]) # Some positions
192198
it.time = 1.0
193199

194200
# Sample direction on the emitter
@@ -211,12 +217,12 @@ def test06_sample_direction_rgb(variants_vec_rgb):
211217
A = 1
212218
dist_from_c = dr.norm(it.p - center)
213219
B = 2*dr.dot(shape_ds.d, it.p - center + dist_from_c*shape_ds.d)
214-
C = dr.squared_norm(it.p - center + dist_from_c*shape_ds.d) - dr.sqr(radius)
220+
C = dr.squared_norm(it.p - center + dist_from_c*shape_ds.d) - dr.square(radius)
215221

216-
r0 = 0.5 * (-B - dr.sqrt(dr.sqr(B) - 4*A*C))+dist_from_c
217-
r1 = 0.5 * (-B + dr.sqrt(dr.sqr(B) - 4*A*C))+dist_from_c
218-
r0 = dr.clamp(r0, 0.0, dr.inf)
219-
r1 = dr.clamp(r1, 0.0, dr.inf)
222+
r0 = 0.5 * (-B - dr.sqrt(dr.square(B) - 4*A*C))+dist_from_c
223+
r1 = 0.5 * (-B + dr.sqrt(dr.square(B) - 4*A*C))+dist_from_c
224+
r0 = dr.clip(r0, 0.0, dr.inf)
225+
r1 = dr.clip(r1, 0.0, dr.inf)
220226

221227
analytic_pdf = dr.select(
222228
dr.isfinite(r0) & dr.isfinite(r1),
@@ -231,3 +237,17 @@ def test06_sample_direction_rgb(variants_vec_rgb):
231237
assert dr.allclose(res, spec)
232238

233239
assert dr.allclose(emitter.eval_direction(it, ds) / ds.pdf, spec)
240+
241+
242+
def test07_shape_accessors(variants_vec_rgb):
243+
shape, _ = create_emitter_and_spectrum()
244+
shape_ptr = mi.ShapePtr(shape)
245+
246+
assert type(shape.emitter()) == mi.Emitter
247+
assert type(shape_ptr.emitter()) == mi.EmitterPtr
248+
249+
emitter = shape.emitter()
250+
emitter_ptr = mi.EmitterPtr(emitter)
251+
252+
assert type(emitter.get_shape()) == mi.Mesh
253+
assert type(emitter_ptr.get_shape()) == mi.ShapePtr

0 commit comments

Comments
 (0)