Browse Source

Add reflection probe support to compatibility renderer using 2 probe approach.

Bastiaan Olij 1 year ago
parent
commit
509d8dba2a

+ 209 - 0
drivers/gles3/effects/cubemap_filter.cpp

@@ -0,0 +1,209 @@
+/**************************************************************************/
+/*  cubemap_filter.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 "cubemap_filter.h"
+
+#include "../storage/texture_storage.h"
+#include "core/config/project_settings.h"
+
+using namespace GLES3;
+
+CubemapFilter *CubemapFilter::singleton = nullptr;
+
+CubemapFilter::CubemapFilter() {
+	singleton = this;
+	ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
+
+	{
+		String defines;
+		defines += "\n#define MAX_SAMPLE_COUNT " + itos(ggx_samples) + "\n";
+		cubemap_filter.shader.initialize(defines);
+		cubemap_filter.shader_version = cubemap_filter.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
+	}
+}
+
+CubemapFilter::~CubemapFilter() {
+	glDeleteBuffers(1, &screen_triangle);
+	glDeleteVertexArrays(1, &screen_triangle_array);
+
+	cubemap_filter.shader.version_free(cubemap_filter.shader_version);
+	singleton = nullptr;
+}
+
+// Helper functions for IBL filtering
+
+Vector3 importance_sample_GGX(Vector2 xi, float roughness4) {
+	// Compute distribution direction
+	float phi = 2.0 * Math_PI * xi.x;
+	float cos_theta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
+	float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
+
+	// Convert to spherical direction
+	Vector3 half_vector;
+	half_vector.x = sin_theta * cos(phi);
+	half_vector.y = sin_theta * sin(phi);
+	half_vector.z = cos_theta;
+
+	return half_vector;
+}
+
+float distribution_GGX(float NdotH, float roughness4) {
+	float NdotH2 = NdotH * NdotH;
+	float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
+	denom = Math_PI * denom * denom;
+
+	return roughness4 / denom;
+}
+
+float radical_inverse_vdC(uint32_t bits) {
+	bits = (bits << 16) | (bits >> 16);
+	bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1);
+	bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2);
+	bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4);
+	bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8);
+
+	return float(bits) * 2.3283064365386963e-10;
+}
+
+Vector2 hammersley(uint32_t i, uint32_t N) {
+	return Vector2(float(i) / float(N), radical_inverse_vdC(i));
+}
+
+void CubemapFilter::filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer) {
+	glActiveTexture(GL_TEXTURE0);
+	glBindTexture(GL_TEXTURE_CUBE_MAP, p_source_cubemap);
+	glBindFramebuffer(GL_FRAMEBUFFER, p_dest_framebuffer);
+
+	CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT;
+
+	if (p_layer == 0) {
+		glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+		// Copy over base layer without filtering.
+		mode = CubemapFilterShaderGLES3::MODE_COPY;
+	}
+
+	int size = p_source_size >> p_layer;
+	glViewport(0, 0, size, size);
+	glBindVertexArray(screen_triangle_array);
+
+	bool success = cubemap_filter.shader.version_bind_shader(cubemap_filter.shader_version, mode);
+	if (!success) {
+		return;
+	}
+
+	if (p_layer > 0) {
+		const uint32_t sample_counts[4] = { 1, ggx_samples / 4, ggx_samples / 2, ggx_samples };
+		uint32_t sample_count = sample_counts[MIN(3, p_layer)];
+
+		float roughness = float(p_layer) / (p_mipmap_count);
+		float roughness4 = roughness * roughness;
+		roughness4 *= roughness4;
+
+		float solid_angle_texel = 4.0 * Math_PI / float(6 * size * size);
+
+		LocalVector<float> sample_directions;
+		sample_directions.resize(4 * sample_count);
+
+		uint32_t index = 0;
+		float weight = 0.0;
+		for (uint32_t i = 0; i < sample_count; i++) {
+			Vector2 xi = hammersley(i, sample_count);
+			Vector3 dir = importance_sample_GGX(xi, roughness4);
+			Vector3 light_vec = (2.0 * dir.z * dir - Vector3(0.0, 0.0, 1.0));
+
+			if (light_vec.z < 0.0) {
+				continue;
+			}
+
+			sample_directions[index * 4] = light_vec.x;
+			sample_directions[index * 4 + 1] = light_vec.y;
+			sample_directions[index * 4 + 2] = light_vec.z;
+
+			float D = distribution_GGX(dir.z, roughness4);
+			float pdf = D * dir.z / (4.0 * dir.z) + 0.0001;
+
+			float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001);
+
+			float mip_level = MAX(0.5 * log2(solid_angle_sample / solid_angle_texel) + float(MAX(1, p_layer - 3)), 1.0);
+
+			sample_directions[index * 4 + 3] = mip_level;
+			weight += light_vec.z;
+			index++;
+		}
+
+		glUniform4fv(cubemap_filter.shader.version_get_uniform(CubemapFilterShaderGLES3::SAMPLE_DIRECTIONS_MIP, cubemap_filter.shader_version, mode), sample_count, sample_directions.ptr());
+		cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::WEIGHT, weight, cubemap_filter.shader_version, mode);
+		cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, index, cubemap_filter.shader_version, mode);
+	}
+
+	for (int i = 0; i < 6; i++) {
+		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_dest_cubemap, p_layer);
+#ifdef DEBUG_ENABLED
+		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+		if (status != GL_FRAMEBUFFER_COMPLETE) {
+			WARN_PRINT("Could not bind sky radiance face: " + itos(i) + ", status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
+		}
+#endif
+		cubemap_filter.shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, cubemap_filter.shader_version, mode);
+
+		glDrawArrays(GL_TRIANGLES, 0, 3);
+	}
+	glBindVertexArray(0);
+	glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+}
+
+#endif // GLES3_ENABLED

+ 70 - 0
drivers/gles3/effects/cubemap_filter.h

@@ -0,0 +1,70 @@
+/**************************************************************************/
+/*  cubemap_filter.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 CUBEMAP_FILTER_GLES3_H
+#define CUBEMAP_FILTER_GLES3_H
+
+#ifdef GLES3_ENABLED
+
+#include "drivers/gles3/shaders/effects/cubemap_filter.glsl.gen.h"
+
+namespace GLES3 {
+
+class CubemapFilter {
+private:
+	struct CMF {
+		CubemapFilterShaderGLES3 shader;
+		RID shader_version;
+	} cubemap_filter;
+
+	static CubemapFilter *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;
+
+	uint32_t ggx_samples = 128;
+
+public:
+	static CubemapFilter *get_singleton() {
+		return singleton;
+	}
+
+	CubemapFilter();
+	~CubemapFilter();
+
+	void filter_radiance(GLuint p_source_cubemap, GLuint p_dest_cubemap, GLuint p_dest_framebuffer, int p_source_size, int p_mipmap_count, int p_layer);
+};
+
+} //namespace GLES3
+
+#endif // GLES3_ENABLED
+
+#endif // CUBEMAP_FILTER_GLES3_H

+ 2 - 0
drivers/gles3/rasterizer_gles3.cpp

@@ -208,6 +208,7 @@ void RasterizerGLES3::finalize() {
 	memdelete(fog);
 	memdelete(fog);
 	memdelete(post_effects);
 	memdelete(post_effects);
 	memdelete(glow);
 	memdelete(glow);
+	memdelete(cubemap_filter);
 	memdelete(copy_effects);
 	memdelete(copy_effects);
 	memdelete(light_storage);
 	memdelete(light_storage);
 	memdelete(particles_storage);
 	memdelete(particles_storage);
@@ -354,6 +355,7 @@ RasterizerGLES3::RasterizerGLES3() {
 	particles_storage = memnew(GLES3::ParticlesStorage);
 	particles_storage = memnew(GLES3::ParticlesStorage);
 	light_storage = memnew(GLES3::LightStorage);
 	light_storage = memnew(GLES3::LightStorage);
 	copy_effects = memnew(GLES3::CopyEffects);
 	copy_effects = memnew(GLES3::CopyEffects);
+	cubemap_filter = memnew(GLES3::CubemapFilter);
 	glow = memnew(GLES3::Glow);
 	glow = memnew(GLES3::Glow);
 	post_effects = memnew(GLES3::PostEffects);
 	post_effects = memnew(GLES3::PostEffects);
 	gi = memnew(GLES3::GI);
 	gi = memnew(GLES3::GI);

+ 2 - 0
drivers/gles3/rasterizer_gles3.h

@@ -34,6 +34,7 @@
 #ifdef GLES3_ENABLED
 #ifdef GLES3_ENABLED
 
 
 #include "effects/copy_effects.h"
 #include "effects/copy_effects.h"
+#include "effects/cubemap_filter.h"
 #include "effects/glow.h"
 #include "effects/glow.h"
 #include "effects/post_effects.h"
 #include "effects/post_effects.h"
 #include "environment/fog.h"
 #include "environment/fog.h"
@@ -70,6 +71,7 @@ protected:
 	GLES3::GI *gi = nullptr;
 	GLES3::GI *gi = nullptr;
 	GLES3::Fog *fog = nullptr;
 	GLES3::Fog *fog = nullptr;
 	GLES3::CopyEffects *copy_effects = nullptr;
 	GLES3::CopyEffects *copy_effects = nullptr;
+	GLES3::CubemapFilter *cubemap_filter = nullptr;
 	GLES3::Glow *glow = nullptr;
 	GLES3::Glow *glow = nullptr;
 	GLES3::PostEffects *post_effects = nullptr;
 	GLES3::PostEffects *post_effects = nullptr;
 	RasterizerCanvasGLES3 *canvas = nullptr;
 	RasterizerCanvasGLES3 *canvas = nullptr;

+ 143 - 176
drivers/gles3/rasterizer_scene_gles3.cpp

@@ -65,7 +65,7 @@ RenderGeometryInstance *RasterizerSceneGLES3::geometry_instance_create(RID p_bas
 }
 }
 
 
 uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() {
 uint32_t RasterizerSceneGLES3::geometry_instance_get_pair_mask() {
-	return (1 << RS::INSTANCE_LIGHT);
+	return ((1 << RS::INSTANCE_LIGHT) | (1 << RS::INSTANCE_REFLECTION_PROBE));
 }
 }
 
 
 void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) {
 void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) {
@@ -97,6 +97,14 @@ void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_light_instances(const RID
 	}
 	}
 }
 }
 
 
+void RasterizerSceneGLES3::GeometryInstanceGLES3::pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) {
+	paired_reflection_probes.clear();
+
+	for (uint32_t i = 0; i < p_reflection_probe_instance_count; i++) {
+		paired_reflection_probes.push_back(p_reflection_probe_instances[i]);
+	}
+}
+
 void RasterizerSceneGLES3::geometry_instance_free(RenderGeometryInstance *p_geometry_instance) {
 void RasterizerSceneGLES3::geometry_instance_free(RenderGeometryInstance *p_geometry_instance) {
 	GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
 	GeometryInstanceGLES3 *ginstance = static_cast<GeometryInstanceGLES3 *>(p_geometry_instance);
 	ERR_FAIL_NULL(ginstance);
 	ERR_FAIL_NULL(ginstance);
@@ -854,6 +862,7 @@ void RasterizerSceneGLES3::_draw_sky(RID p_env, const Projection &p_projection,
 }
 }
 
 
 void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier) {
 void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_multiplier) {
+	GLES3::CubemapFilter *cubemap_filter = GLES3::CubemapFilter::get_singleton();
 	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
 	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
 	ERR_FAIL_COND(p_env.is_null());
 	ERR_FAIL_COND(p_env.is_null());
 
 
@@ -970,10 +979,10 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
 
 
 		if (update_single_frame) {
 		if (update_single_frame) {
 			for (int i = 0; i < max_processing_layer; i++) {
 			for (int i = 0; i < max_processing_layer; i++) {
-				_filter_sky_radiance(sky, i);
+				cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, i);
 			}
 			}
 		} else {
 		} else {
-			_filter_sky_radiance(sky, 0); //Just copy over the first mipmap
+			cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, 0); // Just copy over the first mipmap.
 		}
 		}
 		sky->processing_layer = 1;
 		sky->processing_layer = 1;
 		sky->baked_exposure = p_sky_energy_multiplier;
 		sky->baked_exposure = p_sky_energy_multiplier;
@@ -984,135 +993,11 @@ void RasterizerSceneGLES3::_update_sky_radiance(RID p_env, const Projection &p_p
 			scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
 			scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_DISABLED);
 			scene_state.enable_gl_blend(false);
 			scene_state.enable_gl_blend(false);
 
 
-			_filter_sky_radiance(sky, sky->processing_layer);
+			cubemap_filter->filter_radiance(sky->raw_radiance, sky->radiance, sky->radiance_framebuffer, sky->radiance_size, sky->mipmap_count, sky->processing_layer);
 			sky->processing_layer++;
 			sky->processing_layer++;
 		}
 		}
 	}
 	}
-}
-
-// Helper functions for IBL filtering
-
-Vector3 importance_sample_GGX(Vector2 xi, float roughness4) {
-	// Compute distribution direction
-	float phi = 2.0 * Math_PI * xi.x;
-	float cos_theta = sqrt((1.0 - xi.y) / (1.0 + (roughness4 - 1.0) * xi.y));
-	float sin_theta = sqrt(1.0 - cos_theta * cos_theta);
-
-	// Convert to spherical direction
-	Vector3 half_vector;
-	half_vector.x = sin_theta * cos(phi);
-	half_vector.y = sin_theta * sin(phi);
-	half_vector.z = cos_theta;
-
-	return half_vector;
-}
-
-float distribution_GGX(float NdotH, float roughness4) {
-	float NdotH2 = NdotH * NdotH;
-	float denom = (NdotH2 * (roughness4 - 1.0) + 1.0);
-	denom = Math_PI * denom * denom;
-
-	return roughness4 / denom;
-}
-
-float radical_inverse_vdC(uint32_t bits) {
-	bits = (bits << 16) | (bits >> 16);
-	bits = ((bits & 0x55555555) << 1) | ((bits & 0xAAAAAAAA) >> 1);
-	bits = ((bits & 0x33333333) << 2) | ((bits & 0xCCCCCCCC) >> 2);
-	bits = ((bits & 0x0F0F0F0F) << 4) | ((bits & 0xF0F0F0F0) >> 4);
-	bits = ((bits & 0x00FF00FF) << 8) | ((bits & 0xFF00FF00) >> 8);
-
-	return float(bits) * 2.3283064365386963e-10;
-}
-
-Vector2 hammersley(uint32_t i, uint32_t N) {
-	return Vector2(float(i) / float(N), radical_inverse_vdC(i));
-}
-
-void RasterizerSceneGLES3::_filter_sky_radiance(Sky *p_sky, int p_base_layer) {
-	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
-
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_CUBE_MAP, p_sky->raw_radiance);
-	glBindFramebuffer(GL_FRAMEBUFFER, p_sky->radiance_framebuffer);
-
-	CubemapFilterShaderGLES3::ShaderVariant mode = CubemapFilterShaderGLES3::MODE_DEFAULT;
-
-	if (p_base_layer == 0) {
-		glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
-		// Copy over base layer without filtering.
-		mode = CubemapFilterShaderGLES3::MODE_COPY;
-	}
-
-	int size = p_sky->radiance_size >> p_base_layer;
-	glViewport(0, 0, size, size);
-	glBindVertexArray(sky_globals.screen_triangle_array);
-
-	bool success = material_storage->shaders.cubemap_filter_shader.version_bind_shader(scene_globals.cubemap_filter_shader_version, mode);
-	if (!success) {
-		return;
-	}
-
-	if (p_base_layer > 0) {
-		const uint32_t sample_counts[4] = { 1, sky_globals.ggx_samples / 4, sky_globals.ggx_samples / 2, sky_globals.ggx_samples };
-		uint32_t sample_count = sample_counts[MIN(3, p_base_layer)];
-
-		float roughness = float(p_base_layer) / (p_sky->mipmap_count);
-		float roughness4 = roughness * roughness;
-		roughness4 *= roughness4;
-
-		float solid_angle_texel = 4.0 * Math_PI / float(6 * size * size);
-
-		LocalVector<float> sample_directions;
-		sample_directions.resize(4 * sample_count);
-
-		uint32_t index = 0;
-		float weight = 0.0;
-		for (uint32_t i = 0; i < sample_count; i++) {
-			Vector2 xi = hammersley(i, sample_count);
-			Vector3 dir = importance_sample_GGX(xi, roughness4);
-			Vector3 light_vec = (2.0 * dir.z * dir - Vector3(0.0, 0.0, 1.0));
-
-			if (light_vec.z < 0.0) {
-				continue;
-			}
-
-			sample_directions[index * 4] = light_vec.x;
-			sample_directions[index * 4 + 1] = light_vec.y;
-			sample_directions[index * 4 + 2] = light_vec.z;
-
-			float D = distribution_GGX(dir.z, roughness4);
-			float pdf = D * dir.z / (4.0 * dir.z) + 0.0001;
-
-			float solid_angle_sample = 1.0 / (float(sample_count) * pdf + 0.0001);
-
-			float mip_level = MAX(0.5 * log2(solid_angle_sample / solid_angle_texel) + float(MAX(1, p_base_layer - 3)), 1.0);
-
-			sample_directions[index * 4 + 3] = mip_level;
-			weight += light_vec.z;
-			index++;
-		}
-
-		glUniform4fv(material_storage->shaders.cubemap_filter_shader.version_get_uniform(CubemapFilterShaderGLES3::SAMPLE_DIRECTIONS_MIP, scene_globals.cubemap_filter_shader_version, mode), sample_count, sample_directions.ptr());
-		material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::WEIGHT, weight, scene_globals.cubemap_filter_shader_version, mode);
-		material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::SAMPLE_COUNT, index, scene_globals.cubemap_filter_shader_version, mode);
-	}
-
-	for (int i = 0; i < 6; i++) {
-		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, p_sky->radiance, p_base_layer);
-#ifdef DEBUG_ENABLED
-		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
-		if (status != GL_FRAMEBUFFER_COMPLETE) {
-			WARN_PRINT("Could not bind sky radiance face: " + itos(i) + ", status: " + GLES3::TextureStorage::get_singleton()->get_framebuffer_error(status));
-		}
-#endif
-		material_storage->shaders.cubemap_filter_shader.version_set_uniform(CubemapFilterShaderGLES3::FACE_ID, i, scene_globals.cubemap_filter_shader_version, mode);
-
-		glDrawArrays(GL_TRIANGLES, 0, 3);
-	}
-	glBindVertexArray(0);
-	glViewport(0, 0, p_sky->screen_size.x, p_sky->screen_size.y);
-	glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+	glViewport(0, 0, sky->screen_size.x, sky->screen_size.y);
 }
 }
 
 
 Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) {
 Ref<Image> RasterizerSceneGLES3::sky_bake_panorama(RID p_sky, float p_energy, bool p_bake_irradiance, const Size2i &p_size) {
@@ -1334,6 +1219,7 @@ _FORCE_INLINE_ static uint32_t _indices_to_primitives(RS::PrimitiveType p_primit
 }
 }
 void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append) {
 void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const RenderDataGLES3 *p_render_data, PassMode p_pass_mode, bool p_append) {
 	GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
 	GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton();
+	GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton();
 
 
 	if (p_render_list == RENDER_LIST_OPAQUE) {
 	if (p_render_list == RENDER_LIST_OPAQUE) {
 		scene_state.used_screen_texture = false;
 		scene_state.used_screen_texture = false;
@@ -1392,22 +1278,24 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
 			inst->light_passes.clear();
 			inst->light_passes.clear();
 			inst->spot_light_gl_cache.clear();
 			inst->spot_light_gl_cache.clear();
 			inst->omni_light_gl_cache.clear();
 			inst->omni_light_gl_cache.clear();
+			inst->reflection_probes_local_transform_cache.clear();
+			inst->reflection_probe_rid_cache.clear();
 			uint64_t current_frame = RSG::rasterizer->get_frame_number();
 			uint64_t current_frame = RSG::rasterizer->get_frame_number();
 
 
 			if (inst->paired_omni_light_count) {
 			if (inst->paired_omni_light_count) {
 				for (uint32_t j = 0; j < inst->paired_omni_light_count; j++) {
 				for (uint32_t j = 0; j < inst->paired_omni_light_count; j++) {
 					RID light_instance = inst->paired_omni_lights[j];
 					RID light_instance = inst->paired_omni_lights[j];
-					if (GLES3::LightStorage::get_singleton()->light_instance_get_render_pass(light_instance) != current_frame) {
+					if (light_storage->light_instance_get_render_pass(light_instance) != current_frame) {
 						continue;
 						continue;
 					}
 					}
-					RID light = GLES3::LightStorage::get_singleton()->light_instance_get_base_light(light_instance);
-					int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance);
+					RID light = light_storage->light_instance_get_base_light(light_instance);
+					int32_t shadow_id = light_storage->light_instance_get_shadow_id(light_instance);
 
 
-					if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) {
+					if (light_storage->light_has_shadow(light) && shadow_id >= 0) {
 						// Skip static lights when a lightmap is used.
 						// Skip static lights when a lightmap is used.
-						if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
+						if (!inst->lightmap_instance.is_valid() || light_storage->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
 							GeometryInstanceGLES3::LightPass pass;
 							GeometryInstanceGLES3::LightPass pass;
-							pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
+							pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
 							pass.shadow_id = shadow_id;
 							pass.shadow_id = shadow_id;
 							pass.light_instance_rid = light_instance;
 							pass.light_instance_rid = light_instance;
 							pass.is_omni = true;
 							pass.is_omni = true;
@@ -1415,7 +1303,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
 						}
 						}
 					} else {
 					} else {
 						// Lights without shadow can all go in base pass.
 						// Lights without shadow can all go in base pass.
-						inst->omni_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance));
+						inst->omni_light_gl_cache.push_back((uint32_t)light_storage->light_instance_get_gl_id(light_instance));
 					}
 					}
 				}
 				}
 			}
 			}
@@ -1423,24 +1311,42 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const
 			if (inst->paired_spot_light_count) {
 			if (inst->paired_spot_light_count) {
 				for (uint32_t j = 0; j < inst->paired_spot_light_count; j++) {
 				for (uint32_t j = 0; j < inst->paired_spot_light_count; j++) {
 					RID light_instance = inst->paired_spot_lights[j];
 					RID light_instance = inst->paired_spot_lights[j];
-					if (GLES3::LightStorage::get_singleton()->light_instance_get_render_pass(light_instance) != current_frame) {
+					if (light_storage->light_instance_get_render_pass(light_instance) != current_frame) {
 						continue;
 						continue;
 					}
 					}
-					RID light = GLES3::LightStorage::get_singleton()->light_instance_get_base_light(light_instance);
-					int32_t shadow_id = GLES3::LightStorage::get_singleton()->light_instance_get_shadow_id(light_instance);
+					RID light = light_storage->light_instance_get_base_light(light_instance);
+					int32_t shadow_id = light_storage->light_instance_get_shadow_id(light_instance);
 
 
-					if (GLES3::LightStorage::get_singleton()->light_has_shadow(light) && shadow_id >= 0) {
+					if (light_storage->light_has_shadow(light) && shadow_id >= 0) {
 						// Skip static lights when a lightmap is used.
 						// Skip static lights when a lightmap is used.
-						if (!inst->lightmap_instance.is_valid() || GLES3::LightStorage::get_singleton()->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
+						if (!inst->lightmap_instance.is_valid() || light_storage->light_get_bake_mode(light) != RenderingServer::LIGHT_BAKE_STATIC) {
 							GeometryInstanceGLES3::LightPass pass;
 							GeometryInstanceGLES3::LightPass pass;
-							pass.light_id = GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance);
+							pass.light_id = light_storage->light_instance_get_gl_id(light_instance);
 							pass.shadow_id = shadow_id;
 							pass.shadow_id = shadow_id;
 							pass.light_instance_rid = light_instance;
 							pass.light_instance_rid = light_instance;
 							inst->light_passes.push_back(pass);
 							inst->light_passes.push_back(pass);
 						}
 						}
 					} else {
 					} else {
 						// Lights without shadow can all go in base pass.
 						// Lights without shadow can all go in base pass.
-						inst->spot_light_gl_cache.push_back((uint32_t)GLES3::LightStorage::get_singleton()->light_instance_get_gl_id(light_instance));
+						inst->spot_light_gl_cache.push_back((uint32_t)light_storage->light_instance_get_gl_id(light_instance));
+					}
+				}
+			}
+
+			if (p_render_data->reflection_probe.is_null() && inst->paired_reflection_probes.size() > 0) {
+				// Do not include if we're rendering reflection probes.
+				// We only support two probes for now and we handle them first come, first serve.
+				// This should be improved one day, at minimum the list should be sorted by priority.
+
+				for (uint32_t pi = 0; pi < inst->paired_reflection_probes.size(); pi++) {
+					RID probe_instance = inst->paired_reflection_probes[pi];
+					RID atlas = light_storage->reflection_probe_instance_get_atlas(probe_instance);
+					RID probe = light_storage->reflection_probe_instance_get_probe(probe_instance);
+					uint32_t reflection_mask = light_storage->reflection_probe_get_reflection_mask(probe);
+					if (atlas.is_valid() && (inst->layer_mask & reflection_mask)) {
+						Transform3D local_matrix = p_render_data->inv_cam_transform * light_storage->reflection_probe_instance_get_transform(probe_instance);
+						inst->reflection_probes_local_transform_cache.push_back(local_matrix.affine_inverse());
+						inst->reflection_probe_rid_cache.push_back(probe_instance);
 					}
 					}
 				}
 				}
 			}
 			}
@@ -2321,20 +2227,21 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 	RENDER_TIMESTAMP("Setup 3D Scene");
 	RENDER_TIMESTAMP("Setup 3D Scene");
 
 
 	bool apply_color_adjustments_in_post = false;
 	bool apply_color_adjustments_in_post = false;
+	bool is_reflection_probe = p_reflection_probe.is_valid();
 
 
-	Ref<RenderSceneBuffersGLES3> rb;
-	if (p_render_buffers.is_valid()) {
-		rb = p_render_buffers;
-		ERR_FAIL_COND(rb.is_null());
+	Ref<RenderSceneBuffersGLES3> 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;
-		}
+	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);
+	GLES3::RenderTarget *rt = nullptr; // No render target for reflection probe
+	if (!is_reflection_probe) {
+		rt = texture_storage->get_render_target(rb->render_target);
+		ERR_FAIL_NULL(rt);
+	}
 
 
 	bool glow_enabled = false;
 	bool glow_enabled = false;
 	if (p_environment.is_valid() && rb.is_valid()) {
 	if (p_environment.is_valid() && rb.is_valid()) {
@@ -2351,7 +2258,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 	RenderDataGLES3 render_data;
 	RenderDataGLES3 render_data;
 	{
 	{
 		render_data.render_buffers = rb;
 		render_data.render_buffers = rb;
-		render_data.transparent_bg = rb.is_valid() ? rt->is_transparent : false;
+		render_data.transparent_bg = rt ? rt->is_transparent : false;
 		// Our first camera is used by default
 		// Our first camera is used by default
 		render_data.cam_transform = p_camera_data->main_transform;
 		render_data.cam_transform = p_camera_data->main_transform;
 		render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
 		render_data.inv_cam_transform = render_data.cam_transform.affine_inverse();
@@ -2381,7 +2288,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 		// this should be the same for all cameras..
 		// this should be the same for all cameras..
 		render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier();
 		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) {
+		if (rt != nullptr && 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...
 			// 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;
 			render_data.luminance_multiplier = 0.25;
 		} else {
 		} else {
@@ -2415,7 +2322,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 	glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer);
 	glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer);
 
 
 	Color clear_color;
 	Color clear_color;
-	if (p_render_buffers.is_valid()) {
+	if (!is_reflection_probe && rb->render_target.is_valid()) {
 		clear_color = texture_storage->render_target_get_clear_request_color(rb->render_target);
 		clear_color = texture_storage->render_target_get_clear_request_color(rb->render_target);
 	} else {
 	} else {
 		clear_color = texture_storage->get_default_clear_color();
 		clear_color = texture_storage->get_default_clear_color();
@@ -2448,9 +2355,9 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 
 
 	scene_state.ubo.emissive_exposure_normalization = -1.0; // Use default exposure normalization.
 	scene_state.ubo.emissive_exposure_normalization = -1.0; // Use default exposure normalization.
 
 
-	bool flip_y = !render_data.reflection_probe.is_valid();
+	bool flip_y = !is_reflection_probe;
 
 
-	if (rt->overridden.color.is_valid()) {
+	if (rt && rt->overridden.color.is_valid()) {
 		// If we've overridden the render target's color texture, then don't render upside down.
 		// If we've overridden the render target's color texture, then don't render upside down.
 		// We're probably rendering directly to an XR device.
 		// We're probably rendering directly to an XR device.
 		flip_y = false;
 		flip_y = false;
@@ -2462,7 +2369,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 	_render_shadows(&render_data, screen_size);
 	_render_shadows(&render_data, screen_size);
 
 
 	_setup_lights(&render_data, true, render_data.directional_light_count, render_data.omni_light_count, render_data.spot_light_count, render_data.directional_shadow_count);
 	_setup_lights(&render_data, true, render_data.directional_light_count, render_data.omni_light_count, render_data.spot_light_count, render_data.directional_shadow_count);
-	_setup_environment(&render_data, render_data.reflection_probe.is_valid(), screen_size, flip_y, clear_color, false);
+	_setup_environment(&render_data, is_reflection_probe, screen_size, flip_y, clear_color, false);
 
 
 	_fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR);
 	_fill_render_list(RENDER_LIST_OPAQUE, &render_data, PASS_MODE_COLOR);
 	render_list[RENDER_LIST_OPAQUE].sort_by_key();
 	render_list[RENDER_LIST_OPAQUE].sort_by_key();
@@ -2522,7 +2429,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 		if (draw_sky || draw_sky_fog_only || environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY) {
 		if (draw_sky || draw_sky_fog_only || environment_get_reflection_source(render_data.environment) == RS::ENV_REFLECTION_SOURCE_SKY || environment_get_ambient_source(render_data.environment) == RS::ENV_AMBIENT_SOURCE_SKY) {
 			RENDER_TIMESTAMP("Setup Sky");
 			RENDER_TIMESTAMP("Setup Sky");
 			Projection projection = render_data.cam_projection;
 			Projection projection = render_data.cam_projection;
-			if (render_data.reflection_probe.is_valid()) {
+			if (is_reflection_probe) {
 				Projection correction;
 				Projection correction;
 				correction.set_depth_correction(true, true, false);
 				correction.set_depth_correction(true, true, false);
 				projection = correction * render_data.cam_projection;
 				projection = correction * render_data.cam_projection;
@@ -2543,7 +2450,12 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 		}
 		}
 	}
 	}
 
 
-	GLuint fbo = rb->get_render_fbo();
+	GLuint fbo = 0;
+	if (is_reflection_probe) {
+		fbo = GLES3::LightStorage::get_singleton()->reflection_probe_instance_get_framebuffer(render_data.reflection_probe, render_data.reflection_probe_pass);
+	} else {
+		fbo = rb->get_render_fbo();
+	}
 
 
 	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
 	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
 	glViewport(0, 0, rb->internal_size.x, rb->internal_size.y);
 	glViewport(0, 0, rb->internal_size.x, rb->internal_size.y);
@@ -2664,10 +2576,17 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 		scene_state.enable_gl_blend(false);
 		scene_state.enable_gl_blend(false);
 		scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK);
 		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, render_data.luminance_multiplier, p_camera_data->view_count > 1, flip_y, apply_color_adjustments_in_post);
+		Projection projection = render_data.cam_projection;
+		if (is_reflection_probe) {
+			Projection correction;
+			correction.columns[1][1] = -1.0;
+			projection = correction * render_data.cam_projection;
+		}
+
+		_draw_sky(render_data.environment, 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) {
+	if (rt && (scene_state.used_screen_texture || scene_state.used_depth_texture)) {
 		Size2i size;
 		Size2i size;
 		GLuint backbuffer_fbo = 0;
 		GLuint backbuffer_fbo = 0;
 		GLuint backbuffer = 0;
 		GLuint backbuffer = 0;
@@ -2725,7 +2644,7 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 		glFrontFace(GL_CCW);
 		glFrontFace(GL_CCW);
 	}
 	}
 
 
-	if (rb.is_valid()) {
+	if (!is_reflection_probe && rb.is_valid()) {
 		_render_buffers_debug_draw(rb, p_shadow_atlas, fbo);
 		_render_buffers_debug_draw(rb, p_shadow_atlas, fbo);
 	}
 	}
 
 
@@ -2733,9 +2652,11 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
 	scene_state.reset_gl_state();
 	scene_state.reset_gl_state();
 	glUseProgram(0);
 	glUseProgram(0);
 
 
-	_render_post_processing(&render_data);
+	if (!is_reflection_probe) {
+		_render_post_processing(&render_data);
 
 
-	texture_storage->render_target_disable_clear_request(rb->render_target);
+		texture_storage->render_target_disable_clear_request(rb->render_target);
+	}
 
 
 	glActiveTexture(GL_TEXTURE0);
 	glActiveTexture(GL_TEXTURE0);
 }
 }
@@ -3203,6 +3124,14 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 						spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
 						spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
 					}
 					}
 
 
+					if (inst->reflection_probe_rid_cache.size() == 0) {
+						// We don't have any probes.
+						spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
+					} else if (inst->reflection_probe_rid_cache.size() > 1) {
+						// We have a second probe.
+						spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
+					}
+
 					if (inst->lightmap_instance.is_valid()) {
 					if (inst->lightmap_instance.is_valid()) {
 						spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
 						spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
 
 
@@ -3224,6 +3153,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 					spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
 					spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
 					spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
 					spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
 					spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
 					spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
+					spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;
 				}
 				}
 
 
 				if (uses_additive_lighting) {
 				if (uses_additive_lighting) {
@@ -3383,6 +3313,52 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
 				}
 				}
 			}
 			}
 
 
+			// Pass in reflection probe data
+			if constexpr (p_pass_mode == PASS_MODE_COLOR || p_pass_mode == PASS_MODE_COLOR_TRANSPARENT) {
+				if (pass == 0 && inst->reflection_probe_rid_cache.size() > 0) {
+					GLES3::Config *config = GLES3::Config::get_singleton();
+					GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton();
+
+					// Setup first probe.
+					{
+						RID probe_rid = light_storage->reflection_probe_instance_get_probe(inst->reflection_probe_rid_cache[0]);
+						GLES3::ReflectionProbe *probe = light_storage->get_reflection_probe(probe_rid);
+
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_USE_BOX_PROJECT, probe->box_projection, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_BOX_EXTENTS, probe->size * 0.5, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_BOX_OFFSET, probe->origin_offset, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_EXTERIOR, !probe->interior, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_INTENSITY, probe->intensity, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_AMBIENT_MODE, int(probe->ambient_mode), shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[0], shader->version, instance_variant, spec_constants);
+
+						glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
+						glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[0]));
+					}
+
+					if (inst->reflection_probe_rid_cache.size() > 1) {
+						// Setup second probe.
+						RID probe_rid = light_storage->reflection_probe_instance_get_probe(inst->reflection_probe_rid_cache[1]);
+						GLES3::ReflectionProbe *probe = light_storage->get_reflection_probe(probe_rid);
+
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_USE_BOX_PROJECT, probe->box_projection, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_BOX_EXTENTS, probe->size * 0.5, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_BOX_OFFSET, probe->origin_offset, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_EXTERIOR, !probe->interior, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_INTENSITY, probe->intensity, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_AMBIENT_MODE, int(probe->ambient_mode), shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
+						material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[1], shader->version, instance_variant, spec_constants);
+
+						glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
+						glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[1]));
+
+						spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
+					}
+				}
+			}
+
 			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, spec_constants);
 			material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::WORLD_TRANSFORM, world_transform, shader->version, instance_variant, spec_constants);
 			{
 			{
 				GLES3::Mesh::Surface *s = reinterpret_cast<GLES3::Mesh::Surface *>(surf->surface);
 				GLES3::Mesh::Surface *s = reinterpret_cast<GLES3::Mesh::Surface *>(surf->surface);
@@ -4074,6 +4050,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() {
 		global_defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(config->max_renderable_lights) + "\n";
 		global_defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(config->max_renderable_lights) + "\n";
 		global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 		global_defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(MAX_DIRECTIONAL_LIGHTS) + "\n";
 		global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "u\n";
 		global_defines += "\n#define MAX_FORWARD_LIGHTS " + itos(config->max_lights_per_object) + "u\n";
+		global_defines += "\n#define MAX_ROUGHNESS_LOD " + itos(sky_globals.roughness_layers - 1) + ".0\n";
 		material_storage->shaders.scene_shader.initialize(global_defines);
 		material_storage->shaders.scene_shader.initialize(global_defines);
 		scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
 		scene_globals.shader_default_version = material_storage->shaders.scene_shader.version_create();
 		material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);
 		material_storage->shaders.scene_shader.version_bind_shader(scene_globals.shader_default_version, SceneShaderGLES3::MODE_COLOR);
@@ -4129,7 +4106,6 @@ void fragment() {
 	{
 	{
 		// Initialize Sky stuff
 		// Initialize Sky stuff
 		sky_globals.roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
 		sky_globals.roughness_layers = GLOBAL_GET("rendering/reflections/sky_reflections/roughness_layers");
-		sky_globals.ggx_samples = GLOBAL_GET("rendering/reflections/sky_reflections/ggx_samples");
 
 
 		String global_defines;
 		String global_defines;
 		global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now
 		global_defines += "#define MAX_GLOBAL_SHADER_UNIFORMS 256\n"; // TODO: this is arbitrary for now
@@ -4138,13 +4114,6 @@ void fragment() {
 		sky_globals.shader_default_version = material_storage->shaders.sky_shader.version_create();
 		sky_globals.shader_default_version = material_storage->shaders.sky_shader.version_create();
 	}
 	}
 
 
-	{
-		String global_defines;
-		global_defines += "\n#define MAX_SAMPLE_COUNT " + itos(sky_globals.ggx_samples) + "\n";
-		material_storage->shaders.cubemap_filter_shader.initialize(global_defines);
-		scene_globals.cubemap_filter_shader_version = material_storage->shaders.cubemap_filter_shader.version_create();
-	}
-
 	{
 	{
 		sky_globals.default_shader = material_storage->shader_allocate();
 		sky_globals.default_shader = material_storage->shader_allocate();
 
 
@@ -4234,7 +4203,6 @@ RasterizerSceneGLES3::~RasterizerSceneGLES3() {
 
 
 	// Scene Shader
 	// Scene Shader
 	GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_free(scene_globals.shader_default_version);
 	GLES3::MaterialStorage::get_singleton()->shaders.scene_shader.version_free(scene_globals.shader_default_version);
-	GLES3::MaterialStorage::get_singleton()->shaders.cubemap_filter_shader.version_free(scene_globals.cubemap_filter_shader_version);
 	RSG::material_storage->material_free(scene_globals.default_material);
 	RSG::material_storage->material_free(scene_globals.default_material);
 	RSG::material_storage->shader_free(scene_globals.default_shader);
 	RSG::material_storage->shader_free(scene_globals.default_shader);
 
 
@@ -4250,7 +4218,6 @@ RasterizerSceneGLES3::~RasterizerSceneGLES3() {
 	RSG::material_storage->shader_free(sky_globals.fog_shader);
 	RSG::material_storage->shader_free(sky_globals.fog_shader);
 	GLES3::Utilities::get_singleton()->buffer_free_data(sky_globals.screen_triangle);
 	GLES3::Utilities::get_singleton()->buffer_free_data(sky_globals.screen_triangle);
 	glDeleteVertexArrays(1, &sky_globals.screen_triangle_array);
 	glDeleteVertexArrays(1, &sky_globals.screen_triangle_array);
-	glDeleteTextures(1, &sky_globals.radical_inverse_vdc_cache_tex);
 	GLES3::Utilities::get_singleton()->buffer_free_data(sky_globals.directional_light_buffer);
 	GLES3::Utilities::get_singleton()->buffer_free_data(sky_globals.directional_light_buffer);
 	memdelete_arr(sky_globals.directional_lights);
 	memdelete_arr(sky_globals.directional_lights);
 	memdelete_arr(sky_globals.last_frame_directional_lights);
 	memdelete_arr(sky_globals.last_frame_directional_lights);

+ 6 - 6
drivers/gles3/rasterizer_scene_gles3.h

@@ -37,7 +37,7 @@
 #include "core/templates/paged_allocator.h"
 #include "core/templates/paged_allocator.h"
 #include "core/templates/rid_owner.h"
 #include "core/templates/rid_owner.h"
 #include "core/templates/self_list.h"
 #include "core/templates/self_list.h"
-#include "drivers/gles3/shaders/cubemap_filter.glsl.gen.h"
+#include "drivers/gles3/shaders/effects/cubemap_filter.glsl.gen.h"
 #include "drivers/gles3/shaders/sky.glsl.gen.h"
 #include "drivers/gles3/shaders/sky.glsl.gen.h"
 #include "scene/resources/mesh.h"
 #include "scene/resources/mesh.h"
 #include "servers/rendering/renderer_compositor.h"
 #include "servers/rendering/renderer_compositor.h"
@@ -157,7 +157,6 @@ private:
 		RID shader_default_version;
 		RID shader_default_version;
 		RID default_material;
 		RID default_material;
 		RID default_shader;
 		RID default_shader;
-		RID cubemap_filter_shader_version;
 		RID overdraw_material;
 		RID overdraw_material;
 		RID overdraw_shader;
 		RID overdraw_shader;
 	} scene_globals;
 	} scene_globals;
@@ -314,6 +313,10 @@ private:
 		LocalVector<uint32_t> omni_light_gl_cache;
 		LocalVector<uint32_t> omni_light_gl_cache;
 		LocalVector<uint32_t> spot_light_gl_cache;
 		LocalVector<uint32_t> spot_light_gl_cache;
 
 
+		LocalVector<RID> paired_reflection_probes;
+		LocalVector<RID> reflection_probe_rid_cache;
+		LocalVector<Transform3D> reflection_probes_local_transform_cache;
+
 		RID lightmap_instance;
 		RID lightmap_instance;
 		Rect2 lightmap_uv_scale;
 		Rect2 lightmap_uv_scale;
 		uint32_t lightmap_slice_index;
 		uint32_t lightmap_slice_index;
@@ -331,7 +334,7 @@ private:
 		virtual void set_lightmap_capture(const Color *p_sh9) override;
 		virtual void set_lightmap_capture(const Color *p_sh9) override;
 
 
 		virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
 		virtual void pair_light_instances(const RID *p_light_instances, uint32_t p_light_instance_count) override;
-		virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override {}
+		virtual void pair_reflection_probe_instances(const RID *p_reflection_probe_instances, uint32_t p_reflection_probe_instance_count) override;
 		virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
 		virtual void pair_decal_instances(const RID *p_decal_instances, uint32_t p_decal_instance_count) override {}
 		virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
 		virtual void pair_voxel_gi_instances(const RID *p_voxel_gi_instances, uint32_t p_voxel_gi_instance_count) override {}
 
 
@@ -686,10 +689,8 @@ protected:
 		RID fog_shader;
 		RID fog_shader;
 		GLuint screen_triangle = 0;
 		GLuint screen_triangle = 0;
 		GLuint screen_triangle_array = 0;
 		GLuint screen_triangle_array = 0;
-		GLuint radical_inverse_vdc_cache_tex = 0;
 		uint32_t max_directional_lights = 4;
 		uint32_t max_directional_lights = 4;
 		uint32_t roughness_layers = 8;
 		uint32_t roughness_layers = 8;
-		uint32_t ggx_samples = 128;
 	} sky_globals;
 	} sky_globals;
 
 
 	struct Sky {
 	struct Sky {
@@ -733,7 +734,6 @@ protected:
 	void _invalidate_sky(Sky *p_sky);
 	void _invalidate_sky(Sky *p_sky);
 	void _update_dirty_skys();
 	void _update_dirty_skys();
 	void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_sky_energy_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_sky_energy_multiplier, float p_luminance_multiplier, bool p_use_multiview, bool p_flip_y, bool p_apply_color_adjustments_in_post);
 	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);
 	void _free_sky_data(Sky *p_sky);
 
 

+ 0 - 1
drivers/gles3/shaders/SCsub

@@ -18,7 +18,6 @@ if "GLES3_GLSL" in env["BUILDERS"]:
     env.GLES3_GLSL("canvas.glsl")
     env.GLES3_GLSL("canvas.glsl")
     env.GLES3_GLSL("scene.glsl")
     env.GLES3_GLSL("scene.glsl")
     env.GLES3_GLSL("sky.glsl")
     env.GLES3_GLSL("sky.glsl")
-    env.GLES3_GLSL("cubemap_filter.glsl")
     env.GLES3_GLSL("canvas_occlusion.glsl")
     env.GLES3_GLSL("canvas_occlusion.glsl")
     env.GLES3_GLSL("canvas_sdf.glsl")
     env.GLES3_GLSL("canvas_sdf.glsl")
     env.GLES3_GLSL("particles.glsl")
     env.GLES3_GLSL("particles.glsl")

+ 0 - 0
drivers/gles3/shaders/cubemap_filter.glsl → drivers/gles3/shaders/effects/cubemap_filter.glsl


+ 155 - 3
drivers/gles3/shaders/scene.glsl

@@ -12,6 +12,7 @@ DISABLE_LIGHTMAP = false
 DISABLE_LIGHT_DIRECTIONAL = false
 DISABLE_LIGHT_DIRECTIONAL = false
 DISABLE_LIGHT_OMNI = false
 DISABLE_LIGHT_OMNI = false
 DISABLE_LIGHT_SPOT = false
 DISABLE_LIGHT_SPOT = false
+DISABLE_REFLECTION_PROBE = true
 DISABLE_FOG = false
 DISABLE_FOG = false
 USE_DEPTH_FOG = false
 USE_DEPTH_FOG = false
 USE_RADIANCE_MAP = true
 USE_RADIANCE_MAP = true
@@ -34,6 +35,7 @@ APPLY_TONEMAPPING = true
 ADDITIVE_OMNI = false
 ADDITIVE_OMNI = false
 ADDITIVE_SPOT = false
 ADDITIVE_SPOT = false
 RENDER_MATERIAL = false
 RENDER_MATERIAL = false
+SECOND_REFLECTION_PROBE = false
 
 
 
 
 #[vertex]
 #[vertex]
@@ -568,8 +570,11 @@ void main() {
 1-color correction // In tonemap_inc.glsl
 1-color correction // In tonemap_inc.glsl
 2-radiance
 2-radiance
 3-shadow
 3-shadow
+4-lightmap textures
 5-screen
 5-screen
 6-depth
 6-depth
+7-reflection probe 1
+8-reflection probe 2
 
 
 */
 */
 
 
@@ -626,7 +631,39 @@ in highp vec4 shadow_coord4;
 
 
 uniform samplerCube radiance_map; // texunit:-2
 uniform samplerCube radiance_map; // texunit:-2
 
 
-#endif
+#endif // USE_RADIANCE_MAP
+
+#ifndef DISABLE_REFLECTION_PROBE
+
+#define REFLECTION_PROBE_MAX_LOD 8.0
+
+uniform bool refprobe1_use_box_project;
+uniform highp vec3 refprobe1_box_extents;
+uniform vec3 refprobe1_box_offset;
+uniform highp mat4 refprobe1_local_matrix;
+uniform bool refprobe1_exterior;
+uniform float refprobe1_intensity;
+uniform int refprobe1_ambient_mode;
+uniform vec4 refprobe1_ambient_color;
+
+uniform samplerCube refprobe1_texture; // texunit:-7
+
+#ifdef SECOND_REFLECTION_PROBE
+
+uniform bool refprobe2_use_box_project;
+uniform highp vec3 refprobe2_box_extents;
+uniform vec3 refprobe2_box_offset;
+uniform highp mat4 refprobe2_local_matrix;
+uniform bool refprobe2_exterior;
+uniform float refprobe2_intensity;
+uniform int refprobe2_ambient_mode;
+uniform vec4 refprobe2_ambient_color;
+
+uniform samplerCube refprobe2_texture; // texunit:-8
+
+#endif // SECOND_REFLECTION_PROBE
+
+#endif // DISABLE_REFLECTION_PROBE
 
 
 layout(std140) uniform GlobalShaderUniformData { //ubo:1
 layout(std140) uniform GlobalShaderUniformData { //ubo:1
 	vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
 	vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS];
@@ -1289,6 +1326,90 @@ vec4 fog_process(vec3 vertex) {
 	return vec4(fog_color, fog_amount);
 	return vec4(fog_color, fog_amount);
 }
 }
 
 
