diff --git a/src/Magnum/Trade/LightData.cpp b/src/Magnum/Trade/LightData.cpp index ff37ec519..36fb5aca7 100644 --- a/src/Magnum/Trade/LightData.cpp +++ b/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} {} diff --git a/src/Magnum/Trade/LightData.h b/src/Magnum/Trade/LightData.h index a4095953d..8072d6330 100644 --- a/src/Magnum/Trade/LightData.h +++ b/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$. * diff --git a/src/Magnum/Trade/Test/LightDataTest.cpp b/src/Magnum/Trade/Test/LightDataTest.cpp index d3fdddde5..69d56b4a4 100644 --- a/src/Magnum/Trade/Test/LightDataTest.cpp +++ b/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);