Prechádzať zdrojové kódy

Implement glow/bloom on compatibility renderer

Bastiaan Olij 1 rok pred
rodič
commit
aa260e5f3d

+ 4 - 2
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();
 }
 

+ 2 - 2
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);

+ 172 - 0
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

+ 89 - 0
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

+ 152 - 0
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

+ 69 - 0
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

+ 4 - 0
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());

+ 4 - 0
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;

+ 188 - 121
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<PASS_MODE_SHADOW>(&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<RenderSceneBuffers> &p_render_
 	GLES3::Config *config = GLES3::Config::get_singleton();
 	RENDER_TIMESTAMP("Setup 3D Scene");
 
+	bool apply_color_adjustments_in_post = false;
+
 	Ref<RenderSceneBuffersGLES3> 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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<PASS_MODE_COLOR>(&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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffers> &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<RenderSceneBuffersGLES3> 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);
+
+			for (uint32_t v = 0; v < view_count; 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_COLOR_ATTACHMENT0, read_color, 0, 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<RenderGeometryInstance *
 		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;
 
 		TightLocalVector<GLenum> draw_buffers;
 		draw_buffers.push_back(GL_COLOR_ATTACHMENT0);
@@ -3629,10 +3697,9 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
 			glBindFramebuffer(GL_FRAMEBUFFER, shadow_atlas_fb);
 			glViewport(0, 0, shadow_atlas_size, shadow_atlas_size);
 			glActiveTexture(GL_TEXTURE0);
-			glDepthMask(GL_TRUE);
+			scene_state.enable_gl_depth_draw(true);
 			glDepthFunc(GL_ALWAYS);
-			glDisable(GL_CULL_FACE);
-			scene_state.cull_mode = GLES3::SceneShaderData::CULL_DISABLED;
+			scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
 
 			// Loop through quadrants and copy shadows over.
 			for (int quadrant = 0; quadrant < 4; quadrant++) {
@@ -3706,8 +3773,8 @@ void RasterizerSceneGLES3::_render_buffers_debug_draw(Ref<RenderSceneBuffersGLES
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED);
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_A, GL_ONE);
 
-			glDisable(GL_DEPTH_TEST);
-			glDepthMask(GL_FALSE);
+			scene_state.enable_gl_depth_test(false);
+			scene_state.enable_gl_depth_draw(false);
 
 			copy_effects->copy_to_rect(Rect2(Vector2(), Vector2(0.5, 0.5)));
 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_RED);

+ 83 - 7
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<RID> &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).

+ 10 - 1
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")

+ 17 - 0
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)

+ 11 - 2
drivers/gles3/shaders/copy.glsl → 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;

+ 113 - 0
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
+}

+ 96 - 0
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;
+}

+ 15 - 9
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
 }

+ 7 - 3
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
 }

+ 141 - 36
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;

+ 48 - 22
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<Mipmap> 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; }

+ 69 - 6
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);

+ 9 - 2
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;

+ 9 - 2
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") {

+ 0 - 5
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;