+#ifndef DISABLE_REFLECTION_PROBE
+
+#define REFLECTION_AMBIENT_DISABLED 0
+#define REFLECTION_AMBIENT_ENVIRONMENT 1
+#define REFLECTION_AMBIENT_COLOR 2
+
+void reflection_process(samplerCube reflection_map,
+		vec3 normal, vec3 vertex,
+		mat4 local_matrix,
+		bool use_box_project, vec3 box_extents, vec3 box_offset,
+		bool exterior, float intensity, int ref_ambient_mode, vec4 ref_ambient_color,
+		float roughness, vec3 ambient, vec3 skybox,
+		inout highp vec4 reflection_accum, inout highp vec4 ambient_accum) {
+	vec4 reflection;
+
+	vec3 local_pos = (local_matrix * vec4(vertex, 1.0)).xyz;
+
+	if (any(greaterThan(abs(local_pos), box_extents))) { //out of the reflection box
+		return;
+	}
+
+	vec3 inner_pos = abs(local_pos / box_extents);
+	float blend = max(inner_pos.x, max(inner_pos.y, inner_pos.z));
+	blend = mix(length(inner_pos), blend, blend);
+	blend *= blend;
+	blend = max(0.0, 1.0 - blend);
+
+	//reflect and make local
+	vec3 ref_normal = normalize(reflect(vertex, normal));
+	ref_normal = (local_matrix * vec4(ref_normal, 0.0)).xyz;
+
+	if (use_box_project) { //box project
+
+		vec3 nrdir = normalize(ref_normal);
+		vec3 rbmax = (box_extents - local_pos) / nrdir;
+		vec3 rbmin = (-box_extents - local_pos) / nrdir;
+
+		vec3 rbminmax = mix(rbmin, rbmax, vec3(greaterThan(nrdir, vec3(0.0, 0.0, 0.0))));
+
+		float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+		vec3 posonbox = local_pos + nrdir * fa;
+		ref_normal = posonbox - box_offset.xyz;
+	}
+
+	reflection.rgb = srgb_to_linear(textureLod(reflection_map, ref_normal, roughness * MAX_ROUGHNESS_LOD).rgb);
+
+	if (exterior) {
+		reflection.rgb = mix(skybox, reflection.rgb, blend);
+	}
+	reflection.rgb *= intensity;
+	reflection.a = blend;
+	reflection.rgb *= blend;
+
+	reflection_accum += reflection;
+
+#ifndef USE_LIGHTMAP
+	if (ref_ambient_mode == REFLECTION_AMBIENT_ENVIRONMENT) {
+		vec4 ambient_out;
+		vec3 amb_normal = (local_matrix * vec4(normal, 0.0)).xyz;
+
+		ambient_out.rgb = srgb_to_linear(textureLod(reflection_map, amb_normal, MAX_ROUGHNESS_LOD).rgb);
+		if (exterior) {
+			ambient_out.rgb = mix(ambient, ambient_out.rgb, blend);
+		}
+
+		ambient_out.a = blend;
+		ambient_out.rgb *= blend;
+		ambient_accum += ambient_out;
+	} else if (ref_ambient_mode == REFLECTION_AMBIENT_COLOR) {
+		vec4 ambient_out;
+		ambient_out.rgb = ref_ambient_color.rgb;
+		if (exterior) {
+			ambient_out.rgb = mix(ambient, ambient_out.rgb, blend);
+		}
+
+		ambient_out.a = blend;
+		ambient_out.rgb *= blend;
+		ambient_accum += ambient_out;
+	}
+#endif // USE_LIGHTMAP
+}
+
+#endif // DISABLE_REFLECTION_PROBE
+
 #endif // !MODE_RENDER_DEPTH
 #endif // !MODE_RENDER_DEPTH
 
 
 void main() {
 void main() {
@@ -1489,9 +1610,33 @@ void main() {
 		specular_light *= horizon * horizon;
 		specular_light *= horizon * horizon;
 		specular_light *= scene_data.ambient_light_color_energy.a;
 		specular_light *= scene_data.ambient_light_color_energy.a;
 	}
 	}
-#endif
+#endif // USE_RADIANCE_MAP
 
 
 	// Calculate Reflection probes
 	// Calculate Reflection probes
+#ifndef DISABLE_REFLECTION_PROBE
+	vec4 ambient_accum = vec4(0.0);
+	{
+		vec4 reflection_accum = vec4(0.0);
+
+		reflection_process(refprobe1_texture, normal, vertex_interp, refprobe1_local_matrix,
+				refprobe1_use_box_project, refprobe1_box_extents, refprobe1_box_offset,
+				refprobe1_exterior, refprobe1_intensity, refprobe1_ambient_mode, refprobe1_ambient_color,
+				roughness, ambient_light, specular_light, reflection_accum, ambient_accum);
+
+#ifdef SECOND_REFLECTION_PROBE
+
+		reflection_process(refprobe2_texture, normal, vertex_interp, refprobe2_local_matrix,
+				refprobe2_use_box_project, refprobe2_box_extents, refprobe2_box_offset,
+				refprobe2_exterior, refprobe2_intensity, refprobe2_ambient_mode, refprobe2_ambient_color,
+				roughness, ambient_light, specular_light, reflection_accum, ambient_accum);
+
+#endif // SECOND_REFLECTION_PROBE
+
+		if (reflection_accum.a > 0.0) {
+			specular_light = reflection_accum.rgb / reflection_accum.a;
+		}
+	}
+#endif // DISABLE_REFLECTION_PROBE
 
 
 #if defined(CUSTOM_RADIANCE_USED)
 #if defined(CUSTOM_RADIANCE_USED)
 	specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a);
 	specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a);
