post processing

main
Brett 2024-05-03 22:14:48 -04:00
parent 622b706618
commit ecae5287cc
11 changed files with 342 additions and 127 deletions

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.25)
set(BLT_GRAPHICS_VERSION 0.13.2)
set(BLT_GRAPHICS_VERSION 0.13.3)
set(BLT_GRAPHICS_TEST_VERSION 0.0.1)
project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION})

View File

@ -123,6 +123,8 @@ namespace blt::gfx
static void unbind();
static void bindScreen(draw_t type);
void destroy();
[[nodiscard]] i32 getHeight() const

View File

@ -23,6 +23,7 @@
#include "blt/gfx/shader.h"
#include "blt/gfx/framebuffer.h"
#include "blt/gfx/renderer/resource_manager.h"
#include "blt/gfx/renderer/postprocess.h"
#include <blt/std/hashmap.h>
#include <blt/std/memory_util.h>
#include <blt/math/vectors.h>
@ -148,7 +149,7 @@ namespace blt::gfx
class batch_renderer_2d
{
private:
frame_buffer_t draw_buffer;
pp_engine_t engine;
template<typename T>
struct render_object_t

View File

@ -28,56 +28,74 @@
namespace blt::gfx
{
namespace detail
{
class pp_link;
class pp_iterator;
class pp_link
{
public:
private:
};
class pp_iterator
{
public:
private:
};
}
class pp_engine_t;
class pp_step
class pp_step_t;
class pp_step_t
{
public:
virtual void create(i32 width, i32 height) = 0;
virtual void create() = 0;
virtual void draw(i32 width, i32 height) = 0;
virtual void draw() = 0;
frame_buffer_t& getBuffer()
{
return draw_buffer;
}
shader_t& getShader()
{
return *shader;
}
virtual ~pp_step_t() = default;
protected:
frame_buffer_t draw_buffer;
std::unique_ptr<shader_t> shader;
};
class pp_engine
class pp_render_target_t : public pp_step_t
{
public:
void create() override;
void draw() override;
};
class pp_to_screen_step_t : public pp_step_t
{
public:
void create() override;
void draw() override;
};
class pp_engine_t
{
public:
void create();
void render(i32 width, i32 height);
void bind();
void render();
void cleanup();
void addStep(std::unique_ptr<pp_step_t> step)
{
steps.emplace_back(std::move(step));
}
static std::unique_ptr<shader_t> createShader(std::string_view fragment);
private:
std::vector<std::unique_ptr<pp_step>> steps;
shader_t screen_shader;
std::vector<std::unique_ptr<pp_step_t>> steps;
std::unique_ptr<vertex_array_t> screen_vao;
#ifdef __EMSCRIPTEN__
std::unique_ptr<pp_to_screen_step_t> to_screen;
#endif
};
}

View File

@ -0,0 +1,32 @@
#ifdef __cplusplus
#include <string>
const std::string shader_postprocess_vert = R"("
#version 300 es
precision mediump float;
layout (location = 0) in vec3 vertex;
layout (location = 1) in vec2 uv_in;
out vec2 pos;
out vec2 uv;
layout (std140) uniform GlobalMatrices
{
mat4 projection;
mat4 ortho;
mat4 view;
mat4 pvm;
mat4 ovm;
};
uniform mat4 model;
uniform float z_index;
void main() {
gl_Position = ovm * model * vec4(vertex.xy, z_index, 1.0);
pos = vertex.xy;
uv = uv_in;
}
")";
#endif

View File

@ -0,0 +1,22 @@
#ifdef __cplusplus
#include <string>
const std::string shader_pp_to_screen_frag = R"("
#version 300 es
precision mediump float;
out vec4 FragColor;
in vec2 uv;
in vec2 pos;
uniform sampler2D tex;
vec4 linear_iter(vec4 i, vec4 p, float factor){
return (i + p) * factor;
}
void main() {
FragColor = texture(tex, uv);
}
")";
#endif

View File

@ -9,6 +9,7 @@
#define BLT_WITH_GRAPHICS_TEMPLATE_WINDOW_H
#include <blt/gfx/gl_includes.h>
#include <blt/std/types.h>
#include <GLFW/glfw3.h>
#include <functional>
#include <cstdint>
@ -19,11 +20,11 @@ namespace blt::gfx
{
struct window_context
{
std::int32_t GL_MAJOR = 4;
std::int32_t GL_MINOR = 6;
std::int32_t DOUBLE_BUFFER = GLFW_TRUE;
std::int32_t GL_PROFILE = GLFW_OPENGL_CORE_PROFILE;
std::int32_t SAMPLES = 8;
i32 GL_MAJOR = 4;
i32 GL_MINOR = 6;
i32 DOUBLE_BUFFER = GLFW_TRUE;
i32 GL_PROFILE = GLFW_OPENGL_CORE_PROFILE;
i32 SAMPLES = 8;
};
struct window_data
@ -39,9 +40,9 @@ namespace blt::gfx
window_context context{};
std::int32_t sync_interval = 0;
window_data(std::string title, std::function<void(const window_data&)> init, std::function<void(const window_data&)> update,
std::int32_t width = 640, std::int32_t height = 480): init(std::move(init)), update(std::move(update)),
title(std::move(title)), width(width), height(height)
window_data(std::string_view title, std::function<void(const window_data&)> init, std::function<void(const window_data&)> update,
i32 width = 640, i32 height = 480): init(std::move(init)), update(std::move(update)),
title(title), width(width), height(height)
{}
inline void call_init() const
@ -128,7 +129,11 @@ namespace blt::gfx
double getFrameDeltaMilliseconds();
std::int64_t getFrameDelta();
i64 getFrameDelta();
i32 getWindowWidth();
i32 getWindowHeight();
void cleanup();

View File

@ -279,9 +279,14 @@ namespace blt::gfx
fbo.attachRenderBuffer(depth_rbo, attachment_t::DEPTH_STENCIL);
if (!frame_buffer_t::validate())
BLT_ERROR("Failed to create multi-sampled render texture framebuffer!");
BLT_ERROR("Failed to create render texture framebuffer!");
frame_buffer_t::unbind();
return fbo;
}
void frame_buffer_t::bindScreen(frame_buffer_t::draw_t type)
{
glBindFramebuffer(static_cast<i32>(type), 0);
}
}

