diff --git a/libs/gfx/source/renderer/deferred.cpp b/libs/gfx/source/renderer/deferred.cpp index eb2a15fa..cb2aacc8 100644 --- a/libs/gfx/source/renderer/deferred.cpp +++ b/libs/gfx/source/renderer/deferred.cpp @@ -956,12 +956,18 @@ void main() // Sort objects by mask & compute bbox - std::unordered_map, std::vector> sorted_objects; + struct objects_bucket + { + std::vector objects; + std::size_t first_visible; + }; + + std::unordered_map, objects_bucket> buckets; geom::box lit_bbox; geom::box casts_shadow_bbox; - std::vector clipped_by_camera(objects.size()); + std::vector camera_distance(objects.size()); for (std::size_t i = 0; i < objects.size(); ++i) { @@ -977,29 +983,31 @@ void main() if (o.mat->casts_shadow && o.mat->transparent) throw std::runtime_error("Transparent objects cannot cast shadow"); - sorted_objects[std::tuple{mask(o), o.mat}].push_back(i); + buckets[std::tuple{mask(o), o.mat}].objects.push_back(i); if (o.mat->lit) lit_bbox |= o.bbox; if (o.mat->casts_shadow) casts_shadow_bbox |= o.bbox; - bool clipped = true; + float dist = -std::numeric_limits::infinity(); for (int c = 0; c < 8; ++c) { - if (dot(o.bbox.corner(c & 1, (c & 2) >> 1, (c & 4) >> 1) - camera_position, camera_direction) > 0.f) - { - clipped = false; - break; - } + dist = std::max(dist, dot(o.bbox.corner(c & 1, (c & 2) >> 1, (c & 4) >> 1) - camera_position, camera_direction)); } - clipped_by_camera[i] = clipped; + camera_distance[i] = dist; + } + + for (auto & p : buckets) + { + std::sort(p.second.objects.begin(), p.second.objects.end(), [&](auto i, auto j){ return camera_distance[i] < camera_distance[j]; }); + p.second.first_visible = std::partition_point(p.second.objects.begin(), p.second.objects.end(), [&](auto i){ return camera_distance[i] < 0.f; }) - p.second.objects.begin(); } auto render_all = [&](auto & program, auto && predicate, bool clip_by_camera = false) { - for (auto const & p : sorted_objects) + for (auto const & p : buckets) { - if (p.second.empty()) continue; + if (p.second.objects.empty()) continue; std::uint32_t mask = std::get<0>(p.first); @@ -1020,11 +1028,9 @@ void main() program["u_material"] = geom::vector{mat->specular.intensity, mat->specular.shininess}; - for (std::size_t i : p.second) + for (std::size_t i = (clip_by_camera ? p.second.first_visible : 0); i < p.second.objects.size(); ++i) { - auto const & o = objects[i]; - - if (clip_by_camera && clipped_by_camera[i]) continue; + auto const & o = objects[p.second.objects[i]]; if (mask & O_PRE_TRANSFORM) program["u_pre_transform"] = *o.pre_transform;