Implement blooming in deferred renderer

This commit is contained in:
Nikita Lisitsa 2020-12-09 23:21:15 +03:00
parent 15fcfdd053
commit 1620eef29d
2 changed files with 217 additions and 3 deletions

View file

@ -35,6 +35,7 @@ namespace psemek::gfx
texture_2d const * texture = nullptr;
bool transparent = false;
bool lit = true;
bool blooming = false;
float diffuse = 1.f;
struct
@ -98,6 +99,15 @@ namespace psemek::gfx
float max_intensity;
// Equals max_intensity / 256 by default
std::optional<float> min_intensity;
struct bloom_data
{
int size = 5;
float sigma = 3.f;
std::size_t downsample = 2;
};
std::optional<bloom_data> bloom;
};
void render(std::vector<object> const & objects, render_target const & target, options const & opts);

View file

@ -5,6 +5,8 @@
#include <psemek/gfx/texture.hpp>
#include <psemek/gfx/mesh.hpp>
#include <psemek/gfx/error.hpp>
#include <psemek/gfx/effect/blur.hpp>
#include <psemek/gfx/effect/overlay.hpp>
#include <psemek/geom/homogeneous.hpp>
#include <psemek/geom/gram_schmidt.hpp>
@ -13,7 +15,7 @@
#include <psemek/cg/convex_hull_2d/graham.hpp>
#include <fstream>
#include <map>
namespace psemek::gfx
{
@ -29,6 +31,7 @@ const uint O_CASTS_SHADOW = 1u << 4;
const uint O_PRE_TRANSFORM = 1u << 5;
const uint O_POST_TRANSFORM = 1u << 6;
const uint O_INSTANCED = 1u << 7;
const uint O_BLOOMING = 1u << 8;
uniform uint u_flag_mask;
)";
@ -641,6 +644,7 @@ void main()
gfx::program shadow_builder_program{std::string(g_buffer_pass_common) + shadow_builder_vs, shadow_builder_fs};
gfx::program cubemap_shadow_builder_program{std::string(g_buffer_pass_common) + shadow_builder_vs, shadow_builder_gs, shadow_builder_fs};
gfx::program transparent_pass_program{std::string(g_buffer_pass_common) + transparent_pass_vs, std::string(g_buffer_pass_common) + transparent_pass_fs};
gfx::program bloom_pass_program{std::string(g_buffer_pass_common) + transparent_pass_vs, std::string(g_buffer_pass_common) + transparent_pass_fs};
// G-buffer attachments:
// 0 - position (rbg)
@ -663,6 +667,19 @@ void main()
gfx::framebuffer point_shadow_framebuffer;
gfx::texture_cubemap point_shadow_texture;
std::map<std::pair<int, float>, hblur> hblur_container;
std::map<std::pair<int, float>, vblur> vblur_container;
overlay bloom_overlay;
std::optional<geom::vector<std::size_t, 2>> bloom_size;
std::optional<std::size_t> bloom_downsample;
// 0: original, unblurred bloom
// 1: horizontally blurred bloom
// 2: fully blurred bloom
gfx::framebuffer bloom_framebuffer[3];
gfx::texture_2d bloom_texture[3];
gfx::mesh screen_mesh;
};
@ -718,6 +735,13 @@ void main()
impl().point_shadow_framebuffer.depth(impl().point_shadow_texture);
impl().point_shadow_framebuffer.assert_complete();
impl().bloom_texture[0].linear_filter();
impl().bloom_texture[1].linear_filter();
impl().bloom_texture[2].linear_filter();
impl().bloom_texture[0].clamp();
impl().bloom_texture[1].clamp();
impl().bloom_texture[2].clamp();
impl().screen_mesh.setup<geom::point<float, 2>>();
}
@ -737,6 +761,7 @@ void main()
static std::uint32_t const O_PRE_TRANSFORM = 1 << 5;
static std::uint32_t const O_POST_TRANSFORM = 1 << 6;
static std::uint32_t const O_INSTANCED = 1 << 7;
static std::uint32_t const O_BLOOMING = 1 << 8;
std::uint32_t mask(deferred_renderer::object const & o)
{
@ -749,6 +774,7 @@ void main()
if (o.pre_transform) m |= O_PRE_TRANSFORM;
if (o.post_transform) m |= O_POST_TRANSFORM;
if (o.mesh->is_instanced()) m |= O_INSTANCED;
if (o.mat.blooming) m |= O_BLOOMING;
return m;
}
@ -777,6 +803,9 @@ void main()
if (o.mat.lit && o.mat.transparent)
throw std::runtime_error("Materials that are both tit & transparent are not supported");
if (o.mat.lit && o.mat.blooming)
throw std::runtime_error("Materials that are both tit & blooming are not supported");
if (o.casts_shadow && o.mat.transparent)
throw std::runtime_error("Transparent objects cannot cast shadow");
@ -788,7 +817,7 @@ void main()
// Resize g-buffer if needed
auto buffer_size = geom::cast<std::size_t>(target.viewport.dimensions());
auto const buffer_size = geom::cast<std::size_t>(target.viewport.dimensions());
bool const buffer_size_changed = !impl().g_buffer_size || *impl().g_buffer_size != buffer_size;
if (buffer_size_changed || impl().position_mode_changed)
{
@ -824,6 +853,46 @@ void main()
impl().position_mode_changed = false;
}
bool const bloom_size_changed = opts.bloom && (!impl().bloom_size || *impl().bloom_size != buffer_size || !impl().bloom_downsample || *impl().bloom_downsample != opts.bloom->downsample);
if (bloom_size_changed)
{
impl().bloom_texture[0].load<gfx::color_rgba>(buffer_size);
impl().bloom_framebuffer[0].color(impl().bloom_texture[0]);
impl().bloom_framebuffer[0].depth(impl().g_buffer_depth);
impl().bloom_framebuffer[0].assert_complete();
impl().bloom_texture[1].load<gfx::color_rgba>({buffer_size[0] / opts.bloom->downsample, buffer_size[1]});
impl().bloom_framebuffer[1].color(impl().bloom_texture[1]);
impl().bloom_framebuffer[1].assert_complete();
impl().bloom_texture[2].load<gfx::color_rgba>(buffer_size / opts.bloom->downsample);
impl().bloom_framebuffer[2].color(impl().bloom_texture[2]);
impl().bloom_framebuffer[2].assert_complete();
impl().bloom_size = buffer_size;
impl().bloom_downsample = opts.bloom->downsample;
}
std::optional<std::pair<std::size_t, float>> hblur_params;
std::optional<std::pair<std::size_t, float>> vblur_params;
if (opts.bloom)
{
hblur_params = std::pair{opts.bloom->size, opts.bloom->sigma};
if (impl().hblur_container.count(*hblur_params) == 0)
{
auto params = std::tuple{hblur_params->first, hblur_params->second};
impl().hblur_container.emplace(std::piecewise_construct, params, params);
}
vblur_params = std::pair{opts.bloom->size, opts.bloom->sigma};
if (impl().vblur_container.count(*vblur_params) == 0)
{
auto params = std::tuple{vblur_params->first, vblur_params->second};
impl().vblur_container.emplace(std::piecewise_construct, params, params);
}
}
// Setup g-buffer
impl().g_framebuffer.bind();
@ -897,10 +966,12 @@ void main()
// Render unlit transparent objects
gl::DrawBuffer(gl::COLOR_ATTACHMENT0);
impl().transparent_framebuffer.bind();
impl().transparent_pass_program.bind();
impl().transparent_pass_program["u_camera_transform"] = camera_transform;
impl().transparent_pass_program["u_max_intensity"] = opts.max_intensity;
impl().transparent_pass_program["u_max_intensity"] = 1.f;
gl::Enable(gl::BLEND);
gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::ZERO, gl::ONE);
@ -941,7 +1012,133 @@ void main()
}
}
// Render bloom
impl().bloom_framebuffer[0].bind();
gl::ClearColor(0.f, 0.f, 0.f, 0.f);
gl::Clear(gl::COLOR_BUFFER_BIT);
gl::Enable(gl::DEPTH_TEST);
gl::DepthFunc(gl::LEQUAL);
gl::Disable(gl::BLEND);
impl().bloom_pass_program.bind();
impl().bloom_pass_program["u_camera_transform"] = camera_transform;
impl().bloom_pass_program["u_max_intensity"] = opts.max_intensity;
for (auto const & p : objects_by_mask)
{
std::uint32_t mask = p.first;
if (!(mask & O_BLOOMING))
continue;
if (p.second.empty()) continue;
impl().bloom_pass_program["u_flag_mask"] = mask;
for (std::size_t i : p.second)
{
auto const & o = objects[i];
if (mask & O_UNIFORM_COLOR)
impl().bloom_pass_program["u_color"] = *o.mat.color;
if (mask & O_TEXTURE_COLOR)
{
gl::ActiveTexture(gl::TEXTURE0);
o.mat.texture->bind();
}
if (mask & O_PRE_TRANSFORM)
impl().bloom_pass_program["u_pre_transform"] = *o.pre_transform;
if (mask & O_POST_TRANSFORM)
impl().bloom_pass_program["u_post_transform"] = *o.post_transform;
impl().bloom_pass_program["u_material"] = geom::vector<float, 3>{o.mat.diffuse, o.mat.specular.intensity, o.mat.specular.shininess};
o.mesh->draw();
}
}
// Render unlit transparent objects to bloom
impl().transparent_pass_program.bind();
impl().transparent_pass_program["u_camera_transform"] = camera_transform;
impl().transparent_pass_program["u_max_intensity"] = 1.f;
gl::Enable(gl::BLEND);
gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::ZERO, gl::ONE);
for (auto const & p : objects_by_mask)
{
std::uint32_t mask = p.first;
if (!(mask & O_TRANSPARENT))
continue;
if (p.second.empty()) continue;
impl().transparent_pass_program["u_flag_mask"] = mask;
for (std::size_t i : p.second)
{
auto const & o = objects[i];
if (mask & O_UNIFORM_COLOR)
impl().transparent_pass_program["u_color"] = *o.mat.color;
if (mask & O_TEXTURE_COLOR)
{
gl::ActiveTexture(gl::TEXTURE0);
o.mat.texture->bind();
}
if (mask & O_PRE_TRANSFORM)
impl().transparent_pass_program["u_pre_transform"] = *o.pre_transform;
if (mask & O_POST_TRANSFORM)
impl().transparent_pass_program["u_post_transform"] = *o.post_transform;
o.mesh->draw();
}
}
gl::DepthMask(gl::TRUE);
gl::Disable(gl::BLEND);
// Apply horizontal blur to bloom
{
render_target target;
target.framebuffer = &impl().bloom_framebuffer[1];
target.draw_buffer = gl::COLOR_ATTACHMENT0;
target.viewport = {{{0, impl().bloom_texture[1].width()}, {0, impl().bloom_texture[1].height()}}};
target.bind();
gl::ClearColor(0.f, 0.f, 0.f, 0.f);
gl::Clear(gl::COLOR_BUFFER_BIT);
impl().hblur_container.at(*hblur_params).invoke(impl().bloom_texture[0], target);
}
// Apply vertical blur to bloom
{
render_target target;
target.framebuffer = &impl().bloom_framebuffer[2];
target.draw_buffer = gl::COLOR_ATTACHMENT0;
target.viewport = {{{0, impl().bloom_texture[2].width()}, {0, impl().bloom_texture[2].height()}}};
target.bind();
gl::ClearColor(0.f, 0.f, 0.f, 0.f);
gl::Clear(gl::COLOR_BUFFER_BIT);
impl().vblur_container.at(*vblur_params).invoke(impl().bloom_texture[1], target);
}
// Setup destination framebuffer
@ -1275,6 +1472,13 @@ void main()
impl().screen_mesh.draw();
}
// Overlay bloom
gl::Enable(gl::BLEND);
gl::BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
impl().bloom_overlay.invoke(impl().bloom_texture[2], target);
check_error();
}