View File

@ -89,20 +89,23 @@ namespace blt::gfx
point_shader->bindAttribute(0, "vertex");
point_shader->bindAttribute(1, "uv_in");
draw_buffer.create();
draw_buffer.bind();
engine.addStep(std::make_unique<pp_render_target_t>());
engine.create();
auto* texture = new texture_gl2D(1440, 720);
draw_buffer.attachTexture(texture, frame_buffer_t::attachment_t::COLOR0);
auto* mask = new texture_gl2D(1440, 720);
draw_buffer.attachTexture(mask, frame_buffer_t::attachment_t::COLOR1);
render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, 1440, 720);
draw_buffer.attachRenderBuffer(depth_rbo, frame_buffer_t::attachment_t::DEPTH_STENCIL);
if (!frame_buffer_t::validate())
BLT_ERROR("Failed to create render framebuffer!");
frame_buffer_t::unbind();
// draw_buffer.create();
// draw_buffer.bind();
//
// auto* texture = new texture_gl2D(1440, 720);
// draw_buffer.attachTexture(texture, frame_buffer_t::attachment_t::COLOR0);
// auto* mask = new texture_gl2D(1440, 720);
// draw_buffer.attachTexture(mask, frame_buffer_t::attachment_t::COLOR1);
//
// render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, 1440, 720);
// draw_buffer.attachRenderBuffer(depth_rbo, frame_buffer_t::attachment_t::DEPTH_STENCIL);
//
// if (!frame_buffer_t::validate())
// BLT_ERROR("Failed to create render framebuffer!");
// frame_buffer_t::unbind();
}
void batch_renderer_2d::drawRectangleInternal(const std::string_view texture, const rectangle2d_t& rectangle, const f32 z_index)
@ -161,6 +164,7 @@ namespace blt::gfx
void batch_renderer_2d::cleanup()
{
engine.cleanup();
delete square_vao;
delete square_shader;
delete point_shader;
@ -168,8 +172,8 @@ namespace blt::gfx
void batch_renderer_2d::render(i32 width, i32 height, const bool transparency)
{
draw_buffer.bind();
draw_buffer.updateBuffersStorage(width, height);
//draw_buffer.bind();
//draw_buffer.updateBuffersStorage(width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (transparency)
{
@ -179,6 +183,7 @@ namespace blt::gfx
glEnable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0);
engine.bind();
draw_objects();
glDisable(GL_DEPTH_TEST);
@ -186,8 +191,9 @@ namespace blt::gfx
glDisable(GL_BLEND);
render_reset();
draw_buffer.blitToScreen(width, height);
frame_buffer_t::unbind();
engine.render();
}
void batch_renderer_2d::draw_objects()

View File

@ -15,17 +15,115 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <blt/gfx/renderer/post_process.h>
#include <blt/gfx/window.h>
#include <blt/gfx/renderer/postprocess.h>
#include <blt/gfx/renderer/shaders/postprocess.vert>
#include <blt/gfx/renderer/shaders/pp_to_screen.frag>
#include <blt/std/ranges.h>
float full_screen_vertices[20] = {
1.0f, 1.0f, 0.0f, 1.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 0.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 0.0f, 1.0f
};
const unsigned int full_screen_indices[6] = {
0, 1, 3,
1, 2, 3
};
namespace blt::gfx
{
void pp_engine::create()
void pp_engine_t::create()
{
{
vertex_buffer_t vertices_vbo;
element_buffer_t indices_vbo;
vertices_vbo.create();
indices_vbo.create();
vertices_vbo.allocate(sizeof(full_screen_vertices), full_screen_vertices);
indices_vbo.allocate(sizeof(full_screen_indices), full_screen_indices);
screen_vao = std::make_unique<vertex_array_t>();
const auto tb = screen_vao->bindVBO(vertices_vbo, 0, 3, GL_FLOAT, 5 * sizeof(float), 0);
screen_vao->bindVBO(tb, 1, 2, GL_FLOAT, 5 * sizeof(float), 3 * sizeof(float));
screen_vao->bindElement(indices_vbo);
}
#ifdef __EMSCRIPTEN__
to_screen = std::make_unique<pp_to_screen_step_t>();
#endif
for (auto& step : steps)
step->create();
}
void pp_engine::render(i32 width, i32 height)
void pp_engine_t::render()
{
screen_vao->bind();
for (const auto& [index, step] : blt::enumerate(steps))
{
if (index == 0)
{
continue;
} else
steps[index - 1]->getBuffer().bind(frame_buffer_t::draw_t::READ);
step->draw();
step->getBuffer().bind(frame_buffer_t::draw_t::DRAW);
step->getShader().bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
}
frame_buffer_t::unbind();
#ifdef __EMSCRIPTEN__
to_screen->getShader().bind();
steps.back()->getBuffer().bind(frame_buffer_t::draw_t::READ);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
#else
steps.back()->getBuffer().blitToScreen(getWindowWidth(), getWindowHeight());
#endif
}
void pp_engine_t::cleanup()
{
screen_vao = nullptr;
for (auto& v : steps)
v = nullptr;
steps.clear();
}
std::unique_ptr<shader_t> pp_engine_t::createShader(std::string_view fragment)
{
auto shader = std::make_unique<shader_t>(shader_postprocess_vert, std::string(fragment));
shader->bindAttribute(0, "vertex");
shader->bindAttribute(1, "uv_in");
return shader;
}
void pp_engine_t::bind()
{
steps.front()->draw();
steps.front()->getBuffer().bind(frame_buffer_t::draw_t::DRAW);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
}
void pp_to_screen_step_t::create()
{
shader = pp_engine_t::createShader(shader_pp_to_screen_frag);
}
void pp_to_screen_step_t::draw()
{}
void pp_render_target_t::create()
{
shader = pp_engine_t::createShader(shader_pp_to_screen_frag);
draw_buffer = frame_buffer_t::make_render_target(getWindowWidth(), getWindowHeight());
}
void pp_render_target_t::draw()
{
draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight());
}
}

