post processing works on emscripten now
parent
2c85c0f93c
commit
a6853e6ff7
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.25)
|
||||
|
||||
set(BLT_GRAPHICS_VERSION 0.13.10)
|
||||
set(BLT_GRAPHICS_VERSION 0.13.11)
|
||||
set(BLT_GRAPHICS_TEST_VERSION 0.0.1)
|
||||
|
||||
project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION})
|
||||
|
|
|
@ -216,7 +216,7 @@ namespace blt::gfx
|
|||
std::make_unique<pp_blur_step_inplace_t>(state, frame_buffer_t::attachment_t::COLOR1),
|
||||
std::make_unique<pp_expansion_step_inplace_t>(frame_buffer_t::attachment_t::COLOR1,
|
||||
vec4{4, 4, 4, 1}),
|
||||
std::make_unique<pp_blur_step_inplace_t>(state, frame_buffer_t::attachment_t::COLOR1, 2, 2),
|
||||
std::make_unique<pp_overlay_blur_step_t>(frame_buffer_t::attachment_t::COLOR1, 2, 2),
|
||||
std::make_unique<pp_outline_step_t>(state)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include <vector>
|
||||
#include <memory>
|
||||
#include <blt/std/assert.h>
|
||||
#include <blt/parse/templating.h>
|
||||
#include <optional>
|
||||
|
||||
namespace blt::gfx
|
||||
{
|
||||
|
@ -171,6 +173,27 @@ namespace blt::gfx
|
|||
i32 y_blur;
|
||||
};
|
||||
|
||||
class pp_overlay_blur_step_t : public pp_in_place_t
|
||||
{
|
||||
public:
|
||||
pp_overlay_blur_step_t(frame_buffer_t::attachment_t from, i32 x_blur = 2, i32 y_blur = 2):
|
||||
pp_in_place_t(from), x_blur(x_blur), y_blur(y_blur)
|
||||
{}
|
||||
|
||||
pp_overlay_blur_step_t(frame_buffer_t::attachment_t from, frame_buffer_t::attachment_t to, i32 x_blur = 2,
|
||||
i32 y_blur = 2):
|
||||
pp_in_place_t(from, to), x_blur(x_blur), y_blur(y_blur)
|
||||
{}
|
||||
|
||||
void create() override;
|
||||
|
||||
void draw(frame_buffer_t& previous);
|
||||
|
||||
private:
|
||||
i32 x_blur;
|
||||
i32 y_blur;
|
||||
};
|
||||
|
||||
class pp_expansion_step_inplace_t : public pp_in_place_t
|
||||
{
|
||||
public:
|
||||
|
@ -207,7 +230,7 @@ namespace blt::gfx
|
|||
steps.emplace_back(std::move(step));
|
||||
}
|
||||
|
||||
static std::unique_ptr<shader_t> createShader(std::string_view fragment);
|
||||
static std::unique_ptr<shader_t> createShader(std::string_view fragment, std::optional<std::reference_wrapper<template_engine_t>> engine = {});
|
||||
|
||||
static std::unique_ptr<pp_engine_t> make_basic_pp()
|
||||
{
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
#ifdef __cplusplus
|
||||
#include <string>
|
||||
const std::string shader_overlay_blur_frag = R"("
|
||||
#version 300 es
|
||||
precision mediump float;
|
||||
|
||||
out vec4 FragColor;
|
||||
in vec2 uv;
|
||||
in vec2 pos;
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform ivec4 size;
|
||||
|
||||
void main() {
|
||||
vec2 texelSize = 1.0 / vec2(float(size.x), float(size.y));
|
||||
vec4 result = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
for (int x = -size.z; x <= size.z; ++x) {
|
||||
for (int y = -size.w; y <= size.w; ++y) {
|
||||
vec2 offset = (vec2(float(x), float(y)) * texelSize);
|
||||
result += texture(tex, uv + offset);
|
||||
}
|
||||
}
|
||||
vec4 blur_val = result / vec4((float(size.z) * 2.0) * (float(size.w) * 2.0));
|
||||
vec4 tex_val = texture(tex, uv);
|
||||
if (tex_val != vec4(0.0,0.0,0.0,0.0))
|
||||
FragColor = tex_val;
|
||||
else
|
||||
FragColor = blur_val;
|
||||
}
|
||||
|
||||
")";
|
||||
#endif
|
|
@ -23,6 +23,9 @@
|
|||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <blt/math/math.h>
|
||||
#include <blt/parse/templating.h>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace blt::gfx
|
||||
{
|
||||
|
@ -32,99 +35,103 @@ namespace blt::gfx
|
|||
GLuint uboID = 0;
|
||||
size_t size_;
|
||||
GLuint location_;
|
||||
|
||||
|
||||
public:
|
||||
explicit uniform_buffer(size_t size, GLuint location = 0);
|
||||
|
||||
|
||||
uniform_buffer(void* data, size_t size, GLuint location = 0);
|
||||
|
||||
|
||||
/**
|
||||
* Resizes the internal UBO
|
||||
* @param newSize new size for the UBO
|
||||
*/
|
||||
uniform_buffer& resize(size_t newSize);
|
||||
|
||||
|
||||
/**
|
||||
* Uploads data to the UBO. This can be an arbitrary locations and does not need to be the whole UBO.
|
||||
*/
|
||||
uniform_buffer& upload(void* data, size_t size, size_t offset = 0);
|
||||
|
||||
|
||||
uniform_buffer& bind();
|
||||
|
||||
|
||||
inline uniform_buffer& unbind()
|
||||
{
|
||||
glBindBuffer(GL_UNIFORM_BUFFER, 0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
~uniform_buffer();
|
||||
|
||||
[[nodiscard]] inline size_t size() const { return size_; }
|
||||
|
||||
[[nodiscard]] inline GLuint location() const { return location_; }
|
||||
|
||||
[[nodiscard]] inline size_t size() const
|
||||
{ return size_; }
|
||||
|
||||
[[nodiscard]] inline GLuint location() const
|
||||
{ return location_; }
|
||||
};
|
||||
|
||||
|
||||
class shader_base_t
|
||||
{
|
||||
friend uniform_buffer;
|
||||
|
||||
friend uniform_buffer;
|
||||
|
||||
protected:
|
||||
struct IntDefaultedToMinusOne
|
||||
{
|
||||
GLint i = -1;
|
||||
|
||||
inline explicit operator bool() const { return i != -1; }
|
||||
|
||||
inline explicit operator bool() const
|
||||
{ return i != -1; }
|
||||
};
|
||||
|
||||
|
||||
std::unordered_map<std::string, IntDefaultedToMinusOne> uniformVars;
|
||||
GLuint programID = 0;
|
||||
|
||||
|
||||
IntDefaultedToMinusOne getUniformLocation(const std::string& name);
|
||||
|
||||
|
||||
public:
|
||||
const shader_base_t& bind() const
|
||||
{
|
||||
glUseProgram(programID);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
shader_base_t& bind()
|
||||
{
|
||||
glUseProgram(programID);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
shader_base_t& unbind()
|
||||
{
|
||||
glUseProgram(0);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
shader_base_t& setBool(const std::string& name, bool value);
|
||||
|
||||
|
||||
shader_base_t& setInt(const std::string& name, int value);
|
||||
|
||||
|
||||
shader_base_t& setFloat(const std::string& name, float value);
|
||||
|
||||
|
||||
shader_base_t& setMatrix(const std::string& name, blt::mat4x4& matrix);
|
||||
|
||||
|
||||
// poor solution: TODO
|
||||
shader_base_t& setMatrix(const std::string& name, blt::mat4x4&& matrix);
|
||||
|
||||
|
||||
shader_base_t& setVec2(const std::string& name, const blt::vec2& vec);
|
||||
|
||||
|
||||
shader_base_t& setVec3(const std::string& name, const blt::vec3& vec);
|
||||
|
||||
|
||||
shader_base_t& setVec4(const std::string& name, const blt::vec4& vec);
|
||||
|
||||
shader_base_t& setVec4i(const std::string& name, const blt::vec4i& vec);
|
||||
|
||||
|
||||
shader_base_t& setVec2(const std::string& name, float x, float y);
|
||||
|
||||
|
||||
shader_base_t& setVec3(const std::string& name, float x, float y, float z);
|
||||
|
||||
|
||||
shader_base_t& setVec4(const std::string& name, float x, float y, float z, float w);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* a basic computer shader class, contains the functions and resources required to use compute shaders!
|
||||
*/
|
||||
|
@ -132,47 +139,87 @@ namespace blt::gfx
|
|||
{
|
||||
private:
|
||||
GLuint shaderID = 0;
|
||||
|
||||
|
||||
public:
|
||||
explicit compute_shader_t(const std::string& shader_source, bool loadAsString = true);
|
||||
|
||||
|
||||
void execute(const int x, const int y, const int z) const
|
||||
{
|
||||
bind();
|
||||
glDispatchCompute(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
~compute_shader_t();
|
||||
};
|
||||
|
||||
|
||||
class shader_t : public shader_base_t
|
||||
{
|
||||
private:
|
||||
GLuint vertexShaderID = 0;
|
||||
GLuint fragmentShaderID = 0;
|
||||
|
||||
|
||||
static unsigned int createShader(const std::string& source, int type);
|
||||
|
||||
|
||||
static std::string loadShader(std::string_view file);
|
||||
|
||||
|
||||
shader_t(std::string_view vertex, std::string_view fragment);
|
||||
|
||||
static std::pair<std::string, std::string> process_templates(template_engine_t& engine, std::string_view vertex,
|
||||
std::string_view fragment);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Creates a shader
|
||||
* @param vertex vertex shader source or file
|
||||
* @param fragment fragment shader source or file
|
||||
* @param geometry geometry shader source or file (optional)
|
||||
* @param load_as_string load the shader as a string (true) or use the string to load the shader as a file (false)
|
||||
* Creates a shader from source file
|
||||
* @param vertex vertex shader source
|
||||
* @param fragment fragment shader source
|
||||
*/
|
||||
shader_t(const std::string& vertex, const std::string& fragment, bool load_as_string = true);
|
||||
|
||||
static shader_t* make(std::optional<std::reference_wrapper<template_engine_t>> engine, std::string_view vertex,
|
||||
std::string_view fragment);
|
||||
|
||||
static shader_t* make(std::string_view vertex, std::string_view fragment)
|
||||
{
|
||||
return make({}, vertex, fragment);
|
||||
}
|
||||
|
||||
static std::unique_ptr<shader_t> make_unique(std::optional<std::reference_wrapper<template_engine_t>> engine,
|
||||
std::string_view vertex, std::string_view fragment);
|
||||
|
||||
static std::unique_ptr<shader_t> make_unique(std::string_view vertex, std::string_view fragment)
|
||||
{
|
||||
return make_unique({}, vertex, fragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a shader by loading lines of source from the file.
|
||||
* @param vertex vertex shader file path
|
||||
* @param fragment fragment shader file path
|
||||
*/
|
||||
static shader_t* load(std::optional<std::reference_wrapper<template_engine_t>> engine, std::string_view vertex,
|
||||
std::string_view fragment);
|
||||
|
||||
static shader_t* load(std::string_view vertex, std::string_view fragment)
|
||||
{
|
||||
return load({}, vertex, fragment);
|
||||
}
|
||||
|
||||
static std::unique_ptr<shader_t> load_unique(std::optional<std::reference_wrapper<template_engine_t>> engine,
|
||||
std::string_view vertex, std::string_view fragment);
|
||||
|
||||
static std::unique_ptr<shader_t> load_unique(std::string_view vertex, std::string_view fragment)
|
||||
{
|
||||
return load_unique({}, vertex, fragment);
|
||||
}
|
||||
|
||||
shader_t(shader_t&& move) noexcept;
|
||||
|
||||
|
||||
shader_t(const shader_t& copy) = delete;
|
||||
|
||||
// used to set the location of VAOs to the in variables in opengl shaders.
|
||||
void bindAttribute(int attribute, const std::string& name) const;
|
||||
|
||||
|
||||
// used to set location of shared UBOs like the perspective and view matrix
|
||||
void setUniformBlockLocation(const std::string& name, int location) const;
|
||||
|
||||
|
||||
~shader_t();
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit fa5083b637a96af5b430c9f0015ee11d5638d201
|
||||
Subproject commit f228cfbbe31538731a2ef1bd990d41c99562c4d3
|
|
@ -1 +1 @@
|
|||
Subproject commit a1b06823fe2d964a62fda99385499b218cf5cea5
|
||||
Subproject commit 231cbee0fc4f59dbe5b8b853a11b08dc84e57c65
|
|
@ -1 +1 @@
|
|||
Subproject commit 6675317107257c2cc16c947b359d557821d85bf2
|
||||
Subproject commit 111397c71a5f1c2c88e05da9c84edfdba2e472a4
|
|
@ -81,11 +81,11 @@ namespace blt::gfx
|
|||
line_vao->bindElement(indices_vbo);
|
||||
}
|
||||
|
||||
square_shader = new shader_t(shader_2d_textured_vert, shader_2d_textured_frag);
|
||||
square_shader = shader_t::make(shader_2d_textured_vert, shader_2d_textured_frag);
|
||||
square_shader->bindAttribute(0, "vertex");
|
||||
square_shader->bindAttribute(1, "uv_in");
|
||||
|
||||
point_shader = new shader_t(shader_2d_textured_vert, shader_2d_textured_cirlce_frag);
|
||||
point_shader = shader_t::make(shader_2d_textured_vert, shader_2d_textured_cirlce_frag);
|
||||
point_shader->bindAttribute(0, "vertex");
|
||||
point_shader->bindAttribute(1, "uv_in");
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <blt/gfx/renderer/shaders/pp_screen.frag>
|
||||
#include <blt/gfx/renderer/shaders/pp_outline_step.frag>
|
||||
#include <blt/gfx/renderer/shaders/pp_gaussian_blur.frag>
|
||||
#include <blt/gfx/renderer/shaders/pp_overlay_blur.frag>
|
||||
#include <blt/gfx/renderer/shaders/pp_multiplier.frag>
|
||||
#include <blt/std/ranges.h>
|
||||
#include <blt/math/log_util.h>
|
||||
|
@ -117,9 +118,18 @@ namespace blt::gfx
|
|||
steps.clear();
|
||||
}
|
||||
|
||||
std::unique_ptr<shader_t> pp_engine_t::createShader(std::string_view fragment)
|
||||
std::unique_ptr<shader_t> pp_engine_t::createShader(std::string_view fragment, std::optional<std::reference_wrapper<template_engine_t>> engine)
|
||||
{
|
||||
auto shader = std::make_unique<shader_t>(shader_postprocess_vert, std::string(fragment));
|
||||
std::unique_ptr<shader_t> shader;
|
||||
if (engine)
|
||||
{
|
||||
shader = shader_t::make_unique(engine, shader_postprocess_vert, std::string(fragment));
|
||||
} else
|
||||
{
|
||||
template_engine_t templateEngine;
|
||||
templateEngine.set("LAYOUT_STRING", "");
|
||||
shader = shader_t::make_unique(templateEngine, shader_postprocess_vert, std::string(fragment));
|
||||
}
|
||||
shader->bindAttribute(0, "vertex");
|
||||
shader->bindAttribute(1, "uv_in");
|
||||
return shader;
|
||||
|
@ -139,7 +149,10 @@ namespace blt::gfx
|
|||
|
||||
void pp_in_place_t::create()
|
||||
{
|
||||
shader_pass = pp_engine_t::createShader(shader_pp_screen_frag);
|
||||
template_engine_t engine;
|
||||
engine.set("LAYOUT_STRING", "layout (location = ${IF(LAYOUT_LOCATION) { LAYOUT_LOCATION } ELSE { ~DISCARD }}) ");
|
||||
engine.set("LAYOUT_LOCATION", std::to_string(static_cast<int>(to) - static_cast<int>(frame_buffer_t::attachment_t::COLOR0)));
|
||||
shader_pass = pp_engine_t::createShader(shader_pp_screen_frag, engine);
|
||||
draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight());
|
||||
}
|
||||
|
||||
|
@ -274,4 +287,18 @@ namespace blt::gfx
|
|||
shader->bind();
|
||||
shader->setVec4("multiplier", multiplier);
|
||||
}
|
||||
|
||||
void pp_overlay_blur_step_t::create()
|
||||
{
|
||||
pp_in_place_t::create();
|
||||
shader = pp_engine_t::createShader(shader_overlay_blur_frag);
|
||||
}
|
||||
|
||||
void pp_overlay_blur_step_t::draw(frame_buffer_t& previous)
|
||||
{
|
||||
pp_in_place_t::draw(previous);
|
||||
shader->bind();
|
||||
auto v = vec2(getWindowWidth(), getWindowHeight());
|
||||
shader->setVec4i("size", {static_cast<i32>(v.x()), static_cast<i32>(v.y()), x_blur, y_blur});
|
||||
}
|
||||
}
|
|
@ -70,13 +70,13 @@ namespace blt::gfx
|
|||
glDeleteBuffers(1, &uboID);
|
||||
}
|
||||
|
||||
static std::string removeEmptyFirstLines(const std::string& string)
|
||||
static std::string removeEmptyFirstLines(std::string_view string)
|
||||
{
|
||||
auto lines = blt::string::split(string, "\n");
|
||||
auto lines = blt::string::split_sv(string, "\n");
|
||||
std::string new_source_string;
|
||||
for (const auto& line : lines)
|
||||
{
|
||||
if (!line.empty() && !blt::string::contains(line, "\""))
|
||||
if (!blt::string::trim(line).empty() && !blt::string::starts_with(line, "\""))
|
||||
{
|
||||
new_source_string += line;
|
||||
new_source_string += "\n";
|
||||
|
@ -122,21 +122,11 @@ namespace blt::gfx
|
|||
return shaderID;
|
||||
}
|
||||
|
||||
shader_t::shader_t(const std::string& vertex, const std::string& fragment, bool load_as_string)
|
||||
shader_t::shader_t(std::string_view vertex, std::string_view fragment)
|
||||
{
|
||||
// load shader sources
|
||||
std::string vertex_source = vertex;
|
||||
std::string fragment_source = fragment;
|
||||
if (!load_as_string)
|
||||
{
|
||||
// BLT provides a recursive file loader for glsl shaders. It's pretty much just a recursive function looking for include statements.
|
||||
vertex_source = loadShader(vertex);
|
||||
fragment_source = loadShader(fragment);
|
||||
} else
|
||||
{
|
||||
vertex_source = removeEmptyFirstLines(vertex_source);
|
||||
fragment_source = removeEmptyFirstLines(fragment_source);
|
||||
}
|
||||
// load shader sources
|
||||
std::string vertex_source = removeEmptyFirstLines(vertex);
|
||||
std::string fragment_source = removeEmptyFirstLines(fragment);
|
||||
|
||||
// create the shaders
|
||||
vertexShaderID = createShader(vertex_source, GL_VERTEX_SHADER);
|
||||
|
@ -232,6 +222,64 @@ namespace blt::gfx
|
|||
return str;
|
||||
}
|
||||
|
||||
shader_t* shader_t::make(std::optional<std::reference_wrapper<template_engine_t>> engine, std::string_view vertex,
|
||||
std::string_view fragment)
|
||||
{
|
||||
std::string vertex_source;
|
||||
std::string fragment_source;
|
||||
if (engine)
|
||||
{
|
||||
auto s_pair = process_templates(engine->get(), vertex, fragment);
|
||||
vertex_source = std::move(s_pair.first);
|
||||
fragment_source = std::move(s_pair.second);
|
||||
} else
|
||||
{
|
||||
vertex_source = vertex;
|
||||
fragment_source = fragment;
|
||||
}
|
||||
|
||||
return new shader_t(vertex_source, fragment_source);
|
||||
}
|
||||
|
||||
shader_t* shader_t::load(std::optional<std::reference_wrapper<template_engine_t>> engine, std::string_view vertex,
|
||||
std::string_view fragment)
|
||||
{
|
||||
auto vertex_source = loadShader(vertex);
|
||||
auto fragment_source = loadShader((fragment));
|
||||
return make(engine, vertex_source, fragment_source);
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> shader_t::process_templates(template_engine_t& engine, std::string_view vertex, std::string_view fragment)
|
||||
{
|
||||
auto vertex_e = engine.evaluate(vertex);
|
||||
auto fragment_e = engine.evaluate(fragment);
|
||||
if (!vertex_e)
|
||||
{
|
||||
BLT_ERROR("Error vertex function returned: %d", static_cast<int>(vertex_e.error()));
|
||||
throw std::runtime_error("Unable to substitute vertex source");
|
||||
}
|
||||
if (!fragment_e)
|
||||
{
|
||||
BLT_ERROR("Error fragment function returned: %d", static_cast<int>(vertex_e.error()));
|
||||
throw std::runtime_error("Unable to substitute fragment source");
|
||||
}
|
||||
return {vertex_e.value(), fragment_e.value()};
|
||||
}
|
||||
|
||||
std::unique_ptr<shader_t> shader_t::make_unique(std::optional<std::reference_wrapper<template_engine_t>> engine, std::string_view vertex,
|
||||
std::string_view fragment)
|
||||
{
|
||||
return std::unique_ptr<shader_t>(shader_t::make(engine, vertex, fragment));
|
||||
}
|
||||
|
||||
std::unique_ptr<shader_t> shader_t::load_unique(std::optional<std::reference_wrapper<template_engine_t>> engine, std::string_view vertex,
|
||||
std::string_view fragment)
|
||||
{
|
||||
auto vertex_source = loadShader(vertex);
|
||||
auto fragment_source = loadShader((fragment));
|
||||
return make_unique(engine, vertex_source, fragment_source);
|
||||
}
|
||||
|
||||
shader_base_t& shader_base_t::setBool(const std::string& name, bool value)
|
||||
{
|
||||
if (auto i = getUniformLocation(name))
|
||||
|
|
|
@ -251,9 +251,11 @@ namespace blt::gfx
|
|||
glfwSwapInterval(data.sync_interval);
|
||||
gladLoadGL(glfwGetProcAddress);
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glDebugMessageCallback(gl_error_callback, nullptr);
|
||||
#endif
|
||||
|
||||
/* -- Set up our local callbacks, ImGUI will then call these -- */
|
||||
create_callbacks();
|
||||
|
|
Loading…
Reference in New Issue