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;
|
texture_2d const * texture = nullptr;
|
||||||
bool transparent = false;
|
bool transparent = false;
|
||||||
bool lit = true;
|
bool lit = true;
|
||||||
|
bool blooming = false;
|
||||||
|
|
||||||
float diffuse = 1.f;
|
float diffuse = 1.f;
|
||||||
struct
|
struct
|
||||||
|
|
@ -98,6 +99,15 @@ namespace psemek::gfx
|
||||||
float max_intensity;
|
float max_intensity;
|
||||||
// Equals max_intensity / 256 by default
|
// Equals max_intensity / 256 by default
|
||||||
std::optional<float> min_intensity;
|
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);
|
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/texture.hpp>
|
||||||
#include <psemek/gfx/mesh.hpp>
|
#include <psemek/gfx/mesh.hpp>
|
||||||
#include <psemek/gfx/error.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/homogeneous.hpp>
|
||||||
#include <psemek/geom/gram_schmidt.hpp>
|
#include <psemek/geom/gram_schmidt.hpp>
|
||||||
|
|
@ -13,7 +15,7 @@
|
||||||
|
|
||||||
#include <psemek/cg/convex_hull_2d/graham.hpp>
|
#include <psemek/cg/convex_hull_2d/graham.hpp>
|
||||||
|
|
||||||
#include <fstream>
|
#include <map>
|
||||||
|
|
||||||
namespace psemek::gfx
|
namespace psemek::gfx
|
||||||
{
|
{
|
||||||
|
|
@ -29,6 +31,7 @@ const uint O_CASTS_SHADOW = 1u << 4;
|
||||||
const uint O_PRE_TRANSFORM = 1u << 5;
|
const uint O_PRE_TRANSFORM = 1u << 5;
|
||||||
const uint O_POST_TRANSFORM = 1u << 6;
|
const uint O_POST_TRANSFORM = 1u << 6;
|
||||||
const uint O_INSTANCED = 1u << 7;
|
const uint O_INSTANCED = 1u << 7;
|
||||||
|
const uint O_BLOOMING = 1u << 8;
|
||||||
|
|
||||||
uniform uint u_flag_mask;
|
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 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 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 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:
|
// G-buffer attachments:
|
||||||
// 0 - position (rbg)
|
// 0 - position (rbg)
|
||||||
|
|
@ -663,6 +667,19 @@ void main()
|
||||||
gfx::framebuffer point_shadow_framebuffer;
|
gfx::framebuffer point_shadow_framebuffer;
|
||||||
gfx::texture_cubemap point_shadow_texture;
|
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;
|
gfx::mesh screen_mesh;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -718,6 +735,13 @@ void main()
|
||||||
impl().point_shadow_framebuffer.depth(impl().point_shadow_texture);
|
impl().point_shadow_framebuffer.depth(impl().point_shadow_texture);
|
||||||
impl().point_shadow_framebuffer.assert_complete();
|
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>>();
|
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_PRE_TRANSFORM = 1 << 5;
|
||||||
static std::uint32_t const O_POST_TRANSFORM = 1 << 6;
|
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_INSTANCED = 1 << 7;
|
||||||
|
static std::uint32_t const O_BLOOMING = 1 << 8;
|
||||||
|
|
||||||
std::uint32_t mask(deferred_renderer::object const & o)
|
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.pre_transform) m |= O_PRE_TRANSFORM;
|
||||||
if (o.post_transform) m |= O_POST_TRANSFORM;
|
if (o.post_transform) m |= O_POST_TRANSFORM;
|
||||||
if (o.mesh->is_instanced()) m |= O_INSTANCED;
|
if (o.mesh->is_instanced()) m |= O_INSTANCED;
|
||||||
|
if (o.mat.blooming) m |= O_BLOOMING;
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -777,6 +803,9 @@ void main()
|
||||||
if (o.mat.lit && o.mat.transparent)
|
if (o.mat.lit && o.mat.transparent)
|
||||||
throw std::runtime_error("Materials that are both tit & transparent are not supported");
|
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)
|
if (o.casts_shadow && o.mat.transparent)
|
||||||
throw std::runtime_error("Transparent objects cannot cast shadow");
|
throw std::runtime_error("Transparent objects cannot cast shadow");
|
||||||
|
|
||||||
|
|
@ -788,7 +817,7 @@ void main()
|
||||||
|
|
||||||
// Resize g-buffer if needed
|
// 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;
|
bool const buffer_size_changed = !impl().g_buffer_size || *impl().g_buffer_size != buffer_size;
|
||||||
if (buffer_size_changed || impl().position_mode_changed)
|
if (buffer_size_changed || impl().position_mode_changed)
|
||||||
{
|
{
|
||||||
|
|
@ -824,6 +853,46 @@ void main()
|
||||||
impl().position_mode_changed = false;
|
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
|
// Setup g-buffer
|
||||||
|
|
||||||
impl().g_framebuffer.bind();
|
impl().g_framebuffer.bind();
|
||||||
|
|
@ -897,10 +966,12 @@ void main()
|
||||||
|
|
||||||
// Render unlit transparent objects
|
// Render unlit transparent objects
|
||||||
|
|
||||||
|
gl::DrawBuffer(gl::COLOR_ATTACHMENT0);
|
||||||
|
|
||||||
impl().transparent_framebuffer.bind();
|
impl().transparent_framebuffer.bind();
|
||||||
impl().transparent_pass_program.bind();
|
impl().transparent_pass_program.bind();
|
||||||
impl().transparent_pass_program["u_camera_transform"] = camera_transform;
|
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::Enable(gl::BLEND);
|
||||||
gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA, gl::ZERO, gl::ONE);
|
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::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
|
// Setup destination framebuffer
|
||||||
|
|
||||||
|
|
@ -1275,6 +1472,13 @@ void main()
|
||||||
impl().screen_mesh.draw();
|
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();
|
check_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue