Deferred renderer: sort objects by distance to camera

This commit is contained in:
Nikita Lisitsa 2020-12-11 17:51:04 +03:00
parent 72e0681521
commit f4908f2368

View file

@ -956,12 +956,18 @@ void main()
// Sort objects by mask & compute bbox
std::unordered_map<std::tuple<std::uint32_t, material const *>, std::vector<std::size_t>> sorted_objects;
struct objects_bucket
{
std::vector<std::size_t> objects;
std::size_t first_visible;
};
std::unordered_map<std::tuple<std::uint32_t, material const *>, objects_bucket> buckets;
geom::box<float, 3> lit_bbox;
geom::box<float, 3> casts_shadow_bbox;
std::vector<bool> clipped_by_camera(objects.size());
std::vector<float> 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<float>::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<float, 2>{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;