From 112472702de8d2f2ac9cd6b1da744db1a45324d0 Mon Sep 17 00:00:00 2001 From: past-due <30942300+past-due@users.noreply.github.com> Date: Wed, 1 May 2024 12:59:34 -0400 Subject: [PATCH] pielighting.cpp: Merge nearby point lights --- lib/ivis_opengl/pielighting.cpp | 118 ++++++++++++++++++++++++++++++-- lib/ivis_opengl/pielighting.h | 9 ++- 2 files changed, 122 insertions(+), 5 deletions(-) diff --git a/lib/ivis_opengl/pielighting.cpp b/lib/ivis_opengl/pielighting.cpp index 789a5a26112..7c69e44588a 100644 --- a/lib/ivis_opengl/pielighting.cpp +++ b/lib/ivis_opengl/pielighting.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "culling.h" #include "src/profiling.h" @@ -113,6 +114,29 @@ namespace { } +/* The shift on a world coordinate to get the tile coordinate */ +#define TILE_SHIFT 7 + +static inline int32_t pielight_maptile_coord(int32_t worldCoord) +{ + return worldCoord >> TILE_SHIFT; +} + +struct TileCoordsHasher +{ + std::size_t operator()(const std::pair& p) const + { + return std::hash()(static_cast(p.first) * (static_cast(INT_MAX) + 1) + p.second); + } +}; + +static float pointLightDistanceCalc(const renderingNew::LightingManager::CalculatedPointLight& a, const LIGHT& b) +{ + glm::vec3 pointLightVector = a.position - glm::vec3(b.position); + auto length = glm::length(pointLightVector); + return length; +} + void renderingNew::LightingManager::ComputeFrameData(const LightingData& data, LightMap&, const glm::mat4& worldViewProjectionMatrix) { PointLightBuckets result; @@ -140,6 +164,14 @@ void renderingNew::LightingManager::ComputeFrameData(const LightingData& data, L [](const glm::vec3& in) { return in.z <= 1; } }; + std::unordered_map, std::vector, TileCoordsHasher> tileRangeLights; // map tile coordinates to vector of culledLight indexes + constexpr size_t maxRangedLightsPerTile = 16; + constexpr size_t minLightRange = 5; + constexpr float distanceCalcCombineThreshold = 32.f; + size_t lightsCombined = 0; + size_t lightsSkipped = 0; + size_t tinyLightsSkipped = 0; + culledLights.clear(); for (const auto& light : data.lights) { @@ -152,9 +184,87 @@ void renderingNew::LightingManager::ComputeFrameData(const LightingData& data, L { continue; } - culledLights.push_back({light, clipSpaceBoundingBox}); + + if (light.range >= minLightRange) + { + std::pair lightTileCoords(pielight_maptile_coord(light.position.x), pielight_maptile_coord(light.position.y)); + auto it = tileRangeLights.find(lightTileCoords); + if (it != tileRangeLights.end()) + { + // merge point lights (if possible) + bool combinedLight = false; + for (auto& o : it->second) + { + auto& existingLight = culledLights[o]; + auto newLightRange = static_cast(light.range); + auto distanceCalc = pointLightDistanceCalc(existingLight.light, light); + if ((distanceCalc < distanceCalcCombineThreshold) + && (distanceCalc < (existingLight.light.range + newLightRange))) + { + // Found two lights close to each other - combine them + if (newLightRange > existingLight.light.range) + { + // If the new light has a greater range, use that as the "base" + CalculatedPointLight calcLight; + calcLight.position = glm::vec3(light.position.x, light.position.y, light.position.z); + calcLight.colour = glm::vec3(light.colour.byte.r / 255.f, light.colour.byte.g / 255.f, light.colour.byte.b / 255.f); + calcLight.range = light.range; + + float weight = existingLight.light.range / calcLight.range; + calcLight.colour.x += (existingLight.light.colour.x) * weight; + calcLight.colour.y += (existingLight.light.colour.y) * weight; + calcLight.colour.z += (existingLight.light.colour.z) * weight; + + existingLight.light = calcLight; + existingLight.clipSpaceBoundingBox = clipSpaceBoundingBox; + } + else + { + float weight = light.range / existingLight.light.range; + existingLight.light.colour.x += (light.colour.byte.r / 255.f) * weight; + existingLight.light.colour.y += (light.colour.byte.g / 255.f) * weight; + existingLight.light.colour.z += (light.colour.byte.b / 255.f) * weight; + } + combinedLight = true; + break; + } + } + if (combinedLight) + { + ++lightsCombined; + continue; + } + if (it->second.size() >= maxRangedLightsPerTile) + { + ++lightsSkipped; + continue; + } + + it->second.push_back(culledLights.size()); + } + else + { + tileRangeLights[lightTileCoords].push_back(culledLights.size()); + } + } + else + { + ++tinyLightsSkipped; + continue; + } + + CalculatedPointLight calcLight; + calcLight.position = glm::vec3(light.position.x, light.position.y, light.position.z); + calcLight.colour = glm::vec3(light.colour.byte.r / 255.f, light.colour.byte.g / 255.f, light.colour.byte.b / 255.f); + calcLight.range = light.range; + + culledLights.push_back({std::move(calcLight), std::move(clipSpaceBoundingBox)}); } + if (lightsSkipped > 0 || lightsCombined > 0 || tinyLightsSkipped > 0) + { + // debug(LOG_INFO, "Point lights - merged: %zu, skipped (tile limit): %zu, skipped (tiny): %zu", lightsCombined, lightsSkipped, tinyLightsSkipped); + } for (size_t lightIndex = 0, end = culledLights.size(); lightIndex < end; lightIndex++) { @@ -162,9 +272,9 @@ void renderingNew::LightingManager::ComputeFrameData(const LightingData& data, L result.positions[lightIndex].x = light.position.x; result.positions[lightIndex].y = light.position.y; result.positions[lightIndex].z = light.position.z; - result.colorAndEnergy[lightIndex].x = light.colour.byte.r / 255.f; - result.colorAndEnergy[lightIndex].y = light.colour.byte.g / 255.f; - result.colorAndEnergy[lightIndex].z = light.colour.byte.b / 255.f; + result.colorAndEnergy[lightIndex].x = light.colour.x; + result.colorAndEnergy[lightIndex].y = light.colour.y; + result.colorAndEnergy[lightIndex].z = light.colour.z; result.colorAndEnergy[lightIndex].w = light.range; } diff --git a/lib/ivis_opengl/pielighting.h b/lib/ivis_opengl/pielighting.h index 917b975669c..5f4b7c26d15 100644 --- a/lib/ivis_opengl/pielighting.h +++ b/lib/ivis_opengl/pielighting.h @@ -97,11 +97,18 @@ namespace renderingNew struct LightingManager final : ILightingManager { void ComputeFrameData(const LightingData& data, LightMap& lightmap, const glm::mat4& worldViewProjectionMatrix) override; + + struct CalculatedPointLight + { + glm::vec3 position = glm::vec3(0, 0, 0); + glm::vec3 colour; + float range; + }; private: // cached containers to avoid frequent reallocations struct CulledLightInfo { - const LIGHT& light; + CalculatedPointLight light; BoundingBox clipSpaceBoundingBox; }; std::vector culledLights;