Add light softness: spotlight edge softness for physically-based rendering.

The new spotlight attribute softness (real in [0, 1], default 0) is the
fraction of the cone, measured inward from the cutoff, over which
intensity falls to zero. It is used by physically-based lighting models;
the Phong model's corresponding knob remains exponent.

The filament renderer previously hardcoded the inner cone angle to 0,
making the entire beam penumbra: the shader attenuates by the squared
smoothstep ((cos(theta) - cos(outer)) / (cos(inner) - cos(outer)))^2, so
a cutoff-25 spot delivered its rated candela only exactly on-axis and
about a third of it averaged over the light pool, with the deficit
shrinking as the cutoff widens. The inner angle is now
(1 - softness) * cutoff, so at the default the light delivers its full
intensity everywhere inside the cone and illuminance follows E = I/d^2
independent of the cutoff. Setting softness to 1 reproduces the previous
appearance exactly (verified bit-identical), which is the migration path
for models tuned against the old behavior.

The filament light type also changes from FOCUSED_SPOT to SPOT. With
intensity given in candela and the cone set at build time the two types
produce identical output (FOCUSED_SPOT's power-conserving rescale only
applies when the cone changes after the intensity is set), but SPOT
guarantees that candela never rescales with cone angle should the cone
ever become runtime-editable.

Verified with headless renders under a linear tone mapper against an
equal-candela point light at cutoffs 25/45/80: softness 0 gives
spot/point luminance ratio 1.000 at all sampled angles inside the cone;
softness 0.2 is flat over the inner 80% of the cone; softness 1 matches
the previous renderer with zero linear-pixel difference. XML round-trip
and the [0, 1] compile-time check verified. Introspect and wasm bindings
regenerated.

PiperOrigin-RevId: 959334706
Change-Id: I0f0729781899880de1729ea9b3d8c055d715a025
This commit is contained in:
Yuval Tassa
2026-08-04 18:11:03 -07:00
committed by Copybara-Service
parent 92261d9095
commit f9a00bd5b5
27 changed files with 118 additions and 15 deletions
+3
View File
@@ -5102,6 +5102,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("light_pos0", &MjModel::light_pos0)
.property("light_poscom0", &MjModel::light_poscom0)
.property("light_range", &MjModel::light_range)
.property("light_softness", &MjModel::light_softness)
.property("light_specular", &MjModel::light_specular)
.property("light_targetbodyid", &MjModel::light_targetbodyid)
.property("light_texid", &MjModel::light_texid)
@@ -5893,6 +5894,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("mode", &MjsLight::mode, &MjsLight::set_mode, reference())
.property("pos", &MjsLight::pos)
.property("range", &MjsLight::range, &MjsLight::set_range, reference())
.property("softness", &MjsLight::softness, &MjsLight::set_softness, reference())
.property("specular", &MjsLight::specular)
.property("targetbody", &MjsLight::targetbody, &MjsLight::set_targetbody, reference())
.property("texture", &MjsLight::texture, &MjsLight::set_texture, reference())
@@ -6170,6 +6172,7 @@ EMSCRIPTEN_BINDINGS(mujoco_bindings) {
.property("intensity", &MjvLight::intensity, &MjvLight::set_intensity, reference())
.property("pos", &MjvLight::pos)
.property("range", &MjvLight::range, &MjvLight::set_range, reference())
.property("softness", &MjvLight::softness, &MjvLight::set_softness, reference())
.property("specular", &MjvLight::specular)
.property("texid", &MjvLight::texid, &MjvLight::set_texid, reference())
.property("type", &MjvLight::type, &MjvLight::set_type, reference());
+15
View File
@@ -2033,6 +2033,12 @@ struct MjvLight {
void set_range(float value) {
ptr_->range = value;
}
float softness() const {
return ptr_->softness;
}
void set_softness(float value) {
ptr_->softness = value;
}
private:
mjvLight* ptr_;
@@ -2919,6 +2925,12 @@ struct MjsLight {
void set_cutoff(float value) {
ptr_->cutoff = value;
}
float softness() const {
return ptr_->softness;
}
void set_softness(float value) {
ptr_->softness = value;
}
float exponent() const {
return ptr_->exponent;
}
@@ -4785,6 +4797,9 @@ struct MjModel {
emscripten::val light_cutoff() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_cutoff));
}
emscripten::val light_softness() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_softness));
}
emscripten::val light_exponent() const {
return emscripten::val(emscripten::typed_memory_view(ptr_->nlight, ptr_->light_exponent));
}