@@ -1501,6 +1646,7 @@ void main() {
 	//lightmap overrides everything
 	//lightmap overrides everything
 	if (scene_data.use_ambient_light) {
 	if (scene_data.use_ambient_light) {
 		ambient_light = scene_data.ambient_light_color_energy.rgb;
 		ambient_light = scene_data.ambient_light_color_energy.rgb;
+
 #ifdef USE_RADIANCE_MAP
 #ifdef USE_RADIANCE_MAP
 		if (scene_data.use_ambient_cubemap) {
 		if (scene_data.use_ambient_cubemap) {
 			vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
 			vec3 ambient_dir = scene_data.radiance_inverse_xform * normal;
@@ -1508,7 +1654,13 @@ void main() {
 			cubemap_ambient = srgb_to_linear(cubemap_ambient);
 			cubemap_ambient = srgb_to_linear(cubemap_ambient);
 			ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
 			ambient_light = mix(ambient_light, cubemap_ambient * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
 		}
 		}
-#endif
+#endif // USE_RADIANCE_MAP
+
+#ifndef DISABLE_REFLECTION_PROBE
+		if (ambient_accum.a > 0.0) {
+			ambient_light = mix(ambient_light, (ambient_accum.rgb / ambient_accum.a) * scene_data.ambient_light_color_energy.a, scene_data.ambient_color_sky_mix);
+		}
+#endif // DISABLE_REFLECTION_PROBE
 	}
 	}
 #endif // USE_LIGHTMAP
 #endif // USE_LIGHTMAP
 
 

+ 474 - 19
drivers/gles3/storage/light_storage.cpp

@@ -423,149 +423,604 @@ void LightStorage::light_instance_mark_visible(RID p_light_instance) {
 /* PROBE API */
 /* PROBE API */
 
 
 RID LightStorage::reflection_probe_allocate() {
 RID LightStorage::reflection_probe_allocate() {
-	return RID();
+	return reflection_probe_owner.allocate_rid();
 }
 }
 
 
 void LightStorage::reflection_probe_initialize(RID p_rid) {
 void LightStorage::reflection_probe_initialize(RID p_rid) {
+	ReflectionProbe probe;
+
+	reflection_probe_owner.initialize_rid(p_rid, probe);
 }
 }
 
 
 void LightStorage::reflection_probe_free(RID p_rid) {
 void LightStorage::reflection_probe_free(RID p_rid) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_rid);
+	reflection_probe->dependency.deleted_notify(p_rid);
+
+	reflection_probe_owner.free(p_rid);
 }
 }
 
 
 void LightStorage::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) {
 void LightStorage::reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->update_mode = p_mode;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
 void LightStorage::reflection_probe_set_intensity(RID p_probe, float p_intensity) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->intensity = p_intensity;
 }
 }
 
 
 void LightStorage::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) {
 void LightStorage::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->ambient_mode = p_mode;
 }
 }
 
 
 void LightStorage::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) {
 void LightStorage::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->ambient_color = p_color;
 }
 }
 
 
 void LightStorage::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) {
 void LightStorage::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->ambient_color_energy = p_energy;
 }
 }
 
 
 void LightStorage::reflection_probe_set_max_distance(RID p_probe, float p_distance) {
 void LightStorage::reflection_probe_set_max_distance(RID p_probe, float p_distance) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->max_distance = p_distance;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_size(RID p_probe, const Vector3 &p_size) {
 void LightStorage::reflection_probe_set_size(RID p_probe, const Vector3 &p_size) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->size = p_size;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) {
 void LightStorage::reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->origin_offset = p_offset;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_as_interior(RID p_probe, bool p_enable) {
 void LightStorage::reflection_probe_set_as_interior(RID p_probe, bool p_enable) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->interior = p_enable;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {
 void LightStorage::reflection_probe_set_enable_box_projection(RID p_probe, bool p_enable) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->box_projection = p_enable;
 }
 }
 
 
 void LightStorage::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {
 void LightStorage::reflection_probe_set_enable_shadows(RID p_probe, bool p_enable) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->enable_shadows = p_enable;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {
 void LightStorage::reflection_probe_set_cull_mask(RID p_probe, uint32_t p_layers) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->cull_mask = p_layers;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_reflection_mask(RID p_probe, uint32_t p_layers) {
 void LightStorage::reflection_probe_set_reflection_mask(RID p_probe, uint32_t p_layers) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->reflection_mask = p_layers;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 void LightStorage::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
 void LightStorage::reflection_probe_set_resolution(RID p_probe, int p_resolution) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->resolution = p_resolution;
 }
 }
 
 
 AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const {
 AABB LightStorage::reflection_probe_get_aabb(RID p_probe) const {
-	return AABB();
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, AABB());
+
+	AABB aabb;
+	aabb.position = -reflection_probe->size / 2;
+	aabb.size = reflection_probe->size;
+
+	return aabb;
 }
 }
 
 
 RS::ReflectionProbeUpdateMode LightStorage::reflection_probe_get_update_mode(RID p_probe) const {
 RS::ReflectionProbeUpdateMode LightStorage::reflection_probe_get_update_mode(RID p_probe) const {
-	return RenderingServer::REFLECTION_PROBE_UPDATE_ONCE;
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, RenderingServer::REFLECTION_PROBE_UPDATE_ONCE);
+
+	return reflection_probe->update_mode;
 }
 }
 
 
 uint32_t LightStorage::reflection_probe_get_cull_mask(RID p_probe) const {
 uint32_t LightStorage::reflection_probe_get_cull_mask(RID p_probe) const {
-	return 0;
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, 0);
+
+	return reflection_probe->cull_mask;
 }
 }
 
 
 uint32_t LightStorage::reflection_probe_get_reflection_mask(RID p_probe) const {
 uint32_t LightStorage::reflection_probe_get_reflection_mask(RID p_probe) const {
-	return 0;
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, 0);
+
+	return reflection_probe->reflection_mask;
 }
 }
 
 
 Vector3 LightStorage::reflection_probe_get_size(RID p_probe) const {
 Vector3 LightStorage::reflection_probe_get_size(RID p_probe) const {
-	return Vector3();
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, Vector3());
+
+	return reflection_probe->size;
 }
 }
 
 
 Vector3 LightStorage::reflection_probe_get_origin_offset(RID p_probe) const {
 Vector3 LightStorage::reflection_probe_get_origin_offset(RID p_probe) const {
-	return Vector3();
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, Vector3());
+
+	return reflection_probe->origin_offset;
 }
 }
 
 
 float LightStorage::reflection_probe_get_origin_max_distance(RID p_probe) const {
 float LightStorage::reflection_probe_get_origin_max_distance(RID p_probe) const {
-	return 0.0;
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, 0.0);
+
+	return reflection_probe->max_distance;
 }
 }
 
 
 bool LightStorage::reflection_probe_renders_shadows(RID p_probe) const {
 bool LightStorage::reflection_probe_renders_shadows(RID p_probe) const {
-	return false;
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, false);
+
+	return reflection_probe->enable_shadows;
 }
 }
 
 
 void LightStorage::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) {
 void LightStorage::reflection_probe_set_mesh_lod_threshold(RID p_probe, float p_ratio) {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL(reflection_probe);
+
+	reflection_probe->mesh_lod_threshold = p_ratio;
+	reflection_probe->dependency.changed_notify(Dependency::DEPENDENCY_CHANGED_REFLECTION_PROBE);
 }
 }
 
 
 float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const {
 float LightStorage::reflection_probe_get_mesh_lod_threshold(RID p_probe) const {
-	return 0.0;
+	const ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, 0.0);
+
+	return reflection_probe->mesh_lod_threshold;
+}
+
+Dependency *LightStorage::reflection_probe_get_dependency(RID p_probe) const {
+	ReflectionProbe *reflection_probe = reflection_probe_owner.get_or_null(p_probe);
+	ERR_FAIL_NULL_V(reflection_probe, nullptr);
+
+	return &reflection_probe->dependency;
 }
 }
 
 
 /* REFLECTION ATLAS */
 /* REFLECTION ATLAS */
 
 
 RID LightStorage::reflection_atlas_create() {
 RID LightStorage::reflection_atlas_create() {
-	return RID();
+	ReflectionAtlas ra;
+	ra.count = GLOBAL_GET("rendering/reflections/reflection_atlas/reflection_count");
+	ra.size = GLOBAL_GET("rendering/reflections/reflection_atlas/reflection_size");
+
+	return reflection_atlas_owner.make_rid(ra);
 }
 }
 
 
 void LightStorage::reflection_atlas_free(RID p_ref_atlas) {
 void LightStorage::reflection_atlas_free(RID p_ref_atlas) {
+	reflection_atlas_set_size(p_ref_atlas, 0, 0);
+
+	reflection_atlas_owner.free(p_ref_atlas);
 }
 }
 
 
 int LightStorage::reflection_atlas_get_size(RID p_ref_atlas) const {
 int LightStorage::reflection_atlas_get_size(RID p_ref_atlas) const {
-	return 0;
+	ReflectionAtlas *ra = reflection_atlas_owner.get_or_null(p_ref_atlas);
+	ERR_FAIL_NULL_V(ra, 0);
+
+	return ra->size;
 }
 }
 
 
 void LightStorage::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) {
 void LightStorage::reflection_atlas_set_size(RID p_ref_atlas, int p_reflection_size, int p_reflection_count) {
+	ReflectionAtlas *ra = reflection_atlas_owner.get_or_null(p_ref_atlas);
+	ERR_FAIL_NULL(ra);
+
+	if (ra->size == p_reflection_size && ra->count == p_reflection_count) {
+		return; //no changes
+	}
+
+	ra->size = p_reflection_size;
+	ra->count = p_reflection_count;
+
+	if (ra->depth != 0) {
+		//clear and invalidate everything
+		for (int i = 0; i < ra->reflections.size(); i++) {
+			for (int j = 0; j < 7; j++) {
+				if (ra->reflections[i].fbos[j] != 0) {
+					glDeleteFramebuffers(1, &ra->reflections[i].fbos[j]);
+					ra->reflections.write[i].fbos[j] = 0;
+				}
+			}
+
+			GLES3::Utilities::get_singleton()->texture_free_data(ra->reflections[i].color);
+			ra->reflections.write[i].color = 0;
+
+			GLES3::Utilities::get_singleton()->texture_free_data(ra->reflections[i].radiance);
+			ra->reflections.write[i].radiance = 0;
+
+			if (ra->reflections[i].owner.is_null()) {
+				continue;
+			}
+			reflection_probe_release_atlas_index(ra->reflections[i].owner);
+			//rp->atlasindex clear
+		}
+
+		ra->reflections.clear();
+
+		GLES3::Utilities::get_singleton()->texture_free_data(ra->depth);
+		ra->depth = 0;
+	}
+
+	if (ra->render_buffers.is_valid()) {
+		ra->render_buffers->free_render_buffer_data();
+	}
 }
 }
 
 
 /* REFLECTION PROBE INSTANCE */
 /* REFLECTION PROBE INSTANCE */
 
 
 RID LightStorage::reflection_probe_instance_create(RID p_probe) {
 RID LightStorage::reflection_probe_instance_create(RID p_probe) {
-	return RID();
+	ReflectionProbeInstance rpi;
+	rpi.probe = p_probe;
+
+	return reflection_probe_instance_owner.make_rid(rpi);
 }
 }
 
 
 void LightStorage::reflection_probe_instance_free(RID p_instance) {
 void LightStorage::reflection_probe_instance_free(RID p_instance) {
+	reflection_probe_release_atlas_index(p_instance);
+	reflection_probe_instance_owner.free(p_instance);
 }
 }
 
 
 void LightStorage::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) {
 void LightStorage::reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) {
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL(rpi);
+
+	rpi->transform = p_transform;
+	rpi->dirty = true;
 }
 }
 
 
 bool LightStorage::reflection_probe_has_atlas_index(RID p_instance) {
 bool LightStorage::reflection_probe_has_atlas_index(RID p_instance) {
-	return false;
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL_V(rpi, false);
+
+	if (rpi->atlas.is_null()) {
+		return false;
+	}
+
+	return rpi->atlas_index >= 0;
 }
 }
 
 
 void LightStorage::reflection_probe_release_atlas_index(RID p_instance) {
 void LightStorage::reflection_probe_release_atlas_index(RID p_instance) {
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL(rpi);
+
+	if (rpi->atlas.is_null()) {
+		return; //nothing to release
+	}
+	ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+	ERR_FAIL_NULL(atlas);
+
+	ERR_FAIL_INDEX(rpi->atlas_index, atlas->reflections.size());
+	atlas->reflections.write[rpi->atlas_index].owner = RID();
+
+	if (rpi->rendering) {
+		// We were cancelled mid rendering, trigger refresh.
+		rpi->rendering = false;
+		rpi->dirty = true;
+		rpi->processing_layer = 0;
+	}
+
+	rpi->atlas_index = -1;
+	rpi->atlas = RID();
 }
 }
 
 
 bool LightStorage::reflection_probe_instance_needs_redraw(RID p_instance) {
 bool LightStorage::reflection_probe_instance_needs_redraw(RID p_instance) {
-	return false;
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL_V(rpi, false);
+
+	if (rpi->rendering) {
+		return false;
+	}
+
+	if (rpi->dirty) {
+		return true;
+	}
+
+	if (reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
+		return true;
+	}
+
+	return rpi->atlas_index == -1;
 }
 }
 
 
 bool LightStorage::reflection_probe_instance_has_reflection(RID p_instance) {
 bool LightStorage::reflection_probe_instance_has_reflection(RID p_instance) {
-	return false;
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL_V(rpi, false);
+
+	return rpi->atlas.is_valid();
 }
 }
 
 
 bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) {
 bool LightStorage::reflection_probe_instance_begin_render(RID p_instance, RID p_reflection_atlas) {
-	return false;
+	TextureStorage *texture_storage = TextureStorage::get_singleton();
+	ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas);
+
+	ERR_FAIL_NULL_V(atlas, false);
+
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL_V(rpi, false);
+
+	if (atlas->render_buffers.is_null()) {
+		atlas->render_buffers.instantiate();
+		atlas->render_buffers->configure_for_probe(Size2i(atlas->size, atlas->size));
+	}
+
+	// First we check if our atlas is initialized.
+
+	// Not making an exception for update_mode = REFLECTION_PROBE_UPDATE_ALWAYS, we are using
+	// the same render techniques regardless of realtime or update once (for now).
+
+	if (atlas->depth == 0) {
+		// We need to create our textures
+		atlas->mipmap_count = Image::get_image_required_mipmaps(atlas->size, atlas->size, Image::FORMAT_RGBAH) - 1;
+		atlas->mipmap_count = MIN(atlas->mipmap_count, 8); // No more than 8 please..
+
+		glActiveTexture(GL_TEXTURE0);
+
+		{
+			// We create one set of 6 layers for depth, we can reuse this when rendering.
+			glGenTextures(1, &atlas->depth);
+			glBindTexture(GL_TEXTURE_2D_ARRAY, atlas->depth);
+
+			glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH_COMPONENT24, atlas->size, atlas->size, 6, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, nullptr);
+
+			GLES3::Utilities::get_singleton()->texture_allocated_data(atlas->depth, atlas->size * atlas->size * 6 * 3, "Reflection probe atlas (depth)");
+		}
+
+		// Make room for our atlas entries
+		atlas->reflections.resize(atlas->count);
+
+		for (int i = 0; i < atlas->count; i++) {
+			// Create a cube map for this atlas entry
+			GLuint color = 0;
+			glGenTextures(1, &color);
+			glBindTexture(GL_TEXTURE_CUBE_MAP, color);
+			atlas->reflections.write[i].color = color;
+
+#ifdef GL_API_ENABLED
+			if (RasterizerGLES3::is_gles_over_gl()) {
+				for (int s = 0; s < 6; s++) {
+					glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + s, 0, GL_RGB10_A2, atlas->size, atlas->size, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
+				}
+				glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+			}
+#endif
+#ifdef GLES_API_ENABLED
+			if (!RasterizerGLES3::is_gles_over_gl()) {
+				glTexStorage2D(GL_TEXTURE_CUBE_MAP, atlas->mipmap_count, GL_RGB10_A2, atlas->size, atlas->size);
+			}
+#endif // GLES_API_ENABLED
+
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, atlas->mipmap_count - 1);
+
+			// Setup sizes and calculate how much memory we're using.
+			int mipmap_size = atlas->size;
+			uint32_t data_size = 0;
+			for (int m = 0; m < atlas->mipmap_count; m++) {
+				atlas->mipmap_size[m] = mipmap_size;
+				data_size += mipmap_size * mipmap_size * 6 * 4;
+				mipmap_size = MAX(mipmap_size >> 1, 1);
+			}
+
+			GLES3::Utilities::get_singleton()->texture_allocated_data(color, data_size, String("Reflection probe atlas (") + String::num_int64(i) + String(", color)"));
+
+			// Create a radiance map for this atlas entry
+			GLuint radiance = 0;
+			glGenTextures(1, &radiance);
+			glBindTexture(GL_TEXTURE_CUBE_MAP, radiance);
+			atlas->reflections.write[i].radiance = radiance;
+
+#ifdef GL_API_ENABLED
+			if (RasterizerGLES3::is_gles_over_gl()) {
+				for (int s = 0; s < 6; s++) {
+					glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + s, 0, GL_RGB10_A2, atlas->size, atlas->size, 0, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, nullptr);
+				}
+				glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
+			}
+#endif
+#ifdef GLES_API_ENABLED
+			if (!RasterizerGLES3::is_gles_over_gl()) {
+				glTexStorage2D(GL_TEXTURE_CUBE_MAP, atlas->mipmap_count, GL_RGB10_A2, atlas->size, atlas->size);
+			}
+#endif // GLES_API_ENABLED
+
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
+			glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, atlas->mipmap_count - 1);
+
+			// Same data size as our color buffer
+			GLES3::Utilities::get_singleton()->texture_allocated_data(radiance, data_size, String("Reflection probe atlas (") + String::num_int64(i) + String(", radiance)"));
+
+			// Create our framebuffers so we can draw to all sides
+			for (int side = 0; side < 6; side++) {
+				GLuint fbo = 0;
+				glGenFramebuffers(1, &fbo);
+				glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+				// We use glFramebufferTexture2D for the color buffer as glFramebufferTextureLayer doesn't always work with cubemaps.
+				glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_CUBE_MAP_POSITIVE_X + side, color, 0);
+				glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, atlas->depth, 0, side);
+
+				// Validate framebuffer
+				GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+				if (status != GL_FRAMEBUFFER_COMPLETE) {
+					WARN_PRINT("Could not create reflections framebuffer, status: " + texture_storage->get_framebuffer_error(status));
+				}
+
+				atlas->reflections.write[i].fbos[side] = fbo;
+			}
+
+			// Create an extra framebuffer for building our radiance
+			{
+				GLuint fbo = 0;
+				glGenFramebuffers(1, &fbo);
+				glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+
+				atlas->reflections.write[i].fbos[6] = fbo;
+			}
+		}
+
+		glBindFramebuffer(GL_FRAMEBUFFER, GLES3::TextureStorage::system_fbo);
+		glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
+		glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
+	}
+
+	// Then we find a free slot for our reflection probe
+
+	if (rpi->atlas_index == -1) {
+		for (int i = 0; i < atlas->reflections.size(); i++) {
+			if (atlas->reflections[i].owner.is_null()) {
+				rpi->atlas_index = i;
+				break;
+			}
+		}
+		//find the one used last
+		if (rpi->atlas_index == -1) {
+			//everything is in use, find the one least used via LRU
+			uint64_t pass_min = 0;
+
+			for (int i = 0; i < atlas->reflections.size(); i++) {
+				ReflectionProbeInstance *rpi2 = reflection_probe_instance_owner.get_or_null(atlas->reflections[i].owner);
+				if (rpi2->last_pass < pass_min) {
+					pass_min = rpi2->last_pass;
+					rpi->atlas_index = i;
+				}
+			}
+		}
+	}
+
+	if (rpi->atlas_index != -1) { // should we fail if this is still -1 ?
+		atlas->reflections.write[rpi->atlas_index].owner = p_instance;
+	}
+
+	rpi->atlas = p_reflection_atlas;
+	rpi->rendering = true;
+	rpi->dirty = false;
+	rpi->processing_layer = 0;
+
+	return true;
 }
 }
 
 
 Ref<RenderSceneBuffers> LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) {
 Ref<RenderSceneBuffers> LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) {
-	return Ref<RenderSceneBuffers>();
+	ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas);
+	ERR_FAIL_NULL_V(atlas, Ref<RenderSceneBuffersGLES3>());
+
+	return atlas->render_buffers;
 }
 }
 
 
 bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) {
 bool LightStorage::reflection_probe_instance_postprocess_step(RID p_instance) {
-	return true;
+	GLES3::CubemapFilter *cubemap_filter = GLES3::CubemapFilter::get_singleton();
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL_V(rpi, false);
+	ERR_FAIL_COND_V(!rpi->rendering, false);
+	ERR_FAIL_COND_V(rpi->atlas.is_null(), false);
+
+	ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+	if (!atlas || rpi->atlas_index == -1) {
+		//does not belong to an atlas anymore, cancel (was removed from atlas or atlas changed while rendering)
+		rpi->rendering = false;
+		rpi->processing_layer = 0;
+		return false;
+	}
+
+	if (LightStorage::get_singleton()->reflection_probe_get_update_mode(rpi->probe) == RS::REFLECTION_PROBE_UPDATE_ALWAYS) {
+		// Using real time reflections, all roughness is done in one step
+		for (int m = 0; m < atlas->mipmap_count; m++) {
+			const GLES3::ReflectionAtlas::Reflection &reflection = atlas->reflections[rpi->atlas_index];
+			cubemap_filter->filter_radiance(reflection.color, reflection.radiance, reflection.fbos[6], atlas->size, atlas->mipmap_count, m);
+		}
+
+		rpi->rendering = false;
+		rpi->processing_layer = 0;
+		return true;
+	} else {
+		const GLES3::ReflectionAtlas::Reflection &reflection = atlas->reflections[rpi->atlas_index];
+		cubemap_filter->filter_radiance(reflection.color, reflection.radiance, reflection.fbos[6], atlas->size, atlas->mipmap_count, rpi->processing_layer);
+
+		rpi->processing_layer++;
+		if (rpi->processing_layer == atlas->mipmap_count) {
+			rpi->rendering = false;
+			rpi->processing_layer = 0;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+GLuint LightStorage::reflection_probe_instance_get_texture(RID p_instance) {
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL_V(rpi, 0);
+
+	ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+	ERR_FAIL_NULL_V(atlas, 0);
+
+	return atlas->reflections[rpi->atlas_index].radiance;
+}
+
+GLuint LightStorage::reflection_probe_instance_get_framebuffer(RID p_instance, int p_index) {
+	ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+	ERR_FAIL_NULL_V(rpi, 0);
+	ERR_FAIL_INDEX_V(p_index, 6, 0);
+
+	ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(rpi->atlas);
+	ERR_FAIL_NULL_V(atlas, 0);
+	return atlas->reflections[rpi->atlas_index].fbos[p_index];
 }
 }
 
 
 /* LIGHTMAP CAPTURE */
 /* LIGHTMAP CAPTURE */

+ 77 - 0
drivers/gles3/storage/light_storage.h

@@ -34,6 +34,7 @@
 #ifdef GLES3_ENABLED
 #ifdef GLES3_ENABLED
 
 
 #include "platform_gl.h"
 #include "platform_gl.h"
+#include "render_scene_buffers_gles3.h"
 
 
 #include "core/templates/local_vector.h"
 #include "core/templates/local_vector.h"
 #include "core/templates/rid_owner.h"
 #include "core/templates/rid_owner.h"
@@ -126,12 +127,51 @@ struct ReflectionProbe {
 	bool box_projection = false;
 	bool box_projection = false;
 	bool enable_shadows = false;
 	bool enable_shadows = false;
 	uint32_t cull_mask = (1 << 20) - 1;
 	uint32_t cull_mask = (1 << 20) - 1;
+	uint32_t reflection_mask = (1 << 20) - 1;
 	float mesh_lod_threshold = 0.01;
 	float mesh_lod_threshold = 0.01;
 	float baked_exposure = 1.0;
 	float baked_exposure = 1.0;
 
 
 	Dependency dependency;
 	Dependency dependency;
 };
 };
 
 
+/* REFLECTION ATLAS */
+
+struct ReflectionAtlas {
+	int count = 0;
+	int size = 0;
+
+	int mipmap_count = 1; // number of mips, including original
+	int mipmap_size[8];
+	GLuint depth = 0;
+
+	struct Reflection {
+		RID owner;
+		GLuint color = 0;
+		GLuint radiance = 0;
+		GLuint fbos[7];
+	};
+	Vector<Reflection> reflections;
+
+	Ref<RenderSceneBuffersGLES3> render_buffers; // Further render buffers used.
+};
+
+/* REFLECTION PROBE INSTANCE */
+
+struct ReflectionProbeInstance {
+	RID probe;
+	int atlas_index = -1;
+	RID atlas;
+
+	bool dirty = true;
+	bool rendering = false;
+	int processing_layer = 0;
+
+	uint64_t last_pass = 0;
+	uint32_t cull_mask = 0;
+
+	Transform3D transform;
+};
+
 /* LIGHTMAP */
 /* LIGHTMAP */
 
 
 struct Lightmap {
 struct Lightmap {
@@ -181,6 +221,13 @@ private:
 	/* REFLECTION PROBE */
 	/* REFLECTION PROBE */
 	mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner;
 	mutable RID_Owner<ReflectionProbe, true> reflection_probe_owner;
 
 
+	/* REFLECTION ATLAS */
+	mutable RID_Owner<ReflectionAtlas> reflection_atlas_owner;
+
+	/* REFLECTION PROBE INSTANCE */
+
+	mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
+
 	/* LIGHTMAP */
 	/* LIGHTMAP */
 
 
 	Vector<RID> lightmap_textures;
 	Vector<RID> lightmap_textures;
@@ -559,6 +606,9 @@ public:
 
 
 	/* PROBE API */
 	/* PROBE API */
 
 
+	ReflectionProbe *get_reflection_probe(RID p_rid) { return reflection_probe_owner.get_or_null(p_rid); };
+	bool owns_reflection_probe(RID p_rid) { return reflection_probe_owner.owns(p_rid); };
+
 	virtual RID reflection_probe_allocate() override;
 	virtual RID reflection_probe_allocate() override;
 	virtual void reflection_probe_initialize(RID p_rid) override;
 	virtual void reflection_probe_initialize(RID p_rid) override;
 	virtual void reflection_probe_free(RID p_rid) override;
 	virtual void reflection_probe_free(RID p_rid) override;
@@ -589,8 +639,12 @@ public:
 	virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override;
 	virtual float reflection_probe_get_origin_max_distance(RID p_probe) const override;
 	virtual bool reflection_probe_renders_shadows(RID p_probe) const override;
 	virtual bool reflection_probe_renders_shadows(RID p_probe) const override;
 
 
+	Dependency *reflection_probe_get_dependency(RID p_probe) const;
+
 	/* REFLECTION ATLAS */
 	/* REFLECTION ATLAS */
 
 
+	bool owns_reflection_atlas(RID p_rid) { return reflection_atlas_owner.owns(p_rid); }
+
 	virtual RID reflection_atlas_create() override;
 	virtual RID reflection_atlas_create() override;
 	virtual void reflection_atlas_free(RID p_ref_atlas) override;
 	virtual void reflection_atlas_free(RID p_ref_atlas) override;
 	virtual int reflection_atlas_get_size(RID p_ref_atlas) const override;
 	virtual int reflection_atlas_get_size(RID p_ref_atlas) const override;
@@ -598,6 +652,8 @@ public:
 
 
 	/* REFLECTION PROBE INSTANCE */
 	/* REFLECTION PROBE INSTANCE */
 
 
+	bool owns_reflection_probe_instance(RID p_rid) { return reflection_probe_instance_owner.owns(p_rid); }
+
 	virtual RID reflection_probe_instance_create(RID p_probe) override;
 	virtual RID reflection_probe_instance_create(RID p_probe) override;
 	virtual void reflection_probe_instance_free(RID p_instance) override;
 	virtual void reflection_probe_instance_free(RID p_instance) override;
 	virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override;
 	virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override;
@@ -609,6 +665,27 @@ public:
 	virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override;
 	virtual Ref<RenderSceneBuffers> reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) override;
 	virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;
 	virtual bool reflection_probe_instance_postprocess_step(RID p_instance) override;
 
 
+	_FORCE_INLINE_ RID reflection_probe_instance_get_probe(RID p_instance) {
+		ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+		ERR_FAIL_NULL_V(rpi, RID());
+
+		return rpi->probe;
+	}
+	_FORCE_INLINE_ RID reflection_probe_instance_get_atlas(RID p_instance) {
+		ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+		ERR_FAIL_NULL_V(rpi, RID());
+
+		return rpi->atlas;
+	}
+	Transform3D reflection_probe_instance_get_transform(RID p_instance) {
+		ReflectionProbeInstance *rpi = reflection_probe_instance_owner.get_or_null(p_instance);
+		ERR_FAIL_NULL_V(rpi, Transform3D());
+
+		return rpi->transform;
+	}
+	GLuint reflection_probe_instance_get_texture(RID p_instance);
+	GLuint reflection_probe_instance_get_framebuffer(RID p_instance, int p_index);
+
 	/* LIGHTMAP CAPTURE */
 	/* LIGHTMAP CAPTURE */
 
 
 	Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); };
 	Lightmap *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); };

