Skip to content

Commit a9ebdb5

Browse files
committed
Finalised directional sampling of volume emitters, and appropriately implemented tests.
1 parent 705c22e commit a9ebdb5

File tree

26 files changed

+524
-191
lines changed

26 files changed

+524
-191
lines changed

include/mitsuba/python/docstr.h

Lines changed: 57 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4508,7 +4508,9 @@ static const char *__doc_mitsuba_Mesh_parameters_changed = R"doc()doc";
45084508

45094509
static const char *__doc_mitsuba_Mesh_parameters_grad_enabled = R"doc()doc";
45104510

4511-
static const char *__doc_mitsuba_Mesh_pdf_position = R"doc()doc";
4511+
static const char *__doc_mitsuba_Mesh_pdf_position_surface = R"doc()doc";
4512+
4513+
static const char *__doc_mitsuba_Mesh_pdf_position_volume = R"doc()doc";
45124514

45134515
static const char *__doc_mitsuba_Mesh_primitive_count = R"doc()doc";
45144516

@@ -4545,7 +4547,9 @@ static const char *__doc_mitsuba_Mesh_recompute_bbox = R"doc(Recompute the bound
45454547

45464548
static const char *__doc_mitsuba_Mesh_recompute_vertex_normals = R"doc(Compute smooth vertex normals and replace the current normal values)doc";
45474549

4548-
static const char *__doc_mitsuba_Mesh_sample_position = R"doc()doc";
4550+
static const char *__doc_mitsuba_Mesh_sample_position_surface = R"doc()doc";
4551+
4552+
static const char *__doc_mitsuba_Mesh_sample_position_volume = R"doc()doc";
45494553

45504554
static const char *__doc_mitsuba_Mesh_set_scene = R"doc()doc";
45514555

@@ -7513,8 +7517,20 @@ static const char *__doc_mitsuba_Shape_parameters_grad_enabled =
75137517
R"doc(Return whether any shape's parameters require gradients (default
75147518
return false))doc";
75157519

7516-
static const char *__doc_mitsuba_Shape_pdf_direction =
7517-
R"doc(Query the probability density of sample_direction()
7520+
static const char *__doc_mitsuba_Shape_pdf_direction_surface =
7521+
R"doc(Query the probability density of sample_direction_surface()
7522+
7523+
Parameter ``it``:
7524+
A reference position somewhere within the scene.
7525+
7526+
Parameter ``ps``:
7527+
A position record describing the sample in question
7528+
7529+
Returns:
7530+
The probability density per unit solid angle)doc";
7531+
7532+
static const char *__doc_mitsuba_Shape_pdf_direction_volume =
7533+
R"doc(Query the probability density of sample_direction_volume()
75187534
75197535
Parameter ``it``:
75207536
A reference position somewhere within the scene.
@@ -7525,8 +7541,8 @@ Parameter ``ps``:
75257541
Returns:
75267542
The probability density per unit solid angle)doc";
75277543

7528-
static const char *__doc_mitsuba_Shape_pdf_position =
7529-
R"doc(Query the probability density of sample_position() for a particular
7544+
static const char *__doc_mitsuba_Shape_pdf_position_surface =
7545+
R"doc(Query the probability density of sample_position_surface() for a particular
75307546
point on the surface.
75317547
75327548
Parameter ``ps``:
@@ -7535,8 +7551,8 @@ Parameter ``ps``:
75357551
Returns:
75367552
The probability density per unit area)doc";
75377553

7538-
static const char *__doc_mitsuba_Shape_pdf_position_3d =
7539-
R"doc(Query the probability density of sample_position() for a particular
7554+
static const char *__doc_mitsuba_Shape_pdf_position_volume =
7555+
R"doc(Query the probability density of sample_position_surface() for a particular
75407556
point in the volume.
75417557
75427558
Parameter ``ps``:
@@ -7619,8 +7635,8 @@ static const char *__doc_mitsuba_Shape_ray_test_packet_3 = R"doc()doc";
76197635

76207636
static const char *__doc_mitsuba_Shape_ray_test_scalar = R"doc()doc";
76217637

