diff --git a/drivers/gles3/effects/cubemap_filter.cpp b/drivers/gles3/effects/cubemap_filter.cpp new file mode 100644 index 00000000000..b88e6554923 --- /dev/null +++ b/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 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 diff --git a/drivers/gles3/effects/cubemap_filter.h b/drivers/gles3/effects/cubemap_filter.h new file mode 100644 index 00000000000..eaaa6f40753 --- /dev/null +++ b/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 diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index cee4f93b3d0..767a394ce52 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -208,6 +208,7 @@ void RasterizerGLES3::finalize() { memdelete(fog); memdelete(post_effects); memdelete(glow); + memdelete(cubemap_filter); memdelete(copy_effects); memdelete(light_storage); memdelete(particles_storage); @@ -354,6 +355,7 @@ RasterizerGLES3::RasterizerGLES3() { particles_storage = memnew(GLES3::ParticlesStorage); light_storage = memnew(GLES3::LightStorage); copy_effects = memnew(GLES3::CopyEffects); + cubemap_filter = memnew(GLES3::CubemapFilter); glow = memnew(GLES3::Glow); post_effects = memnew(GLES3::PostEffects); gi = memnew(GLES3::GI); diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index 8d52dc23655..09d3c7bd527 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -34,6 +34,7 @@ #ifdef GLES3_ENABLED #include "effects/copy_effects.h" +#include "effects/cubemap_filter.h" #include "effects/glow.h" #include "effects/post_effects.h" #include "environment/fog.h" @@ -70,6 +71,7 @@ protected: GLES3::GI *gi = nullptr; GLES3::Fog *fog = nullptr; GLES3::CopyEffects *copy_effects = nullptr; + GLES3::CubemapFilter *cubemap_filter = nullptr; GLES3::Glow *glow = nullptr; GLES3::PostEffects *post_effects = nullptr; RasterizerCanvasGLES3 *canvas = nullptr; diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index b8cc3928eb6..ecb563214ce 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/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() { - 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) { @@ -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) { GeometryInstanceGLES3 *ginstance = static_cast(p_geometry_instance); 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) { + GLES3::CubemapFilter *cubemap_filter = GLES3::CubemapFilter::get_singleton(); GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton(); 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) { 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 { - _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->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.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++; } } -} - -// 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 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 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) { GLES3::MeshStorage *mesh_storage = GLES3::MeshStorage::get_singleton(); + GLES3::LightStorage *light_storage = GLES3::LightStorage::get_singleton(); if (p_render_list == RENDER_LIST_OPAQUE) { 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->spot_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(); if (inst->paired_omni_light_count) { for (uint32_t j = 0; j < inst->paired_omni_light_count; 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; } - 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. - 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; - 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.light_instance_rid = light_instance; pass.is_omni = true; @@ -1415,7 +1303,7 @@ void RasterizerSceneGLES3::_fill_render_list(RenderListType p_render_list, const } } else { // 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) { for (uint32_t j = 0; j < inst->paired_spot_light_count; 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; } - 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. - 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; - 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.light_instance_rid = light_instance; inst->light_passes.push_back(pass); } } else { // 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 &p_render_ RENDER_TIMESTAMP("Setup 3D Scene"); bool apply_color_adjustments_in_post = false; + bool is_reflection_probe = p_reflection_probe.is_valid(); - Ref rb; - if (p_render_buffers.is_valid()) { - rb = p_render_buffers; - ERR_FAIL_COND(rb.is_null()); + Ref 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; if (p_environment.is_valid() && rb.is_valid()) { @@ -2351,7 +2258,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ RenderDataGLES3 render_data; { 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 render_data.cam_transform = p_camera_data->main_transform; render_data.inv_cam_transform = render_data.cam_transform.affine_inverse(); @@ -2381,7 +2288,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ // this should be the same for all cameras.. render_data.lod_distance_multiplier = p_camera_data->main_projection.get_lod_multiplier(); - if (rt->color_type == GL_UNSIGNED_INT_2_10_10_10_REV && glow_enabled) { + 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... render_data.luminance_multiplier = 0.25; } else { @@ -2415,7 +2322,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glBindBufferBase(GL_UNIFORM_BUFFER, SCENE_GLOBALS_UNIFORM_LOCATION, global_buffer); 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); } else { clear_color = texture_storage->get_default_clear_color(); @@ -2448,9 +2355,9 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ 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. // We're probably rendering directly to an XR device. flip_y = false; @@ -2462,7 +2369,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ _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_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); render_list[RENDER_LIST_OPAQUE].sort_by_key(); @@ -2522,7 +2429,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &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) { RENDER_TIMESTAMP("Setup Sky"); Projection projection = render_data.cam_projection; - if (render_data.reflection_probe.is_valid()) { + if (is_reflection_probe) { Projection correction; correction.set_depth_correction(true, true, false); projection = correction * render_data.cam_projection; @@ -2543,7 +2450,12 @@ void RasterizerSceneGLES3::render_scene(const Ref &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); glViewport(0, 0, rb->internal_size.x, rb->internal_size.y); @@ -2664,10 +2576,17 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ scene_state.enable_gl_blend(false); scene_state.set_gl_cull_mode(GLES3::SceneShaderData::CULL_BACK); - _draw_sky(render_data.environment, render_data.cam_projection, render_data.cam_transform, sky_energy_multiplier, 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; GLuint backbuffer_fbo = 0; GLuint backbuffer = 0; @@ -2725,7 +2644,7 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glFrontFace(GL_CCW); } - if (rb.is_valid()) { + if (!is_reflection_probe && rb.is_valid()) { _render_buffers_debug_draw(rb, p_shadow_atlas, fbo); } @@ -2733,9 +2652,11 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ scene_state.reset_gl_state(); 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); } @@ -3203,6 +3124,14 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, 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()) { 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_DIRECTIONAL; spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; + spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE; } 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); { GLES3::Mesh::Surface *s = reinterpret_cast(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_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_ROUGHNESS_LOD " + itos(sky_globals.roughness_layers - 1) + ".0\n"; material_storage->shaders.scene_shader.initialize(global_defines); 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); @@ -4129,7 +4106,6 @@ void fragment() { { // Initialize Sky stuff 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; 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(); } - { - 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(); @@ -4234,7 +4203,6 @@ RasterizerSceneGLES3::~RasterizerSceneGLES3() { // Scene Shader 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->shader_free(scene_globals.default_shader); @@ -4250,7 +4218,6 @@ RasterizerSceneGLES3::~RasterizerSceneGLES3() { RSG::material_storage->shader_free(sky_globals.fog_shader); GLES3::Utilities::get_singleton()->buffer_free_data(sky_globals.screen_triangle); 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); memdelete_arr(sky_globals.directional_lights); memdelete_arr(sky_globals.last_frame_directional_lights); diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 71cd1525204..cc479bd4e94 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -37,7 +37,7 @@ #include "core/templates/paged_allocator.h" #include "core/templates/rid_owner.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 "scene/resources/mesh.h" #include "servers/rendering/renderer_compositor.h" @@ -157,7 +157,6 @@ private: RID shader_default_version; RID default_material; RID default_shader; - RID cubemap_filter_shader_version; RID overdraw_material; RID overdraw_shader; } scene_globals; @@ -314,6 +313,10 @@ private: LocalVector omni_light_gl_cache; LocalVector spot_light_gl_cache; + LocalVector paired_reflection_probes; + LocalVector reflection_probe_rid_cache; + LocalVector reflection_probes_local_transform_cache; + RID lightmap_instance; Rect2 lightmap_uv_scale; uint32_t lightmap_slice_index; @@ -331,7 +334,7 @@ private: 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_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_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; GLuint screen_triangle = 0; GLuint screen_triangle_array = 0; - GLuint radical_inverse_vdc_cache_tex = 0; uint32_t max_directional_lights = 4; uint32_t roughness_layers = 8; - uint32_t ggx_samples = 128; } sky_globals; struct Sky { @@ -733,7 +734,6 @@ protected: void _invalidate_sky(Sky *p_sky); void _update_dirty_skys(); void _update_sky_radiance(RID p_env, const Projection &p_projection, const Transform3D &p_transform, float p_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 _free_sky_data(Sky *p_sky); diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index 0292b5d5194..e70912cb4d7 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -18,7 +18,6 @@ if "GLES3_GLSL" in env["BUILDERS"]: env.GLES3_GLSL("canvas.glsl") env.GLES3_GLSL("scene.glsl") env.GLES3_GLSL("sky.glsl") - env.GLES3_GLSL("cubemap_filter.glsl") env.GLES3_GLSL("canvas_occlusion.glsl") env.GLES3_GLSL("canvas_sdf.glsl") env.GLES3_GLSL("particles.glsl") diff --git a/drivers/gles3/shaders/cubemap_filter.glsl b/drivers/gles3/shaders/effects/cubemap_filter.glsl similarity index 100% rename from drivers/gles3/shaders/cubemap_filter.glsl rename to drivers/gles3/shaders/effects/cubemap_filter.glsl diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 8bf844991dd..36bbca87283 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -12,6 +12,7 @@ DISABLE_LIGHTMAP = false DISABLE_LIGHT_DIRECTIONAL = false DISABLE_LIGHT_OMNI = false DISABLE_LIGHT_SPOT = false +DISABLE_REFLECTION_PROBE = true DISABLE_FOG = false USE_DEPTH_FOG = false USE_RADIANCE_MAP = true @@ -34,6 +35,7 @@ APPLY_TONEMAPPING = true ADDITIVE_OMNI = false ADDITIVE_SPOT = false RENDER_MATERIAL = false +SECOND_REFLECTION_PROBE = false #[vertex] @@ -568,8 +570,11 @@ void main() { 1-color correction // In tonemap_inc.glsl 2-radiance 3-shadow +4-lightmap textures 5-screen 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 -#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 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; @@ -1289,6 +1326,90 @@ vec4 fog_process(vec3 vertex) { 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 void main() { @@ -1489,9 +1610,33 @@ void main() { specular_light *= horizon * horizon; specular_light *= scene_data.ambient_light_color_energy.a; } -#endif +#endif // USE_RADIANCE_MAP // 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) specular_light = mix(specular_light, custom_radiance.rgb, custom_radiance.a); @@ -1501,6 +1646,7 @@ void main() { //lightmap overrides everything if (scene_data.use_ambient_light) { ambient_light = scene_data.ambient_light_color_energy.rgb; + #ifdef USE_RADIANCE_MAP if (scene_data.use_ambient_cubemap) { vec3 ambient_dir = scene_data.radiance_inverse_xform * normal; @@ -1508,7 +1654,13 @@ void main() { 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); } -#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 diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index eebba6b00d1..f9547502f4b 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -423,149 +423,604 @@ void LightStorage::light_instance_mark_visible(RID p_light_instance) { /* PROBE API */ RID LightStorage::reflection_probe_allocate() { - return RID(); + return reflection_probe_owner.allocate_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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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) { + 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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 { - 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) { + 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 { - 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 */ 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) { + 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 { - 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) { + 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 */ 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) { + 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) { + 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) { - 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) { + 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) { - 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) { - 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) { - 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 LightStorage::reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) { - return Ref(); + ReflectionAtlas *atlas = reflection_atlas_owner.get_or_null(p_reflection_atlas); + ERR_FAIL_NULL_V(atlas, Ref()); + + return atlas->render_buffers; } 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 */ diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 51c5c481066..8107a3ad4d7 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -34,6 +34,7 @@ #ifdef GLES3_ENABLED #include "platform_gl.h" +#include "render_scene_buffers_gles3.h" #include "core/templates/local_vector.h" #include "core/templates/rid_owner.h" @@ -126,12 +127,51 @@ struct ReflectionProbe { bool box_projection = false; bool enable_shadows = false; uint32_t cull_mask = (1 << 20) - 1; + uint32_t reflection_mask = (1 << 20) - 1; float mesh_lod_threshold = 0.01; float baked_exposure = 1.0; 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 reflections; + + Ref 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 */ struct Lightmap { @@ -181,6 +221,13 @@ private: /* REFLECTION PROBE */ mutable RID_Owner reflection_probe_owner; + /* REFLECTION ATLAS */ + mutable RID_Owner reflection_atlas_owner; + + /* REFLECTION PROBE INSTANCE */ + + mutable RID_Owner reflection_probe_instance_owner; + /* LIGHTMAP */ Vector lightmap_textures; @@ -559,6 +606,9 @@ public: /* 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 void reflection_probe_initialize(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 bool reflection_probe_renders_shadows(RID p_probe) const override; + Dependency *reflection_probe_get_dependency(RID p_probe) const; + /* REFLECTION ATLAS */ + bool owns_reflection_atlas(RID p_rid) { return reflection_atlas_owner.owns(p_rid); } + virtual RID reflection_atlas_create() override; virtual void reflection_atlas_free(RID p_ref_atlas) override; virtual int reflection_atlas_get_size(RID p_ref_atlas) const override; @@ -598,6 +652,8 @@ public: /* 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 void reflection_probe_instance_free(RID p_instance) override; virtual void reflection_probe_instance_set_transform(RID p_instance, const Transform3D &p_transform) override; @@ -609,6 +665,27 @@ public: virtual Ref reflection_probe_atlas_get_render_buffers(RID p_reflection_atlas) 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 *get_lightmap(RID p_rid) { return lightmap_owner.get_or_null(p_rid); }; diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 59f5682362d..02aecf33d64 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -43,7 +43,6 @@ #include "servers/rendering/storage/utilities.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/scene.glsl.gen.h" #include "drivers/gles3/shaders/sky.glsl.gen.h" @@ -543,7 +542,6 @@ public: CanvasShaderGLES3 canvas_shader; SkyShaderGLES3 sky_shader; SceneShaderGLES3 scene_shader; - CubemapFilterShaderGLES3 cubemap_filter_shader; ParticlesShaderGLES3 particles_process_shader; ShaderCompiler compiler_canvas; diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.cpp b/drivers/gles3/storage/render_scene_buffers_gles3.cpp index de0a64f5fec..6803c92dc93 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.cpp +++ b/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() { for (const FBDEF &cached_fbo : msaa3d.cached_fbos) { GLuint fbo = cached_fbo.fbo; diff --git a/drivers/gles3/storage/render_scene_buffers_gles3.h b/drivers/gles3/storage/render_scene_buffers_gles3.h index 8d03d3438dd..85a02c860d5 100644 --- a/drivers/gles3/storage/render_scene_buffers_gles3.h +++ b/drivers/gles3/storage/render_scene_buffers_gles3.h @@ -101,6 +101,7 @@ public: RenderSceneBuffersGLES3(); virtual ~RenderSceneBuffersGLES3(); 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_texture_mipmap_bias(float p_texture_mipmap_bias) override{}; diff --git a/drivers/gles3/storage/utilities.cpp b/drivers/gles3/storage/utilities.cpp index c4fbe098cde..7e2e3dfa2bd 100644 --- a/drivers/gles3/storage/utilities.cpp +++ b/drivers/gles3/storage/utilities.cpp @@ -158,6 +158,8 @@ RS::InstanceType Utilities::get_base_type(RID p_rid) const { return RS::INSTANCE_LIGHTMAP; } else if (GLES3::ParticlesStorage::get_singleton()->owns_particles(p_rid)) { 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)) { return RS::INSTANCE_PARTICLES_COLLISION; } 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)) { GLES3::LightStorage::get_singleton()->lightmap_free(p_rid); 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)) { GLES3::ParticlesStorage::get_singleton()->particles_free(p_rid); return true; @@ -229,6 +240,9 @@ void Utilities::base_update_dependency(RID p_base, DependencyTracker *p_instance if (multimesh->mesh.is_valid()) { 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)) { Light *l = LightStorage::get_singleton()->get_light(p_base); p_instance->update_dependency(&l->dependency); diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index b4dd6d09be3..b6ec55286dd 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -190,17 +190,6 @@ AABB ReflectionProbe::get_aabb() const { 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 { if (p_property.name == "ambient_color" || p_property.name == "ambient_color_energy") { if (ambient_mode != AMBIENT_COLOR) { diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 425fbb5bc2e..72212942289 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -122,8 +122,6 @@ public: virtual AABB get_aabb() const override; - virtual PackedStringArray get_configuration_warnings() const override; - ReflectionProbe(); ~ReflectionProbe(); };