diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ae4b44..193b9de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.25) -set(BLT_GRAPHICS_VERSION 0.13.3) +set(BLT_GRAPHICS_VERSION 0.13.4) set(BLT_GRAPHICS_TEST_VERSION 0.0.1) project(BLT_WITH_GRAPHICS VERSION ${BLT_GRAPHICS_VERSION}) diff --git a/include/blt/gfx/framebuffer.h b/include/blt/gfx/framebuffer.h index 94bb604..1a49258 100644 --- a/include/blt/gfx/framebuffer.h +++ b/include/blt/gfx/framebuffer.h @@ -24,6 +24,7 @@ #include #include "blt/std/types.h" + namespace blt::gfx { @@ -127,6 +128,16 @@ namespace blt::gfx void destroy(); + [[nodiscard]] auto& getTexture(attachment_t attachment) + { + return *texture_buffers[static_cast(attachment) - static_cast(attachment_t::COLOR0)]; + } + + [[nodiscard]] const auto& getTexture(attachment_t attachment) const + { + return *texture_buffers[static_cast(attachment)]; + } + [[nodiscard]] i32 getHeight() const { return height_; @@ -136,6 +147,11 @@ namespace blt::gfx { return width_; } + + auto getBufferID() const + { + return fboID; + } public: static frame_buffer_t make_render_texture(blt::i32 width, blt::i32 height); diff --git a/include/blt/gfx/renderer/batch_2d_renderer.h b/include/blt/gfx/renderer/batch_2d_renderer.h index e34d925..3186d56 100644 --- a/include/blt/gfx/renderer/batch_2d_renderer.h +++ b/include/blt/gfx/renderer/batch_2d_renderer.h @@ -149,7 +149,8 @@ namespace blt::gfx class batch_renderer_2d { private: - pp_engine_t engine; + std::unique_ptr engine = pp_engine_t::make_multi_pp(std::make_unique(), + std::make_unique()); template struct render_object_t diff --git a/include/blt/gfx/renderer/postprocess.h b/include/blt/gfx/renderer/postprocess.h index 7d6cbc4..7ce6b04 100644 --- a/include/blt/gfx/renderer/postprocess.h +++ b/include/blt/gfx/renderer/postprocess.h @@ -35,20 +35,32 @@ namespace blt::gfx class pp_step_t { public: - virtual void create() = 0; + virtual void create() + {} - virtual void draw() = 0; + // only called on render() + virtual void draw(pp_step_t&) + {} - frame_buffer_t& getBuffer() + // only called on bind() + virtual void draw() + {} + + auto& getBuffer() { return draw_buffer; } - shader_t& getShader() + auto& getShader() { return *shader; } + bool hasShader() + { + return shader != nullptr; + } + virtual ~pp_step_t() = default; protected: @@ -69,9 +81,26 @@ namespace blt::gfx public: void create() override; + void draw(pp_step_t& previous) override; + }; + + class pp_outline_target : public pp_step_t + { + public: + void create() override; + void draw() override; }; + class pp_outline_step_t : public pp_step_t + { + public: + + void create() override; + + void draw(pp_step_t& previous) override; + }; + class pp_engine_t { public: @@ -89,13 +118,32 @@ namespace blt::gfx } static std::unique_ptr createShader(std::string_view fragment); + + static std::unique_ptr make_basic_pp() + { + return make_single_pp(std::make_unique()); + } + + static std::unique_ptr make_single_pp(std::unique_ptr step) + { + auto engine = new pp_engine_t(); + engine->addStep(std::move(step)); + return std::unique_ptr(engine); + } + + template + static std::unique_ptr make_multi_pp(Args... steps) + { + auto engine = new pp_engine_t(); + (engine->addStep(std::move(steps)), ...); + return std::unique_ptr(engine); + } private: + pp_engine_t() = default; + std::vector> steps; std::unique_ptr screen_vao; -#ifdef __EMSCRIPTEN__ - std::unique_ptr to_screen; -#endif }; } diff --git a/include/blt/gfx/renderer/shaders/2d_textured.frag b/include/blt/gfx/renderer/shaders/2d_textured.frag index 89df101..68c8b0c 100644 --- a/include/blt/gfx/renderer/shaders/2d_textured.frag +++ b/include/blt/gfx/renderer/shaders/2d_textured.frag @@ -4,13 +4,15 @@ const std::string shader_2d_textured_frag = R"(" #version 300 es precision mediump float; -out vec4 FragColor; +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 Mask; in vec2 uv; in vec2 pos; uniform sampler2D tex; uniform vec4 color; uniform vec4 use_texture; +uniform vec4 outline_color; vec4 linear_iter(vec4 i, vec4 p, float factor){ return (i + p) * factor; @@ -18,6 +20,8 @@ vec4 linear_iter(vec4 i, vec4 p, float factor){ void main() { FragColor = (texture(tex, uv) * use_texture) + color; + Mask = outline_color; + //Mask.a = FragColor.a; } ")"; diff --git a/include/blt/gfx/renderer/shaders/2d_textured_circle.frag b/include/blt/gfx/renderer/shaders/2d_textured_circle.frag index 92c9e35..dd3676c 100644 --- a/include/blt/gfx/renderer/shaders/2d_textured_circle.frag +++ b/include/blt/gfx/renderer/shaders/2d_textured_circle.frag @@ -4,13 +4,15 @@ const std::string shader_2d_textured_cirlce_frag = R"(" #version 300 es precision mediump float; -out vec4 FragColor; +layout (location = 0) out vec4 FragColor; +layout (location = 1) out vec4 Mask; in vec2 uv; in vec2 pos; uniform sampler2D tex; uniform vec4 color; uniform vec4 use_texture; +uniform vec4 outline_color; const float offset = 1.0 / 32.0; @@ -25,10 +27,9 @@ void main() { const float sq = 0.5 * 0.5; if (ts >= sq) discard; - if (ts + offset >= sq) - FragColor = vec4(1.0, 1.0, 1.0, 1.0); - else - FragColor = (texture(tex, uv) * use_texture) + color; + FragColor = (texture(tex, uv) * use_texture) + color; + Mask = outline_color; + //Mask.a = FragColor.a; } ")"; diff --git a/include/blt/gfx/renderer/shaders/postprocess.vert b/include/blt/gfx/renderer/shaders/postprocess.vert index 0e73261..7d27528 100644 --- a/include/blt/gfx/renderer/shaders/postprocess.vert +++ b/include/blt/gfx/renderer/shaders/postprocess.vert @@ -23,7 +23,7 @@ uniform mat4 model; uniform float z_index; void main() { - gl_Position = ovm * model * vec4(vertex.xy, z_index, 1.0); + gl_Position = vec4(vertex.xy, z_index, 1.0); pos = vertex.xy; uv = uv_in; } diff --git a/include/blt/gfx/renderer/shaders/pp_outline_step.frag b/include/blt/gfx/renderer/shaders/pp_outline_step.frag new file mode 100644 index 0000000..0850140 --- /dev/null +++ b/include/blt/gfx/renderer/shaders/pp_outline_step.frag @@ -0,0 +1,48 @@ +#ifdef __cplusplus +#include +const std::string shader_pp_outline_step_frag = R"(" +#version 300 es +precision mediump float; + +out vec4 FragColor; +in vec2 uv; +in vec2 pos; + +uniform vec2 viewportSize; + +uniform sampler2D albedo; +uniform sampler2D mask; + +#define LINE_WEIGHT 3.0 + +void main() { + float dx = (1.0 / viewportSize.x) * LINE_WEIGHT; + float dy = (1.0 / viewportSize.y) * LINE_WEIGHT; + + vec2 uvCenter = uv; + vec2 uvRight = vec2(uvCenter.x + dx, uvCenter.y); + vec2 uvTop = vec2(uvCenter.x, uvCenter.y - dx); + vec2 uvTopRight = vec2(uvCenter.x + dx, uvCenter.y - dx); + + float mCenter = texture(mask, uvCenter).r; + float mTop = texture(mask, uvTop).r; + float mRight = texture(mask, uvRight).r; + float mTopRight = texture(mask, uvTopRight).r; + + float dT = abs(mCenter - mTop); + float dR = abs(mCenter - mRight); + float dTR = abs(mCenter - mTopRight); + + float delta = 0.0; + delta = max(delta, dT); + delta = max(delta, dR); + delta = max(delta, dTR); + + vec4 outline = vec4(delta, delta, delta, 1.0); + vec4 albedo = texture(albedo, uv); + + FragColor = albedo - outline; +} + +")"; +#endif \ No newline at end of file diff --git a/include/blt/gfx/renderer/shaders/pp_to_screen.frag b/include/blt/gfx/renderer/shaders/pp_screen.frag similarity index 83% rename from include/blt/gfx/renderer/shaders/pp_to_screen.frag rename to include/blt/gfx/renderer/shaders/pp_screen.frag index 307d1b8..5c97965 100644 --- a/include/blt/gfx/renderer/shaders/pp_to_screen.frag +++ b/include/blt/gfx/renderer/shaders/pp_screen.frag @@ -1,6 +1,6 @@ #ifdef __cplusplus #include -const std::string shader_pp_to_screen_frag = R"(" +const std::string shader_pp_screen_frag = R"(" #version 300 es precision mediump float; diff --git a/include/blt/gfx/texture.h b/include/blt/gfx/texture.h index 503174e..73d1e51 100644 --- a/include/blt/gfx/texture.h +++ b/include/blt/gfx/texture.h @@ -135,7 +135,7 @@ namespace blt::gfx } protected: - void setDefaults() const; + void setDefaults(GLint type) const; inline void generateMipmaps() const { diff --git a/include/blt/gfx/window.h b/include/blt/gfx/window.h index 0bacabd..59059a1 100644 --- a/include/blt/gfx/window.h +++ b/include/blt/gfx/window.h @@ -24,7 +24,7 @@ namespace blt::gfx i32 GL_MINOR = 6; i32 DOUBLE_BUFFER = GLFW_TRUE; i32 GL_PROFILE = GLFW_OPENGL_CORE_PROFILE; - i32 SAMPLES = 8; + i32 SAMPLES = 4; }; struct window_data diff --git a/src/blt/gfx/renderer/batch_2d_renderer.cpp b/src/blt/gfx/renderer/batch_2d_renderer.cpp index 4f70f6d..a8b43f0 100644 --- a/src/blt/gfx/renderer/batch_2d_renderer.cpp +++ b/src/blt/gfx/renderer/batch_2d_renderer.cpp @@ -89,8 +89,7 @@ namespace blt::gfx point_shader->bindAttribute(0, "vertex"); point_shader->bindAttribute(1, "uv_in"); - engine.addStep(std::make_unique()); - engine.create(); + engine->create(); // draw_buffer.create(); // draw_buffer.bind(); @@ -164,17 +163,18 @@ namespace blt::gfx void batch_renderer_2d::cleanup() { - engine.cleanup(); + engine->cleanup(); + engine = nullptr; delete square_vao; delete square_shader; delete point_shader; } - void batch_renderer_2d::render(i32 width, i32 height, const bool transparency) + void batch_renderer_2d::render(i32, i32, const bool transparency) { //draw_buffer.bind(); //draw_buffer.updateBuffersStorage(width, height); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + //glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (transparency) { glEnable(GL_BLEND); @@ -183,17 +183,15 @@ namespace blt::gfx glEnable(GL_DEPTH_TEST); glActiveTexture(GL_TEXTURE0); - engine.bind(); + engine->bind(); draw_objects(); glDisable(GL_DEPTH_TEST); if (transparency) glDisable(GL_BLEND); + engine->render(); render_reset(); - frame_buffer_t::unbind(); - - engine.render(); } void batch_renderer_2d::draw_objects() @@ -220,6 +218,7 @@ namespace blt::gfx square_shader->setVec4("color", render_info.color); square_shader->setVec4("use_texture", render_info.blend); + square_shader->setVec4("outline_color", render_info.outline_color); square_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); square_shader->setMatrix("model", model); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); @@ -244,6 +243,7 @@ namespace blt::gfx square_shader->setVec4("color", render_info.color); square_shader->setVec4("use_texture", render_info.blend); + square_shader->setVec4("outline_color", render_info.outline_color); square_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); // 0, 1 (top right) @@ -297,6 +297,7 @@ namespace blt::gfx point_shader->setVec4("color", render_info.color); point_shader->setVec4("use_texture", render_info.blend); + point_shader->setVec4("outline_color", render_info.outline_color); point_shader->setFloat("z_index", (z_index - draw.z_min) * denominator); point_shader->setMatrix("model", model); diff --git a/src/blt/gfx/renderer/postprocess.cpp b/src/blt/gfx/renderer/postprocess.cpp index eb0c93a..764e6b7 100644 --- a/src/blt/gfx/renderer/postprocess.cpp +++ b/src/blt/gfx/renderer/postprocess.cpp @@ -18,9 +18,11 @@ #include #include #include -#include +#include +#include #include +#define __EMSCRIPTEN__ float full_screen_vertices[20] = { 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, @@ -33,6 +35,13 @@ const unsigned int full_screen_indices[6] = { 1, 2, 3 }; +template +bool once() +{ + static bool ran = true; + return std::exchange(ran, false); +} + namespace blt::gfx { void pp_engine_t::create() @@ -53,7 +62,7 @@ namespace blt::gfx screen_vao->bindElement(indices_vbo); } #ifdef __EMSCRIPTEN__ - to_screen = std::make_unique(); + addStep(std::make_unique()); #endif for (auto& step : steps) step->create(); @@ -65,22 +74,18 @@ namespace blt::gfx 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(); + auto& previous = steps[index - 1]; + step->getBuffer().bind(frame_buffer_t::draw_t::BOTH); + step->draw(*previous); + if (step->hasShader()) + step->getShader().bind(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); } + glBindTexture(GL_TEXTURE_2D, 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 +#ifndef __EMSCRIPTEN__ steps.back()->getBuffer().blitToScreen(getWindowWidth(), getWindowHeight()); #endif } @@ -104,26 +109,73 @@ namespace blt::gfx void pp_engine_t::bind() { steps.front()->draw(); - steps.front()->getBuffer().bind(frame_buffer_t::draw_t::DRAW); + steps.front()->getBuffer().bind(); 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); + shader = pp_engine_t::createShader(shader_pp_screen_frag); } - void pp_to_screen_step_t::draw() - {} + void pp_to_screen_step_t::draw(pp_step_t& previous) + { + glActiveTexture(GL_TEXTURE0); + previous.getBuffer().getTexture(frame_buffer_t::attachment_t::COLOR0).bind(); + } 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()); + draw_buffer = frame_buffer_t::make_render_texture(getWindowWidth(), getWindowHeight()); } void pp_render_target_t::draw() { draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight()); } + + void pp_outline_target::create() + { + draw_buffer.create(); + draw_buffer.bind(); + + auto* texture = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8); + draw_buffer.attachTexture(texture, frame_buffer_t::attachment_t::COLOR0); + + auto* mask = new texture_gl2D(getWindowWidth(), getWindowHeight(), GL_RGBA8); + draw_buffer.attachTexture(mask, frame_buffer_t::attachment_t::COLOR1); + + render_buffer_t depth_rbo = render_buffer_t::make_render_buffer(GL_DEPTH24_STENCIL8, getWindowWidth(), getWindowHeight()); + draw_buffer.attachRenderBuffer(depth_rbo, frame_buffer_t::attachment_t::DEPTH_STENCIL); + + if (!frame_buffer_t::validate()) + BLT_ERROR("Failed to create render texture framebuffer!"); + frame_buffer_t::unbind(); + } + + void pp_outline_target::draw() + { + draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight()); + glEnable(GL_TEXTURE0); + glEnable(GL_TEXTURE1); + } + + void pp_outline_step_t::create() + { + draw_buffer = frame_buffer_t::make_render_texture(getWindowHeight(), getWindowHeight()); + shader = pp_engine_t::createShader(shader_pp_outline_step_frag); + shader->setInt("albedo", 0); + shader->setInt("mask", 1); + } + + void pp_outline_step_t::draw(pp_step_t& previous) + { + draw_buffer.updateBuffersStorage(getWindowWidth(), getWindowHeight()); + glActiveTexture(GL_TEXTURE0); + previous.getBuffer().getTexture(frame_buffer_t::attachment_t::COLOR0).bind(); + glActiveTexture(GL_TEXTURE1); + previous.getBuffer().getTexture(frame_buffer_t::attachment_t::COLOR1).bind(); + shader->bind(); + shader->setVec2("viewportSize", static_cast(getWindowWidth()), static_cast(getWindowHeight())); + } } \ No newline at end of file diff --git a/src/blt/gfx/texture.cpp b/src/blt/gfx/texture.cpp index 7c34e63..b0296b3 100644 --- a/src/blt/gfx/texture.cpp +++ b/src/blt/gfx/texture.cpp @@ -152,7 +152,7 @@ blt::gfx::texture_file& blt::gfx::texture_file::resize(int target_width, int tar return *this; } -void blt::gfx::texture_gl::setDefaults() const +void blt::gfx::texture_gl::setDefaults(GLint type) const { glTexParameteri(textureBindType, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(textureBindType, GL_TEXTURE_WRAP_T, GL_REPEAT); @@ -161,8 +161,10 @@ void blt::gfx::texture_gl::setDefaults() const glTexParameteri(textureBindType, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(textureBindType, GL_TEXTURE_MAG_FILTER, GL_NEAREST); #else - glTexParameteri(textureBindType, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); - glTexParameteri(textureBindType, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_LINEAR); +// glTexParameteri(textureBindType, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR); +// glTexParameteri(textureBindType, GL_TEXTURE_MAG_FILTER, GL_NEAREST_MIPMAP_LINEAR); + glTexParameteri(textureBindType, GL_TEXTURE_MIN_FILTER, type); + glTexParameteri(textureBindType, GL_TEXTURE_MAG_FILTER, type); #endif #ifdef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT // Anisotropy helps preserve textures at oblique angles @@ -213,7 +215,7 @@ void blt::gfx::texture_gl2D::resize(int width, int height) blt::gfx::texture_gl2D::texture_gl2D(int width, int height, GLint colorMode): texture_gl(width, height, GL_TEXTURE_2D, colorMode) { bind(); - setDefaults(); + setDefaults(GL_LINEAR); resize(width, height); //glTexStorage2D(textureBindType, 4, colorMode, width, height); } @@ -222,7 +224,7 @@ blt::gfx::texture_gl2D::texture_gl2D(const blt::gfx::texture_data& data): texture_gl(data.width(), data.height(), GL_TEXTURE_2D, data.channels() == 4 ? GL_RGBA8 : GL_RGB8) { bind(); - setDefaults(); + setDefaults(GL_NEAREST); glTexStorage2D(textureBindType, 4, textureColorMode, data.width(), data.height()); upload((void*) data.data(), data.channels() == 4 ? GL_RGBA : GL_RGB, 0, 0, 0, data.width(), data.height()); bind(); @@ -247,7 +249,7 @@ blt::gfx::texture_gl2D_multisample::texture_gl2D_multisample(int width, int heig texture_gl(width, height, GL_TEXTURE_2D_MULTISAMPLE, colorMode), samples(samples) { bind(); - setDefaults(); + setDefaults(GL_LINEAR); //glTexImage2DMultisample(textureBindType, samples, colorMode, width, height, GL_TRUE); glTexStorage2DMultisample(textureBindType, samples, colorMode, width, height, GL_TRUE); } @@ -262,7 +264,7 @@ void blt::gfx::texture_gl2D_multisample::resize(int width, int height) blt::gfx::gl_texture2D_array::gl_texture2D_array(int width, int height, int layers, GLint colorMode): texture_gl(width, height, layers, colorMode) { bind(); - setDefaults(); + setDefaults(GL_LINEAR); // 6+ mipmaps is about where I stop noticing any difference (size is 4x4 pixels, so that makes sense) glTexStorage3D(textureBindType, 6, colorMode, width, height, layers); BLT_DEBUG("Creating 2D Texture Array with ID: %d", textureID);