7622-
static const char *__doc_mitsuba_Shape_sample_direction =
7623-
R"doc(Sample a direction towards this shape with respect to solid angles
7638+
static const char *__doc_mitsuba_Shape_sample_direction_surface =
7639+
R"doc(Sample a direction towards this shape with respect to solid angles
76247640
measured at a reference position within the scene
76257641
76267642
An ideal implementation of this interface would achieve a uniform
@@ -7633,7 +7649,7 @@ per unit solid angle associated with the sample.
76337649
76347650
When the Shape subclass does not supply a custom implementation of
76357651
this function, the Shape class reverts to a fallback approach that
7636-
piggybacks on sample_position(). This will generally lead to a
7652+
piggybacks on sample_position_surface(). This will generally lead to a
76377653
suboptimal sample placement and higher variance in Monte Carlo
76387654
estimators using the samples.
76397655
@@ -7646,7 +7662,34 @@ Parameter ``sample``:
76467662
Returns:
76477663
A DirectionSample instance describing the generated sample)doc";
76487664

7649-
static const char *__doc_mitsuba_Shape_sample_position =
7665+
static const char *__doc_mitsuba_Shape_sample_direction_volume =
7666+
R"doc(Sample a direction towards this shape with respect to solid angles
7667+
measured at a reference position within the scene
7668+
7669+
An ideal implementation of this interface would achieve a uniform
7670+
solid angle density within the volume region that is visible from the
7671+
reference position ``it.p`` (though such an ideal implementation is
7672+
usually neither feasible nor advisable due to poor efficiency).
7673+
7674+
The function returns the sampled position and the inverse probability
7675+
per unit solid angle associated with the sample.
7676+
7677+
When the Shape subclass does not supply a custom implementation of
7678+
this function, the Shape class reverts to a fallback approach that
7679+
piggybacks on sample_position_volume(). This will generally lead to a
7680+
suboptimal sample placement and higher variance in Monte Carlo
7681+
estimators using the samples.
7682+
7683+
Parameter ``it``:
7684+
A reference position somewhere within the scene.
7685+
7686+
Parameter ``sample``:
7687+
A uniformly distributed 3D point on the domain ``[0,1]^3``
7688+
7689+
Returns:
7690+
A DirectionSample instance describing the generated sample)doc";
7691+
7692+
static const char *__doc_mitsuba_Shape_sample_position_surface =
76507693
R"doc(Sample a point on the surface of this shape
76517694
76527695
The sampling strategy is ideally uniform over the surface, though
@@ -7663,7 +7706,7 @@ Parameter ``sample``:
76637706
Returns:
76647707
A PositionSample instance describing the generated sample)doc";
76657708

7666-
static const char *__doc_mitsuba_Shape_sample_position_3d =
7709+
static const char *__doc_mitsuba_Shape_sample_position_volume =
76677710
R"doc(Sample a point in the volume of this shape
76687711
76697712
The sampling strategy is ideally uniform over the volume, though
@@ -8990,7 +9033,7 @@ default implementation throws an exception.
89909033
Even if the operation is provided, it may only return an
89919034
approximation.)doc";
89929035

8993-
static const char *__doc_mitsuba_Texture_pdf_position = R"doc(Returns the probability per unit area of sample_position())doc";
9036+
static const char *__doc_mitsuba_Texture_pdf_position = R"doc(Returns the probability per unit area of sample_position_surface())doc";
89949037

89959038
static const char *__doc_mitsuba_Texture_pdf_spectrum =
89969039
R"doc(Evaluate the density function of the sample_spectrum() method as a

