From aa260e5f3d4ce0c5551e84ef0b098ddf9e78fd0d Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Fri, 19 Jan 2024 16:14:36 +1100 Subject: [PATCH] Implement glow/bloom on compatibility renderer --- drivers/gles3/effects/copy_effects.cpp | 6 +- drivers/gles3/effects/copy_effects.h | 4 +- drivers/gles3/effects/glow.cpp | 172 ++++++++++ drivers/gles3/effects/glow.h | 89 +++++ drivers/gles3/effects/post_effects.cpp | 152 +++++++++ drivers/gles3/effects/post_effects.h | 69 ++++ drivers/gles3/rasterizer_gles3.cpp | 4 + drivers/gles3/rasterizer_gles3.h | 4 + drivers/gles3/rasterizer_scene_gles3.cpp | 311 +++++++++++------- drivers/gles3/rasterizer_scene_gles3.h | 90 ++++- drivers/gles3/shaders/SCsub | 11 +- drivers/gles3/shaders/effects/SCsub | 17 + drivers/gles3/shaders/{ => effects}/copy.glsl | 13 +- drivers/gles3/shaders/effects/glow.glsl | 113 +++++++ drivers/gles3/shaders/effects/post.glsl | 96 ++++++ drivers/gles3/shaders/scene.glsl | 24 +- drivers/gles3/shaders/sky.glsl | 10 +- .../storage/render_scene_buffers_gles3.cpp | 177 ++++++++-- .../storage/render_scene_buffers_gles3.h | 70 ++-- drivers/gles3/storage/texture_storage.cpp | 75 ++++- drivers/gles3/storage/texture_storage.h | 11 +- scene/resources/environment.cpp | 11 +- .../rendering/storage/environment_storage.cpp | 5 - 23 files changed, 1313 insertions(+), 221 deletions(-) create mode 100644 drivers/gles3/effects/glow.cpp create mode 100644 drivers/gles3/effects/glow.h create mode 100644 drivers/gles3/effects/post_effects.cpp create mode 100644 drivers/gles3/effects/post_effects.h create mode 100644 drivers/gles3/shaders/effects/SCsub rename drivers/gles3/shaders/{ => effects}/copy.glsl (95%) create mode 100644 drivers/gles3/shaders/effects/glow.glsl create mode 100644 drivers/gles3/shaders/effects/post.glsl diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index 29e7de873b6..43bc6d54762 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -155,12 +155,14 @@ void CopyEffects::copy_to_and_from_rect(const Rect2 &p_rect) { draw_screen_quad(); } -void CopyEffects::copy_screen() { - bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT); +void CopyEffects::copy_screen(float p_multiply) { + bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_SCREEN); if (!success) { return; } + copy.shader.version_set_uniform(CopyShaderGLES3::MULTIPLY, p_multiply, copy.shader_version, CopyShaderGLES3::MODE_SCREEN); + draw_screen_triangle(); } diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index e65ebbce03f..1f7b3ee689f 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -33,7 +33,7 @@ #ifdef GLES3_ENABLED -#include "drivers/gles3/shaders/copy.glsl.gen.h" +#include "drivers/gles3/shaders/effects/copy.glsl.gen.h" namespace GLES3 { @@ -64,7 +64,7 @@ public: void copy_to_rect(const Rect2 &p_rect); void copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod = 0.0f); void copy_to_and_from_rect(const Rect2 &p_rect); - void copy_screen(); + void copy_screen(float p_multiply = 1.0); void copy_cube_to_rect(const Rect2 &p_rect); void copy_cube_to_panorama(float p_mip_level); void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region); diff --git a/drivers/gles3/effects/glow.cpp b/drivers/gles3/effects/glow.cpp new file mode 100644 index 00000000000..9fc2eef65bf --- /dev/null +++ b/drivers/gles3/effects/glow.cpp @@ -0,0 +1,172 @@ +/**************************************************************************/ +/* glow.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "glow.h" + +using namespace GLES3; + +Glow *Glow::singleton = nullptr; + +Glow *Glow::get_singleton() { + return singleton; +} + +Glow::Glow() { + singleton = this; + + glow.shader.initialize(); + glow.shader_version = glow.shader.version_create(); + + { // Screen Triangle. + glGenBuffers(1, &screen_triangle); + glBindBuffer(GL_ARRAY_BUFFER, screen_triangle); + + const float qv[6] = { + -1.0f, + -1.0f, + 3.0f, + -1.0f, + -1.0f, + 3.0f, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &screen_triangle_array); + glBindVertexArray(screen_triangle_array); + glBindBuffer(GL_ARRAY_BUFFER, screen_triangle); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } +} + +Glow::~Glow() { + glDeleteBuffers(1, &screen_triangle); + glDeleteVertexArrays(1, &screen_triangle_array); + + glow.shader.version_free(glow.shader_version); + + singleton = nullptr; +} + +void Glow::_draw_screen_triangle() { + glBindVertexArray(screen_triangle_array); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); +} + +void Glow::process_glow(GLuint p_source_color, Size2i p_size, const Glow::GLOWLEVEL *p_glow_buffers, uint32_t p_view, bool p_use_multiview) { + ERR_FAIL_COND(p_source_color == 0); + ERR_FAIL_COND(p_glow_buffers[3].color == 0); + + // Reset some OpenGL state... + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + + // Start with our filter pass + { + glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[0].fbo); + glViewport(0, 0, p_glow_buffers[0].size.x, p_glow_buffers[0].size.y); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(p_use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D, p_source_color); + + uint64_t specialization = p_use_multiview ? GlowShaderGLES3::USE_MULTIVIEW : 0; + bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + if (!success) { + return; + } + + glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[0].size.x, 1.0 / p_glow_buffers[0].size.y, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + glow.shader.version_set_uniform(GlowShaderGLES3::VIEW, float(p_view), glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + glow.shader.version_set_uniform(GlowShaderGLES3::LUMINANCE_MULTIPLIER, luminance_multiplier, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_BLOOM, glow_bloom, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_HDR_THRESHOLD, glow_hdr_bleed_threshold, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_HDR_SCALE, glow_hdr_bleed_scale, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + glow.shader.version_set_uniform(GlowShaderGLES3::GLOW_LUMINANCE_CAP, glow_hdr_luminance_cap, glow.shader_version, GlowShaderGLES3::MODE_FILTER, specialization); + + _draw_screen_triangle(); + } + + // Continue with downsampling + { + bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_DOWNSAMPLE, 0); + if (!success) { + return; + } + + for (int i = 1; i < 4; i++) { + glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[i].fbo); + glViewport(0, 0, p_glow_buffers[i].size.x, p_glow_buffers[i].size.y); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, p_glow_buffers[i - 1].color); + + glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[i].size.x, 1.0 / p_glow_buffers[i].size.y, glow.shader_version, GlowShaderGLES3::MODE_DOWNSAMPLE); + + _draw_screen_triangle(); + } + } + + // Now upsample + { + bool success = glow.shader.version_bind_shader(glow.shader_version, GlowShaderGLES3::MODE_UPSAMPLE, 0); + if (!success) { + return; + } + + for (int i = 2; i >= 0; i--) { + glBindFramebuffer(GL_FRAMEBUFFER, p_glow_buffers[i].fbo); + glViewport(0, 0, p_glow_buffers[i].size.x, p_glow_buffers[i].size.y); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, p_glow_buffers[i + 1].color); + + glow.shader.version_set_uniform(GlowShaderGLES3::PIXEL_SIZE, 1.0 / p_glow_buffers[i].size.x, 1.0 / p_glow_buffers[i].size.y, glow.shader_version, GlowShaderGLES3::MODE_UPSAMPLE); + + _draw_screen_triangle(); + } + } + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glUseProgram(0); + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/effects/glow.h b/drivers/gles3/effects/glow.h new file mode 100644 index 00000000000..a1be6e1f4c5 --- /dev/null +++ b/drivers/gles3/effects/glow.h @@ -0,0 +1,89 @@ +/**************************************************************************/ +/* glow.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef GLOW_GLES3_H +#define GLOW_GLES3_H + +#ifdef GLES3_ENABLED + +#include "drivers/gles3/shaders/effects/glow.glsl.gen.h" + +namespace GLES3 { + +class Glow { +private: + static Glow *singleton; + + struct GLOW { + GlowShaderGLES3 shader; + RID shader_version; + } glow; + + float luminance_multiplier = 1.0; + + float glow_intensity = 1.0; + float glow_bloom = 0.0; + float glow_hdr_bleed_threshold = 1.0; + float glow_hdr_bleed_scale = 2.0; + float glow_hdr_luminance_cap = 12.0; + + // Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal. + GLuint screen_triangle = 0; + GLuint screen_triangle_array = 0; + + void _draw_screen_triangle(); + +public: + struct GLOWLEVEL { + Size2i size; + GLuint color = 0; + GLuint fbo = 0; + }; + + static Glow *get_singleton(); + + Glow(); + ~Glow(); + + void set_intensity(float p_value) { glow_intensity = p_value; } + void set_luminance_multiplier(float p_luminance_multiplier) { luminance_multiplier = p_luminance_multiplier; } + void set_glow_bloom(float p_bloom) { glow_bloom = p_bloom; } + void set_glow_hdr_bleed_threshold(float p_threshold) { glow_hdr_bleed_threshold = p_threshold; } + void set_glow_hdr_bleed_scale(float p_scale) { glow_hdr_bleed_scale = p_scale; } + void set_glow_hdr_luminance_cap(float p_cap) { glow_hdr_luminance_cap = p_cap; } + + void process_glow(GLuint p_source_color, Size2i p_size, const GLOWLEVEL *p_glow_buffers, uint32_t p_view = 0, bool p_use_multiview = false); +}; + +} //namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // GLOW_GLES3_H diff --git a/drivers/gles3/effects/post_effects.cpp b/drivers/gles3/effects/post_effects.cpp new file mode 100644 index 00000000000..75af068ab54 --- /dev/null +++ b/drivers/gles3/effects/post_effects.cpp @@ -0,0 +1,152 @@ +/**************************************************************************/ +/* post_effects.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifdef GLES3_ENABLED + +#include "post_effects.h" + +using namespace GLES3; + +PostEffects *PostEffects::singleton = nullptr; + +PostEffects *PostEffects::get_singleton() { + return singleton; +} + +PostEffects::PostEffects() { + singleton = this; + + post.shader.initialize(); + post.shader_version = post.shader.version_create(); + post.shader.version_bind_shader(post.shader_version, PostShaderGLES3::MODE_DEFAULT); + + { // Screen Triangle. + glGenBuffers(1, &screen_triangle); + glBindBuffer(GL_ARRAY_BUFFER, screen_triangle); + + const float qv[6] = { + -1.0f, + -1.0f, + 3.0f, + -1.0f, + -1.0f, + 3.0f, + }; + + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6, qv, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + + glGenVertexArrays(1, &screen_triangle_array); + glBindVertexArray(screen_triangle_array); + glBindBuffer(GL_ARRAY_BUFFER, screen_triangle); + glVertexAttribPointer(RS::ARRAY_VERTEX, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 2, nullptr); + glEnableVertexAttribArray(RS::ARRAY_VERTEX); + glBindVertexArray(0); + glBindBuffer(GL_ARRAY_BUFFER, 0); //unbind + } +} + +PostEffects::~PostEffects() { + singleton = nullptr; + glDeleteBuffers(1, &screen_triangle); + glDeleteVertexArrays(1, &screen_triangle_array); + post.shader.version_free(post.shader_version); +} + +void PostEffects::_draw_screen_triangle() { + glBindVertexArray(screen_triangle_array); + glDrawArrays(GL_TRIANGLES, 0, 3); + glBindVertexArray(0); +} + +void PostEffects::post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view, bool p_use_multiview) { + glDisable(GL_DEPTH_TEST); + glDepthMask(GL_FALSE); + glDisable(GL_BLEND); + + glBindFramebuffer(GL_FRAMEBUFFER, p_dest_framebuffer); + glViewport(0, 0, p_dest_size.x, p_dest_size.y); + + PostShaderGLES3::ShaderVariant mode = PostShaderGLES3::MODE_DEFAULT; + uint64_t flags = 0; + if (p_use_multiview) { + flags |= PostShaderGLES3::USE_MULTIVIEW; + } + if (p_glow_buffers != nullptr) { + flags |= PostShaderGLES3::USE_GLOW; + } + if (p_luminance_multiplier != 1.0) { + flags |= PostShaderGLES3::USE_LUMINANCE_MULTIPLIER; + } + + bool success = post.shader.version_bind_shader(post.shader_version, mode, flags); + if (!success) { + return; + } + + GLenum texture_target = p_use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture_target, p_source_color); + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + if (p_glow_buffers != nullptr) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, p_glow_buffers[0].color); + + post.shader.version_set_uniform(PostShaderGLES3::PIXEL_SIZE, 1.0 / p_source_size.x, 1.0 / p_source_size.y, post.shader_version, mode, flags); + post.shader.version_set_uniform(PostShaderGLES3::GLOW_INTENSITY, p_glow_intensity, post.shader_version, mode, flags); + } + + post.shader.version_set_uniform(PostShaderGLES3::VIEW, float(p_view), post.shader_version, mode, flags); + post.shader.version_set_uniform(PostShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, post.shader_version, mode, flags); + + _draw_screen_triangle(); + + // Reset state + if (p_glow_buffers != nullptr) { + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + } + + // Return back to nearest + glActiveTexture(GL_TEXTURE0); + glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(texture_target, 0); + + glDisable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDepthMask(GL_TRUE); + glUseProgram(0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +#endif // GLES3_ENABLED diff --git a/drivers/gles3/effects/post_effects.h b/drivers/gles3/effects/post_effects.h new file mode 100644 index 00000000000..b90c77d6c7c --- /dev/null +++ b/drivers/gles3/effects/post_effects.h @@ -0,0 +1,69 @@ +/**************************************************************************/ +/* post_effects.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef POST_EFFECTS_GLES3_H +#define POST_EFFECTS_GLES3_H + +#ifdef GLES3_ENABLED + +#include "drivers/gles3/shaders/effects/post.glsl.gen.h" +#include "glow.h" + +namespace GLES3 { + +class PostEffects { +private: + struct Post { + PostShaderGLES3 shader; + RID shader_version; + } post; + + static PostEffects *singleton; + + // Use for full-screen effects. Slightly more efficient than screen_quad as this eliminates pixel overdraw along the diagonal. + GLuint screen_triangle = 0; + GLuint screen_triangle_array = 0; + + void _draw_screen_triangle(); + +public: + static PostEffects *get_singleton(); + + PostEffects(); + ~PostEffects(); + + void post_copy(GLuint p_dest_framebuffer, Size2i p_dest_size, GLuint p_source_color, Size2i p_source_size, float p_luminance_multiplier, const Glow::GLOWLEVEL *p_glow_buffers, float p_glow_intensity, uint32_t p_view = 0, bool p_use_multiview = false); +}; + +} //namespace GLES3 + +#endif // GLES3_ENABLED + +#endif // POST_EFFECTS_GLES3_H diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index f5296f969f0..14ef0f40cf2 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -201,6 +201,8 @@ void RasterizerGLES3::finalize() { memdelete(canvas); memdelete(gi); memdelete(fog); + memdelete(post_effects); + memdelete(glow); memdelete(copy_effects); memdelete(light_storage); memdelete(particles_storage); @@ -347,6 +349,8 @@ RasterizerGLES3::RasterizerGLES3() { particles_storage = memnew(GLES3::ParticlesStorage); light_storage = memnew(GLES3::LightStorage); copy_effects = memnew(GLES3::CopyEffects); + glow = memnew(GLES3::Glow); + post_effects = memnew(GLES3::PostEffects); gi = memnew(GLES3::GI); fog = memnew(GLES3::Fog); canvas = memnew(RasterizerCanvasGLES3()); diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index cf3cedfea16..fc1315035b1 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -34,6 +34,8 @@ #ifdef GLES3_ENABLED #include "effects/copy_effects.h" +#include "effects/glow.h" +#include "effects/post_effects.h" #include "environment/fog.h" #include "environment/gi.h" #include "rasterizer_canvas_gles3.h" @@ -67,6 +69,8 @@ protected: GLES3::GI *gi = nullptr; GLES3::Fog *fog = nullptr; GLES3::CopyEffects *copy_effects = nullptr; + GLES3::Glow *glow = nullptr; + GLES3::PostEffects *post_effects = nullptr; RasterizerCanvasGLES3 *canvas = nullptr; RasterizerSceneGLES3 *scene = nullptr; static RasterizerGLES3 *singleton; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index efd554eac9f..4e31e1bcdd3 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -764,7 +764,7 @@ void RasterizerSceneGLES3::_setup_sky(const RenderDataGLES3 *p_render_data, cons } } -void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y) { +void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y, bool p_apply_color_adjustments_in_post) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -778,6 +778,10 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, if (p_flip_y) { spec_constants |= SkyShaderGLES3::USE_INVERTED_Y; } + if (!p_apply_color_adjustments_in_post) { + spec_constants |= SkyShaderGLES3::APPLY_TONEMAPPING; + // TODO add BCS and color corrections once supported. + } RS::EnvironmentBG background = environment_get_background(p_env); @@ -832,6 +836,7 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, camera.columns[2][0], camera.columns[0][0], camera.columns[2][1], camera.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::SKY_ENERGY_MULTIPLIER, p_sky_energy_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_BACKGROUND, spec_constants); if (p_use_multiview) { @@ -843,7 +848,7 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection, glDrawArrays(GL_TRIANGLES, 0, 3); } -void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier) { +void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier) { GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); ERR_FAIL_COND(p_env.is_null()); @@ -939,20 +944,17 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::POSITION, p_transform.origin, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::TIME, time, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::PROJECTION, cm.columns[2][0], cm.columns[0][0], cm.columns[2][1], cm.columns[1][1], shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); - material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, p_luminance_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::SKY_ENERGY_MULTIPLIER, p_sky_energy_multiplier, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); + material_storage->shaders.sky_shader.version_set_uniform(SkyShaderGLES3::LUMINANCE_MULTIPLIER, 1.0, shader_data->version, SkyShaderGLES3::MODE_CUBEMAP); glBindVertexArray(sky_globals.screen_triangle_array); glViewport(0, 0, sky->radiance_size, sky->radiance_size); glBindFramebuffer(GL_FRAMEBUFFER, sky->radiance_framebuffer); - glDisable(GL_BLEND); - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); - scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; - glDisable(GL_SCISSOR_TEST); - glDisable(GL_CULL_FACE); - scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED; + scene_state.reset_gl_state(); + scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED); + scene_state.enable_gl_blend(false); for (int i = 0; i < 6; i++) { Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); @@ -969,17 +971,13 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p _filter_sky_radiance(sky, 0); //Just copy over the first mipmap } sky->processing_layer = 1; - sky->baked_exposure = p_luminance_multiplier; + sky->baked_exposure = p_sky_energy_multiplier; sky->reflection_dirty = false; } else { if (sky_mode == RS::SKY_MODE_INCREMENTAL && sky->processing_layer < max_processing_layer) { - glDisable(GL_BLEND); - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); - scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; - glDisable(GL_SCISSOR_TEST); - glDisable(GL_CULL_FACE); - scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED; + scene_state.reset_gl_state(); + scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED); + scene_state.enable_gl_blend(false); _filter_sky_radiance(sky, sky->processing_layer); sky->processing_layer++; @@ -1584,6 +1582,8 @@ void RasterizerSceneGLES3::_setup_environment(const RenderDataGLES3 *p_render_da scene_state.ubo.screen_pixel_size[0] = screen_pixel_size.x; scene_state.ubo.screen_pixel_size[1] = screen_pixel_size.y; + scene_state.ubo.luminance_multiplier = p_render_data->luminance_multiplier; + scene_state.ubo.shadow_bias = p_shadow_bias; scene_state.ubo.pancake_shadows = p_pancake_shadows; @@ -2271,14 +2271,10 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); - glEnable(GL_DEPTH_TEST); + scene_state.reset_gl_state(); + scene_state.enable_gl_depth_test(true); + scene_state.enable_gl_depth_draw(true); glDepthFunc(GL_LESS); - glDisable(GL_SCISSOR_TEST); - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; glColorMask(0, 0, 0, 0); glDrawBuffers(0, nullptr); @@ -2303,8 +2299,8 @@ void RasterizerSceneGLES3::_render_shadow_pass(RID p_light, RID p_shadow_atlas, _render_list_template(&render_list_params, &render_data, 0, render_list[RENDER_LIST_SECONDARY].elements.size()); glColorMask(1, 1, 1, 1); - glDisable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); + scene_state.enable_gl_depth_test(false); + scene_state.enable_gl_depth_draw(true); glDisable(GL_CULL_FACE); scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED; glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -2315,15 +2311,32 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ GLES3::Config *config = GLES3::Config::get_singleton(); RENDER_TIMESTAMP("Setup 3D Scene"); + bool apply_color_adjustments_in_post = false; + Ref rb; if (p_render_buffers.is_valid()) { rb = p_render_buffers; ERR_FAIL_COND(rb.is_null()); + + if (rb->get_scaling_3d_mode() != RS::VIEWPORT_SCALING_3D_MODE_OFF) { + // If we're scaling, we apply tonemapping etc. in post, so disable it during rendering + apply_color_adjustments_in_post = true; + } } GLES3::RenderTarget *rt = texture_storage->get_render_target(rb->render_target); ERR_FAIL_NULL(rt); + bool glow_enabled = false; + if (p_environment.is_valid() && rb.is_valid()) { + glow_enabled = environment_get_glow_enabled(p_environment); + rb->set_glow_enabled(glow_enabled); // ensure our intermediate buffer is available if glow is enabled + if (glow_enabled) { + // If glow is enabled, we apply tonemapping etc. in post, so disable it during rendering + apply_color_adjustments_in_post = true; + } + } + // Assign render data // Use the format from rendererRD RenderDataGLES3 render_data; @@ -2359,6 +2372,13 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ // this should be the same for all cameras.. render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); + if (rt->color_type == GL_UNSIGNED_INT_2_10_10_10_REV && glow_enabled) { + // As our output is in sRGB and we're using 10bit color space, we can fake a little HDR to do glow... + render_data.luminance_multiplier = 0.25; + } else { + render_data.luminance_multiplier = 1.0; + } + if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_DISABLE_LOD) { render_data.screen_mesh_lod_threshold = 0.0; } else { @@ -2519,9 +2539,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glBindFramebuffer(GL_FRAMEBUFFER, fbo); glViewport(0, 0, rb->internal_size.x, rb->internal_size.y); - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; + scene_state.reset_gl_state(); // Do depth prepass if it's explicitly enabled bool use_depth_prepass = config->use_depth_prepass; @@ -2533,11 +2551,11 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ RENDER_TIMESTAMP("Depth Prepass"); //pre z pass - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); - glEnable(GL_DEPTH_TEST); + scene_state.enable_gl_depth_test(true); + scene_state.enable_gl_depth_draw(true); + scene_state.enable_gl_blend(false); glDepthFunc(GL_LEQUAL); - glDisable(GL_SCISSOR_TEST); + scene_state.enable_gl_scissor_test(false); glColorMask(0, 0, 0, 0); RasterizerGLES3::clear_depth(1.0); @@ -2560,21 +2578,19 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } glBlendEquation(GL_FUNC_ADD); - if (render_data.transparent_bg) { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); + scene_state.enable_gl_blend(true); } else { glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ZERO, GL_ONE); - glDisable(GL_BLEND); + scene_state.enable_gl_blend(false); } scene_state.current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX; - glEnable(GL_DEPTH_TEST); + scene_state.enable_gl_scissor_test(false); + scene_state.enable_gl_depth_test(true); + scene_state.enable_gl_depth_draw(true); glDepthFunc(GL_LEQUAL); - glDepthMask(GL_TRUE); - scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; - scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS; { GLuint db = GL_COLOR_ATTACHMENT0; @@ -2589,7 +2605,19 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ if (!keep_color) { clear_color.a = render_data.transparent_bg ? 0.0f : 1.0f; glClearBufferfv(GL_COLOR, 0, clear_color.components); + } else if (fbo != rt->fbo) { + // Need to copy our current contents to our intermediate/MSAA buffer + GLES3::CopyEffects *copy_effects = GLES3::CopyEffects::get_singleton(); + + scene_state.enable_gl_depth_test(false); + scene_state.enable_gl_depth_draw(false); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(rt->view_count > 1 ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D, rt->color); + + copy_effects->copy_screen(render_data.luminance_multiplier); } + RENDER_TIMESTAMP("Render Opaque Pass"); uint64_t spec_constant_base_flags = 0; @@ -2606,26 +2634,28 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ if (render_data.environment.is_valid() && environment_get_fog_mode(render_data.environment) == RS::EnvironmentFogMode::ENV_FOG_MODE_DEPTH) { spec_constant_base_flags |= SceneShaderGLES3::USE_DEPTH_FOG; } + + if (!apply_color_adjustments_in_post) { + spec_constant_base_flags |= SceneShaderGLES3::APPLY_TONEMAPPING; + + // TODO add BCS and Color corrections here once supported. + } } // Render Opaque Objects. RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe); _render_list_template(&render_list_params, &render_data, 0, render_list[RENDER_LIST_OPAQUE].elements.size()); - glDepthMask(GL_FALSE); - scene_state.current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_DISABLED; + scene_state.enable_gl_depth_draw(false); if (draw_sky) { RENDER_TIMESTAMP("Render Sky"); - glEnable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - scene_state.current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_ENABLED; - scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; + scene_state.enable_gl_depth_test(true); + scene_state.enable_gl_blend(false); + scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK); - _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier, p_camera_data->view_count > 1, flip_y); + _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post); } if (scene_state.used_screen_texture || scene_state.used_depth_texture) { @@ -2674,7 +2704,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ } RENDER_TIMESTAMP("Render 3D Transparent Pass"); - glEnable(GL_BLEND); + scene_state.enable_gl_blend(true); //Render transparent pass RenderListParameters render_list_params_alpha(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), reverse_cull, spec_constant_base_flags, use_wireframe); @@ -2689,7 +2719,10 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ if (rb.is_valid()) { _render_buffers_debug_draw(rb, p_shadow_atlas, fbo); } - glDisable(GL_BLEND); + + // Reset stuff that may trip up the next process. + scene_state.reset_gl_state(); + glUseProgram(0); _render_post_processing(&render_data); @@ -2700,6 +2733,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_render_data) { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Glow *glow = GLES3::Glow::get_singleton(); + GLES3::PostEffects *post_effects = GLES3::PostEffects::get_singleton(); + Ref rb = p_render_data->render_buffers; ERR_FAIL_COND(rb.is_null()); @@ -2714,6 +2750,26 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend GLuint fbo_int = rb->get_internal_fbo(); GLuint fbo_rt = texture_storage->render_target_get_fbo(render_target); // TODO if MSAA 2D is enabled and we're not using rt_msaa, get 2D render target here. + // Check if we have glow enabled and if so, check if our buffers were allocated + bool glow_enabled = false; + float glow_intensity = 1.0; + float glow_bloom = 0.0; + float glow_hdr_bleed_threshold = 1.0; + float glow_hdr_bleed_scale = 2.0; + float glow_hdr_luminance_cap = 12.0; + if (p_render_data->environment.is_valid()) { + glow_enabled = environment_get_glow_enabled(p_render_data->environment); + glow_intensity = environment_get_glow_intensity(p_render_data->environment); + glow_bloom = environment_get_glow_bloom(p_render_data->environment); + glow_hdr_bleed_threshold = environment_get_glow_hdr_bleed_threshold(p_render_data->environment); + glow_hdr_bleed_scale = environment_get_glow_hdr_bleed_scale(p_render_data->environment); + glow_hdr_luminance_cap = environment_get_glow_hdr_luminance_cap(p_render_data->environment); + } + + if (glow_enabled) { + rb->check_glow_buffers(); + } + if (view_count == 1) { // Resolve if needed. if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { @@ -2729,23 +2785,41 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, internal_size.x, internal_size.y, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); } + // Rendered to intermediate buffer, must copy to our render target if (fbo_int != 0) { - // TODO If we have glow or other post processing, we upscale only depth here, post processing will also do scaling. + // Apply glow/bloom if requested? then populate our glow buffers + GLuint color = fbo_int != 0 ? rb->get_internal_color() : texture_storage->render_target_get_color(render_target); + const GLES3::Glow::GLOWLEVEL *glow_buffers = nullptr; + if (glow_enabled) { + glow_buffers = rb->get_glow_buffers(); + + glow->set_luminance_multiplier(p_render_data->luminance_multiplier); + + glow->set_intensity(glow_intensity); + glow->set_glow_bloom(glow_bloom); + glow->set_glow_hdr_bleed_threshold(glow_hdr_bleed_threshold); + glow->set_glow_hdr_bleed_scale(glow_hdr_bleed_scale); + glow->set_glow_hdr_luminance_cap(glow_hdr_luminance_cap); + + glow->process_glow(color, internal_size, glow_buffers); + } + + // Copy color buffer + post_effects->post_copy(fbo_rt, target_size, color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity); + + // Copy depth buffer glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_int); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_rt); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); } glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); } else if ((fbo_msaa_3d != 0 && msaa3d_needs_resolve) || (fbo_int != 0)) { // TODO investigate if it's smarter to cache these FBOs - GLuint fbos[2]; // read and write - glGenFramebuffers(2, fbos); - - glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); + GLuint fbos[3]; // read, write and post + glGenFramebuffers(3, fbos); + // Resolve if needed. if (fbo_msaa_3d != 0 && msaa3d_needs_resolve) { GLuint read_color = rb->get_msaa3d_color(); GLuint read_depth = rb->get_msaa3d_depth(); @@ -2760,6 +2834,9 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend write_depth = texture_storage->render_target_get_depth(render_target); } + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); + for (uint32_t v = 0; v < view_count; v++) { glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); @@ -2769,25 +2846,53 @@ void RasterizerSceneGLES3::_render_post_processing(const RenderDataGLES3 *p_rend } } + // Rendered to intermediate buffer, must copy to our render target if (fbo_int != 0) { - GLuint read_color = rb->get_internal_color(); - GLuint read_depth = rb->get_internal_depth(); + // Apply glow/bloom if requested? then populate our glow buffers + const GLES3::Glow::GLOWLEVEL *glow_buffers = nullptr; + GLuint source_color = fbo_int != 0 ? rb->get_internal_color() : texture_storage->render_target_get_color(render_target); + + if (glow_enabled) { + glow_buffers = rb->get_glow_buffers(); + + glow->set_luminance_multiplier(p_render_data->luminance_multiplier); + + glow->set_intensity(glow_intensity); + glow->set_glow_bloom(glow_bloom); + glow->set_glow_hdr_bleed_threshold(glow_hdr_bleed_threshold); + glow->set_glow_hdr_bleed_scale(glow_hdr_bleed_scale); + glow->set_glow_hdr_luminance_cap(glow_hdr_luminance_cap); + } + GLuint write_color = texture_storage->render_target_get_color(render_target); - GLuint write_depth = texture_storage->render_target_get_depth(render_target); for (uint32_t v = 0; v < view_count; v++) { - glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, read_color, 0, v); + if (glow_enabled) { + glow->process_glow(source_color, internal_size, glow_buffers, v, true); + } + + glBindFramebuffer(GL_FRAMEBUFFER, fbos[2]); + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); + post_effects->post_copy(fbos[2], target_size, source_color, internal_size, p_render_data->luminance_multiplier, glow_buffers, glow_intensity, v, true); + } + + // Copy depth + GLuint read_depth = rb->get_internal_depth(); + GLuint write_depth = texture_storage->render_target_get_depth(render_target); + + glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos[0]); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbos[1]); + + for (uint32_t v = 0; v < view_count; v++) { glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, read_depth, 0, v); - glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, write_color, 0, v); glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, write_depth, 0, v); - glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_COLOR_BUFFER_BIT, GL_LINEAR); glBlitFramebuffer(0, 0, internal_size.x, internal_size.y, 0, 0, target_size.x, target_size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); } } glBindFramebuffer(GL_FRAMEBUFFER, fbo_rt); - glDeleteFramebuffers(2, fbos); + glDeleteFramebuffers(3, fbos); } } @@ -2884,33 +2989,15 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } if constexpr (p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) { - if (scene_state.current_depth_test != shader->depth_test) { - if (shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_DISABLED) { - glDisable(GL_DEPTH_TEST); - } else { - glEnable(GL_DEPTH_TEST); - } - scene_state.current_depth_test = shader->depth_test; - } + scene_state.enable_gl_depth_test(shader->depth_test == GLES3::SceneShaderData::DEPTH_TEST_ENABLED); } if constexpr (p_pass_mode != PASS_MODE_SHADOW) { - if (scene_state.current_depth_draw != shader->depth_draw) { - switch (shader->depth_draw) { - case GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE: { - glDepthMask((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) || - p_pass_mode == PASS_MODE_DEPTH); - } break; - case GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS: { - glDepthMask(GL_TRUE); - } break; - case GLES3::SceneShaderData::DEPTH_DRAW_DISABLED: { - glDepthMask(GL_FALSE); - } break; - } + if (shader->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE) { + scene_state.enable_gl_depth_draw((p_pass_mode == PASS_MODE_COLOR && !GLES3::Config::get_singleton()->use_depth_prepass) || p_pass_mode == PASS_MODE_DEPTH); + } else { + scene_state.enable_gl_depth_draw(shader->depth_draw == GLES3::SceneShaderData::DEPTH_DRAW_ALWAYS); } - - scene_state.current_depth_draw = shader->depth_draw; } bool uses_additive_lighting = (inst->light_passes.size() + p_render_data->directional_shadow_count) > 0; @@ -2937,7 +3024,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } if (uses_additive_lighting && pass == 1 && !p_render_data->transparent_bg) { // Enable blending if in opaque pass and not already enabled. - glEnable(GL_BLEND); + scene_state.enable_gl_blend(true); } if (pass < int32_t(inst->light_passes.size())) { RID light_instance_rid = inst->light_passes[pass].light_instance_rid; @@ -3017,18 +3104,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } } - if (scene_state.cull_mode != cull_mode) { - if (cull_mode == GLES3::SceneShaderData::CULL_DISABLED) { - glDisable(GL_CULL_FACE); - } else { - if (scene_state.cull_mode == GLES3::SceneShaderData::CULL_DISABLED) { - // Last time was disabled, so enable and set proper face. - glEnable(GL_CULL_FACE); - } - glCullFace(cull_mode == GLES3::SceneShaderData::CULL_FRONT ? GL_FRONT : GL_BACK); - } - scene_state.cull_mode = cull_mode; - } + scene_state.set_gl_cull_mode(cull_mode); RS::PrimitiveType primitive = surf->primitive; if (shader->uses_point_size) { @@ -3417,7 +3493,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if constexpr (p_pass_mode == PASS_MODE_COLOR) { if (uses_additive_lighting && !p_render_data->transparent_bg) { // Disable additive blending if enabled for additive lights. - glDisable(GL_BLEND); + scene_state.enable_gl_blend(false); } } } @@ -3480,14 +3556,10 @@ void RasterizerSceneGLES3::render_particle_collider_heightfield(RID p_collider, glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); - glEnable(GL_DEPTH_TEST); + scene_state.reset_gl_state(); + scene_state.enable_gl_depth_test(true); + scene_state.enable_gl_depth_draw(true); glDepthFunc(GL_LESS); - glDisable(GL_SCISSOR_TEST); - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); - scene_state.cull_mode = GLES3::SceneShaderData::CULL_BACK; glDrawBuffers(0, nullptr); @@ -3530,14 +3602,10 @@ void RasterizerSceneGLES3::_render_uv2(const PagedArray draw_buffers; draw_buffers.push_back(GL_COLOR_ATTACHMENT0); @@ -3629,10 +3697,9 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Refcopy_to_rect(Rect2(Vector2(), Vector2(0.5, 0.5))); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index ed59aba266f..53ecd19895a 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -127,6 +127,8 @@ struct RenderDataGLES3 { uint32_t spot_light_count = 0; uint32_t omni_light_count = 0; + float luminance_multiplier = 1.0; + RenderingMethod::RenderInfo *render_info = nullptr; /* Shadow data */ @@ -404,15 +406,14 @@ private: float fog_height_density; float fog_depth_curve; - float pad; + float fog_sun_scatter; float fog_depth_begin; float fog_light_color[3]; float fog_depth_end; - float fog_sun_scatter; - float shadow_bias; + float luminance_multiplier; uint32_t camera_visible_layers; bool pancake_shadows; }; @@ -442,10 +443,85 @@ private: bool used_depth_prepass = false; GLES3::SceneShaderData::BlendMode current_blend_mode = GLES3::SceneShaderData::BLEND_MODE_MIX; - GLES3::SceneShaderData::DepthDraw current_depth_draw = GLES3::SceneShaderData::DEPTH_DRAW_OPAQUE; - GLES3::SceneShaderData::DepthTest current_depth_test = GLES3::SceneShaderData::DEPTH_TEST_DISABLED; GLES3::SceneShaderData::Cull cull_mode = GLES3::SceneShaderData::CULL_BACK; + bool current_blend_enabled = false; + bool current_depth_draw_enabled = false; + bool current_depth_test_enabled = false; + bool current_scissor_test_enabled = false; + + void reset_gl_state() { + glDisable(GL_BLEND); + current_blend_enabled = false; + + glDisable(GL_SCISSOR_TEST); + current_scissor_test_enabled = false; + + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + cull_mode = GLES3::SceneShaderData::CULL_BACK; + + glDepthMask(GL_FALSE); + current_depth_draw_enabled = false; + glDisable(GL_DEPTH_TEST); + current_depth_test_enabled = false; + } + + void set_gl_cull_mode(GLES3::SceneShaderData::Cull p_mode) { + if (cull_mode != p_mode) { + if (p_mode == GLES3::SceneShaderData::CULL_DISABLED) { + glDisable(GL_CULL_FACE); + } else { + if (cull_mode == GLES3::SceneShaderData::CULL_DISABLED) { + // Last time was disabled, so enable and set proper face. + glEnable(GL_CULL_FACE); + } + glCullFace(p_mode == GLES3::SceneShaderData::CULL_FRONT ? GL_FRONT : GL_BACK); + } + cull_mode = p_mode; + } + } + + void enable_gl_blend(bool p_enabled) { + if (current_blend_enabled != p_enabled) { + if (p_enabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } + current_blend_enabled = p_enabled; + } + } + + void enable_gl_scissor_test(bool p_enabled) { + if (current_scissor_test_enabled != p_enabled) { + if (p_enabled) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + current_scissor_test_enabled = p_enabled; + } + } + + void enable_gl_depth_draw(bool p_enabled) { + if (current_depth_draw_enabled != p_enabled) { + glDepthMask(p_enabled ? GL_TRUE : GL_FALSE); + current_depth_draw_enabled = p_enabled; + } + } + + void enable_gl_depth_test(bool p_enabled) { + if (current_depth_test_enabled != p_enabled) { + if (p_enabled) { + glEnable(GL_DEPTH_TEST); + } else { + glDisable(GL_DEPTH_TEST); + } + current_depth_test_enabled = p_enabled; + } + } + bool texscreen_copied = false; bool used_screen_texture = false; bool used_normal_texture = false; @@ -656,9 +732,9 @@ protected: void _setup_sky(const RenderDataGLES3 *p_render_data, const PagedArray &p_lights, const Projection &p_projection, const Transform3D &p_transform, const Size2i p_screen_size); void _invalidate_sky(Sky *p_sky); void _update_dirty_skys(); - void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier); + void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier); void _filter_sky_radiance(Sky *p_sky, int p_base_layer); - void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y); + void _draw_sky(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y, bool p_apply_color_adjustments_in_post); void _free_sky_data(Sky *p_sky); // Needed for a single argument calls (material and uv2). diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index 34713e7e29f..0292b5d5194 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -12,8 +12,10 @@ if "GLES3_GLSL" in env["BUILDERS"]: # make sure we recompile shaders if include files change env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#gles3_builders.py"]) + # compile shaders + + # as we have a few, not yet, converted files we name the ones we want to include: env.GLES3_GLSL("canvas.glsl") - env.GLES3_GLSL("copy.glsl") env.GLES3_GLSL("scene.glsl") env.GLES3_GLSL("sky.glsl") env.GLES3_GLSL("cubemap_filter.glsl") @@ -22,3 +24,10 @@ if "GLES3_GLSL" in env["BUILDERS"]: env.GLES3_GLSL("particles.glsl") env.GLES3_GLSL("particles_copy.glsl") env.GLES3_GLSL("skeleton.glsl") + + # once we finish conversion we can introduce this to cover all files: + # for glsl_file in glsl_files: + # env.GLES3_GLSL(glsl_file) + + +SConscript("effects/SCsub") diff --git a/drivers/gles3/shaders/effects/SCsub b/drivers/gles3/shaders/effects/SCsub new file mode 100644 index 00000000000..38b185ed885 --- /dev/null +++ b/drivers/gles3/shaders/effects/SCsub @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +Import("env") + +if "GLES3_GLSL" in env["BUILDERS"]: + # find all include files + gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + + # find all shader code(all glsl files excluding our include files) + glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files] + + # make sure we recompile shaders if include files change + env.Depends([f + ".gen.h" for f in glsl_files], gl_include_files + ["#gles3_builders.py"]) + + # compile shaders + for glsl_file in glsl_files: + env.GLES3_GLSL(glsl_file) diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/effects/copy.glsl similarity index 95% rename from drivers/gles3/shaders/copy.glsl rename to drivers/gles3/shaders/effects/copy.glsl index db63b5d3484..06f63ba6298 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/effects/copy.glsl @@ -6,6 +6,7 @@ mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY mode_copy_section_source = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define MODE_COPY_FROM mode_copy_section_3d = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_3D mode_copy_section_2d_array = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_2D_ARRAY +mode_screen = #define MODE_SIMPLE_COPY \n#define MODE_MULTIPLY mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION @@ -55,6 +56,10 @@ uniform float lod; uniform vec4 color_in; #endif +#ifdef MODE_MULTIPLY +uniform float multiply; +#endif + #ifdef MODE_GAUSSIAN_BLUR // Defined in 0-1 coords. uniform highp vec2 pixel_size; @@ -105,10 +110,14 @@ void main() { vec4 color = textureLod(source_2d_array, vec3(uv_interp, layer), lod); #else vec4 color = texture(source, uv_interp); -#endif +#endif // USE_TEXTURE_3D + +#ifdef MODE_MULTIPLY + color *= multiply; +#endif // MODE_MULTIPLY frag_color = color; -#endif +#endif // MODE_SIMPLE_COPY #ifdef MODE_SIMPLE_COLOR frag_color = color_in; diff --git a/drivers/gles3/shaders/effects/glow.glsl b/drivers/gles3/shaders/effects/glow.glsl new file mode 100644 index 00000000000..09f2f8afcbd --- /dev/null +++ b/drivers/gles3/shaders/effects/glow.glsl @@ -0,0 +1,113 @@ +/* clang-format off */ +#[modes] + +// Based on Dual filtering glow as explained in Marius Bjørge presentation at Siggraph 2015 "Bandwidth-Efficient Rendering" + +mode_filter = #define MODE_FILTER +mode_downsample = #define MODE_DOWNSAMPLE +mode_upsample = #define MODE_UPSAMPLE + +#[specializations] + +USE_MULTIVIEW = false + +#[vertex] +layout(location = 0) in vec2 vertex_attrib; + +/* clang-format on */ + +out vec2 uv_interp; + +void main() { + uv_interp = vertex_attrib * 0.5 + 0.5; + gl_Position = vec4(vertex_attrib, 1.0, 1.0); +} + +/* clang-format off */ +#[fragment] +/* clang-format on */ + +#ifdef MODE_FILTER +#ifdef USE_MULTIVIEW +uniform sampler2DArray source_color; // texunit:0 +#else +uniform sampler2D source_color; // texunit:0 +#endif // USE_MULTIVIEW +uniform float view; +uniform vec2 pixel_size; +uniform float luminance_multiplier; +uniform float glow_bloom; +uniform float glow_hdr_threshold; +uniform float glow_hdr_scale; +uniform float glow_luminance_cap; +#endif // MODE_FILTER + +#ifdef MODE_DOWNSAMPLE +uniform sampler2D source_color; // texunit:0 +uniform vec2 pixel_size; +#endif // MODE_DOWNSAMPLE + +#ifdef MODE_UPSAMPLE +uniform sampler2D source_color; // texunit:0 +uniform vec2 pixel_size; +#endif // MODE_UPSAMPLE + +in vec2 uv_interp; + +layout(location = 0) out vec4 frag_color; + +void main() { +#ifdef MODE_FILTER + // Note, we read from an image with double resolution, so we average those out +#ifdef USE_MULTIVIEW + vec2 half_pixel = pixel_size * 0.5; + vec3 uv = vec3(uv_interp, view); + vec3 color = textureLod(source_color, uv, 0.0).rgb * 4.0; + color += textureLod(source_color, uv - vec3(half_pixel, 0.0), 0.0).rgb; + color += textureLod(source_color, uv + vec3(half_pixel, 0.0), 0.0).rgb; + color += textureLod(source_color, uv - vec3(half_pixel.x, -half_pixel.y, 0.0), 0.0).rgb; + color += textureLod(source_color, uv + vec3(half_pixel.x, -half_pixel.y, 0.0), 0.0).rgb; +#else + vec2 half_pixel = pixel_size * 0.5; + vec2 uv = uv_interp; + vec3 color = textureLod(source_color, uv, 0.0).rgb * 4.0; + color += textureLod(source_color, uv - half_pixel, 0.0).rgb; + color += textureLod(source_color, uv + half_pixel, 0.0).rgb; + color += textureLod(source_color, uv - vec2(half_pixel.x, -half_pixel.y), 0.0).rgb; + color += textureLod(source_color, uv + vec2(half_pixel.x, -half_pixel.y), 0.0).rgb; +#endif // USE_MULTIVIEW + color /= luminance_multiplier * 8.0; + + float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722)); + float feedback = max(smoothstep(glow_hdr_threshold, glow_hdr_threshold + glow_hdr_scale, luminance), glow_bloom); + + color = min(color * feedback, vec3(glow_luminance_cap)); + + frag_color = vec4(luminance_multiplier * color, 1.0); +#endif // MODE_FILTER + +#ifdef MODE_DOWNSAMPLE + vec2 half_pixel = pixel_size * 0.5; + vec4 color = textureLod(source_color, uv_interp, 0.0) * 4.0; + color += textureLod(source_color, uv_interp - half_pixel, 0.0); + color += textureLod(source_color, uv_interp + half_pixel, 0.0); + color += textureLod(source_color, uv_interp - vec2(half_pixel.x, -half_pixel.y), 0.0); + color += textureLod(source_color, uv_interp + vec2(half_pixel.x, -half_pixel.y), 0.0); + frag_color = color / 8.0; +#endif // MODE_DOWNSAMPLE + +#ifdef MODE_UPSAMPLE + vec2 half_pixel = pixel_size * 0.5; + + vec4 color = textureLod(source_color, uv_interp + vec2(-half_pixel.x * 2.0, 0.0), 0.0); + color += textureLod(source_color, uv_interp + vec2(-half_pixel.x, half_pixel.y), 0.0) * 2.0; + color += textureLod(source_color, uv_interp + vec2(0.0, half_pixel.y * 2.0), 0.0); + color += textureLod(source_color, uv_interp + vec2(half_pixel.x, half_pixel.y), 0.0) * 2.0; + color += textureLod(source_color, uv_interp + vec2(half_pixel.x * 2.0, 0.0), 0.0); + color += textureLod(source_color, uv_interp + vec2(half_pixel.x, -half_pixel.y), 0.0) * 2.0; + color += textureLod(source_color, uv_interp + vec2(0.0, -half_pixel.y * 2.0), 0.0); + color += textureLod(source_color, uv_interp + vec2(-half_pixel.x, -half_pixel.y), 0.0) * 2.0; + + frag_color = color / 12.0; +#endif // MODE_UPSAMPLE +} diff --git a/drivers/gles3/shaders/effects/post.glsl b/drivers/gles3/shaders/effects/post.glsl new file mode 100644 index 00000000000..e61171c92a1 --- /dev/null +++ b/drivers/gles3/shaders/effects/post.glsl @@ -0,0 +1,96 @@ +/* clang-format off */ +#[modes] +mode_default = #define MODE_DEFAULT +// mode_glow = #define MODE_GLOW + +#[specializations] + +USE_MULTIVIEW = false +USE_GLOW = false +USE_LUMINANCE_MULTIPLIER = false + +#[vertex] +layout(location = 0) in vec2 vertex_attrib; + +/* clang-format on */ + +out vec2 uv_interp; + +void main() { + uv_interp = vertex_attrib * 0.5 + 0.5; + gl_Position = vec4(vertex_attrib, 1.0, 1.0); +} + +/* clang-format off */ +#[fragment] +/* clang-format on */ + +#include "../tonemap_inc.glsl" + +#ifdef USE_MULTIVIEW +uniform sampler2DArray source_color; // texunit:0 +#else +uniform sampler2D source_color; // texunit:0 +#endif // USE_MULTIVIEW + +uniform float view; +uniform float luminance_multiplier; + +#ifdef USE_GLOW +uniform sampler2D glow_color; // texunit:1 +uniform vec2 pixel_size; +uniform float glow_intensity; + +vec4 get_glow_color(vec2 uv) { + vec2 half_pixel = pixel_size * 0.5; + + vec4 color = textureLod(glow_color, uv + vec2(-half_pixel.x * 2.0, 0.0), 0.0); + color += textureLod(glow_color, uv + vec2(-half_pixel.x, half_pixel.y), 0.0) * 2.0; + color += textureLod(glow_color, uv + vec2(0.0, half_pixel.y * 2.0), 0.0); + color += textureLod(glow_color, uv + vec2(half_pixel.x, half_pixel.y), 0.0) * 2.0; + color += textureLod(glow_color, uv + vec2(half_pixel.x * 2.0, 0.0), 0.0); + color += textureLod(glow_color, uv + vec2(half_pixel.x, -half_pixel.y), 0.0) * 2.0; + color += textureLod(glow_color, uv + vec2(0.0, -half_pixel.y * 2.0), 0.0); + color += textureLod(glow_color, uv + vec2(-half_pixel.x, -half_pixel.y), 0.0) * 2.0; + + return color / 12.0; +} +#endif // USE_GLOW + +in vec2 uv_interp; + +layout(location = 0) out vec4 frag_color; + +void main() { +#ifdef USE_MULTIVIEW + vec4 color = texture(source_color, vec3(uv_interp, view)); +#else + vec4 color = texture(source_color, uv_interp); +#endif + +#ifdef USE_GLOW + vec4 glow = get_glow_color(uv_interp) * glow_intensity; + + // Just use softlight... + glow.rgb = clamp(glow.rgb, vec3(0.0f), vec3(1.0f)); + color.rgb = max((color.rgb + glow.rgb) - (color.rgb * glow.rgb), vec3(0.0)); +#endif // USE_GLOW + +#ifdef USE_LUMINANCE_MULTIPLIER + color = color / luminance_multiplier; +#endif + + color.rgb = srgb_to_linear(color.rgb); + color.rgb = apply_tonemapping(color.rgb, white); + color.rgb = linear_to_srgb(color.rgb); + +#ifdef USE_BCS + color.rgb = apply_bcs(color.rgb, bcs); +#endif + +#ifdef USE_COLOR_CORRECTION + color.rgb = apply_color_correction(color.rgb, color_correction); +#endif + + frag_color = color; +} diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 667cbb3d906..2aeec42e154 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -28,6 +28,7 @@ LIGHT_USE_PSSM4 = false LIGHT_USE_PSSM_BLEND = false BASE_PASS = true USE_ADDITIVE_LIGHTING = false +APPLY_TONEMAPPING = true // We can only use one type of light per additive pass. This means that if USE_ADDITIVE_LIGHTING is defined, and // these are false, we are doing a directional light pass. ADDITIVE_OMNI = false @@ -185,18 +186,17 @@ layout(std140) uniform SceneData { // ubo:2 uint fog_mode; float fog_density; float fog_height; - float fog_height_density; + float fog_height_density; float fog_depth_curve; - float pad; + float fog_sun_scatter; float fog_depth_begin; vec3 fog_light_color; float fog_depth_end; - float fog_sun_scatter; - float shadow_bias; + float luminance_multiplier; uint camera_visible_layers; bool pancake_shadows; } @@ -676,18 +676,17 @@ layout(std140) uniform SceneData { // ubo:2 uint fog_mode; float fog_density; float fog_height; - float fog_height_density; + float fog_height_density; float fog_depth_curve; - float pad; + float fog_sun_scatter; float fog_depth_begin; vec3 fog_light_color; float fog_depth_end; - float fog_sun_scatter; - float shadow_bias; + float luminance_multiplier; uint camera_visible_layers; bool pancake_shadows; } @@ -1758,7 +1757,9 @@ void main() { // Tonemap before writing as we are writing to an sRGB framebuffer frag_color.rgb *= exposure; +#ifdef APPLY_TONEMAPPING frag_color.rgb = apply_tonemapping(frag_color.rgb, white); +#endif frag_color.rgb = linear_to_srgb(frag_color.rgb); #ifdef USE_BCS @@ -1973,7 +1974,9 @@ void main() { // Tonemap before writing as we are writing to an sRGB framebuffer additive_light_color *= exposure; +#ifdef APPLY_TONEMAPPING additive_light_color = apply_tonemapping(additive_light_color, white); +#endif additive_light_color = linear_to_srgb(additive_light_color); #ifdef USE_BCS @@ -1986,6 +1989,9 @@ void main() { frag_color.rgb += additive_light_color; #endif // USE_ADDITIVE_LIGHTING + + frag_color.rgb *= scene_data.luminance_multiplier; + #endif // !RENDER_MATERIAL -#endif //!MODE_RENDER_DEPTH +#endif // !MODE_RENDER_DEPTH } diff --git a/drivers/gles3/shaders/sky.glsl b/drivers/gles3/shaders/sky.glsl index 9f9c22cf6d3..b10ea12e6ef 100644 --- a/drivers/gles3/shaders/sky.glsl +++ b/drivers/gles3/shaders/sky.glsl @@ -12,6 +12,7 @@ mode_cubemap_quarter_res = #define USE_CUBEMAP_PASS \n#define USE_QUARTER_RES_PA USE_MULTIVIEW = false USE_INVERTED_Y = true +APPLY_TONEMAPPING = true #[vertex] @@ -103,6 +104,7 @@ uniform mat4 orientation; uniform vec4 projection; uniform vec3 position; uniform float time; +uniform float sky_energy_multiplier; uniform float luminance_multiplier; uniform float fog_aerial_perspective; @@ -195,12 +197,14 @@ void main() { } - color *= luminance_multiplier; + color *= sky_energy_multiplier; // Convert to Linear for tonemapping so color matches scene shader better color = srgb_to_linear(color); color *= exposure; +#ifdef APPLY_TONEMAPPING color = apply_tonemapping(color, white); +#endif color = linear_to_srgb(color); #ifdef USE_BCS @@ -211,10 +215,10 @@ void main() { color = apply_color_correction(color, color_correction); #endif - frag_color.rgb = color; + frag_color.rgb = color * luminance_multiplier; frag_color.a = alpha; #ifdef USE_DEBANDING - frag_color.rgb += interleaved_gradient_noise(gl_FragCoord.xy) * luminance_multiplier; + frag_color.rgb += interleaved_gradient_noise(gl_FragCoord.xy) * sky_energy_multiplier * luminance_multiplier; #endif } diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index 33bb8088565..bd964423288 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/drivers/gles3/storage/render_scene_buffers_gles3.cpp @@ -47,6 +47,13 @@ #define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 #endif +RenderSceneBuffersGLES3::RenderSceneBuffersGLES3() { + for (int i = 0; i < 4; i++) { + glow.levels[i].color = 0; + glow.levels[i].fbo = 0; + } +} + RenderSceneBuffersGLES3::~RenderSceneBuffersGLES3() { free_render_buffer_data(); } @@ -137,9 +144,22 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p //use_debanding = p_config->get_use_debanding(); view_count = config->multiview_supported ? p_config->get_view_count() : 1; - ERR_FAIL_COND(view_count == 0); bool use_multiview = view_count > 1; + // Get color format data from our render target so we match those + if (render_target.is_valid()) { + color_internal_format = texture_storage->render_target_get_color_internal_format(render_target); + color_format = texture_storage->render_target_get_color_format(render_target); + color_type = texture_storage->render_target_get_color_type(render_target); + color_format_size = texture_storage->render_target_get_color_format_size(render_target); + } else { + // reflection probe? or error? + color_internal_format = GL_RGBA8; + color_format = GL_RGBA; + color_type = GL_UNSIGNED_BYTE; + color_format_size = 4; + } + // Check our scaling mode if (scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF && internal_size.x == 0 && internal_size.y == 0) { // Disable, no size set. @@ -153,14 +173,38 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR; } - bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF; // TODO also need this if doing post processing like glow + // Check if we support MSAA. + if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && internal_size.x == 0 && internal_size.y == 0) { + // Disable, no size set. + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (!use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_supported && !config->rt_msaa_supported) { + WARN_PRINT_ONCE("MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } else if (use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_multiview_supported && !config->rt_msaa_multiview_supported) { + WARN_PRINT_ONCE("Multiview MSAA is not supported on this device."); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; + } + + // We don't create our buffers right away because post effects can be made active at any time and change our buffer configuration. +} + +void RenderSceneBuffersGLES3::_check_render_buffers() { + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + GLES3::Config *config = GLES3::Config::get_singleton(); + + ERR_FAIL_COND(view_count == 0); + + bool use_internal_buffer = scaling_3d_mode != RS::VIEWPORT_SCALING_3D_MODE_OFF || glow.glow_enabled; + uint32_t depth_format_size = 3; + bool use_multiview = view_count > 1; + + if ((!use_internal_buffer || internal3d.color != 0) && (msaa3d.mode == RS::VIEWPORT_MSAA_DISABLED || msaa3d.color != 0)) { + // already setup! + return; + } + if (use_internal_buffer) { // Setup our internal buffer. - bool is_transparent = texture_storage->render_target_get_transparent(render_target); - GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; - GLuint color_format = GL_RGBA; - GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; - GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; // Create our color buffer. @@ -178,7 +222,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.color, internal_size.x * internal_size.y * view_count * 4, "3D color texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.color, internal_size.x * internal_size.y * view_count * color_format_size, "3D color texture"); // Create our depth buffer. glGenTextures(1, &internal3d.depth); @@ -195,7 +239,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D depth texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(internal3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size, "3D depth texture"); // Create our internal 3D FBO. // Note that if MSAA is used and our rt_msaa_* extensions are available, this is only used for blitting and effects. @@ -224,18 +268,6 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p glBindFramebuffer(GL_FRAMEBUFFER, 0); } - // Check if we support MSAA. - if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && internal_size.x == 0 && internal_size.y == 0) { - // Disable, no size set. - msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; - } else if (!use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_supported && !config->rt_msaa_supported) { - WARN_PRINT_ONCE("MSAA is not supported on this device."); - msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; - } else if (use_multiview && msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED && !config->msaa_multiview_supported && !config->rt_msaa_multiview_supported) { - WARN_PRINT_ONCE("Multiview MSAA is not supported on this device."); - msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; - } - if (msaa3d.mode != RS::VIEWPORT_MSAA_DISABLED) { // Setup MSAA. const GLsizei samples[] = { 1, 2, 4, 8 }; @@ -255,9 +287,6 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p msaa3d.needs_resolve = true; msaa3d.check_fbo_cache = false; - bool is_transparent = texture_storage->render_target_get_transparent(render_target); - GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; - // Create our color buffer. glGenRenderbuffers(1, &msaa3d.color); glBindRenderbuffer(GL_RENDERBUFFER, msaa3d.color); @@ -282,6 +311,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { _clear_msaa3d_buffers(); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); } @@ -293,9 +323,6 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p msaa3d.needs_resolve = true; msaa3d.check_fbo_cache = false; - bool is_transparent = texture_storage->render_target_get_transparent(render_target); - GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; - // Create our color buffer. glGenTextures(1, &msaa3d.color); glBindTexture(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.color); @@ -306,7 +333,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, color_internal_format, internal_size.x, internal_size.y, view_count, GL_TRUE); #endif - GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * 4 * msaa3d.samples, "MSAA 3D color texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.color, internal_size.x * internal_size.y * view_count * color_format_size * msaa3d.samples, "MSAA 3D color texture"); // Create our depth buffer. glGenTextures(1, &msaa3d.depth); @@ -318,7 +345,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p glTexImage3DMultisample(GL_TEXTURE_2D_MULTISAMPLE_ARRAY, msaa3d.samples, GL_DEPTH_COMPONENT24, internal_size.x, internal_size.y, view_count, GL_TRUE); #endif - GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * msaa3d.samples, "MSAA 3D depth texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(msaa3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size * msaa3d.samples, "MSAA 3D depth texture"); // Create our MSAA 3D FBO. glGenFramebuffers(1, &msaa3d.fbo); @@ -330,6 +357,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { _clear_msaa3d_buffers(); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; WARN_PRINT("Could not create 3D MSAA buffers, status: " + texture_storage->get_framebuffer_error(status)); } @@ -358,6 +386,7 @@ void RenderSceneBuffersGLES3::configure(const RenderSceneBuffersConfiguration *p GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { _clear_msaa3d_buffers(); + msaa3d.mode = RS::VIEWPORT_MSAA_DISABLED; WARN_PRINT("Could not create 3D MSAA framebuffer, status: " + texture_storage->get_framebuffer_error(status)); } @@ -435,13 +464,9 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de glBindFramebuffer(GL_FRAMEBUFFER, backbuffer3d.fbo); - bool is_transparent = texture_storage->render_target_get_transparent(render_target); - GLuint color_internal_format = is_transparent ? GL_RGBA8 : GL_RGB10_A2; - GLuint color_format = GL_RGBA; - GLuint color_type = is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; - bool use_multiview = view_count > 1 && GLES3::Config::get_singleton()->multiview_supported; GLenum texture_target = use_multiview ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; + uint32_t depth_format_size = 3; if (backbuffer3d.color == 0 && p_need_color) { glGenTextures(1, &backbuffer3d.color); @@ -458,7 +483,7 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.color, internal_size.x * internal_size.y * view_count * 4, "3D Back buffer color texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.color, internal_size.x * internal_size.y * view_count * color_format_size, "3D Back buffer color texture"); #ifndef IOS_ENABLED if (use_multiview) { @@ -486,7 +511,7 @@ void RenderSceneBuffersGLES3::check_backbuffer(bool p_need_color, bool p_need_de glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.depth, internal_size.x * internal_size.y * view_count * 3, "3D back buffer depth texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(backbuffer3d.depth, internal_size.x * internal_size.y * view_count * depth_format_size, "3D back buffer depth texture"); #ifndef IOS_ENABLED if (use_multiview) { @@ -526,21 +551,101 @@ void RenderSceneBuffersGLES3::_clear_back_buffers() { } } +void RenderSceneBuffersGLES3::set_glow_enabled(bool p_glow_enabled) { + if (glow.glow_enabled != p_glow_enabled) { + glow.glow_enabled = p_glow_enabled; + + // Clear our main buffers, this can impact them. + _clear_msaa3d_buffers(); + _clear_intermediate_buffers(); + } +} + +void RenderSceneBuffersGLES3::check_glow_buffers() { + if (glow.levels[0].color != 0) { + // already have these setup.. + return; + } + + GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); + Size2i level_size = internal_size; + for (int i = 0; i < 4; i++) { + level_size.x = MAX(level_size.x >> 1, 4); + level_size.y = MAX(level_size.y >> 1, 4); + + glow.levels[i].size = level_size; + + // Create our texture + glGenTextures(1, &glow.levels[i].color); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, glow.levels[i].color); + + glTexImage2D(GL_TEXTURE_2D, 0, color_internal_format, level_size.x, level_size.y, 0, color_format, color_type, nullptr); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); + + GLES3::Utilities::get_singleton()->texture_allocated_data(glow.levels[i].color, level_size.x * level_size.y * color_format_size, String("Glow buffer ") + String::num_int64(i)); + + // Create our FBO + glGenFramebuffers(1, &glow.levels[i].fbo); + glBindFramebuffer(GL_FRAMEBUFFER, glow.levels[i].fbo); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glow.levels[i].color, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + WARN_PRINT("Could not create glow buffers, status: " + texture_storage->get_framebuffer_error(status)); + _clear_glow_buffers(); + break; + } + } + + glBindTexture(GL_TEXTURE_2D, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void RenderSceneBuffersGLES3::_clear_glow_buffers() { + for (int i = 0; i < 4; i++) { + if (glow.levels[i].fbo != 0) { + glDeleteFramebuffers(1, &glow.levels[i].fbo); + glow.levels[i].fbo = 0; + } + + if (glow.levels[i].color != 0) { + GLES3::Utilities::get_singleton()->texture_free_data(glow.levels[i].color); + glow.levels[i].color = 0; + } + } +} + void RenderSceneBuffersGLES3::free_render_buffer_data() { _clear_msaa3d_buffers(); _clear_intermediate_buffers(); _clear_back_buffers(); + _clear_glow_buffers(); } GLuint RenderSceneBuffersGLES3::get_render_fbo() { GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton(); GLuint rt_fbo = 0; + _check_render_buffers(); + if (msaa3d.check_fbo_cache) { GLuint color = texture_storage->render_target_get_color(render_target); GLuint depth = texture_storage->render_target_get_depth(render_target); rt_fbo = _rt_get_cached_fbo(color, depth, msaa3d.samples, view_count); + if (rt_fbo == 0) { + // Somehow couldn't obtain this? Just render without MSAA. + rt_fbo = texture_storage->render_target_get_fbo(render_target); + } } else if (msaa3d.fbo != 0) { // We have an MSAA fbo, render to our MSAA buffer return msaa3d.fbo; diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index 39aa1fb9734..8d03d3438dd 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -33,6 +33,7 @@ #ifdef GLES3_ENABLED +#include "drivers/gles3/effects/glow.h" #include "servers/rendering/storage/render_scene_buffers.h" #include "platform_gl.h" @@ -52,6 +53,12 @@ public: RID render_target; + // Color format details from our render target + GLuint color_internal_format = GL_RGBA8; + GLuint color_format = GL_RGBA; + GLuint color_type = GL_UNSIGNED_BYTE; + uint32_t color_format_size = 4; + struct FBDEF { GLuint color = 0; GLuint depth = 0; @@ -74,31 +81,24 @@ public: FBDEF backbuffer3d; // our back buffer - // Built-in textures used for ping pong image processing and blurring. - struct Blur { - RID texture; - - struct Mipmap { - RID texture; - int width; - int height; - GLuint fbo; - }; - - Vector mipmaps; - }; - - Blur blur[2]; //the second one starts from the first mipmap + // Buffers for our glow implementation + struct GLOW { + bool glow_enabled = false; + GLES3::Glow::GLOWLEVEL levels[4]; + } glow; private: + void _check_render_buffers(); void _clear_msaa3d_buffers(); void _clear_intermediate_buffers(); void _clear_back_buffers(); + void _clear_glow_buffers(); void _rt_attach_textures(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count); GLuint _rt_get_cached_fbo(GLuint p_color, GLuint p_depth, GLsizei p_samples, uint32_t p_view_count); public: + RenderSceneBuffersGLES3(); virtual ~RenderSceneBuffersGLES3(); virtual void configure(const RenderSceneBuffersConfiguration *p_config) override; @@ -109,19 +109,45 @@ public: void free_render_buffer_data(); void check_backbuffer(bool p_need_color, bool p_need_depth); // Check if we need to initialize our backbuffer. + void check_glow_buffers(); // Check if we need to initialise our glow buffers. GLuint get_render_fbo(); - GLuint get_msaa3d_fbo() const { return msaa3d.fbo; } - GLuint get_msaa3d_color() const { return msaa3d.color; } - GLuint get_msaa3d_depth() const { return msaa3d.depth; } - bool get_msaa_needs_resolve() const { return msaa3d.needs_resolve; } - GLuint get_internal_fbo() const { return internal3d.fbo; } - GLuint get_internal_color() const { return internal3d.color; } - GLuint get_internal_depth() const { return internal3d.depth; } + GLuint get_msaa3d_fbo() { + _check_render_buffers(); + return msaa3d.fbo; + } + GLuint get_msaa3d_color() { + _check_render_buffers(); + return msaa3d.color; + } + GLuint get_msaa3d_depth() { + _check_render_buffers(); + return msaa3d.depth; + } + bool get_msaa_needs_resolve() { + _check_render_buffers(); + return msaa3d.needs_resolve; + } + GLuint get_internal_fbo() { + _check_render_buffers(); + return internal3d.fbo; + } + GLuint get_internal_color() { + _check_render_buffers(); + return internal3d.color; + } + GLuint get_internal_depth() { + _check_render_buffers(); + return internal3d.depth; + } GLuint get_backbuffer_fbo() const { return backbuffer3d.fbo; } GLuint get_backbuffer() const { return backbuffer3d.color; } GLuint get_backbuffer_depth() const { return backbuffer3d.depth; } + bool get_glow_enabled() const { return glow.glow_enabled; } + void set_glow_enabled(bool p_glow_enabled); + const GLES3::Glow::GLOWLEVEL *get_glow_buffers() const { return &glow.levels[0]; } + // Getters _FORCE_INLINE_ RID get_render_target() const { return render_target; } diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index bd4793f4dc4..e9df3c6fc82 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1981,10 +1981,25 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { Config *config = Config::get_singleton(); - rt->color_internal_format = rt->is_transparent ? GL_RGBA8 : GL_RGB10_A2; - rt->color_format = GL_RGBA; - rt->color_type = rt->is_transparent ? GL_UNSIGNED_BYTE : GL_UNSIGNED_INT_2_10_10_10_REV; - rt->image_format = Image::FORMAT_RGBA8; + if (rt->hdr) { + rt->color_internal_format = GL_RGBA16F; + rt->color_format = GL_RGBA; + rt->color_type = GL_FLOAT; + rt->color_format_size = 8; + rt->image_format = Image::FORMAT_RGBAF; + } else if (rt->is_transparent) { + rt->color_internal_format = GL_RGBA8; + rt->color_format = GL_RGBA; + rt->color_type = GL_UNSIGNED_BYTE; + rt->color_format_size = 4; + rt->image_format = Image::FORMAT_RGBA8; + } else { + rt->color_internal_format = GL_RGB10_A2; + rt->color_format = GL_RGBA; + rt->color_type = GL_UNSIGNED_INT_2_10_10_10_REV; + rt->color_format_size = 4; + rt->image_format = Image::FORMAT_RGBA8; + } glDisable(GL_SCISSOR_TEST); glColorMask(1, 1, 1, 1); @@ -2023,7 +2038,7 @@ void TextureStorage::_update_render_target(RenderTarget *rt) { texture->gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); texture->gl_set_repeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->color, rt->size.x * rt->size.y * rt->view_count * 4, "Render target color texture"); + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->color, rt->size.x * rt->size.y * rt->view_count * rt->color_format_size, "Render target color texture"); } #ifndef IOS_ENABLED if (use_multiview) { @@ -2194,7 +2209,7 @@ void GLES3::TextureStorage::check_backbuffer(RenderTarget *rt, const bool uses_s } else { glTexImage2D(texture_target, 0, rt->color_internal_format, rt->size.x, rt->size.y, 0, rt->color_format, rt->color_type, nullptr); } - GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer, rt->size.x * rt->size.y * rt->view_count * 4, "Render target backbuffer color texture (3D)"); + GLES3::Utilities::get_singleton()->texture_allocated_data(rt->backbuffer, rt->size.x * rt->size.y * rt->view_count * rt->color_format_size, "Render target backbuffer color texture (3D)"); glTexParameteri(texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -2548,6 +2563,54 @@ RS::ViewportMSAA TextureStorage::render_target_get_msaa(RID p_render_target) con return rt->msaa; } +void TextureStorage::render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL(rt); + ERR_FAIL_COND(rt->direct_to_screen); + if (p_use_hdr_2d == rt->hdr) { + return; + } + + _clear_render_target(rt); + rt->hdr = p_use_hdr_2d; + _update_render_target(rt); +} + +bool TextureStorage::render_target_is_using_hdr(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, false); + + return rt->hdr; +} + +GLuint TextureStorage::render_target_get_color_internal_format(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, GL_RGBA8); + + return rt->color_internal_format; +} + +GLuint TextureStorage::render_target_get_color_format(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, GL_RGBA); + + return rt->color_format; +} + +GLuint TextureStorage::render_target_get_color_type(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, GL_UNSIGNED_BYTE); + + return rt->color_type; +} + +uint32_t TextureStorage::render_target_get_color_format_size(RID p_render_target) const { + RenderTarget *rt = render_target_owner.get_or_null(p_render_target); + ERR_FAIL_NULL_V(rt, 4); + + return rt->color_format_size; +} + void TextureStorage::render_target_request_clear(RID p_render_target, const Color &p_clear_color) { RenderTarget *rt = render_target_owner.get_or_null(p_render_target); ERR_FAIL_NULL(rt); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 91bb676711e..ef310262c7a 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -347,9 +347,11 @@ struct RenderTarget { GLuint backbuffer = 0; GLuint backbuffer_depth = 0; + bool hdr = false; // For Compatibility this effects both 2D and 3D rendering! GLuint color_internal_format = GL_RGBA8; GLuint color_format = GL_RGBA; GLuint color_type = GL_UNSIGNED_BYTE; + uint32_t color_format_size = 4; Image::Format image_format = Image::FORMAT_RGBA8; GLuint sdf_texture_write = 0; @@ -631,14 +633,19 @@ public: virtual void render_target_set_msaa_needs_resolve(RID p_render_target, bool p_needs_resolve) override {} virtual bool render_target_get_msaa_needs_resolve(RID p_render_target) const override { return false; } virtual void render_target_do_msaa_resolve(RID p_render_target) override {} - virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override {} - virtual bool render_target_is_using_hdr(RID p_render_target) const override { return false; } + virtual void render_target_set_use_hdr(RID p_render_target, bool p_use_hdr_2d) override; + virtual bool render_target_is_using_hdr(RID p_render_target) const override; // new void render_target_set_as_unused(RID p_render_target) override { render_target_clear_used(p_render_target); } + GLuint render_target_get_color_internal_format(RID p_render_target) const; + GLuint render_target_get_color_format(RID p_render_target) const; + GLuint render_target_get_color_type(RID p_render_target) const; + uint32_t render_target_get_color_format_size(RID p_render_target) const; + void render_target_request_clear(RID p_render_target, const Color &p_clear_color) override; bool render_target_is_clear_requested(RID p_render_target) override; Color render_target_get_clear_request_color(RID p_render_target) override; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 910a6e303db..f8c70c3002a 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -1131,8 +1131,15 @@ void Environment::_validate_property(PropertyInfo &p_property) const { p_property.usage = PROPERTY_USAGE_NO_EDITOR; } - if (p_property.name == "glow_mix" && glow_blend_mode != GLOW_BLEND_MODE_MIX) { - p_property.usage = PROPERTY_USAGE_NO_EDITOR; + if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") { + // Hide glow properties we do not support in GL Compatibility. + if (p_property.name.begins_with("glow_levels") || p_property.name == "glow_normalized" || p_property.name == "glow_strength" || p_property.name == "glow_mix" || p_property.name == "glow_blend_mode" || p_property.name == "glow_map_strength" || p_property.name == "glow_map") { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } + } else { + if (p_property.name == "glow_mix" && glow_blend_mode != GLOW_BLEND_MODE_MIX) { + p_property.usage = PROPERTY_USAGE_NO_EDITOR; + } } if (p_property.name == "background_color") { diff --git a/servers/rendering/storage/environment_storage.cpp b/servers/rendering/storage/environment_storage.cpp index ec26e365094..a2fa7722b20 100644 --- a/servers/rendering/storage/environment_storage.cpp +++ b/servers/rendering/storage/environment_storage.cpp @@ -417,11 +417,6 @@ void RendererEnvironmentStorage::environment_set_glow(RID p_env, bool p_enable, Environment *env = environment_owner.get_or_null(p_env); ERR_FAIL_NULL(env); ERR_FAIL_COND_MSG(p_levels.size() != 7, "Size of array of glow levels must be 7"); -#ifdef DEBUG_ENABLED - if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility" && p_enable) { - WARN_PRINT_ONCE_ED("Glow is not supported when using the GL Compatibility backend yet. Support will be added in a future release."); - } -#endif env->glow_enabled = p_enable; env->glow_levels = p_levels; env->glow_intensity = p_intensity;