Reduce deferred renderer fill rate by using light influence bboxes

This commit is contained in:
Nikita Lisitsa 2020-10-24 19:40:58 +03:00
parent 131f06b012
commit 68d70e3d96
2 changed files with 109 additions and 12 deletions

View file

@ -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

View file

@ -3,9 +3,12 @@
#include <psemek/gfx/program.hpp>
#include <psemek/gfx/framebuffer.hpp>
#include <psemek/gfx/texture.hpp>
#include <psemek/gfx/mesh.hpp>
#include <psemek/gfx/error.hpp>
#include <psemek/util/unused.hpp>
#include <psemek/geom/homogeneous.hpp>
#include <psemek/cg/convex_hull_2d/graham.hpp>
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<float, 4, 4> const & camera_transform, geom::box<float, 3> const & b, geom::point<float, 2> * result)
{
geom::point<float, 2> 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<float, 3> p;
p[0] = geom::lerp<float>(b[0], x);
p[1] = geom::lerp<float>(b[1], y);
p[2] = geom::lerp<float>(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<float, 2> * 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<geom::vector<std::size_t, 2>> 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<geom::point<float, 2>>();
}
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<std::uint32_t, std::vector<std::size_t>> objects_by_mask;
geom::box<float, 3> 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<float, 2> 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<float, 3> 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();