Skip to content

Commit

Permalink
Added Post-processor with blur
Browse files Browse the repository at this point in the history
  • Loading branch information
denyskryvytskyi committed May 3, 2024
1 parent c930f5e commit f36901d
Show file tree
Hide file tree
Showing 24 changed files with 434 additions and 58 deletions.
2 changes: 2 additions & 0 deletions Engine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ set(ENGINE_HEADERS
"src/Renderer/Material.h"
"src/Renderer/Mesh.h"
"src/Renderer/RenderTopology.h"
"src/Renderer/PostProcessor.h"
"src/Platform/Windows/WindowsWindow.h"
"src/Platform/OpenGL/OpenGLBuffer.h"
"src/Platform/OpenGL/OpenGLContext.h"
Expand Down Expand Up @@ -112,6 +113,7 @@ set(ENGINE_SOURCES
"src/Renderer/TextRenderer.cpp"
"src/Renderer/Mesh.cpp"
"src/Renderer/Material.cpp"
"src/Renderer/PostProcessor.cpp"
"src/Platform/OpenGL/OpenGLBuffer.cpp"
"src/Platform/OpenGL/OpenGLContext.cpp"
"src/Platform/OpenGL/OpenGLRendererAPI.cpp"
Expand Down
52 changes: 52 additions & 0 deletions Engine/assets/shaders/post_processing.frag
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
#version 450 core

layout (binding = 0) uniform sampler2D u_ScreenTexture;
layout (binding = 1) uniform sampler2D u_Mask;

in vec2 v_UV;
in vec2 v_Pos;

out vec4 FragColor;

void main()
{
vec3 finalColor = vec3(0.0);
vec3 originalColor = texture(u_ScreenTexture, v_UV).rgb;

float maskValue = texture(u_Mask, v_UV).r;

if (maskValue > 0.0)
{
float offset = 1.0 / 150.0;

vec2 box[9] = vec2[](
vec2(-offset, offset), vec2(0.0, offset), vec2(offset, offset),
vec2(-offset, 0.0), vec2(0.0f, 0.0), vec2(offset, 0.0),
vec2(-offset, -offset), vec2(0.0, -offset), vec2(offset, -offset)
);

float kernel[9] = float[](
0.0625, 0.125, 0.0625,
0.125, 0.25, 0.125,
0.0625, 0.125, 0.0625
);

vec3 sampleTex[9];
for(int i = 0; i < 9; i++)
{
sampleTex[i] = vec3(texture(u_ScreenTexture, v_UV + box[i]));
}

vec3 blurredColor = vec3(0.0);
for(int i = 0; i < 9; i++)
blurredColor += sampleTex[i] * kernel[i];

finalColor = mix(originalColor, blurredColor, maskValue);
}
else
{
finalColor = originalColor;
}

FragColor = vec4(finalColor, 1.0);
}
13 changes: 13 additions & 0 deletions Engine/assets/shaders/post_processing.vert
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#version 450 core
layout (location = 0) in vec2 a_Pos;
layout (location = 1) in vec2 a_UV;

out vec2 v_UV;
out vec2 v_Pos;

void main()
{
v_UV = a_UV;
v_Pos = a_Pos;
gl_Position = vec4(a_Pos, 0.0, 1.0);
}
2 changes: 1 addition & 1 deletion Engine/assets/shaders/quad.frag
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#version 450 core

uniform sampler2D u_screenTexture;
layout (binding = 0) uniform sampler2D u_screenTexture;

in vec2 v_uv;

Expand Down
2 changes: 1 addition & 1 deletion Engine/src/Core/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ Application::Application()
if (gEngineSettings.enableFpsCounter) {
m_fpsCounterEntityId = scene.CreateEntity();
scene.AddComponent<TagComponent>(m_fpsCounterEntityId, "FPS text");
scene.AddComponent<RectTransformComponent>(m_fpsCounterEntityId, lia::vec2(0.5f, 97.0f), lia::vec2(0.4f, 0.4f));
scene.AddComponent<UITransformComponent>(m_fpsCounterEntityId, lia::vec2(0.5f, 97.0f), lia::vec2(0.4f, 0.4f));
scene.AddComponent<TextComponent>(m_fpsCounterEntityId, "0");
}
}
Expand Down
58 changes: 58 additions & 0 deletions Engine/src/Editor/Editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@
#include <imgui.h>

namespace elv::editor {
constexpr std::uint16_t kMaxMaskthreshold = 128;
constexpr std::uint16_t kMaskPreviewWidth = 128;
constexpr std::uint16_t kMaskPreviewHeight = 96;

void DrawPostProcessSettings(Renderer& renderer);

void Editor::OnInit()
{
if (gEngineSettings.enableSceneGraph) {
Expand Down Expand Up @@ -54,6 +60,9 @@ void Editor::OnImGuiRender()
if (ImGui::Checkbox("Blinn-Phong", &isBlinnPhongEnabled)) {
renderer.EnableBlinnPhong(isBlinnPhongEnabled);
}

DrawPostProcessSettings(renderer);

ImGui::End();

// ============ Environment ============
Expand All @@ -71,4 +80,53 @@ void Editor::OnImGuiRender()

m_profileTelemetry.OnImGuiRender();
}

void DrawPostProcessSettings(Renderer& renderer)
{
const ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_DefaultOpen
| ImGuiTreeNodeFlags_Framed
| ImGuiTreeNodeFlags_SpanAvailWidth
| ImGuiTreeNodeFlags_AllowItemOverlap
| ImGuiTreeNodeFlags_FramePadding;

const bool opened = ImGui::TreeNodeEx("Post processing", flags);

if (opened) {
auto& postProcessor = renderer.GetPostProcessor();

ImGui::Checkbox("Blur enabled", &postProcessor.IsBlurEnabled);
DrawSliderInt("Threshold by width", 0, kMaxMaskthreshold, postProcessor.BlurMaskThreshold);

const char* items[] = { "Linear", "Checkerboard", "Triangular", "Radial", "Exponential" };
static int currentIndex = 0;
const char* itemStr = items[currentIndex];
if (ImGui::BeginCombo("Type", itemStr)) {
for (int i = 0; i < IM_ARRAYSIZE(items); ++i) {
const bool is_selected = (currentIndex == i);
if (ImGui::Selectable(items[i], is_selected)) {
currentIndex = i;
itemStr = items[currentIndex];
postProcessor.GradientMaskType = static_cast<PostProcessor::BlurGradientMaskType>(currentIndex);
}

if (is_selected)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}

if (ImGui::Button("Rebuild mask")) {
postProcessor.BuildBlurMask(postProcessor.GradientMaskType);
}

ImGui::Text("Mask preview:");
ImVec2 uv_min = ImVec2(0.0f, 1.0f);
ImVec2 uv_max = ImVec2(1.0f, 0.0f);
ImVec4 tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
ImVec4 border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f);

ImGui::Image((void*)(intptr_t)postProcessor.GetMaskTextureId(), ImVec2(kMaskPreviewWidth, kMaskPreviewHeight), uv_min, uv_max, tint_col, border_col);
ImGui::TreePop();
}
}
} // namespace elv::editor
5 changes: 5 additions & 0 deletions Engine/src/Editor/EditorHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ bool DrawSliderFloat(const std::string& label, const float min, const float max,
return ImGui::SliderFloat(label.c_str(), &property, min, max, "value = %.3f");
}

bool DrawSliderInt(const std::string& label, const int min, const int max, int& property)
{
return ImGui::SliderInt(label.c_str(), &property, min, max, "value = %d");
}

bool DrawRGBColorControl(const std::string& label, lia::vec3& colorProperty)
{
return ImGui::ColorEdit3(label.c_str(), colorProperty.elementsPtr());
Expand Down
1 change: 1 addition & 0 deletions Engine/src/Editor/EditorHelpers.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ bool DrawVec2Control(const std::string& id, const std::string& label, lia::vec2&
bool DrawVec3Control(const std::string& id, const std::string& label, lia::vec3& properties);

bool DrawSliderFloat(const std::string& label, const float min, const float max, float& property);
bool DrawSliderInt(const std::string& label, const int min, const int max, int& property);

bool DrawRGBColorControl(const std::string& label, lia::vec3& colorProperty);
bool DrawRGBAColorControl(const std::string& label, lia::vec4& colorProperty);
Expand Down
4 changes: 2 additions & 2 deletions Engine/src/Editor/Panels/SceneHierarchyPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ void SceneHierarchyPanel::DrawProperties()
DisplayAddComponentEntry<PointLightComponent>("Point Light");
DisplayAddComponentEntry<SpotLightComponent>("Spotlight");
DisplayAddComponentEntry<TextComponent>("Text");
DisplayAddComponentEntry<RectTransformComponent>("UI Transform");
DisplayAddComponentEntry<UITransformComponent>("UI Transform");
DisplayAddComponentEntry<SpriteComponent>("Sprite");
DisplayAddComponentEntry<SoundComponent>("Sound");

Expand Down Expand Up @@ -447,7 +447,7 @@ void SceneHierarchyPanel::DrawProperties()
editor::DrawRGBAColorControl("color##text", component.color);
});

DrawComponent<RectTransformComponent>("UI Transform", m_selectedEntity, m_context, [](RectTransformComponent& component) {
DrawComponent<UITransformComponent>("UI Transform", m_selectedEntity, m_context, [](UITransformComponent& component) {
editor::DrawVec2Control("Position", "pos", component.pos);
editor::DrawVec2Control("Scale", "scale", component.scale);
});
Expand Down
5 changes: 5 additions & 0 deletions Engine/src/Platform/OpenGL/OpenGLRendererAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ void OpenGLRendererAPI::DisableByteAlignment()
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
}

void OpenGLRendererAPI::BindDefaultFramebuffer()
{
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void OpenGLRendererAPI::DrawIndexed(const SharedPtr<VertexArray>& vertexArray, const std::uint32_t indexCount, const RenderTopology topology)
{
const std::uint32_t count = indexCount ? indexCount : vertexArray->GetIndexCount();
Expand Down
2 changes: 2 additions & 0 deletions Engine/src/Platform/OpenGL/OpenGLRendererAPI.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class OpenGLRendererAPI : public RendererAPI {

void DisableByteAlignment() override;

void BindDefaultFramebuffer() override;

void DrawIndexed(const SharedPtr<VertexArray>& vertexArray, const std::uint32_t indexCount = 0, const RenderTopology topology = RenderTopology::Triangles) override;
};

Expand Down
150 changes: 150 additions & 0 deletions Engine/src/Renderer/PostProcessor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
#include "PostProcessor.h"

#include "RHI/Shader.h"
#include "RHI/Texture.h"
#include "Renderer.h"

#include "Resources/TextureManager.h"

namespace elv {

constexpr std::uint16_t kDefaultMaskThreshold = 64;
constexpr std::uint16_t kMaskWidth = 128;
constexpr std::uint16_t kMaskHeight = 96;
constexpr std::uint16_t kMaskDataSize = kMaskWidth * kMaskHeight;

constexpr float kMaxIntensity = 255.0f;
constexpr std::uint16_t kCheckboardSquareSize = 8;
constexpr std::uint16_t kExpGradientRate = 3;

PostProcessor::PostProcessor()
: m_renderTargetPostProcessor(RenderTarget::Create())
{
}

void PostProcessor::Init(const std::uint32_t width, const std::uint32_t height)
{
m_blurShader = ShaderManager::Load("post_processing", "post_processing.vert", "post_processing.frag");
m_renderTargetPostProcessor->Init(width, height);

BlurMaskThreshold = kDefaultMaskThreshold;
BuildBlurMask(BlurGradientMaskType::Linear);
}

void PostProcessor::BuildBlurMask(const BlurGradientMaskType type)
{
GradientMaskType = type;

std::uint8_t* data = new std::uint8_t[kMaskDataSize];

switch (type) {
case BlurGradientMaskType::Linear:
CreateLinearGradientMask(data);
break;
case BlurGradientMaskType::Checkboard:
CreateCheckerboarGradientMask(data);
break;
case BlurGradientMaskType::Triangular:
CreateTriangularGradientMask(data);
break;
case BlurGradientMaskType::Radial:
CreateRadialGradientMask(data);
break;
case BlurGradientMaskType::Exponential:
CreateExponentialGradientMask(data);
break;
}

m_blurMaskTexture = textures::Load("blur_mask", kMaskWidth, kMaskHeight, 1);
m_blurMaskTexture->SetData(data, false);

delete[] data;
}

std::shared_ptr<Texture> PostProcessor::Process(Renderer* renderer, const std::shared_ptr<Texture>& src)
{
if (IsBlurEnabled) {
m_renderTargetPostProcessor->Bind();
renderer->EnableDepthTesting(false);
renderer->ClearBufferBit(Renderer::BufferBitType::Color);

m_blurShader->Bind();

src->BindToSlot(0);
m_blurMaskTexture->BindToSlot(1);

renderer->RenderNDCQuad();

return m_renderTargetPostProcessor->GetColorTextureAttachment();
}

return src;
}

void PostProcessor::OnWindowResized(const std::uint32_t width, const std::uint32_t height)
{
m_renderTargetPostProcessor->Resize(width, height);
}

void PostProcessor::CreateLinearGradientMask(unsigned char* data)
{
for (int y = 0; y < kMaskHeight; ++y) {
memset(data + y * kMaskWidth, 0, BlurMaskThreshold);
memset(data + y * kMaskWidth + BlurMaskThreshold, static_cast<std::uint8_t>(kMaxIntensity), kMaskWidth - BlurMaskThreshold);
}
}

void PostProcessor::CreateCheckerboarGradientMask(unsigned char* data)
{
for (int y = 0; y < kMaskHeight; ++y) {
memset(data + y * kMaskWidth, 0, BlurMaskThreshold);
for (int x = BlurMaskThreshold; x < kMaskWidth; ++x) {
data[y * kMaskWidth + x] = ((x / kCheckboardSquareSize) + (y / kCheckboardSquareSize)) % 2 == 0 ? 0 : static_cast<std::uint8_t>(kMaxIntensity);
}
}
}

void PostProcessor::CreateTriangularGradientMask(unsigned char* data)
{
for (int y = 0; y < kMaskHeight; ++y) {
memset(data + y * kMaskWidth, 0, BlurMaskThreshold);
for (int x = BlurMaskThreshold; x < kMaskWidth; ++x) {
const float intensity = (x + y) / static_cast<float>(kMaskWidth + kMaskHeight) * kMaxIntensity;
data[y * kMaskWidth + x] = static_cast<std::uint8_t>(intensity);
}
}
}

void PostProcessor::CreateRadialGradientMask(unsigned char* data)
{
const int centerX = (kMaskWidth - BlurMaskThreshold) / 2;
const int centerY = kMaskHeight / 2;
const float maxDistance = sqrt(static_cast<float>((centerX) * (centerX) + (centerY) * (centerY)));

for (int y = 0; y < kMaskHeight; ++y) {
memset(data + y * kMaskWidth, 0, BlurMaskThreshold);
for (int x = BlurMaskThreshold, i = 0; x < kMaskWidth; ++x, ++i) {
const float distance = sqrt(static_cast<float>((i - centerX) * (i - centerX) + (y - centerY) * (y - centerY)));
const float intensity = distance / maxDistance * kMaxIntensity;

data[y * kMaskWidth + x] = static_cast<std::uint8_t>(intensity);
}
}
}

void PostProcessor::CreateExponentialGradientMask(unsigned char* data)
{
for (int y = 0; y < kMaskHeight; ++y) {
memset(data + y * kMaskWidth, 0, BlurMaskThreshold);
for (int x = BlurMaskThreshold, i = 0; x < kMaskWidth; ++x, ++i) {
const float intensity = expf(kExpGradientRate * i / static_cast<float>(kMaskWidth - BlurMaskThreshold)) * kMaxIntensity;
data[y * kMaskWidth + x] = static_cast<std::uint8_t>(intensity);
}
}
}

int PostProcessor::GetMaskTextureId() const
{
return m_blurMaskTexture->GetId();
}
} // namespace elv
Loading

0 comments on commit f36901d

Please sign in to comment.