include/mitsuba/render/mesh.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,23 @@ class MI_EXPORT_LIB Mesh : public Shape<Float, Spectrum> {
170170

171171
virtual Float volume() const override;
172172

173-
virtual PositionSample3f sample_position(Float time, const Point2f &sample,
173+
virtual PositionSample3f
174+
sample_position_surface(Float time, const Point2f &sample,
174175
Mask active = true) const override;
175176

176-
virtual Float pdf_position(const PositionSample3f &ps, Mask active = true) const override;
177+
virtual Float pdf_position_surface(const PositionSample3f &ps, Mask active = true) const override;
177178

178-
virtual PositionSample3f sample_position_3d(Float time, const Point3f &sample,
179+
virtual DirectionSample3f sample_direction_volume(const Interaction3f &it, const Point3f &sample,
180+
Mask active = true) const override;
181+
182+
virtual Float pdf_direction_volume(const Interaction3f &it, const DirectionSample3f &ds,
183+
Mask active = true) const override;
184+
185+
virtual PositionSample3f
186+
sample_position_volume(Float time, const Point3f &sample,
179187
Mask active = true) const override;
180188

181-
virtual Float pdf_position_3d(const PositionSample3f &ps, Mask active = true) const override;
189+
virtual Float pdf_position_volume(const PositionSample3f &ps, Mask active = true) const override;
182190

183191
virtual Point3f
184192
barycentric_coordinates(const SurfaceInteraction3f &si,
@@ -339,7 +347,7 @@ class MI_EXPORT_LIB Mesh : public Shape<Float, Spectrum> {
339347
* ray/object intersections.
340348
*
341349
* Internally, the function creates a nested scene to leverage optimized
342-
* ray tracing functionality in \ref pdf_position_3d()
350+
* ray tracing functionality in \ref pdf_position_volume()
343351
*/
344352
void build_volume_parameterization();
345353

include/mitsuba/render/shape.h

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ class MI_EXPORT_LIB Shape : public Object {
5353
* \return
5454
* A \ref PositionSample instance describing the generated sample
5555
*/
56-
virtual PositionSample3f sample_position(Float time, const Point2f &sample,
56+
virtual PositionSample3f sample_position_surface(Float time, const Point2f &sample,
5757
Mask active = true) const;
5858

5959
/**
60-
* \brief Query the probability density of \ref sample_position() for
60+
* \brief Query the probability density of \ref sample_position_surface() for
6161
* a particular point on the surface.
6262
*
6363
* \param ps
@@ -66,7 +66,7 @@ class MI_EXPORT_LIB Shape : public Object {
6666
* \return
6767
* The probability density per unit area
6868
*/
69-
virtual Float pdf_position(const PositionSample3f &ps, Mask active = true) const;
69+
virtual Float pdf_position_surface(const PositionSample3f &ps, Mask active = true) const;
7070

7171
/**
7272
* \brief Sample a point in the volume of this shape
@@ -85,11 +85,11 @@ class MI_EXPORT_LIB Shape : public Object {
8585
* \return
8686
* A \ref PositionSample instance describing the generated sample
8787
*/
88-
virtual PositionSample3f sample_position_3d(Float time, const Point3f &sample,
88+
virtual PositionSample3f sample_position_volume(Float time, const Point3f &sample,
8989
Mask active = true) const;
9090

9191
/**
92-
* \brief Query the probability density of \ref sample_position() for
92+
* \brief Query the probability density of \ref sample_position_volume() for
9393
* a particular point in the volume.
9494
*
9595
* \param ps
@@ -98,7 +98,7 @@ class MI_EXPORT_LIB Shape : public Object {
9898
* \return
9999
* The probability density per unit volume
100100
*/
101-
virtual Float pdf_position_3d(const PositionSample3f &ps, Mask active = true) const;
101+
virtual Float pdf_position_volume(const PositionSample3f &ps, Mask active = true) const;
102102

103103
/**
104104
* \brief Sample a direction towards this shape with respect to solid
@@ -114,7 +114,7 @@ class MI_EXPORT_LIB Shape : public Object {
114114
*
115115
* When the Shape subclass does not supply a custom implementation of this
116116
* function, the \ref Shape class reverts to a fallback approach that
117-
* piggybacks on \ref sample_position(). This will generally lead to a
117+
* piggybacks on \ref sample_position_surface(). This will generally lead to a
118118
* suboptimal sample placement and higher variance in Monte Carlo
119119
* estimators using the samples.
120120
*
@@ -127,11 +127,59 @@ class MI_EXPORT_LIB Shape : public Object {
127127
* \return
128128
* A \ref DirectionSample instance describing the generated sample
129129
*/
130-
virtual DirectionSample3f sample_direction(const Interaction3f &it, const Point2f &sample,
131-
Mask active = true) const;
130+
virtual DirectionSample3f
131+
sample_direction_surface(const Interaction3f &it, const Point2f &sample,
132+
Mask active = true) const;
133+
134+
/**
135+
* \brief Query the probability density of \ref sample_direction_surface()
136+
*
137+
* \param it
138+
* A reference position somewhere within the scene.
139+
*
140+
* \param ps
141+
* A position record describing the sample in question
142+
*
143+
* \return
144+
* The probability density per unit solid angle
145+
*/
146+
virtual Float pdf_direction_surface(const Interaction3f &it, const DirectionSample3f &ds,
147+
Mask active = true) const;
148+
149+
/**
150+
* \brief Sample a direction towards a point contained within this shape
151+
* with respect to solid angles measured at a reference position
152+
* within the scene
153+
*
154+
* An ideal implementation of this interface would achieve a uniform solid
155+
* angle density within the volume that is visible from the
156+
* reference position <tt>it.p</tt> (though such an ideal implementation
157+
* is usually neither feasible nor advisable due to poor efficiency).
158+
*
159+
* The function returns the sampled position and the inverse probability
160+
* per unit solid angle associated with the sample.
161+
*
162+
* When the Shape subclass does not supply a custom implementation of this
163+
* function, the \ref Shape class reverts to a fallback approach that
164+
* piggybacks on \ref sample_position_volume(). This will generally lead to a
165+
* suboptimal sample placement and higher variance in Monte Carlo
166+
* estimators using the samples.
167+
*
168+
* \param it
169+
* A reference position somewhere within the scene.
170+
*
171+
* \param sample
172+
* A uniformly distributed 3D point on the domain <tt>[0,1]^3</tt>
173+
*
174+
* \return
175+
* A \ref DirectionSample instance describing the generated sample
176+
*/
177+
virtual DirectionSample3f
178+
sample_direction_volume(const Interaction3f &it, const Point3f &sample,
179+
Mask active = true) const;
132180

133181
/**
134-
* \brief Query the probability density of \ref sample_direction()
182+
* \brief Query the probability density of \ref sample_direction_volume()
135183
*
136184
* \param it
137185
* A reference position somewhere within the scene.
@@ -142,8 +190,8 @@ class MI_EXPORT_LIB Shape : public Object {
142190
* \return
143191
* The probability density per unit solid angle
144192
*/
145-
virtual Float pdf_direction(const Interaction3f &it, const DirectionSample3f &ds,
146-
Mask active = true) const;
193+
virtual Float pdf_direction_volume(const Interaction3f &it, const DirectionSample3f &ds,
194+
Mask active = true) const;
147195

148196
//! @}
149197
// =============================================================
@@ -699,12 +747,14 @@ DRJIT_VCALL_TEMPLATE_BEGIN(mitsuba::Shape)
699747
DRJIT_VCALL_METHOD(ray_intersect_preliminary)
700748
DRJIT_VCALL_METHOD(ray_intersect)
701749
DRJIT_VCALL_METHOD(ray_test)
702-
DRJIT_VCALL_METHOD(sample_position)
703-
DRJIT_VCALL_METHOD(pdf_position)
704-
DRJIT_VCALL_METHOD(sample_position_3d)
705-
DRJIT_VCALL_METHOD(pdf_position_3d)
706-
DRJIT_VCALL_METHOD(sample_direction)
707-
DRJIT_VCALL_METHOD(pdf_direction)
750+
DRJIT_VCALL_METHOD(sample_position_surface)
751+
DRJIT_VCALL_METHOD(pdf_position_surface)
752+
DRJIT_VCALL_METHOD(sample_position_volume)
753+
DRJIT_VCALL_METHOD(pdf_position_volume)
754+
DRJIT_VCALL_METHOD(sample_direction_surface)
755+
DRJIT_VCALL_METHOD(pdf_direction_surface)
756+
DRJIT_VCALL_METHOD(sample_direction_volume)
757+
DRJIT_VCALL_METHOD(pdf_direction_volume)
708758
DRJIT_VCALL_METHOD(surface_area)
709759
DRJIT_VCALL_METHOD(volume)
710760
DRJIT_VCALL_GETTER(emitter, const typename Class::Emitter *)

src/emitters/area.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ class AreaLight final : public Emitter<Float, Spectrum> {
125125
// One of two very different strategies is used depending on 'm_radiance'
126126
if (likely(!m_radiance->is_spatially_varying())) {
127127
// Texture is uniform, try to importance sample the shape wrt. solid angle at 'it'
128-
ds = m_shape->sample_direction(it, Point2f(sample.x(), sample.y()), active);
128+
ds = m_shape->sample_direction_surface(
129+
it, Point2f(sample.x(), sample.y()), active);
129130
active &= dr::dot(ds.d, ds.n) < 0.f && dr::neq(ds.pdf, 0.f);
130131

131132
si = SurfaceInteraction3f(ds, it.wavelengths);
@@ -168,7 +169,7 @@ class AreaLight final : public Emitter<Float, Spectrum> {
168169

169170
Float value;
170171
if (!m_radiance->is_spatially_varying()) {
171-
value = m_shape->pdf_direction(it, ds, active);
172+
value = m_shape->pdf_direction_surface(it, ds, active);
172173
} else {
173174
// This surface intersection would be nice to avoid..
174175
SurfaceInteraction3f si = m_shape->eval_parameterization(ds.uv, +RayFlags::dPdUV, active);
@@ -203,7 +204,8 @@ class AreaLight final : public Emitter<Float, Spectrum> {
203204
PositionSample3f ps;
204205
if (!m_radiance->is_spatially_varying()) {
205206
// Radiance not spatially varying, use area-based sampling of shape
206-
ps = m_shape->sample_position(time, Point2f(sample.x(), sample.y()), active);
207+
ps = m_shape->sample_position_surface(
208+
time, Point2f(sample.x(), sample.y()), active);
207209
} else {
208210
// Importance sample texture
209211
auto [uv, pdf] = m_radiance->sample_position(Point2f(sample.x(), sample.y()), active);

src/emitters/directionalarea.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,8 @@ class DirectionalArea final : public Emitter<Float, Spectrum> {
130130
sample_position(Float time, const Point3f &sample,
131131
Mask active) const override {
132132
Assert(m_shape, "Can't sample from an area emitter without an associated Shape.");
133-
PositionSample3f ps = m_shape->sample_position(time, Point2f(sample.x(), sample.y()), active);
133+
PositionSample3f ps = m_shape->sample_position_surface(
134+
time, Point2f(sample.x(), sample.y()), active);
134135
Float weight = dr::select(ps.pdf > 0.f, dr::rcp(ps.pdf), 0.f);
135136
return { ps, weight };
136137
}

src/emitters/tests/test_area.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def test03_sample_ray(variants_vec_spectral, spectrum_key):
8484
wav, spec = spectrum.sample_spectrum(it, mi.sample_shifted(wavelength_sample))
8585

8686
# Sample a position on the shape
87-
ps = shape.sample_position(time, pos_sample[:2])
87+
ps = shape.sample_position_surface(time, pos_sample[:2])
8888

8989
assert dr.allclose(res, spec * shape.surface_area() * dr.pi)
9090
assert dr.allclose(ray.time, time)
@@ -112,7 +112,7 @@ def test04_sample_direction(variants_vec_spectral, spectrum_key):
112112
ds, res = emitter.sample_direction(it, samples)
113113

114114
# Sample direction on the shape
115-
shape_ds = shape.sample_direction(it, samples[:2])
115+
shape_ds = shape.sample_direction_surface(it, samples[:2])
116116

117117
assert dr.allclose(ds.pdf, shape_ds.pdf)
118118
assert dr.allclose(ds.pdf, emitter.pdf_direction(it, ds))

src/emitters/tests/test_directionalarea.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def test03_sample_ray(variants_vec_spectral, spectrum_key):
8484
wav, spec = spectrum.sample_spectrum(it, mi.sample_shifted(wavelength_sample))
8585

8686
# Sample a position on the shape
87-
ps = shape.sample_position(time, pos_sample[:2])
87+
ps = shape.sample_position_surface(time, pos_sample[:2])
8888

8989
assert dr.allclose(res, spec * shape.surface_area())
9090
assert dr.allclose(ray.time, time)

0 commit comments

Comments
 (0)