RENDER: added blur and bloom renderer
parent
d146fed6db
commit
a1f2676937
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
Binary file not shown.
After Width: | Height: | Size: 110 KiB |
|
@ -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
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
|
@ -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;
|
||||
};
|
Loading…
Reference in New Issue