RENDER: added blur and bloom renderer

master
Martin Gerhardy 2022-01-29 11:17:58 +01:00
parent d146fed6db
commit a1f2676937
15 changed files with 447 additions and 0 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -0,0 +1,49 @@
/**
* @file
*/
#include "BloomRenderer.h"
#include "core/Log.h"
#include "video/Types.h"
namespace render {
bool BloomRenderer::init(bool yFlipped) {
if (!_shader.setup()) {
Log::error("Failed to init the bloom shader");
return false;
}
const glm::ivec2 &fullscreenQuadIndices = _vbo.createFullscreenTexturedQuad(yFlipped);
core_assert_always(_vbo.addAttribute(_shader.getPosAttribute(fullscreenQuadIndices.x, &glm::vec2::x)));
core_assert_always(_vbo.addAttribute(_shader.getTexcoordAttribute(fullscreenQuadIndices.y, &glm::vec2::x)));
return true;
}
void BloomRenderer::shutdown() {
_shader.shutdown();
_vbo.shutdown();
}
void BloomRenderer::render(video::Id color0, video::Id color1, bool bloom) {
video::bindTexture(video::TextureUnit::Zero, video::TextureType::Texture2D, color0);
video::bindTexture(video::TextureUnit::One, video::TextureType::Texture2D, color1);
video::ScopedShader scoped(_shader);
core_assert_always(_shader.setColor0(video::TextureUnit::Zero));
core_assert_always(_shader.setColor1(video::TextureUnit::One));
core_assert_always(_shader.setExposure(1.0f));
core_assert_always(_shader.setBloom(bloom));
video::ScopedBuffer scopedBuf(_vbo);
const int elements = (int)_vbo.elements(0, _shader.getComponentsPos());
core_assert_msg(elements == 6, "Unexpected amount of elements: %i", elements);
video::drawArrays(video::Primitive::Triangles, elements);
video::bindTexture(video::TextureUnit::One, video::TextureType::Texture2D, video::InvalidId);
// ensure that texunit0 is active again
video::bindTexture(video::TextureUnit::Zero, video::TextureType::Texture2D, video::InvalidId);
}
} // namespace render

View File

@ -0,0 +1,40 @@
/**
* @file
*/
#pragma once
#include "video/Buffer.h"
#include "RenderShaders.h"
#include <glm/fwd.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
namespace render {
/**
* @brief Renders two textures with the shader::BloomShader
* @see BlurRenderer
*/
class BloomRenderer {
private:
shader::BloomShader _shader;
video::Buffer _vbo;
public:
/**
* @sa shutdown()
*/
bool init(bool yFlipped);
/**
* @sa init()
*/
void shutdown();
/**
* @param color0 The color texture
* @param color1 The bloom texture to blend
*/
void render(video::Id color0, video::Id color1, bool bloom = true);
};
}

View File

@ -0,0 +1,87 @@
/**
* @file
*/
#include "BlurRenderer.h"
#include "core/Log.h"
#include "video/FrameBufferConfig.h"
#include "video/Renderer.h"
#include "video/ScopedFrameBuffer.h"
#include "video/ScopedViewPort.h"
#include "video/Types.h"
namespace render {
bool BlurRenderer::init(bool yFlipped, int width, int height) {
if (!_shader.setup()) {
Log::error("Failed to init the blur shader");
return false;
}
for (int i = 0; i < lengthof(_frameBuffers); ++i) {
video::FrameBufferConfig cfg;
cfg.dimension(glm::ivec2(width, height));
cfg.addTextureAttachment(video::createDefaultTextureConfig(), video::FrameBufferAttachment::Color0);
if (!_frameBuffers[i].init(cfg)) {
Log::error("Failed to init the blur framebuffer %i", i);
return false;
}
}
const glm::ivec2 &fullscreenQuadIndices = _vbo.createFullscreenTexturedQuad(yFlipped);
core_assert_always(_vbo.addAttribute(_shader.getPosAttribute(fullscreenQuadIndices.x, &glm::vec2::x)));
core_assert_always(_vbo.addAttribute(_shader.getTexcoordAttribute(fullscreenQuadIndices.y, &glm::vec2::x)));
return true;
}
void BlurRenderer::render(video::Id srcTextureId, int amount) {
core_assert(amount > 0);
core_assert(srcTextureId != video::InvalidId);
_horizontal = true;
bool firstIteration = true;
const video::TextureUnit texUnit = video::TextureUnit::Zero;
video::ScopedShader scoped(_shader);
core_assert_always(_shader.setImage(texUnit));
video::ScopedBuffer scopedBuf(_vbo);
const int elements = (int)_vbo.elements(0, _shader.getComponentsPos());
core_assert_msg(elements == 6, "Unexpected amount of elements: %i", elements);
for (int i = 0; i < amount; i++) {
const int index = _horizontal ? 1 : 0;
const int indexFlip = _horizontal ? 0 : 1;
video::ScopedFrameBuffer scoped(_frameBuffers[index]);
const glm::ivec2 &dim = _frameBuffers[index].dimension();
video::ScopedViewPort viewPort(0, 0, dim.x, dim.y);
core_assert_always(_shader.setHorizontal(_horizontal));
const video::TexturePtr& srcTexture = _frameBuffers[indexFlip].texture(video::FrameBufferAttachment::Color0);
video::Id srcTextureHandle = srcTexture->handle();
core_assert(srcTextureHandle != video::InvalidId);
if (firstIteration) {
// the first iteration uses the given textureId - normally from a framebuffer color texture attachment
// were we previously rendered to
srcTextureHandle = srcTextureId;
}
video::bindTexture(texUnit, video::TextureType::Texture2D, srcTextureHandle);
video::drawArrays(video::Primitive::Triangles, elements);
_horizontal = !_horizontal;
firstIteration = false;
}
video::bindTexture(texUnit, video::TextureType::Texture2D, video::InvalidId);
}
video::TexturePtr BlurRenderer::texture() const {
const int indexFlip = _horizontal ? 0 : 1;
return _frameBuffers[indexFlip].texture(video::FrameBufferAttachment::Color0);
}
void BlurRenderer::shutdown() {
for (int i = 0; i < lengthof(_frameBuffers); ++i) {
_frameBuffers[i].shutdown();
}
_shader.shutdown();
_vbo.shutdown();
}
} // namespace render

View File

@ -0,0 +1,48 @@
/**
* @file
*/
#pragma once
#include "RenderShaders.h"
#include "video/Buffer.h"
#include "video/FrameBuffer.h"
#include <glm/fwd.hpp>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
namespace render {
/**
* @brief Renders a textures with the shader::BlurShader
*/
class BlurRenderer {
private:
shader::BlurShader _shader;
video::Buffer _vbo;
video::FrameBuffer _frameBuffers[2];
bool _horizontal = true;
public:
/**
* @sa shutdown()
*/
bool init(bool yFlipped, int width = 512, int height = 512);
/**
* @sa init()
*/
void shutdown();
/**
* @param srcTextureId The video::Id of the original texture that should get blurred
* @note The result is put into a texture that can get queried to continue to use it
* @see texture()
*/
void render(video::Id srcTextureId, int amount = 10);
/**
* @return video::TexturePtr of the render() pass
*/
video::TexturePtr texture() const;
};
} // namespace render

View File

@ -1,5 +1,7 @@
set(SRCS
Axis.cpp Axis.h
BlurRenderer.cpp BlurRenderer.h
BloomRenderer.cpp BloomRenderer.h
CameraFrustum.cpp CameraFrustum.h
Gizmo.cpp Gizmo.h
GridRenderer.cpp GridRenderer.h
@ -12,6 +14,8 @@ set(SRCS
)
set(SHADERS
skybox
blur
bloom
color
color_instanced
constants

View File

@ -0,0 +1,25 @@
layout(location = 0) $out vec4 o_color;
$in vec2 v_texcoord;
uniform sampler2D u_color0;
uniform sampler2D u_color1;
uniform float u_exposure;
uniform bool u_bloom;
#ifndef cl_gamma
#define cl_gamma 2.2
#endif
void main() {
vec4 sceneColor = $texture2D(u_color0, v_texcoord);
vec4 bloomColor = $texture2D(u_color1, v_texcoord);
vec3 color = sceneColor.rgb;
if (u_bloom) {
color += bloomColor.rgb;
}
// apply tone mapping
vec3 result = vec3(1.0) - exp(-color * u_exposure);
result = pow(result, vec3(1.0 / cl_gamma));
o_color = vec4(result, 1.0);
}

View File

@ -0,0 +1,10 @@
// attributes from the VAOs
$in vec2 a_pos;
$in vec2 a_texcoord;
$out vec2 v_texcoord;
void main(void) {
v_texcoord = a_texcoord;
gl_Position = vec4(a_pos.x, a_pos.y, 0.0, 1.0);
}

View File

@ -0,0 +1,25 @@
layout(location = 0) $out vec4 o_color;
$in vec2 v_texcoord;
uniform sampler2D u_image;
uniform bool u_horizontal;
void main() {
vec2 offset = 1.0 / textureSize(u_image, 0);
float u_weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
vec3 result = $texture2D(u_image, v_texcoord).rgb * u_weight[0];
if (u_horizontal) {
for (int i = 1; i < 5; ++i) {
vec2 step_offset = vec2(offset.x * i, 0.0);
result += $texture2D(u_image, v_texcoord + step_offset).rgb * u_weight[i];
result += $texture2D(u_image, v_texcoord - step_offset).rgb * u_weight[i];
}
} else {
for (int i = 1; i < 5; ++i) {
vec2 step_offset = vec2(0.0, offset.y * i);
result += $texture2D(u_image, v_texcoord + step_offset).rgb * u_weight[i];
result += $texture2D(u_image, v_texcoord - step_offset).rgb * u_weight[i];
}
}
o_color = vec4(result, 1.0);
}

View File

@ -0,0 +1,10 @@
// attributes from the VAOs
$in vec2 a_pos;
$in vec2 a_texcoord;
$out vec2 v_texcoord;
void main(void) {
v_texcoord = a_texcoord;
gl_Position = vec4(a_pos.x, a_pos.y, 0.0, 1.0);
}

View File

@ -8,6 +8,7 @@ add_subdirectory(testglslgeom)
add_subdirectory(testimgui)
add_subdirectory(testnuklear)
add_subdirectory(testgpumc)
add_subdirectory(testbloom)
add_subdirectory(testluaui)
add_subdirectory(testcamera)
add_subdirectory(testoctree)

View File

@ -0,0 +1,11 @@
project(testbloom)
set(SRCS
TestBloom.h TestBloom.cpp
)
set(FILES
testbloom/bloom_scene.png
testbloom/bloom_extracted.png
)
engine_add_executable(TARGET ${PROJECT_NAME} SRCS ${SRCS} FILES ${FILES} WINDOWED NOINSTALL)
engine_target_link_libraries(TARGET ${PROJECT_NAME} DEPENDENCIES testcore)

View File

@ -0,0 +1,104 @@
/**
* @file
*/
#include "TestBloom.h"
#include "image/Image.h"
#include "imgui.h"
#include "testcore/TestAppMain.h"
#include "video/PersistentMappingBuffer.h"
#include "video/Texture.h"
TestBloom::TestBloom(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem,
const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider) :
Super(metric, filesystem, eventBus, timeProvider) {
init(ORGANISATION, "testbloom");
setCameraMotion(false);
_allowRelativeMouseMode = false;
}
app::AppState TestBloom::onInit() {
app::AppState state = Super::onInit();
if (state != app::AppState::Running) {
return state;
}
setUICamera();
if (!_blurRenderer.init(false)) {
Log::error("Failed to initialize the blur renderer");
return app::AppState::InitFailure;
}
if (!_bloomRenderer.init(false)) {
Log::error("Failed to initialize the bloom renderer");
return app::AppState::InitFailure;
}
const image::ImagePtr& sceneImg = image::loadImage("bloom_scene", false);
if (!sceneImg->isLoaded()) {
Log::error("Failed to load the image for the scene");
return app::AppState::InitFailure;
}
_sceneTexture = video::createTextureFromImage(sceneImg);
if (!_sceneTexture) {
Log::error("Failed to create texture for the scene");
return app::AppState::InitFailure;
}
const image::ImagePtr& bloomImg = image::loadImage("bloom_extracted", false);
if (!bloomImg->isLoaded()) {
Log::error("Failed to load the image for the bloom");
return app::AppState::InitFailure;
}
_bloomTexture = video::createTextureFromImage(bloomImg);
if (!_bloomTexture) {
Log::error("Failed to create texture for the bloom");
return app::AppState::InitFailure;
}
return state;
}
app::AppState TestBloom::onCleanup() {
_blurRenderer.shutdown();
_bloomRenderer.shutdown();
if (_bloomTexture) {
_bloomTexture->shutdown();
}
if (_sceneTexture) {
_sceneTexture->shutdown();
}
app::AppState state = Super::onCleanup();
return state;
}
void TestBloom::onRenderUI() {
Super::onRenderUI();
static glm::ivec2 size(256, 256);
if (ImGui::InputInt("blur passes: ", &_passes)) {
_passes = glm::clamp(_passes, 1, 10);
}
ImGui::Checkbox("apply bloom: ", &_bloom);
ImGui::Image(_bloomTexture->handle(), size);
ImGui::Text("scene");
ImGui::Image(_sceneTexture->handle(), size);
ImGui::Text("bloom raw");
ImGui::Image(_bloomTexture->handle(), size);
const video::TexturePtr& blurred = _blurRenderer.texture();
ImGui::Text("blurred bloom: %i:%i", blurred->width(), blurred->height());
ImGui::Image(blurred->handle(), size);
}
void TestBloom::doRender() {
_blurRenderer.render(_bloomTexture->handle(), _passes);
_bloomRenderer.render(_sceneTexture->handle(), _blurRenderer.texture()->handle(), _bloom);
}
TEST_APP(TestBloom)

View File

@ -0,0 +1,33 @@
/**
* @file
*/
#pragma once
#include "testcore/TestApp.h"
#include "render/BloomRenderer.h"
#include "render/BlurRenderer.h"
#include "video/Texture.h"
/**
* https://learnopengl.com/Advanced-Lighting/Bloom
*/
class TestBloom: public TestApp {
private:
using Super = TestApp;
render::BloomRenderer _bloomRenderer;
render::BlurRenderer _blurRenderer;
video::TexturePtr _sceneTexture;
video::TexturePtr _bloomTexture;
int _passes = 10;
bool _bloom = true;
void doRender() override;
public:
TestBloom(const metric::MetricPtr& metric, const io::FilesystemPtr& filesystem, const core::EventBusPtr& eventBus, const core::TimeProviderPtr& timeProvider);
virtual app::AppState onInit() override;
virtual app::AppState onCleanup() override;
virtual void onRenderUI() override;
};