Reduce deferred renderer fill rate by using light influence bboxes
This commit is contained in:
parent
131f06b012
commit
68d70e3d96
2 changed files with 109 additions and 12 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue