diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2024-03-20 01:53:34 +1100 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2024-03-20 01:53:34 +1100 |
commit | 6d76a11e256cd96c9cdd7ae5a10c0276e6347277 (patch) | |
tree | d52459889408115d1e0eb657a05dc58e098e50eb /source/base | |
parent | 58a346e563df12af9194c198c7ffe974411abb28 (diff) |
experiment: unclamped lighting
Diffstat (limited to 'source/base')
-rw-r--r-- | source/base/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/base/StarCellularLightArray.cpp | 130 | ||||
-rw-r--r-- | source/base/StarCellularLightArray.hpp | 72 | ||||
-rw-r--r-- | source/base/StarCellularLighting.cpp | 65 | ||||
-rw-r--r-- | source/base/StarCellularLighting.hpp | 89 |
5 files changed, 295 insertions, 62 deletions
diff --git a/source/base/CMakeLists.txt b/source/base/CMakeLists.txt index f16257a..8f4d18b 100644 --- a/source/base/CMakeLists.txt +++ b/source/base/CMakeLists.txt @@ -25,6 +25,7 @@ SET (star_base_HEADERS SET (star_base_SOURCES StarAnimatedPartSet.cpp StarAssets.cpp + StarCellularLightArray.cpp StarCellularLighting.cpp StarConfiguration.cpp StarDirectoryAssetSource.cpp diff --git a/source/base/StarCellularLightArray.cpp b/source/base/StarCellularLightArray.cpp new file mode 100644 index 0000000..60a6d95 --- /dev/null +++ b/source/base/StarCellularLightArray.cpp @@ -0,0 +1,130 @@ +#include "StarCellularLightArray.hpp" +// just specializing these in a cpp file so I can iterate on them without recompiling like 40 files!! + +namespace Star { + +template <> +void CellularLightArray<ScalarLightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) { + float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle; + float perBlockAirAttenuation = 1.0f / m_pointMaxAir; + + for (PointLight light : m_pointLights) { + if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1) + continue; + + float maxIntensity = ScalarLightTraits::maxIntensity(light.value); + Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle); + + float maxRange = maxIntensity * m_pointMaxAir; + // The min / max considering the radius of the light + size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange)); + size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange)); + size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange)); + size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange)); + + for (size_t x = lxmin; x < lxmax; ++x) { + for (size_t y = lymin; y < lymax; ++y) { + LightValue lvalue = getLight(x, y); + // + 0.5f to correct block position to center + Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f); + + Vec2F relativeLightPosition = blockPos - light.position; + float distance = relativeLightPosition.magnitude(); + if (distance == 0.0f) { + setLight(x, y, light.value + lvalue); + continue; + } + + float attenuation = distance * perBlockAirAttenuation; + if (attenuation >= 1.0f) + continue; + + Vec2F direction = relativeLightPosition / distance; + if (light.beam > 0.0f) { + attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f); + if (attenuation >= 1.0f) + continue; + } + + float remainingAttenuation = maxIntensity - attenuation; + if (remainingAttenuation <= 0.0f) + continue; + + // Need to circularize manhattan attenuation here + float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1])); + float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation); + + // Apply single obstacle boost (determine single obstacle by one + // block unit of attenuation). + attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost; + + if (attenuation < 1.0f) + setLight(x, y, lvalue + ScalarLightTraits::subtract(light.value, attenuation)); + } + } + } +} + +template <> +void CellularLightArray<ColoredLightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) { + float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle; + float perBlockAirAttenuation = 1.0f / m_pointMaxAir; + + for (PointLight light : m_pointLights) { + if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1) + continue; + + float maxIntensity = ColoredLightTraits::maxIntensity(light.value); + Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle); + + float maxRange = maxIntensity * m_pointMaxAir; + // The min / max considering the radius of the light + size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange)); + size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange)); + size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange)); + size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange)); + + for (size_t x = lxmin; x < lxmax; ++x) { + for (size_t y = lymin; y < lymax; ++y) { + LightValue lvalue = getLight(x, y); + // + 0.5f to correct block position to center + Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f); + + Vec2F relativeLightPosition = blockPos - light.position; + float distance = relativeLightPosition.magnitude(); + if (distance == 0.0f) { + setLight(x, y, light.value + lvalue); + continue; + } + + float attenuation = distance * perBlockAirAttenuation; + if (attenuation >= 1.0f) + continue; + + Vec2F direction = relativeLightPosition / distance; + if (light.beam > 0.0f) { + attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f); + if (attenuation >= 1.0f) + continue; + } + + float remainingAttenuation = maxIntensity - attenuation; + if (remainingAttenuation <= 0.0f) + continue; + + // Need to circularize manhattan attenuation here + float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1])); + float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation); + + // Apply single obstacle boost (determine single obstacle by one + // block unit of attenuation). + attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost; + + if (attenuation < 1.0f) + setLight(x, y, lvalue + ColoredLightTraits::subtract(light.value, attenuation)); + } + } + } +} + +}
\ No newline at end of file diff --git a/source/base/StarCellularLightArray.hpp b/source/base/StarCellularLightArray.hpp index d73cacf..225d42b 100644 --- a/source/base/StarCellularLightArray.hpp +++ b/source/base/StarCellularLightArray.hpp @@ -11,6 +11,7 @@ struct ScalarLightTraits { static float spread(float source, float dest, float drop); static float subtract(float value, float drop); + static float multiply(float v1, float v2); static float maxIntensity(float value); static float minIntensity(float value); @@ -26,6 +27,7 @@ struct ColoredLightTraits { static Vec3F spread(Vec3F const& source, Vec3F const& dest, float drop); static Vec3F subtract(Vec3F value, float drop); + static Vec3F multiply(Vec3F value, float drop); static float maxIntensity(Vec3F const& value); static float minIntensity(Vec3F const& value); @@ -139,6 +141,10 @@ inline float ScalarLightTraits::subtract(float c, float drop) { return std::max(c - drop, 0.0f); } +inline float ScalarLightTraits::multiply(float v1, float v2) { + return v1 * v2; +} + inline float ScalarLightTraits::maxIntensity(float value) { return value; } @@ -179,6 +185,10 @@ inline Vec3F ColoredLightTraits::subtract(Vec3F c, float drop) { return c; } +inline Vec3F ColoredLightTraits::multiply(Vec3F c, float drop) { + return c * drop; +} + inline float ColoredLightTraits::maxIntensity(Vec3F const& value) { return value.max(); } @@ -388,68 +398,6 @@ void CellularLightArray<LightTraits>::calculateLightSpread(size_t xMin, size_t y } template <typename LightTraits> -void CellularLightArray<LightTraits>::calculatePointLighting(size_t xmin, size_t ymin, size_t xmax, size_t ymax) { - float perBlockObstacleAttenuation = 1.0f / m_pointMaxObstacle; - float perBlockAirAttenuation = 1.0f / m_pointMaxAir; - - for (PointLight light : m_pointLights) { - if (light.position[0] < 0 || light.position[0] > m_width - 1 || light.position[1] < 0 || light.position[1] > m_height - 1) - continue; - - float maxIntensity = LightTraits::maxIntensity(light.value); - Vec2F beamDirection = Vec2F(1, 0).rotate(light.beamAngle); - - float maxRange = maxIntensity * m_pointMaxAir; - // The min / max considering the radius of the light - size_t lxmin = std::floor(std::max<float>(xmin, light.position[0] - maxRange)); - size_t lymin = std::floor(std::max<float>(ymin, light.position[1] - maxRange)); - size_t lxmax = std::ceil(std::min<float>(xmax, light.position[0] + maxRange)); - size_t lymax = std::ceil(std::min<float>(ymax, light.position[1] + maxRange)); - - for (size_t x = lxmin; x < lxmax; ++x) { - for (size_t y = lymin; y < lymax; ++y) { - LightValue lvalue = getLight(x, y); - // + 0.5f to correct block position to center - Vec2F blockPos = Vec2F(x + 0.5f, y + 0.5f); - - Vec2F relativeLightPosition = blockPos - light.position; - float distance = relativeLightPosition.magnitude(); - if (distance == 0.0f) { - setLight(x, y, LightTraits::max(light.value, lvalue)); - continue; - } - - float attenuation = distance * perBlockAirAttenuation; - if (attenuation >= 1.0f) - continue; - - Vec2F direction = relativeLightPosition / distance; - if (light.beam > 0.0f) { - attenuation += (1.0f - light.beamAmbience) * clamp(light.beam * (1.0f - direction * beamDirection), 0.0f, 1.0f); - if (attenuation >= 1.0f) - continue; - } - - float remainingAttenuation = maxIntensity - LightTraits::minIntensity(lvalue) - attenuation; - if (remainingAttenuation <= 0.0f) - continue; - - // Need to circularize manhattan attenuation here - float circularizedPerBlockObstacleAttenuation = perBlockObstacleAttenuation / max(fabs(direction[0]), fabs(direction[1])); - float blockAttenuation = lineAttenuation(blockPos, light.position, circularizedPerBlockObstacleAttenuation, remainingAttenuation); - - // Apply single obstacle boost (determine single obstacle by one - // block unit of attenuation). - attenuation += blockAttenuation + min(blockAttenuation, circularizedPerBlockObstacleAttenuation) * m_pointObstacleBoost; - - if (attenuation < 1.0f) - setLight(x, y, LightTraits::max(LightTraits::subtract(light.value, attenuation), lvalue)); - } - } - } -} - -template <typename LightTraits> float CellularLightArray<LightTraits>::lineAttenuation(Vec2F const& start, Vec2F const& end, float perObstacleAttenuation, float maxAttenuation) { // Run Xiaolin Wu's line algorithm from start to end, summing over colliding diff --git a/source/base/StarCellularLighting.cpp b/source/base/StarCellularLighting.cpp index c0a92ea..579f48a 100644 --- a/source/base/StarCellularLighting.cpp +++ b/source/base/StarCellularLighting.cpp @@ -2,6 +2,45 @@ namespace Star { +Lightmap::Lightmap() : m_width(0), m_height(0) {} + +Lightmap::Lightmap(unsigned width, unsigned height) : m_width(width), m_height(height) { + m_data = std::make_unique<float[]>(len()); +} + +Lightmap::Lightmap(Lightmap const& lightMap) { + operator=(lightMap); +} + +Lightmap::Lightmap(Lightmap&& lightMap) noexcept { + operator=(std::move(lightMap)); +} + +Lightmap& Lightmap::operator=(Lightmap const& lightMap) { + m_width = lightMap.m_width; + m_height = lightMap.m_height; + if (lightMap.m_data) { + m_data = std::make_unique<float[]>(len()); + memcpy(m_data.get(), lightMap.m_data.get(), len()); + } + return *this; +} + +Lightmap& Lightmap::operator=(Lightmap&& lightMap) noexcept { + m_width = take(lightMap.m_width); + m_height = take(lightMap.m_height); + m_data = take(lightMap.m_data); + return *this; +} + +Lightmap::operator ImageView() { + ImageView view; + view.data = (uint8_t*)m_data.get(); + view.size = size(); + view.format = PixelFormat::RGB_F; + return view; +} + CellularLightingCalculator::CellularLightingCalculator(bool monochrome) : m_monochrome(monochrome) { @@ -104,6 +143,32 @@ void CellularLightingCalculator::calculate(Image& output) { } } +void CellularLightingCalculator::calculate(Lightmap& output) { + Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min()); + Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min()); + + if (m_monochrome) + m_lightArray.right().calculate(arrayMin[0], arrayMin[1], arrayMax[0], arrayMax[1]); + else + m_lightArray.left().calculate(arrayMin[0], arrayMin[1], arrayMax[0], arrayMax[1]); + + output = Lightmap(arrayMax[0] - arrayMin[0], arrayMax[1] - arrayMin[1]); + + if (m_monochrome) { + for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) { + for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) { + output.set(x - arrayMin[0], y - arrayMin[1], m_lightArray.right().getLight(x, y)); + } + } + } else { + for (size_t x = arrayMin[0]; x < arrayMax[0]; ++x) { + for (size_t y = arrayMin[1]; y < arrayMax[1]; ++y) { + output.set(x - arrayMin[0], y - arrayMin[1], m_lightArray.left().getLight(x, y)); + } + } + } +} + void CellularLightingCalculator::setupImage(Image& image, PixelFormat format) const { Vec2S arrayMin = Vec2S(m_queryRegion.min() - m_calculationRegion.min()); Vec2S arrayMax = Vec2S(m_queryRegion.max() - m_calculationRegion.min()); diff --git a/source/base/StarCellularLighting.hpp b/source/base/StarCellularLighting.hpp index caa52ae..170cbb5 100644 --- a/source/base/StarCellularLighting.hpp +++ b/source/base/StarCellularLighting.hpp @@ -11,6 +11,93 @@ namespace Star { +STAR_EXCEPTION(LightmapException, StarException); + +class Lightmap { +public: + Lightmap(); + Lightmap(unsigned width, unsigned height); + Lightmap(Lightmap const& lightMap); + Lightmap(Lightmap&& lightMap) noexcept; + + Lightmap& operator=(Lightmap const& lightMap); + Lightmap& operator=(Lightmap&& lightMap) noexcept; + + operator ImageView(); + + void set(unsigned x, unsigned y, float v); + void set(unsigned x, unsigned y, Vec3F const& v); + Vec3F get(unsigned x, unsigned y) const; + + bool empty() const; + + Vec2U size() const; + unsigned width() const; + unsigned height() const; + float* data(); + +private: + size_t len() const; + + std::unique_ptr<float[]> m_data; + unsigned m_width; + unsigned m_height; +}; + +inline void Lightmap::set(unsigned x, unsigned y, float v) { + if (x >= m_width || y >= m_height) { + throw LightmapException(strf("[{}, {}] out of range in Lightmap::set", x, y)); + return; + } + float* ptr = m_data.get() + (y * m_width * 3 + x * 3); + ptr[0] = ptr[1] = ptr[2] = v; +} + +inline void Lightmap::set(unsigned x, unsigned y, Vec3F const& v) { + if (x >= m_width || y >= m_height) { + throw LightmapException(strf("[{}, {}] out of range in Lightmap::set", x, y)); + return; + } + float* ptr = m_data.get() + (y * m_width * 3 + x * 3); + ptr[0] = v.x(); + ptr[1] = v.y(); + ptr[2] = v.z(); +} + + +inline Vec3F Lightmap::get(unsigned x, unsigned y) const { + if (x >= m_width || y >= m_height) { + throw LightmapException(strf("[{}, {}] out of range in Lightmap::get", x, y)); + return Vec3F(); + } + float* ptr = m_data.get() + (y * m_width * 3 + x * 3); + return Vec3F(ptr[0], ptr[1], ptr[2]); +} + +inline bool Lightmap::empty() const { + return m_width == 0 || m_height == 0; +} + +inline Vec2U Lightmap::size() const { + return { m_width, m_height }; +} + +inline unsigned Lightmap::width() const { + return m_width; +} + +inline unsigned Lightmap::height() const { + return m_height; +} + +inline float* Lightmap::data() { + return m_data.get(); +} + +inline size_t Lightmap::len() const { + return m_width * m_height * 3; +} + // Produce lighting values from an integral cellular grid. Allows for floating // positional point and cellular light sources, as well as pre-lighting cells // individually. @@ -43,6 +130,8 @@ public: // output image. The image will be reset to the size of the region given in // the call to 'begin', and formatted as RGB24. void calculate(Image& output); + // Same as above, but the color data in a float buffer instead. + void calculate(Lightmap& output); void setupImage(Image& image, PixelFormat format = PixelFormat::RGB24) const; private: |