+ 0 - 2
drivers/gles3/storage/material_storage.h

@@ -43,7 +43,6 @@
 #include "servers/rendering/storage/utilities.h"
 #include "servers/rendering/storage/utilities.h"
 
 
 #include "drivers/gles3/shaders/canvas.glsl.gen.h"
 #include "drivers/gles3/shaders/canvas.glsl.gen.h"
-#include "drivers/gles3/shaders/cubemap_filter.glsl.gen.h"
 #include "drivers/gles3/shaders/particles.glsl.gen.h"
 #include "drivers/gles3/shaders/particles.glsl.gen.h"
 #include "drivers/gles3/shaders/scene.glsl.gen.h"
 #include "drivers/gles3/shaders/scene.glsl.gen.h"
 #include "drivers/gles3/shaders/sky.glsl.gen.h"
 #include "drivers/gles3/shaders/sky.glsl.gen.h"
@@ -543,7 +542,6 @@ public:
 		CanvasShaderGLES3 canvas_shader;
 		CanvasShaderGLES3 canvas_shader;
 		SkyShaderGLES3 sky_shader;
 		SkyShaderGLES3 sky_shader;
 		SceneShaderGLES3 scene_shader;
 		SceneShaderGLES3 scene_shader;
-		CubemapFilterShaderGLES3 cubemap_filter_shader;
 		ParticlesShaderGLES3 particles_process_shader;
 		ParticlesShaderGLES3 particles_process_shader;
 
 
 		ShaderCompiler compiler_canvas;
 		ShaderCompiler compiler_canvas;

