Implement blooming in deferred renderer
This commit is contained in:
parent
15fcfdd053
commit
1620eef29d
2 changed files with 217 additions and 3 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue