Skip to content

Add eval(), sample_direction() and pdf_direction() to distant sensor #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
50 changes: 46 additions & 4 deletions src/sensors/distant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Distant radiancemeter sensor (:monosp:`distant`)
* - direction
- |vector|
- Alternative (and exclusive) to ``to_world``. Direction orienting the
sensor's reference hemisphere.
sensor.
* - target
- |point| or nested :paramtype:`shape` plugin
- *Optional.* Define the ray target sampling strategy.
Expand Down Expand Up @@ -241,20 +241,62 @@ class DistantSensorImpl final : public Sensor<Float, Spectrum> {
return { ray, ray_weight & active };
}

Spectrum eval(const SurfaceInteraction3f & /*si*/,
Mask /*active*/) const override {
return 0.f;
}

std::pair<DirectionSample3f, Spectrum>
sample_direction(const Interaction3f &it, const Point2f & /*sample*/,
Mask active) const override {
MTS_MASKED_FUNCTION(ProfilerPhase::EndpointSampleDirection, active);

Vector3f d = m_to_world.value().transform_affine(Vector3f(0.f, 0.f, 1.f));
Float dist = 2.f * m_bsphere.radius + math::RayEpsilon<Float>;

DirectionSample3f ds = ek::zero<DirectionSample3f>();
ds.p = it.p - d * dist;
ds.n = d;
ds.uv = Point2f(0.f);
ds.time = it.time;
ds.pdf = 1.f;
ds.delta = true;
ds.d = -d;
ds.dist = dist;
// ds.emitter = this;
// TODO: --^ must be fixed as soon as SurfaceInteraction3f is fit for
// sensor sampling
Comment on lines +266 to +268
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the issue here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, it's not SurfaceInteraction3f but DirectionSample3f. The emitter member is set to point to the sampled object e.g. upon call to DirectionalEmitter::sample_direction(), and nothing equivalent can happen here. So I was wondering if generalising this member to accept any EndPoint would make sense or if I should just ignore it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right, this makes sense. And what prevents you from doing so? (e.g. something related to the ptrace PR?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing, it's just out of the scope of mere plugin changes, so I just wanted to know if there was a reason not to do so :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually overlooked this: both SurfaceInteraction and DirectionSample would need some changes. I'll submit a separate PR for this.


SurfaceInteraction3f si = ek::zero<SurfaceInteraction3f>();
si.wavelengths = it.wavelengths;

// No need to divide by the PDF here (always equal to 1.f)
// Note: Must be updated if a sensor response function is added to this plugin
UnpolarizedSpectrum spec = 1.f;

return { ds, spec & active };
}

Float pdf_direction(const Interaction3f & /*it*/,
const DirectionSample3f & /*ds*/,
Mask /*active*/) const override {
return 0.f;
}

// This sensor does not occupy any particular region of space, return an
// invalid bounding box
ScalarBoundingBox3f bbox() const override { return ScalarBoundingBox3f(); }

std::string to_string() const override {
std::ostringstream oss;
oss << "DistantSensor[" << std::endl
<< " to_world = " << m_to_world << "," << std::endl
<< " film = " << m_film << "," << std::endl;
<< " to_world = " << string::indent(m_to_world) << "," << std::endl
<< " film = " << string::indent(m_film) << "," << std::endl;

if constexpr (TargetType == RayTargetType::Point)
oss << " target = " << m_target_point << std::endl;
else if constexpr (TargetType == RayTargetType::Shape)
oss << " target = " << m_target_shape << std::endl;
oss << " target = " << string::indent(m_target_shape) << std::endl;
else // if constexpr (TargetType == RayTargetType::None)
oss << " target = none" << std::endl;

Expand Down
32 changes: 28 additions & 4 deletions src/sensors/tests/test_distant.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ def test_construct(variant_scalar_rgb):
sensor = make_sensor(sensor_dict(direction=direction))
result = sensor.world_transform().matrix
assert ek.allclose(result, expected)
# Couldn't get ek.allclose() to work here

# Test different combinations of target and origin values
# -- No target
Expand Down Expand Up @@ -310,14 +309,39 @@ def test_sample_target(variant_scalar_rgb, sensor_setup, w_e, w_o):
assert np.allclose(result, expected_value, rtol=rtol_value)


def test_sample_direction(variants_vec_rgb):
from mitsuba.render import SurfaceInteraction3f
from mitsuba.core import ScalarVector3f

direction = ScalarVector3f([1, 1, 1])
sensor = make_sensor(sensor_dict(direction=direction))

it = ek.zero(SurfaceInteraction3f, 3)
# Some positions inside the unit sphere
it.p = [[-0.5, 0.3, -0.1], [0.8, -0.3, -0.2], [-0.2, 0.6, -0.6]]
it.time = 1.0

# Sample sensor direction
samples = [[0.4, 0.5, 0.3], [0.1, 0.4, 0.9]]
ds, res = sensor.sample_direction(it, samples)

assert ek.allclose(ds.pdf, 1.0)
assert ek.allclose(ds.d, -ek.normalize(direction))
assert ek.allclose(sensor.pdf_direction(it, ds), 0.0)
assert ek.allclose(ds.time, it.time)

# Check spectrum
assert ek.allclose(res, 1.0)


def test_checkerboard(variants_all_rgb):
"""
Very basic render test with checkerboard texture and square target.
"""
from mitsuba.core import ScalarTransform4f
from mitsuba.core.xml import load_dict

l_o = 1.0
l_e = 1.0 # Emitted radiance
rho0 = 0.5
rho1 = 1.0

Expand All @@ -339,7 +363,7 @@ def test_checkerboard(variants_all_rgb):
"emitter": {
"type": "directional",
"direction": [0, 0, -1],
"irradiance": 1.0
"irradiance": l_e
},
"sensor0": {
"type": "distant",
Expand Down Expand Up @@ -386,5 +410,5 @@ def test_checkerboard(variants_all_rgb):
scene = load_dict(scene_dict)
data = np.array(scene.render())

expected = l_o * 0.5 * (rho0 + rho1) / ek.Pi
expected = l_e * 0.5 * (rho0 + rho1) / ek.Pi
assert np.allclose(data, expected, atol=1e-3)