+ 7 - 0
drivers/gles3/storage/render_scene_buffers_gles3.cpp

@@ -405,6 +405,13 @@ void RenderSceneBuffersGLES3::_check_render_buffers() {
 	}
 	}
 }
 }
 
 
+void RenderSceneBuffersGLES3::configure_for_probe(Size2i p_size) {
+	internal_size = p_size;
+	target_size = p_size;
+	scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
+	view_count = 1;
+}
+
 void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() {
 void RenderSceneBuffersGLES3::_clear_msaa3d_buffers() {
 	for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
 	for (const FBDEF &cached_fbo : msaa3d.cached_fbos) {
 		GLuint fbo = cached_fbo.fbo;
 		GLuint fbo = cached_fbo.fbo;

+ 1 - 0
drivers/gles3/storage/render_scene_buffers_gles3.h

@@ -101,6 +101,7 @@ public:
 	RenderSceneBuffersGLES3();
 	RenderSceneBuffersGLES3();
 	virtual ~RenderSceneBuffersGLES3();
 	virtual ~RenderSceneBuffersGLES3();
 	virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
 	virtual void configure(const RenderSceneBuffersConfiguration *p_config) override;
+	void configure_for_probe(Size2i p_size);
 
 
 	virtual void set_fsr_sharpness(float p_fsr_sharpness) override{};
 	virtual void set_fsr_sharpness(float p_fsr_sharpness) override{};
 	virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{};
 	virtual void set_texture_mipmap_bias(float p_texture_mipmap_bias) override{};

+ 14 - 0
drivers/gles3/storage/utilities.cpp

@@ -158,6 +158,8 @@ RS::InstanceType Utilities::get_base_type(RID p_rid) const {
 		return RS::INSTANCE_LIGHTMAP;
 		return RS::INSTANCE_LIGHTMAP;
 	} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
 	} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
 		return RS::INSTANCE_PARTICLES;
 		return RS::INSTANCE_PARTICLES;
+	} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
+		return RS::INSTANCE_REFLECTION_PROBE;
 	} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
 	} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles_collision(p_rid)) {
 		return RS::INSTANCE_PARTICLES_COLLISION;
 		return RS::INSTANCE_PARTICLES_COLLISION;
 	} else if (owns_visibility_notifier(p_rid)) {
 	} else if (owns_visibility_notifier(p_rid)) {
@@ -197,6 +199,15 @@ bool Utilities::free(RID p_rid) {
 	} else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
 	} else if (GLES3::LightStorage::get_singleton()->owns_lightmap(p_rid)) {
 		GLES3::LightStorage::get_singleton()->lightmap_free(p_rid);
 		GLES3::LightStorage::get_singleton()->lightmap_free(p_rid);
 		return true;
 		return true;
+	} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe(p_rid)) {
+		GLES3::LightStorage::get_singleton()->reflection_probe_free(p_rid);
+		return true;
+	} else if (GLES3::LightStorage::get_singleton()->owns_reflection_atlas(p_rid)) {
+		GLES3::LightStorage::get_singleton()->reflection_atlas_free(p_rid);
+		return true;
+	} else if (GLES3::LightStorage::get_singleton()->owns_reflection_probe_instance(p_rid)) {
+		GLES3::LightStorage::get_singleton()->reflection_probe_instance_free(p_rid);
+		return true;
 	} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
 	} else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) {
 		GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid);
 		GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid);
 		return true;
 		return true;
@@ -229,6 +240,9 @@ void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance
 		if (multimesh->mesh.is_valid()) {
 		if (multimesh->mesh.is_valid()) {
 			base_update_dependency(multimesh->mesh, p_instance);
 			base_update_dependency(multimesh->mesh, p_instance);
 		}
 		}
+	} else if (LightStorage::get_singleton()->owns_reflection_probe(p_base)) {
+		Dependency *dependency = LightStorage::get_singleton()->reflection_probe_get_dependency(p_base);
+		p_instance->update_dependency(dependency);
 	} else if (LightStorage::get_singleton()->owns_light(p_base)) {
 	} else if (LightStorage::get_singleton()->owns_light(p_base)) {
 		Light *l = LightStorage::get_singleton()->get_light(p_base);
 		Light *l = LightStorage::get_singleton()->get_light(p_base);
 		p_instance->update_dependency(&l->dependency);
 		p_instance->update_dependency(&l->dependency);

+ 0 - 11
scene/3d/reflection_probe.cpp

@@ -190,17 +190,6 @@ AABB ReflectionProbe::get_aabb() const {
 	return aabb;
 	return aabb;
 }
 }
 
 
-PackedStringArray ReflectionProbe::get_configuration_warnings() const {
-	PackedStringArray warnings = Node::get_configuration_warnings();
-
-	if (OS::get_singleton()->get_current_rendering_method() == "gl_compatibility") {
-		warnings.push_back(RTR("ReflectionProbes are not supported when using the GL Compatibility backend yet. Support will be added in a future release."));
-		return warnings;
-	}
-
-	return warnings;
-}
-
 void ReflectionProbe::_validate_property(PropertyInfo &p_property) const {
 void ReflectionProbe::_validate_property(PropertyInfo &p_property) const {
 	if (p_property.name == "ambient_color" || p_property.name == "ambient_color_energy") {
 	if (p_property.name == "ambient_color" || p_property.name == "ambient_color_energy") {
 		if (ambient_mode != AMBIENT_COLOR) {
 		if (ambient_mode != AMBIENT_COLOR) {

+ 0 - 2
scene/3d/reflection_probe.h

@@ -122,8 +122,6 @@ public:
 
 
 	virtual AABB get_aabb() const override;
 	virtual AABB get_aabb() const override;
 
 
-	virtual PackedStringArray get_configuration_warnings() const override;
-
 	ReflectionProbe();
 	ReflectionProbe();
 	~ReflectionProbe();
 	~ReflectionProbe();
 };
 };