Browse Source

Trade: change LightData range attenuation to do 1 + d^2 in denominator.

Otherwise the attenuation would explode to infinity at distance < 1,
which doesn't make any sense. This, together with the square of the
nominator, is different from the recommended glTF attenuation in
KHR_lights_punctial, but because I couldn't find any relevant discussion
on the equation used there, I'll assume it's just wrong.

Sigh.
pull/470/head
Vladimír Vondruš 6 years ago
parent
commit
8b665884f0
  1. 4
      src/Magnum/Trade/LightData.cpp
  2. 25
      src/Magnum/Trade/LightData.h
  3. 28
      src/Magnum/Trade/Test/LightDataTest.cpp

4
src/Magnum/Trade/LightData.cpp

@ -51,12 +51,12 @@ LightData::LightData(const Type type, const Color3& color, const Float intensity
LightData::LightData(const Type type, const Color3& color, const Float intensity, const Float range, const Rad innerConeAngle, const Rad outerConeAngle, const void* const importerState) noexcept: LightData{type, color, intensity,
type == Type::Ambient || type == Type::Directional ?
Vector3{1.0f, 0.0f, 0.0f} : Vector3{0.0f, 0.0f, 1.0f},
Vector3{1.0f, 0.0f, 0.0f} : Vector3{1.0f, 0.0f, 1.0f},
range, innerConeAngle, outerConeAngle, importerState} {}
LightData::LightData(const Type type, const Color3& color, const Float intensity, const Float range, const void* const importerState) noexcept: LightData{type, color, intensity,
type == Type::Ambient || type == Type::Directional ?
Vector3{1.0f, 0.0f, 0.0f} : Vector3{0.0f, 0.0f, 1.0f},
Vector3{1.0f, 0.0f, 0.0f} : Vector3{1.0f, 0.0f, 1.0f},
range, importerState} {}
LightData::LightData(const Type type, const Color3& color, const Float intensity, const Rad innerConeAngle, const Rad outerConeAngle, const void* const importerState) noexcept: LightData{type, color, intensity, Constants::inf(), innerConeAngle, outerConeAngle, importerState} {}

25
src/Magnum/Trade/LightData.h

@ -51,7 +51,7 @@ everything except spotlights.
You can choose a constructor overload that matches the subset of input
parameters and let the class set the rest implicitly. For example, a
@ref Type::Point light constructed using a range will have @ref attenuation()
implicitly set to @cpp {0.0f, 0.0f, 1.0f} @ce and cone angles to
implicitly set to @cpp {1.0f, 0.0f, 1.0f} @ce and cone angles to
@cpp 360.0_degf @ce:
@snippet MagnumTrade.cpp LightData-populating-range
@ -90,13 +90,14 @@ and provides a good tradeoff for performance while staying mostly
physically-based. This is modelled after the glTF [KHR_lights_punctual](https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual#range-property)
extension, which in turn is based on the [UE4 implementation](https://github.com/KhronosGroup/glTF/pull/1223#issuecomment-387956919).
In this case, @ref attenuation() is set to
@cpp {0.0f, 0.0f, 1.0f} @ce: @f[
F_{att} = \frac{\operatorname{clamp}(1 - (\frac{d}{\color{m-info} R})^4, 0, 1)^2}{{\color{m-dim} K_c + K_l d + K_q} d^2} = \frac{\operatorname{clamp}(1 - (\frac{d}{\color{m-info} R})^4, 0, 1)^2}{d^2}
@cpp {1.0f, 0.0f, 1.0f} @ce, the constant factor is present in order to prevent
the function from exploding to infinity when @f$ d \to \infty @f$. @f[
F_{att} = \frac{\operatorname{clamp}(1 - (\frac{d}{\color{m-info} R})^4, 0, 1)^2}{{\color{m-success} K_c} + {\color{m-dim} K_l d +} {\color{m-success} K_q} d^2} = \frac{\operatorname{clamp}(1 - (\frac{d}{\color{m-info} R})^4, 0, 1)^2}{1 + d^2}
@f]
If @f$ {\color{m-info} R} \to \infty @f$ as well, the equation reduces down to
a simple inverse square: @f[
F_{att} = \lim_{{\color{m-info} R} \to \infty} \frac{{\color{m-dim} \operatorname{clamp}(} 1 {\color{m-dim} - (\frac{d}{R})^4, 0, 1)^2}}{{\color{m-dim} K_c + K_l d + K_q} d^2} = \frac{1}{d^2}
F_{att} = \lim_{{\color{m-info} R} \to \infty} \frac{{\color{m-dim} \operatorname{clamp}(} 1 {\color{m-dim} - (\frac{d}{R})^4, 0, 1)^2}}{{\color{m-success} K_c} + {\color{m-dim} K_l d +} {\color{m-success} K_q} d^2} = \frac{1}{1 + d^2}
@f]
As a special case, a @ref Type::Directional light is defined by
@ -306,7 +307,7 @@ class MAGNUM_TRADE_EXPORT LightData {
* @param importerState Importer-specific state
* @m_since_latest
*
* The @ref attenuation() is implicitly set to @cpp {0.0f, 0.0f, 1.0f} @ce
* The @ref attenuation() is implicitly set to @cpp {1.0f, 0.0f, 1.0f} @ce
* for a @ref Type::Point and @ref Type::Spot light and to
* @cpp {1.0f, 0.0f, 0.0f} @ce for an @ref Type::Ambient and
* @ref Type::Directional light. See @ref Trade-LightData-attenuation
@ -329,7 +330,7 @@ class MAGNUM_TRADE_EXPORT LightData {
* @param importerState Importer-specific state
* @m_since_latest
*
* The @ref attenuation() is implicitly set to @cpp {0.0f, 0.0f, 1.0f} @ce
* The @ref attenuation() is implicitly set to @cpp {1.0f, 0.0f, 1.0f} @ce
* for a @ref Type::Point and @ref Type::Spot light and to
* @cpp {1.0f, 0.0f, 0.0f} @ce for an @ref Type::Ambient and
* @ref Type::Directional light. See @ref Trade-LightData-attenuation
@ -359,7 +360,7 @@ class MAGNUM_TRADE_EXPORT LightData {
* @param importerState Importer-specific state
* @m_since_latest
*
* The @ref attenuation() is implicitly set to @cpp {0.0f, 0.0f, 1.0f} @ce
* The @ref attenuation() is implicitly set to @cpp {1.0f, 0.0f, 1.0f} @ce
* for a @ref Type::Point and @ref Type::Spot light and to
* @cpp {1.0f, 0.0f, 0.0f} @ce for an @ref Type::Ambient and
* @ref Type::Directional light; @ref range() is always
@ -378,7 +379,7 @@ class MAGNUM_TRADE_EXPORT LightData {
* @param intensity Light intensity
* @param importerState Importer-specific state
*
* The @ref attenuation() is implicitly set to @cpp {0.0f, 0.0f, 1.0f} @ce
* The @ref attenuation() is implicitly set to @cpp {1.0f, 0.0f, 1.0f} @ce
* for a @ref Type::Point and @ref Type::Spot light and to
* @cpp {1.0f, 0.0f, 0.0f} @ce for an @ref Type::Ambient and
* @ref Type::Directional light; @ref range() is always
@ -428,10 +429,10 @@ class MAGNUM_TRADE_EXPORT LightData {
* @f$ \color{m-success} K_l @f$ and @f$ \color{m-success} K_q @f$ in
* the @ref Trade-LightData-attenuation "attenuation equation". Always
* @cpp {1.0f, 0.0f, 0.0f} @ce for an @ref Type::Ambient and
* @ref Type::Directional light, set to @cpp {0.0f, 0.0f, 1.0f} @ce for
* @ref Type::Directional light, set to @cpp {1.0f, 0.0f, 1.0f} @ce for
* range-based attenuation --- and if @ref range() is
* @ref Constants::inf() as well, the attenuation equation is simply
* @f$ F_{att} = \frac{1}{d^2} @f$.
* @f$ F_{att} = \frac{1}{1 + d^2} @f$.
*/
Vector3 attenuation() const { return _attenuation; }
@ -443,8 +444,8 @@ class MAGNUM_TRADE_EXPORT LightData {
* the @ref Trade-LightData-attenuation "attenuation equation". If set
* to @ref Constants::inf(), then:
*
* - if @ref attenuation() is @cpp {0.0f, 0.0f, 1.0f} @ce, the
* attenuation equation is @f$ F_{att} = \frac{1}{d^2} @f$;
* - if @ref attenuation() is @cpp {1.0f, 0.0f, 1.0f} @ce, the
* attenuation equation is @f$ F_{att} = \frac{1}{1 + d^2} @f$;
* - if @ref attenuation() is @cpp {1.0f, 0.0f, 0.0f} @ce, the
* attenuation equation is @f$ F_{att} = 1 @f$.
*

28
src/Magnum/Trade/Test/LightDataTest.cpp

@ -60,11 +60,11 @@ const struct {
const char* message;
} ConstructInvalidData[] {
{"invalid ambient attenuation", LightData::Type::Ambient,
{0.0f, 0.0f, 1.0f}, Constants::inf(), 360.0_degf, 360.0_degf,
"attenuation has to be (1, 0, 0) for an ambient or directional light but got Vector(0, 0, 1)"},
{1.0f, 0.0f, 1.0f}, Constants::inf(), 360.0_degf, 360.0_degf,
"attenuation has to be (1, 0, 0) for an ambient or directional light but got Vector(1, 0, 1)"},
{"invalid directional attenuation", LightData::Type::Directional,
{0.0f, 0.0f, 1.0f}, Constants::inf(), 360.0_degf, 360.0_degf,
"attenuation has to be (1, 0, 0) for an ambient or directional light but got Vector(0, 0, 1)"},
{1.0f, 0.0f, 1.0f}, Constants::inf(), 360.0_degf, 360.0_degf,
"attenuation has to be (1, 0, 0) for an ambient or directional light but got Vector(1, 0, 1)"},
{"invalid ambient range", LightData::Type::Ambient,
{1.0f, 0.0f, 0.0f}, 2.0f, 360.0_degf, 360.0_degf,
"range has to be infinity for an ambient or directional light but got 2"},
@ -72,16 +72,16 @@ const struct {
{1.0f, 0.0f, 0.0f}, 2.0f, 360.0_degf, 360.0_degf,
"range has to be infinity for an ambient or directional light but got 2"},
{"invalid point angles", LightData::Type::Point,
{0.0f, 0.0f, 1.0f}, Constants::inf(), 15.0_degf, 90.0_degf,
{1.0f, 0.0f, 1.0f}, Constants::inf(), 15.0_degf, 90.0_degf,
"cone angles have to be 360° for lights that aren't spot but got Deg(15) and Deg(90)"},
{"negative inner spot angle", LightData::Type::Spot,
{0.0f, 0.0f, 1.0f}, Constants::inf(), -1.0_degf, 90.0_degf,
{1.0f, 0.0f, 1.0f}, Constants::inf(), -1.0_degf, 90.0_degf,
"spot light inner and outer cone angles have to be in range [0°, 360°] and inner not larger than outer but got Deg(-1) and Deg(90)"},
{"too big outer spot angle", LightData::Type::Spot,
{0.0f, 0.0f, 1.0f}, Constants::inf(), 0.0_degf, 361.0_degf,
{1.0f, 0.0f, 1.0f}, Constants::inf(), 0.0_degf, 361.0_degf,
"spot light inner and outer cone angles have to be in range [0°, 360°] and inner not larger than outer but got Deg(0) and Deg(361)"},
{"inner spot angle larger than outer", LightData::Type::Spot,
{0.0f, 0.0f, 1.0f}, Constants::inf(), 35.0_degf, 30.0_degf,
{1.0f, 0.0f, 1.0f}, Constants::inf(), 35.0_degf, 30.0_degf,
"spot light inner and outer cone angles have to be in range [0°, 360°] and inner not larger than outer but got Deg(35) and Deg(30)"}
};
@ -222,7 +222,7 @@ void LightDataTest::constructRange() {
CORRADE_COMPARE(data.type(), LightData::Type::Spot);
CORRADE_COMPARE(data.color(), 0xccff33_rgbf);
CORRADE_COMPARE(data.intensity(), 0.8f);
CORRADE_COMPARE(data.attenuation(), (Vector3{0.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.attenuation(), (Vector3{1.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.range(), 15.0f);
CORRADE_COMPARE(data.innerConeAngle(), 15.0_degf);
CORRADE_COMPARE(data.outerConeAngle(), 35.0_degf);
@ -239,7 +239,7 @@ void LightDataTest::constructRange() {
CORRADE_COMPARE(data.type(), LightData::Type::Spot);
CORRADE_COMPARE(data.color(), 0xccff33_rgbf);
CORRADE_COMPARE(data.intensity(), 0.8f);
CORRADE_COMPARE(data.attenuation(), (Vector3{0.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.attenuation(), (Vector3{1.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.range(), 15.0f);
CORRADE_COMPARE(data.innerConeAngle(), 0.0_degf);
CORRADE_COMPARE(data.outerConeAngle(), 45.0_degf);
@ -256,7 +256,7 @@ void LightDataTest::constructRange() {
CORRADE_COMPARE(data.type(), LightData::Type::Point);
CORRADE_COMPARE(data.color(), 0xccff33_rgbf);
CORRADE_COMPARE(data.intensity(), 0.8f);
CORRADE_COMPARE(data.attenuation(), (Vector3{0.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.attenuation(), (Vector3{1.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.range(), 15.0f);
CORRADE_COMPARE(data.innerConeAngle(), 360.0_degf);
CORRADE_COMPARE(data.outerConeAngle(), 360.0_degf);
@ -310,7 +310,7 @@ void LightDataTest::constructNone() {
CORRADE_COMPARE(data.type(), LightData::Type::Spot);
CORRADE_COMPARE(data.color(), 0xccff33_rgbf);
CORRADE_COMPARE(data.intensity(), 0.8f);
CORRADE_COMPARE(data.attenuation(), (Vector3{0.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.attenuation(), (Vector3{1.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.range(), Constants::inf());
CORRADE_COMPARE(data.innerConeAngle(), 15.0_degf);
CORRADE_COMPARE(data.outerConeAngle(), 35.0_degf);
@ -326,7 +326,7 @@ void LightDataTest::constructNone() {
CORRADE_COMPARE(data.type(), LightData::Type::Spot);
CORRADE_COMPARE(data.color(), 0xccff33_rgbf);
CORRADE_COMPARE(data.intensity(), 0.8f);
CORRADE_COMPARE(data.attenuation(), (Vector3{0.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.attenuation(), (Vector3{1.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.range(), Constants::inf());
CORRADE_COMPARE(data.innerConeAngle(), 0.0_degf);
CORRADE_COMPARE(data.outerConeAngle(), 45.0_degf);
@ -342,7 +342,7 @@ void LightDataTest::constructNone() {
CORRADE_COMPARE(data.type(), LightData::Type::Point);
CORRADE_COMPARE(data.color(), 0xccff33_rgbf);
CORRADE_COMPARE(data.intensity(), 0.8f);
CORRADE_COMPARE(data.attenuation(), (Vector3{0.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.attenuation(), (Vector3{1.0f, 0.0f, 1.0f}));
CORRADE_COMPARE(data.range(), Constants::inf());
CORRADE_COMPARE(data.innerConeAngle(), 360.0_degf);
CORRADE_COMPARE(data.outerConeAngle(), 360.0_degf);

Loading…
Cancel
Save