View File

@ -46,7 +46,7 @@ namespace blt::gfx
double nanoDelta = 0;
double millisDelta = 0;
} window_state;
void create_callbacks()
{
/* Setup keyboard callback */
@ -68,7 +68,7 @@ namespace blt::gfx
window_state.inputManager.key(key) = state;
window_state.inputManager.key_pressed = true;
});
/* Setup mouse button callback */
glfwSetMouseButtonCallback(window_state.window, [](GLFWwindow*, int button, int action, int) {
if (button < 0)
@ -86,40 +86,40 @@ namespace blt::gfx
window_state.inputManager.mouse(button) = state;
window_state.inputManager.mouse_pressed = true;
});
/* Setup mouse cursor callback */
glfwSetCursorPosCallback(window_state.window, [](GLFWwindow*, double x, double y) {
window_state.inputManager.updateMousePos(x, y);
window_state.inputManager.mouse_moved = true;
});
/* Setup mouse scroll callback */
glfwSetScrollCallback(window_state.window, [](GLFWwindow*, double, double s) { window_state.inputManager.updateScroll(s); });
/* Setup drop input callback */
glfwSetDropCallback(window_state.window, [](GLFWwindow*, int count, const char** paths) {
for (int i = 0; i < count; i++)
window_state.pendingPaths.emplace(paths[i]);
});
}
void setup_ImGUI()
{
const char* glsl_version = "#version 100";
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::Spectrum::StyleColorsSpectrum();
ImGui::Spectrum::LoadFont();
ImGui::SetupImGuiStyle(true, 1.0);
// Setup FA
ImFontConfig config;
config.MergeMode = true;
@ -131,9 +131,9 @@ namespace blt::gfx
io.Fonts->AddFontFromMemoryCompressedTTF(fontAwesomeBrands_compressed_data, static_cast<int>(fontAwesomeBrands_compressed_size), 13.0,
&config,
icon_ranges);
//ImGui::StyleColorsLight();
// Setup Platform/Renderer backends
ImGui_ImplGlfw_InitForOpenGL(window_state.window, true);
#ifdef __EMSCRIPTEN__
@ -145,7 +145,7 @@ namespace blt::gfx
io.IniFilename = nullptr;
#endif
}
void loop(void* arg)
{
auto& data = *((window_data*) arg);
@ -156,24 +156,24 @@ namespace blt::gfx
data.height = window_state.height;
// TODO: user option for this
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
/* -- Begin the next ImGUI frame -- */
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
/* -- Call user update function -- */
data.call_update();
/* -- Render the ImGUI frame -- */
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
/* -- Update GLFW state -- */
window_state.inputManager.clear();
glfwSwapBuffers(window_state.window);
glfwPollEvents();
/* -- Update Frame Timing Information -- */
const auto current_time = system::nanoTime();
window_state.deltaTime = current_time - window_state.lastTime;
@ -183,7 +183,7 @@ namespace blt::gfx
}
#ifdef __EMSCRIPTEN__
EM_BOOL emscripten_resize_callback(int, const EmscriptenUiEvent* event, void* data)
{
int width = event->documentBodyClientWidth;
@ -203,7 +203,7 @@ namespace blt::gfx
});
#endif
void init(window_data data)
{
#ifdef __EMSCRIPTEN__
@ -217,28 +217,28 @@ namespace blt::gfx
/* -- Set up Error Callback -- */
glfwSetErrorCallback(error_callback);
BLT_ASSERT(glfwInit() && "Unable to init GLFW. Aborting.");
/* -- Set up Window Context -- */
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, data.context.GL_MAJOR);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, data.context.GL_MINOR);
glfwWindowHint(GLFW_DOUBLEBUFFER, data.context.DOUBLE_BUFFER);
glfwWindowHint(GLFW_OPENGL_PROFILE, data.context.GL_PROFILE);
glfwWindowHint(GLFW_SAMPLES, data.context.SAMPLES);
/* -- Create the Window -- */
window_state.window = glfwCreateWindow(data.width, data.height, data.title.c_str(), nullptr, nullptr);
BLT_ASSERT(window_state.window && "Unable to create GLFW window.");
/* -- Set Window Specifics + OpenGL -- */
glfwMakeContextCurrent(window_state.window);
#ifndef __EMSCRIPTEN__
glfwSwapInterval(data.sync_interval);
gladLoadGL(glfwGetProcAddress);
#endif
/* -- Set up our local callbacks, ImGUI will then call these -- */
create_callbacks();
/* -- Set up ImGUI -- */
setup_ImGUI();
@ -246,7 +246,10 @@ namespace blt::gfx
if (data.context.SAMPLES > 0)
glEnable(GL_MULTISAMPLE);
#endif
window_state.width = data.width;
window_state.height = data.height;
/* -- Call User Provided post-window-init function -- */
data.call_init();
@ -264,31 +267,38 @@ namespace blt::gfx
loop((void*) &data);
#endif
}
void cleanup()
{
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window_state.window);
glfwTerminate();
}
double getMouseX() { return window_state.inputManager.mouseX; }
double getMouseY() { return window_state.inputManager.mouseY; }
double getMouseDX() { return window_state.inputManager.deltaX; }
double getMouseDY() { return window_state.inputManager.deltaY; }
void lockCursor() { glfwSetInputMode(window_state.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); }
void unlockCursor() { glfwSetInputMode(window_state.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
bool isCursorLocked() { return glfwGetInputMode(window_state.window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; }
double getMouseX()
{ return window_state.inputManager.mouseX; }
double getMouseY()
{ return window_state.inputManager.mouseY; }
double getMouseDX()
{ return window_state.inputManager.deltaX; }
double getMouseDY()
{ return window_state.inputManager.deltaY; }
void lockCursor()
{ glfwSetInputMode(window_state.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); }
void unlockCursor()
{ glfwSetInputMode(window_state.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); }
bool isCursorLocked()
{ return glfwGetInputMode(window_state.window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED; }
bool isCursorInWindow()
{
#ifdef __EMSCRIPTEN__
@ -298,7 +308,7 @@ namespace blt::gfx
return glfwGetWindowAttrib(window_state.window, GLFW_HOVERED);
#endif
}
void setRawInput(bool state)
{
#ifdef __EMSCRIPTEN__
@ -308,7 +318,7 @@ namespace blt::gfx
glfwSetInputMode(window_state.window, GLFW_RAW_MOUSE_MOTION, state ? GLFW_TRUE : GLFW_FALSE);
#endif
}
bool isRawInput()
{
#ifdef __EMSCRIPTEN__
@ -317,27 +327,43 @@ namespace blt::gfx
return glfwGetInputMode(window_state.window, GLFW_RAW_MOUSE_MOTION);
#endif
}
void setClipboard(const std::string& str) { glfwSetClipboardString(window_state.window, str.c_str()); }
std::string getClipboard() { return glfwGetClipboardString(window_state.window); }
bool isMousePressed(int button) { return window_state.inputManager.isMousePressed(button); }
bool isKeyPressed(int key) { return window_state.inputManager.isKeyPressed(key); }
double getFrameDeltaSeconds() { return window_state.nanoDelta; }
double getFrameDeltaMilliseconds() { return window_state.millisDelta; }
std::int64_t getFrameDelta() { return window_state.deltaTime; }
bool mouseMovedLastFrame() { return window_state.inputManager.mouse_moved; }
bool mousePressedLastFrame() { return window_state.inputManager.mouse_pressed; }
bool keyPressedLastFrame() { return window_state.inputManager.key_pressed; }
void setClipboard(const std::string& str)
{ glfwSetClipboardString(window_state.window, str.c_str()); }
std::string getClipboard()
{ return glfwGetClipboardString(window_state.window); }
bool isMousePressed(int button)
{ return window_state.inputManager.isMousePressed(button); }
bool isKeyPressed(int key)
{ return window_state.inputManager.isKeyPressed(key); }
double getFrameDeltaSeconds()
{ return window_state.nanoDelta; }
double getFrameDeltaMilliseconds()
{ return window_state.millisDelta; }
i64 getFrameDelta()
{ return window_state.deltaTime; }
bool mouseMovedLastFrame()
{ return window_state.inputManager.mouse_moved; }
bool mousePressedLastFrame()
{ return window_state.inputManager.mouse_pressed; }
bool keyPressedLastFrame()
{ return window_state.inputManager.key_pressed; }
i32 getWindowHeight()
{ return window_state.height; }
i32 getWindowWidth()
{ return window_state.width; }
window_data& window_data::setWindowSize(int32_t new_width, int32_t new_height)
{
width = new_width;