From f4d46b0ca7a408d14a7bfb4194cbc404be46684e Mon Sep 17 00:00:00 2001 From: "C. J. Howard" Date: Mon, 16 Jan 2023 01:09:19 +0800 Subject: [PATCH] Add support for FXAA --- src/game/context.hpp | 16 +++- src/game/graphics.cpp | 139 ++++++++++++++++++++++++--- src/game/graphics.hpp | 3 + src/game/state/boot.cpp | 63 ++++++++++-- src/game/state/graphics-menu.cpp | 142 ++++++++++++++++++++++++---- src/render/anti-aliasing-method.hpp | 39 ++++++++ src/render/pass.cpp | 5 + src/render/pass.hpp | 2 + src/render/passes/bloom-pass.cpp | 6 +- src/render/passes/final-pass.cpp | 18 ++-- src/render/passes/fxaa-pass.cpp | 113 ++++++++++++++++++++++ src/render/passes/fxaa-pass.hpp | 84 ++++++++++++++++ src/render/passes/resample-pass.cpp | 105 ++++++++++++++++++++ src/render/passes/resample-pass.hpp | 81 ++++++++++++++++ 14 files changed, 760 insertions(+), 56 deletions(-) create mode 100644 src/render/anti-aliasing-method.hpp create mode 100644 src/render/passes/fxaa-pass.cpp create mode 100644 src/render/passes/fxaa-pass.hpp create mode 100644 src/render/passes/resample-pass.cpp create mode 100644 src/render/passes/resample-pass.hpp diff --git a/src/game/context.hpp b/src/game/context.hpp index 8b488ac..85744bd 100644 --- a/src/game/context.hpp +++ b/src/game/context.hpp @@ -50,6 +50,7 @@ #include "type/bitmap-font.hpp" #include "render/material.hpp" #include "render/material-property.hpp" +#include "render/anti-aliasing-method.hpp" #include "ui/mouse-tracker.hpp" #include "application.hpp" #include "game/state/base.hpp" @@ -108,6 +109,8 @@ namespace render class clear_pass; class compositor; class final_pass; + class fxaa_pass; + class resample_pass; class material_pass; class renderer; class outline_pass; @@ -185,6 +188,10 @@ struct context gl::texture_2d* hdr_color_texture; gl::texture_2d* hdr_depth_texture; gl::framebuffer* hdr_framebuffer; + gl::texture_2d* ldr_color_texture_a; + gl::framebuffer* ldr_framebuffer_a; + gl::texture_2d* ldr_color_texture_b; + gl::framebuffer* ldr_framebuffer_b; gl::texture_2d* shadow_map_depth_texture; gl::framebuffer* shadow_map_framebuffer; @@ -192,7 +199,7 @@ struct context gl::rasterizer* rasterizer; render::renderer* renderer; int2 render_resolution; - float render_resolution_scale; + float render_scale; gl::vertex_buffer* billboard_vbo; gl::vertex_array* billboard_vao; render::material* fallback_material; @@ -201,8 +208,10 @@ struct context render::clear_pass* ui_clear_pass; render::material_pass* ui_material_pass; render::compositor* ui_compositor; - render::bloom_pass* common_bloom_pass; + render::bloom_pass* bloom_pass; render::final_pass* common_final_pass; + render::fxaa_pass* fxaa_pass; + render::resample_pass* resample_pass; render::clear_pass* underground_clear_pass; render::material_pass* underground_material_pass; render::compositor* underground_compositor; @@ -296,6 +305,9 @@ struct context double3 rgb_wavelengths; const ecoregion* active_ecoregion; + + bool bloom_enabled; + render::anti_aliasing_method anti_aliasing_method; }; } // namespace game diff --git a/src/game/graphics.cpp b/src/game/graphics.cpp index b725e98..2b57774 100644 --- a/src/game/graphics.cpp +++ b/src/game/graphics.cpp @@ -19,6 +19,9 @@ #include "game/graphics.hpp" #include "render/passes/bloom-pass.hpp" +#include "render/passes/fxaa-pass.hpp" +#include "render/passes/final-pass.hpp" +#include "render/passes/resample-pass.hpp" #include "gl/framebuffer.hpp" #include "gl/texture-2d.hpp" #include "gl/texture-wrapping.hpp" @@ -33,20 +36,20 @@ namespace game { namespace graphics { -static void resize_framebuffer_attachment(gl::texture_2d& texture, const int2& resolution); +static void reroute_framebuffers(game::context& ctx); void create_framebuffers(game::context& ctx) { ctx.logger->push_task("Creating framebuffers"); // Load render resolution scale from config - ctx.render_resolution_scale = 1.0f; - if (ctx.config->contains("render_resolution")) - ctx.render_resolution_scale = (*ctx.config)["render_resolution"].get(); + ctx.render_scale = 1.0f; + if (ctx.config->contains("render_scale")) + ctx.render_scale = (*ctx.config)["render_scale"].get(); // Calculate render resolution const int2& viewport_dimensions = ctx.app->get_viewport_dimensions(); - ctx.render_resolution = {static_cast(viewport_dimensions.x() * ctx.render_resolution_scale + 0.5f), static_cast(viewport_dimensions.y() * ctx.render_resolution_scale + 0.5f)}; + ctx.render_resolution = {static_cast(viewport_dimensions.x() * ctx.render_scale + 0.5f), static_cast(viewport_dimensions.y() * ctx.render_scale + 0.5f)}; // Create HDR framebuffer (32F color, 32F depth) ctx.hdr_color_texture = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::float_32, gl::pixel_format::rgb); @@ -62,6 +65,21 @@ void create_framebuffers(game::context& ctx) ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::depth, ctx.hdr_depth_texture); ctx.hdr_framebuffer->attach(gl::framebuffer_attachment_type::stencil, ctx.hdr_depth_texture); + // Create LDR framebuffers (8-bit color, no depth) + ctx.ldr_color_texture_a = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::uint_8, gl::pixel_format::rgb); + ctx.ldr_color_texture_a->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); + ctx.ldr_color_texture_a->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); + ctx.ldr_color_texture_a->set_max_anisotropy(0.0f); + ctx.ldr_framebuffer_a = new gl::framebuffer(ctx.render_resolution.x(), ctx.render_resolution.y()); + ctx.ldr_framebuffer_a->attach(gl::framebuffer_attachment_type::color, ctx.ldr_color_texture_a); + + ctx.ldr_color_texture_b = new gl::texture_2d(ctx.render_resolution.x(), ctx.render_resolution.y(), gl::pixel_type::uint_8, gl::pixel_format::rgb); + ctx.ldr_color_texture_b->set_wrapping(gl::texture_wrapping::extend, gl::texture_wrapping::extend); + ctx.ldr_color_texture_b->set_filters(gl::texture_min_filter::linear, gl::texture_mag_filter::linear); + ctx.ldr_color_texture_b->set_max_anisotropy(0.0f); + ctx.ldr_framebuffer_b = new gl::framebuffer(ctx.render_resolution.x(), ctx.render_resolution.y()); + ctx.ldr_framebuffer_b->attach(gl::framebuffer_attachment_type::color, ctx.ldr_color_texture_b); + // Load shadow map resolution from config int shadow_map_resolution = 4096; if (ctx.config->contains("shadow_map_resolution")) @@ -90,6 +108,17 @@ void destroy_framebuffers(game::context& ctx) delete ctx.hdr_depth_texture; ctx.hdr_depth_texture = nullptr; + // Delete LDR framebuffers and attachments + delete ctx.ldr_framebuffer_a; + ctx.ldr_framebuffer_a = nullptr; + delete ctx.ldr_color_texture_a; + ctx.ldr_color_texture_a = nullptr; + + delete ctx.ldr_framebuffer_b; + ctx.ldr_framebuffer_b = nullptr; + delete ctx.ldr_color_texture_b; + ctx.ldr_color_texture_b = nullptr; + // Delete shadow map framebuffer and its attachments delete ctx.shadow_map_framebuffer; ctx.shadow_map_framebuffer = nullptr; @@ -104,28 +133,40 @@ void change_render_resolution(game::context& ctx, float scale) ctx.logger->push_task("Changing render resolution"); // Update render resolution scale - ctx.render_resolution_scale = scale; + ctx.render_scale = scale; // Recalculate render resolution const int2& viewport_dimensions = ctx.app->get_viewport_dimensions(); - ctx.render_resolution = {static_cast(viewport_dimensions.x() * ctx.render_resolution_scale + 0.5f), static_cast(viewport_dimensions.y() * ctx.render_resolution_scale + 0.5f)}; + ctx.render_resolution = {static_cast(viewport_dimensions.x() * ctx.render_scale + 0.5f), static_cast(viewport_dimensions.y() * ctx.render_scale + 0.5f)}; // Resize HDR framebuffer and attachments ctx.hdr_framebuffer->resize({ctx.render_resolution.x(), ctx.render_resolution.y()}); - resize_framebuffer_attachment(*ctx.hdr_color_texture, ctx.render_resolution); - resize_framebuffer_attachment(*ctx.hdr_depth_texture, ctx.render_resolution); + ctx.hdr_color_texture->resize(ctx.render_resolution.x(), ctx.render_resolution.y(), nullptr); + ctx.hdr_depth_texture->resize(ctx.render_resolution.x(), ctx.render_resolution.y(), nullptr); + + // Resize LDR framebuffers and attachments + ctx.ldr_framebuffer_a->resize({ctx.render_resolution.x(), ctx.render_resolution.y()}); + ctx.ldr_color_texture_a->resize(ctx.render_resolution.x(), ctx.render_resolution.y(), nullptr); + ctx.ldr_framebuffer_b->resize({ctx.render_resolution.x(), ctx.render_resolution.y()}); + ctx.ldr_color_texture_b->resize(ctx.render_resolution.x(), ctx.render_resolution.y(), nullptr); // Resize bloom render pass - ctx.common_bloom_pass->resize(); + ctx.bloom_pass->resize(); + + // Enable or disable resample pass + if (viewport_dimensions.x() != ctx.render_resolution.x() || viewport_dimensions.y() != ctx.render_resolution.y()) + { + ctx.resample_pass->set_enabled(true); + } + else + { + ctx.resample_pass->set_enabled(false); + } + reroute_framebuffers(ctx); ctx.logger->pop_task(EXIT_SUCCESS); } -void resize_framebuffer_attachment(gl::texture_2d& texture, const int2& resolution) -{ - texture.resize(resolution.x(), resolution.y(), texture.get_pixel_type(), texture.get_pixel_format(), texture.get_color_space(), nullptr); -} - void save_screenshot(game::context& ctx) { // Determine screenshot path @@ -158,5 +199,73 @@ void save_screenshot(game::context& ctx) ctx.logger->pop_task(EXIT_SUCCESS); } +void toggle_bloom(game::context& ctx, bool enabled) +{ + if (enabled) + { + ctx.bloom_pass->set_mip_chain_length(6); + ctx.bloom_pass->set_enabled(true); + ctx.common_final_pass->set_bloom_weight(0.04f); + } + else + { + ctx.bloom_pass->set_mip_chain_length(0); + ctx.bloom_pass->set_enabled(false); + ctx.common_final_pass->set_bloom_weight(0.0f); + } + + ctx.bloom_enabled = enabled; +} + +void select_anti_aliasing_method(game::context& ctx, render::anti_aliasing_method method) +{ + // Switch AA method + switch (method) + { + // Off + case render::anti_aliasing_method::none: + ctx.fxaa_pass->set_enabled(false); + reroute_framebuffers(ctx); + break; + + // FXAA + case render::anti_aliasing_method::fxaa: + ctx.fxaa_pass->set_enabled(true); + reroute_framebuffers(ctx); + break; + } + + // Update AA method setting + ctx.anti_aliasing_method = method; +} + +void reroute_framebuffers(game::context& ctx) +{ + if (ctx.fxaa_pass->is_enabled()) + { + if (ctx.resample_pass->is_enabled()) + { + ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a); + ctx.fxaa_pass->set_framebuffer(ctx.ldr_framebuffer_b); + } + else + { + ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a); + ctx.fxaa_pass->set_framebuffer(&ctx.rasterizer->get_default_framebuffer()); + } + } + else + { + if (ctx.resample_pass->is_enabled()) + { + ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_b); + } + else + { + ctx.common_final_pass->set_framebuffer(&ctx.rasterizer->get_default_framebuffer()); + } + } +} + } // namespace graphics } // namespace game diff --git a/src/game/graphics.hpp b/src/game/graphics.hpp index ac91ce5..7fa7482 100644 --- a/src/game/graphics.hpp +++ b/src/game/graphics.hpp @@ -21,6 +21,7 @@ #define ANTKEEPER_GAME_GRAPHICS_HPP #include "game/context.hpp" +#include "render/anti-aliasing-method.hpp" namespace game { namespace graphics { @@ -29,6 +30,8 @@ void create_framebuffers(game::context& ctx); void destroy_framebuffers(game::context& ctx); void change_render_resolution(game::context& ctx, float scale); void save_screenshot(game::context& ctx); +void toggle_bloom(game::context& ctx, bool enabled); +void select_anti_aliasing_method(game::context& ctx, render::anti_aliasing_method method); } // namespace graphics } // namespace game diff --git a/src/game/state/boot.cpp b/src/game/state/boot.cpp index eddb850..c05007f 100644 --- a/src/game/state/boot.cpp +++ b/src/game/state/boot.cpp @@ -43,6 +43,8 @@ #include "render/passes/bloom-pass.hpp" #include "render/passes/clear-pass.hpp" #include "render/passes/final-pass.hpp" +#include "render/passes/fxaa-pass.hpp" +#include "render/passes/resample-pass.hpp" #include "render/passes/material-pass.hpp" #include "render/passes/outline-pass.hpp" #include "render/passes/shadow-map-pass.hpp" @@ -447,16 +449,57 @@ void boot::setup_rendering() // Setup common render passes { - ctx.common_bloom_pass = new render::bloom_pass(ctx.rasterizer, ctx.resource_manager); - ctx.common_bloom_pass->set_source_texture(ctx.hdr_color_texture); - ctx.common_bloom_pass->set_mip_chain_length(6); - ctx.common_bloom_pass->set_filter_radius(0.005f); + // Construct bloom pass + ctx.bloom_pass = new render::bloom_pass(ctx.rasterizer, ctx.resource_manager); + ctx.bloom_pass->set_source_texture(ctx.hdr_color_texture); + ctx.bloom_pass->set_mip_chain_length(0); + ctx.bloom_pass->set_filter_radius(0.005f); - ctx.common_final_pass = new render::final_pass(ctx.rasterizer, &ctx.rasterizer->get_default_framebuffer(), ctx.resource_manager); + ctx.common_final_pass = new render::final_pass(ctx.rasterizer, ctx.ldr_framebuffer_a, ctx.resource_manager); ctx.common_final_pass->set_color_texture(ctx.hdr_color_texture); - ctx.common_final_pass->set_bloom_texture(ctx.common_bloom_pass->get_bloom_texture()); + ctx.common_final_pass->set_bloom_texture(ctx.bloom_pass->get_bloom_texture()); ctx.common_final_pass->set_bloom_weight(0.04f); ctx.common_final_pass->set_blue_noise_texture(blue_noise_map); + + ctx.fxaa_pass = new render::fxaa_pass(ctx.rasterizer, &ctx.rasterizer->get_default_framebuffer(), ctx.resource_manager); + ctx.fxaa_pass->set_source_texture(ctx.ldr_color_texture_a); + + ctx.resample_pass = new render::resample_pass(ctx.rasterizer, &ctx.rasterizer->get_default_framebuffer(), ctx.resource_manager); + ctx.resample_pass->set_source_texture(ctx.ldr_color_texture_b); + ctx.resample_pass->set_enabled(false); + + // Toggle bloom according to settings + ctx.bloom_enabled = true; + if (ctx.config->contains("bloom_enabled")) + ctx.bloom_enabled = (*ctx.config)["bloom_enabled"].get(); + graphics::toggle_bloom(ctx, ctx.bloom_enabled); + + // Configure anti-aliasing according to settings + ctx.anti_aliasing_method = render::anti_aliasing_method::fxaa; + if (ctx.config->contains("anti_aliasing_method")) + { + const std::string aa_method = (*ctx.config)["anti_aliasing_method"].get(); + if (aa_method == "fxaa") + { + ctx.anti_aliasing_method = render::anti_aliasing_method::fxaa; + } + else + { + ctx.anti_aliasing_method = render::anti_aliasing_method::none; + } + } + graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); + + // Configure render scaling according to settings + ctx.render_scale = 1.0f; + if (ctx.config->contains("render_scale")) + { + ctx.render_scale = (*ctx.config)["render_scale"].get(); + if (ctx.render_scale != 1.0f) + { + graphics::change_render_resolution(ctx, ctx.render_scale); + } + } } // Setup UI compositor @@ -487,8 +530,10 @@ void boot::setup_rendering() ctx.underground_compositor = new render::compositor(); ctx.underground_compositor->add_pass(ctx.underground_clear_pass); ctx.underground_compositor->add_pass(ctx.underground_material_pass); - ctx.underground_compositor->add_pass(ctx.common_bloom_pass); + ctx.underground_compositor->add_pass(ctx.bloom_pass); ctx.underground_compositor->add_pass(ctx.common_final_pass); + ctx.underground_compositor->add_pass(ctx.fxaa_pass); + ctx.underground_compositor->add_pass(ctx.resample_pass); } // Setup surface compositor @@ -527,8 +572,10 @@ void boot::setup_rendering() ctx.surface_compositor->add_pass(ctx.ground_pass); ctx.surface_compositor->add_pass(ctx.surface_material_pass); //ctx.surface_compositor->add_pass(ctx.surface_outline_pass); - ctx.surface_compositor->add_pass(ctx.common_bloom_pass); + ctx.surface_compositor->add_pass(ctx.bloom_pass); ctx.surface_compositor->add_pass(ctx.common_final_pass); + ctx.surface_compositor->add_pass(ctx.fxaa_pass); + ctx.surface_compositor->add_pass(ctx.resample_pass); } // Create billboard VAO diff --git a/src/game/state/graphics-menu.cpp b/src/game/state/graphics-menu.cpp index 177ec98..5c83581 100644 --- a/src/game/state/graphics-menu.cpp +++ b/src/game/state/graphics-menu.cpp @@ -44,6 +44,10 @@ graphics_menu::graphics_menu(game::context& ctx): scene::text* resolution_value_text = new scene::text(); scene::text* v_sync_name_text = new scene::text(); scene::text* v_sync_value_text = new scene::text(); + scene::text* aa_method_name_text = new scene::text(); + scene::text* aa_method_value_text = new scene::text(); + scene::text* bloom_name_text = new scene::text(); + scene::text* bloom_value_text = new scene::text(); scene::text* font_size_name_text = new scene::text(); scene::text* font_size_value_text = new scene::text(); scene::text* dyslexia_font_name_text = new scene::text(); @@ -54,6 +58,8 @@ graphics_menu::graphics_menu(game::context& ctx): ctx.menu_item_texts.push_back({fullscreen_name_text, fullscreen_value_text}); ctx.menu_item_texts.push_back({resolution_name_text, resolution_value_text}); ctx.menu_item_texts.push_back({v_sync_name_text, v_sync_value_text}); + ctx.menu_item_texts.push_back({aa_method_name_text, aa_method_value_text}); + ctx.menu_item_texts.push_back({bloom_name_text, bloom_value_text}); ctx.menu_item_texts.push_back({font_size_name_text, font_size_value_text}); ctx.menu_item_texts.push_back({dyslexia_font_name_text, dyslexia_font_value_text}); ctx.menu_item_texts.push_back({back_text, nullptr}); @@ -62,6 +68,8 @@ graphics_menu::graphics_menu(game::context& ctx): fullscreen_name_text->set_content((*ctx.strings)["graphics_menu_fullscreen"]); resolution_name_text->set_content((*ctx.strings)["graphics_menu_resolution"]); v_sync_name_text->set_content((*ctx.strings)["graphics_menu_v_sync"]); + aa_method_name_text->set_content((*ctx.strings)["graphics_menu_aa_method"]); + bloom_name_text->set_content((*ctx.strings)["graphics_menu_bloom"]); font_size_name_text->set_content((*ctx.strings)["graphics_menu_font_size"]); dyslexia_font_name_text->set_content((*ctx.strings)["graphics_menu_dyslexia_font"]); back_text->set_content((*ctx.strings)["back"]); @@ -105,16 +113,16 @@ graphics_menu::graphics_menu(game::context& ctx): { // Increase resolution if (ctx.controls["menu_modifier"]->is_active()) - ctx.render_resolution_scale += 0.05f; + ctx.render_scale += 0.05f; else - ctx.render_resolution_scale += 0.25f; + ctx.render_scale += 0.25f; // Limit resolution - if (ctx.render_resolution_scale > 2.0f) - ctx.render_resolution_scale = 2.0f; + if (ctx.render_scale > 2.0f) + ctx.render_scale = 2.0f; // Resize framebuffers - game::graphics::change_render_resolution(ctx, ctx.render_resolution_scale); + game::graphics::change_render_resolution(ctx, ctx.render_scale); // Update text this->update_value_text_content(); @@ -122,23 +130,23 @@ graphics_menu::graphics_menu(game::context& ctx): game::menu::update_text_tweens(ctx); // Update config - (*ctx.config)["render_resolution"] = ctx.render_resolution_scale; + (*ctx.config)["render_scale"] = ctx.render_scale; }; auto decrease_resolution_callback = [this, &ctx]() { // Increase resolution if (ctx.controls["menu_modifier"]->is_active()) - ctx.render_resolution_scale -= 0.05f; + ctx.render_scale -= 0.05f; else - ctx.render_resolution_scale -= 0.25f; + ctx.render_scale -= 0.25f; // Limit resolution - if (ctx.render_resolution_scale < 0.25f) - ctx.render_resolution_scale = 0.25f; + if (ctx.render_scale < 0.25f) + ctx.render_scale = 0.25f; // Resize framebuffers - game::graphics::change_render_resolution(ctx, ctx.render_resolution_scale); + game::graphics::change_render_resolution(ctx, ctx.render_scale); // Update text this->update_value_text_content(); @@ -146,7 +154,7 @@ graphics_menu::graphics_menu(game::context& ctx): game::menu::update_text_tweens(ctx); // Update config - (*ctx.config)["render_resolution"] = ctx.render_resolution_scale; + (*ctx.config)["render_scale"] = ctx.render_scale; }; auto toggle_v_sync_callback = [this, &ctx]() @@ -163,6 +171,74 @@ graphics_menu::graphics_menu(game::context& ctx): (*ctx.config)["v_sync"] = v_sync; }; + auto next_aa_method_callback = [this, &ctx]() + { + switch (ctx.anti_aliasing_method) + { + case render::anti_aliasing_method::none: + ctx.anti_aliasing_method = render::anti_aliasing_method::fxaa; + (*ctx.config)["anti_aliasing_method"] = "fxaa"; + break; + + case render::anti_aliasing_method::fxaa: + ctx.anti_aliasing_method = render::anti_aliasing_method::none; + (*ctx.config)["anti_aliasing_method"] = "none"; + break; + } + + game::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); + + // Update value text + this->update_value_text_content(); + + // Refresh and realign text + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + }; + + auto previous_aa_method_callback = [this, &ctx]() + { + switch (ctx.anti_aliasing_method) + { + case render::anti_aliasing_method::none: + ctx.anti_aliasing_method = render::anti_aliasing_method::fxaa; + (*ctx.config)["anti_aliasing_method"] = "fxaa"; + break; + + case render::anti_aliasing_method::fxaa: + ctx.anti_aliasing_method = render::anti_aliasing_method::none; + (*ctx.config)["anti_aliasing_method"] = "none"; + break; + } + + game::graphics::select_anti_aliasing_method(ctx, ctx.anti_aliasing_method); + + // Update value text + this->update_value_text_content(); + + // Refresh and realign text + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + }; + + auto toggle_bloom_callback = [this, &ctx]() + { + game::graphics::toggle_bloom(ctx, !ctx.bloom_enabled); + + // Update value text + this->update_value_text_content(); + + // Update config + (*ctx.config)["bloom_enabled"] = ctx.bloom_enabled; + + // Refresh and realign text + game::menu::refresh_text(ctx); + game::menu::align_text(ctx); + game::menu::update_text_tweens(ctx); + }; + auto increase_font_size_callback = [this, &ctx]() { // Increase font size @@ -289,6 +365,8 @@ graphics_menu::graphics_menu(game::context& ctx): ctx.menu_select_callbacks.push_back(toggle_fullscreen_callback); ctx.menu_select_callbacks.push_back(increase_resolution_callback); ctx.menu_select_callbacks.push_back(toggle_v_sync_callback); + ctx.menu_select_callbacks.push_back(next_aa_method_callback); + ctx.menu_select_callbacks.push_back(toggle_bloom_callback); ctx.menu_select_callbacks.push_back(increase_font_size_callback); ctx.menu_select_callbacks.push_back(toggle_dyslexia_font_callback); ctx.menu_select_callbacks.push_back(select_back_callback); @@ -297,6 +375,8 @@ graphics_menu::graphics_menu(game::context& ctx): ctx.menu_left_callbacks.push_back(toggle_fullscreen_callback); ctx.menu_left_callbacks.push_back(decrease_resolution_callback); ctx.menu_left_callbacks.push_back(toggle_v_sync_callback); + ctx.menu_left_callbacks.push_back(previous_aa_method_callback); + ctx.menu_left_callbacks.push_back(toggle_bloom_callback); ctx.menu_left_callbacks.push_back(decrease_font_size_callback); ctx.menu_left_callbacks.push_back(toggle_dyslexia_font_callback); ctx.menu_left_callbacks.push_back(nullptr); @@ -305,6 +385,8 @@ graphics_menu::graphics_menu(game::context& ctx): ctx.menu_right_callbacks.push_back(toggle_fullscreen_callback); ctx.menu_right_callbacks.push_back(increase_resolution_callback); ctx.menu_right_callbacks.push_back(toggle_v_sync_callback); + ctx.menu_right_callbacks.push_back(next_aa_method_callback); + ctx.menu_right_callbacks.push_back(toggle_bloom_callback); ctx.menu_right_callbacks.push_back(increase_font_size_callback); ctx.menu_right_callbacks.push_back(toggle_dyslexia_font_callback); ctx.menu_right_callbacks.push_back(nullptr); @@ -337,20 +419,40 @@ graphics_menu::~graphics_menu() void graphics_menu::update_value_text_content() { - bool fullscreen = ctx.app->is_fullscreen(); - float resolution = ctx.render_resolution_scale; - bool v_sync = ctx.app->get_v_sync(); - float font_size = ctx.font_size; - bool dyslexia_font = ctx.dyslexia_font; + const bool fullscreen = ctx.app->is_fullscreen(); + const float render_scale = ctx.render_scale; + const bool v_sync = ctx.app->get_v_sync(); + const int aa_method_index = static_cast(ctx.anti_aliasing_method); + const bool bloom_enabled = ctx.bloom_enabled; + const float font_size = ctx.font_size; + const bool dyslexia_font = ctx.dyslexia_font; const std::string string_on = (*ctx.strings)["on"]; const std::string string_off = (*ctx.strings)["off"]; + /* + const std::string string_quality[4] = + { + (*ctx.strings)["off"], + (*ctx.strings)["quality_low"], + (*ctx.strings)["quality_medium"], + (*ctx.strings)["quality_high"] + }; + */ + + const std::string string_aa_methods[2] = + { + (*ctx.strings)["graphics_menu_aa_method_none"], + (*ctx.strings)["graphics_menu_aa_method_fxaa"] + }; + std::get<1>(ctx.menu_item_texts[0])->set_content((fullscreen) ? string_on : string_off); - std::get<1>(ctx.menu_item_texts[1])->set_content(std::to_string(static_cast(std::round(resolution * 100.0f))) + "%"); + std::get<1>(ctx.menu_item_texts[1])->set_content(std::to_string(static_cast(std::round(render_scale * 100.0f))) + "%"); std::get<1>(ctx.menu_item_texts[2])->set_content((v_sync) ? string_on : string_off); - std::get<1>(ctx.menu_item_texts[3])->set_content(std::to_string(static_cast(std::round(font_size * 100.0f))) + "%"); - std::get<1>(ctx.menu_item_texts[4])->set_content((dyslexia_font) ? string_on : string_off); + std::get<1>(ctx.menu_item_texts[3])->set_content(string_aa_methods[aa_method_index]); + std::get<1>(ctx.menu_item_texts[4])->set_content((bloom_enabled) ? string_on : string_off); + std::get<1>(ctx.menu_item_texts[5])->set_content(std::to_string(static_cast(std::round(font_size * 100.0f))) + "%"); + std::get<1>(ctx.menu_item_texts[6])->set_content((dyslexia_font) ? string_on : string_off); } } // namespace state diff --git a/src/render/anti-aliasing-method.hpp b/src/render/anti-aliasing-method.hpp new file mode 100644 index 0000000..78129fa --- /dev/null +++ b/src/render/anti-aliasing-method.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_ANTI_ALIASING_METHOD_HPP +#define ANTKEEPER_RENDER_ANTI_ALIASING_METHOD_HPP + +namespace render { + +/** + * Anti-aliasing methods. + */ +enum class anti_aliasing_method +{ + /// No anti-aliasing. + none, + + /// Fast approximate anti-aliasing (FXAA). + fxaa +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_ANTI_ALIASING_METHOD_HPP diff --git a/src/render/pass.cpp b/src/render/pass.cpp index d98fa18..f76b641 100644 --- a/src/render/pass.cpp +++ b/src/render/pass.cpp @@ -35,4 +35,9 @@ void pass::set_enabled(bool enabled) this->enabled = enabled; } +void pass::set_framebuffer(const gl::framebuffer* framebuffer) +{ + this->framebuffer = framebuffer; +} + } // namespace render diff --git a/src/render/pass.hpp b/src/render/pass.hpp index 0997227..8c8e7d9 100644 --- a/src/render/pass.hpp +++ b/src/render/pass.hpp @@ -40,6 +40,8 @@ public: void set_enabled(bool enabled); bool is_enabled() const; + + void set_framebuffer(const gl::framebuffer* framebuffer); protected: gl::rasterizer* rasterizer; diff --git a/src/render/passes/bloom-pass.cpp b/src/render/passes/bloom-pass.cpp index 23f0e91..73b55f7 100644 --- a/src/render/passes/bloom-pass.cpp +++ b/src/render/passes/bloom-pass.cpp @@ -35,7 +35,6 @@ #include #include #include -#include namespace render { @@ -46,7 +45,7 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma filter_radius(0.005f), corrected_filter_radius{filter_radius, filter_radius} { - // Load downsample with Karis average shader + // Load downsample shader with Karis average downsample_karis_shader = resource_manager->load("bloom-downsample-karis.glsl"); downsample_karis_source_texture_input = downsample_karis_shader->get_input("source_texture"); @@ -91,6 +90,9 @@ bloom_pass::bloom_pass(gl::rasterizer* rasterizer, resource_manager* resource_ma bloom_pass::~bloom_pass() { set_mip_chain_length(0); + + delete quad_vao; + delete quad_vbo; } void bloom_pass::render(const render::context& ctx, render::queue& queue) const diff --git a/src/render/passes/final-pass.cpp b/src/render/passes/final-pass.cpp index e4e822c..21821bd 100644 --- a/src/render/passes/final-pass.cpp +++ b/src/render/passes/final-pass.cpp @@ -56,15 +56,15 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb const float vertex_data[] = { - -1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - 1.0f, 1.0f, 0.0f, - -1.0f, -1.0f, 0.0f, - 1.0f, -1.0f, 0.0f + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f }; - - std::size_t vertex_size = 3; + + std::size_t vertex_size = 2; std::size_t vertex_stride = sizeof(float) * vertex_size; std::size_t vertex_count = 6; @@ -77,7 +77,7 @@ final_pass::final_pass(gl::rasterizer* rasterizer, const gl::framebuffer* frameb position_attribute.offset = 0; position_attribute.stride = vertex_stride; position_attribute.type = gl::vertex_attribute_type::float_32; - position_attribute.components = 3; + position_attribute.components = 2; // Bind vertex attributes to VAO quad_vao->bind(render::vertex_attribute::position, position_attribute); diff --git a/src/render/passes/fxaa-pass.cpp b/src/render/passes/fxaa-pass.cpp new file mode 100644 index 0000000..3adc5cf --- /dev/null +++ b/src/render/passes/fxaa-pass.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "render/passes/fxaa-pass.hpp" +#include "resources/resource-manager.hpp" +#include "gl/rasterizer.hpp" +#include "gl/framebuffer.hpp" +#include "gl/shader-program.hpp" +#include "gl/shader-input.hpp" +#include "gl/vertex-buffer.hpp" +#include "gl/vertex-array.hpp" +#include "gl/vertex-attribute.hpp" +#include "gl/drawing-mode.hpp" +#include "gl/texture-2d.hpp" +#include "render/vertex-attribute.hpp" +#include "render/context.hpp" +#include + +namespace render { + +fxaa_pass::fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + source_texture(nullptr) +{ + // Load FXAA shader + shader = resource_manager->load("fxaa.glsl"); + source_texture_input = shader->get_input("source_texture"); + texel_size_input = shader->get_input("texel_size"); + + const float vertex_data[] = + { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f + }; + + std::size_t vertex_size = 2; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new gl::vertex_array(); + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = quad_vbo; + position_attribute.offset = 0; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 2; + + // Bind vertex attributes to VAO + quad_vao->bind(render::vertex_attribute::position, position_attribute); +} + +fxaa_pass::~fxaa_pass() +{ + delete quad_vao; + delete quad_vbo; +} + +void fxaa_pass::render(const render::context& ctx, render::queue& queue) const +{ + if (!source_texture) + return; + + // Set rasterizer state + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + + // Render FXAA + rasterizer->use_framebuffer(*framebuffer); + rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); + rasterizer->use_program(*shader); + source_texture_input->upload(source_texture); + + if (texel_size_input) + { + const float2 texel_size = 1.0f / float2{static_cast(source_texture->get_width()), static_cast(source_texture->get_height())}; + texel_size_input->upload(texel_size); + } + + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); +} + +void fxaa_pass::set_source_texture(const gl::texture_2d* texture) +{ + source_texture = texture; +} + +} // namespace render diff --git a/src/render/passes/fxaa-pass.hpp b/src/render/passes/fxaa-pass.hpp new file mode 100644 index 0000000..432f5e7 --- /dev/null +++ b/src/render/passes/fxaa-pass.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_FXAA_PASS_HPP +#define ANTKEEPER_RENDER_FXAA_PASS_HPP + +#include "render/pass.hpp" +#include "gl/shader-program.hpp" +#include "gl/shader-input.hpp" +#include "gl/vertex-buffer.hpp" +#include "gl/vertex-array.hpp" +#include "gl/texture-2d.hpp" + +class resource_manager; + +namespace render { + +/** + * FXAA render pass. + * + * @see Lottes, T. (2009). Fxaa. White paper, Nvidia, Febuary, 2. + */ +class fxaa_pass: public pass +{ +public: + /** + * Constructs an FXAA pass. + * + * @param rasterizer Rasterizer. + * @param framebuffer Target framebuffer. + * @param resource_manager Resource manager. + */ + fxaa_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + + /** + * Destructs an FXAA pass. + */ + virtual ~fxaa_pass(); + + /** + * Renders FXAA. + * + * @param ctx Render context. + * @param queue Render queue. + */ + virtual void render(const render::context& ctx, render::queue& queue) const final; + + /** + * Sets the FXAA source texture. + * + * @param texture FXAA source texture. + */ + void set_source_texture(const gl::texture_2d* texture); + +private: + const gl::texture_2d* source_texture; + + gl::shader_program* shader; + const gl::shader_input* source_texture_input; + const gl::shader_input* texel_size_input; + + gl::vertex_buffer* quad_vbo; + gl::vertex_array* quad_vao; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_FXAA_PASS_HPP diff --git a/src/render/passes/resample-pass.cpp b/src/render/passes/resample-pass.cpp new file mode 100644 index 0000000..44ae8bd --- /dev/null +++ b/src/render/passes/resample-pass.cpp @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#include "render/passes/resample-pass.hpp" +#include "resources/resource-manager.hpp" +#include "gl/rasterizer.hpp" +#include "gl/framebuffer.hpp" +#include "gl/shader-program.hpp" +#include "gl/shader-input.hpp" +#include "gl/vertex-buffer.hpp" +#include "gl/vertex-array.hpp" +#include "gl/vertex-attribute.hpp" +#include "gl/drawing-mode.hpp" +#include "gl/texture-2d.hpp" +#include "render/vertex-attribute.hpp" +#include "render/context.hpp" +#include + +namespace render { + +resample_pass::resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager): + pass(rasterizer, framebuffer), + source_texture(nullptr) +{ + // Load resample shader + shader = resource_manager->load("resample.glsl"); + source_texture_input = shader->get_input("source_texture"); + + const float vertex_data[] = + { + -1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, 1.0f, + 1.0f, 1.0f, + -1.0f, -1.0f, + 1.0f, -1.0f + }; + + std::size_t vertex_size = 2; + std::size_t vertex_stride = sizeof(float) * vertex_size; + std::size_t vertex_count = 6; + + quad_vbo = new gl::vertex_buffer(sizeof(float) * vertex_size * vertex_count, vertex_data); + quad_vao = new gl::vertex_array(); + + // Define position vertex attribute + gl::vertex_attribute position_attribute; + position_attribute.buffer = quad_vbo; + position_attribute.offset = 0; + position_attribute.stride = vertex_stride; + position_attribute.type = gl::vertex_attribute_type::float_32; + position_attribute.components = 2; + + // Bind vertex attributes to VAO + quad_vao->bind(render::vertex_attribute::position, position_attribute); +} + +resample_pass::~resample_pass() +{ + delete quad_vao; + delete quad_vbo; +} + +void resample_pass::render(const render::context& ctx, render::queue& queue) const +{ + if (!source_texture) + return; + + // Set rasterizer state + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glDisable(GL_BLEND); + + // Render FXAA + rasterizer->use_framebuffer(*framebuffer); + rasterizer->set_viewport(0, 0, framebuffer->get_dimensions()[0], framebuffer->get_dimensions()[1]); + rasterizer->use_program(*shader); + source_texture_input->upload(source_texture); + rasterizer->draw_arrays(*quad_vao, gl::drawing_mode::triangles, 0, 6); +} + +void resample_pass::set_source_texture(const gl::texture_2d* texture) +{ + source_texture = texture; +} + +} // namespace render diff --git a/src/render/passes/resample-pass.hpp b/src/render/passes/resample-pass.hpp new file mode 100644 index 0000000..861c2bf --- /dev/null +++ b/src/render/passes/resample-pass.hpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 Christopher J. Howard + * + * This file is part of Antkeeper source code. + * + * Antkeeper source code is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Antkeeper source code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Antkeeper source code. If not, see . + */ + +#ifndef ANTKEEPER_RENDER_RESAMPLE_PASS_HPP +#define ANTKEEPER_RENDER_RESAMPLE_PASS_HPP + +#include "render/pass.hpp" +#include "gl/shader-program.hpp" +#include "gl/shader-input.hpp" +#include "gl/vertex-buffer.hpp" +#include "gl/vertex-array.hpp" +#include "gl/texture-2d.hpp" + +class resource_manager; + +namespace render { + +/** + * Resamples a texture. + */ +class resample_pass: public pass +{ +public: + /** + * Constructs a resample pass. + * + * @param rasterizer Rasterizer. + * @param framebuffer Target framebuffer. + * @param resource_manager Resource manager. + */ + resample_pass(gl::rasterizer* rasterizer, const gl::framebuffer* framebuffer, resource_manager* resource_manager); + + /** + * Destructs a resample pass. + */ + virtual ~resample_pass(); + + /** + * Resamples a texture. + * + * @param ctx Render context. + * @param queue Render queue. + */ + virtual void render(const render::context& ctx, render::queue& queue) const final; + + /** + * Sets the resample source texture. + * + * @param texture Texture to resample. + */ + void set_source_texture(const gl::texture_2d* texture); + +private: + const gl::texture_2d* source_texture; + + gl::shader_program* shader; + const gl::shader_input* source_texture_input; + + gl::vertex_buffer* quad_vbo; + gl::vertex_array* quad_vao; +}; + +} // namespace render + +#endif // ANTKEEPER_RENDER_RESAMPLE_PASS_HPP