From 68d70e3d962baf333f2757bc43a843f0509cc32a Mon Sep 17 00:00:00 2001 From: lisyarus Date: Sat, 24 Oct 2020 19:40:58 +0300 Subject: [PATCH] Reduce deferred renderer fill rate by using light influence bboxes --- libs/gfx/CMakeLists.txt | 2 +- libs/gfx/source/renderer/deferred.cpp | 119 +++++++++++++++++++++++--- 2 files changed, 109 insertions(+), 12 deletions(-) diff --git a/libs/gfx/CMakeLists.txt b/libs/gfx/CMakeLists.txt index 144240f7..72248c51 100644 --- a/libs/gfx/CMakeLists.txt +++ b/libs/gfx/CMakeLists.txt @@ -6,7 +6,7 @@ file(GLOB_RECURSE PSEMEK_GFX_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "sou add_library(psemek-gfx ${PSEMEK_GFX_HEADERS} ${PSEMEK_GFX_SOURCES}) target_include_directories(psemek-gfx PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include") -target_link_libraries(psemek-gfx PUBLIC psemek-util psemek-geom OpenGL::GL) +target_link_libraries(psemek-gfx PUBLIC psemek-util psemek-geom psemek-cg OpenGL::GL) psemek_add_resources(psemek-gfx resources/font_9x12.pbm psemek/gfx/resource/font_9x12 diff --git a/libs/gfx/source/renderer/deferred.cpp b/libs/gfx/source/renderer/deferred.cpp index 0b1d40b5..c6c75770 100644 --- a/libs/gfx/source/renderer/deferred.cpp +++ b/libs/gfx/source/renderer/deferred.cpp @@ -3,9 +3,12 @@ #include #include #include +#include #include -#include +#include + +#include namespace psemek::gfx { @@ -142,6 +145,21 @@ void main() texcoord = vertices[gl_VertexID].xy * 0.5 + vec2(0.5); } +)"; + + char const screen_vs[] = +R"(#version 330 + +layout (location = 0) in vec2 in_position; + +out vec2 texcoord; + +void main() +{ + gl_Position = vec4(in_position, 0.0, 1.0); + + texcoord = in_position * 0.5 + vec2(0.5); +} )"; char const ambient_pass_fs[] = @@ -261,12 +279,62 @@ void main() } )"; + + static std::size_t bbox_to_screen_fan(geom::matrix const & camera_transform, geom::box const & b, geom::point * result) + { + geom::point bbox_corners_screen[8]; + auto bbox_corners_screen_end = bbox_corners_screen; + + bool need_clipping = false; + for (int z = 0; z < 2; ++z) + { + for (int y = 0; y < 2; ++y) + { + for (int x = 0; x < 2; ++x) + { + geom::point p; + p[0] = geom::lerp(b[0], x); + p[1] = geom::lerp(b[1], y); + p[2] = geom::lerp(b[2], z); + + auto q = camera_transform * geom::homogeneous(p); + + if (q[2] < -q[3] || q[2] > q[3]) + { + need_clipping = true; + break; + } + + *bbox_corners_screen_end++ = {q[0] / q[3], q[1] / q[3]}; + } + } + } + + if (need_clipping) + { + // TODO: clip with near-Z manually instead of filling the entire screen + *result++ = {-1.f, -1.f}; + *result++ = { 1.f, -1.f}; + *result++ = { 1.f, 1.f}; + *result++ = {-1.f, 1.f}; + return 4; + } + + geom::point * bbox_hull_screen_it[8]; + auto bbox_hull_size = cg::graham_convex_hull(bbox_corners_screen, bbox_corners_screen_end, bbox_hull_screen_it) - bbox_hull_screen_it; + + for (std::size_t i = 0; i < bbox_hull_size; ++i) + result[i] = *bbox_hull_screen_it[i]; + + return bbox_hull_size; + } + struct deferred_renderer::impl { gfx::program g_buffer_pass_program{std::string(g_buffer_pass_common) + g_buffer_pass_vs, std::string(g_buffer_pass_common) + g_buffer_pass_fs}; gfx::program ambient_pass_program{fullscreen_vs, ambient_pass_fs}; - gfx::program directional_light_pass_program{fullscreen_vs, directional_light_pass_fs}; - gfx::program point_light_pass_program{fullscreen_vs, point_light_pass_fs}; + gfx::program directional_light_pass_program{screen_vs, directional_light_pass_fs}; + gfx::program point_light_pass_program{screen_vs, point_light_pass_fs}; // G-buffer attachments: // 0 - position (rbg) @@ -280,7 +348,7 @@ void main() std::optional> g_buffer_size; - gfx::array fullscreen_array; + gfx::mesh screen_mesh; }; deferred_renderer::deferred_renderer() @@ -311,6 +379,8 @@ void main() impl().point_light_pass_program["u_g1"] = 1; impl().point_light_pass_program["u_g2"] = 2; impl().point_light_pass_program["u_g3"] = 3; + + impl().screen_mesh.setup>(); } deferred_renderer::~deferred_renderer() = default; @@ -346,10 +416,12 @@ void main() auto const camera_transform = opts.camera->transform(); auto const camera_position = opts.camera->position(); - // Sort objects by mask + // Sort objects by mask & compute bbox std::unordered_map> objects_by_mask; + geom::box lit_bbox; + for (std::size_t i = 0; i < objects.size(); ++i) { auto const & o = objects[i]; @@ -358,6 +430,8 @@ void main() if (o.mat.transparent) throw std::runtime_error("Transparency is not supported yet"); objects_by_mask[mask(objects[i])].push_back(i); + + if (o.mat.lit) lit_bbox |= o.bbox; } // TODO: frustum culling @@ -479,27 +553,28 @@ void main() gl::ActiveTexture(gl::TEXTURE3); impl().g_buffer_texture[3].bind(); - impl().fullscreen_array.bind(); + impl().screen_mesh.bind(); // TODO: directional light shadows // TODO: point light shadows - // TODO: fill only affected areas for lights - // Draw unlit & ambient layers impl().ambient_pass_program.bind(); impl().ambient_pass_program["u_ambient"] = opts.ambient; impl().ambient_pass_program["u_max_intensity"] = opts.max_intensity; gl::DrawArrays(gl::TRIANGLES, 0, 6); - (void)camera_position; gl::Enable(gl::BLEND); gl::BlendFunc(gl::ONE, gl::ONE); // Directional lights + geom::point bbox_hull_screen[8]; + auto lit_bbox_hull_size = bbox_to_screen_fan(camera_transform, lit_bbox, bbox_hull_screen); + impl().screen_mesh.load(bbox_hull_screen, lit_bbox_hull_size, gl::TRIANGLE_FAN); + impl().directional_light_pass_program.bind(); impl().directional_light_pass_program["u_camera_position"] = camera_position; impl().directional_light_pass_program["u_max_intensity"] = opts.max_intensity; @@ -508,21 +583,43 @@ void main() { impl().directional_light_pass_program["u_light_direction"] = geom::normalized(l.direction); impl().directional_light_pass_program["u_light_color"] = l.color; - gl::DrawArrays(gl::TRIANGLES, 0, 6); + impl().screen_mesh.draw(); } // Point lights + float min_intensity = opts.min_intensity.value_or(opts.max_intensity / 256.f); + impl().point_light_pass_program.bind(); impl().point_light_pass_program["u_camera_position"] = camera_position; impl().point_light_pass_program["u_max_intensity"] = opts.max_intensity; for (auto const & l : opts.point_lights) { + float I = std::max({l.color[0], l.color[1], l.color[2]}); + auto r = geom::solve_quadratic(l.attenuation.c2, l.attenuation.c1, l.attenuation.c0 - I / min_intensity); + geom::box light_bbox; + if (r) + { + float light_influence_radius = r->second; + + for (std::size_t i = 0; i < 3; ++i) + light_bbox[i] = {l.position[i] - light_influence_radius, l.position[i] + light_influence_radius}; + + light_bbox &= lit_bbox; + } + else + { + light_bbox = lit_bbox; + } + + auto light_bbox_hull_size = bbox_to_screen_fan(camera_transform, light_bbox, bbox_hull_screen); + impl().screen_mesh.load(bbox_hull_screen, light_bbox_hull_size, gl::TRIANGLE_FAN); + impl().point_light_pass_program["u_light_position"] = l.position; impl().point_light_pass_program["u_light_color"] = l.color; impl().point_light_pass_program["u_light_attenuation"] = geom::vector{l.attenuation.c0, l.attenuation.c1, l.attenuation.c2}; - gl::DrawArrays(gl::TRIANGLES, 0, 6); + impl().screen_mesh.draw(); } check_error();