diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index ff2c0bbfd95..6b3e6ddd316 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -2592,6 +2592,10 @@ - Intel GPUs: SYCL libraries If no GPU acceleration is configured on the system, multi-threaded CPU-based denoising will be performed instead. This CPU-based denoising is significantly slower than the JNLM denoiser in most cases. + + If [code]true[/code], applies a bicubic filter during lightmap sampling. This makes lightmaps look much smoother, at a moderate performance cost. + [b]Note:[/b] The bicubic filter exaggerates the 'bleeding' effect that occurs when a lightmap's resolution is low enough. + The texel_size that is used to calculate the [member Mesh.lightmap_size_hint] on [PrimitiveMesh] resources if [member PrimitiveMesh.add_uv2] is enabled. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index d86b82b72a1..2348d6951e6 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -2206,6 +2206,13 @@ Set the textures on the given [param lightmap] GI instance to the texture array pointed to by the [param light] RID. If the lightmap texture was baked with [member LightmapGI.directional] set to [code]true[/code], then [param uses_sh] must also be [code]true[/code]. + + + + + Toggles whether a bicubic filter should be used when lightmaps are sampled. This smoothens their appearance at a performance cost. + + diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index 84b6ab4bd8b..5f678bf7d6f 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3202,6 +3202,10 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, if (lm->uses_spherical_harmonics) { spec_constants |= SceneShaderGLES3::USE_SH_LIGHTMAP; } + + if (lightmap_bicubic_upscale) { + spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER; + } } else if (inst->lightmap_sh) { spec_constants |= SceneShaderGLES3::USE_LIGHTMAP_CAPTURE; } else { @@ -3344,6 +3348,11 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y); material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants); + if (lightmap_bicubic_upscale) { + Vector2 light_texture_size(lm->light_texture_size.x, lm->light_texture_size.y); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, light_texture_size, shader->version, instance_variant, spec_constants); + } + float exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); @@ -4039,6 +4048,10 @@ void RasterizerSceneGLES3::decals_set_filter(RS::DecalFilter p_filter) { void RasterizerSceneGLES3::light_projectors_set_filter(RS::LightProjectorFilter p_filter) { } +void RasterizerSceneGLES3::lightmaps_set_bicubic_filter(bool p_enable) { + lightmap_bicubic_upscale = p_enable; +} + RasterizerSceneGLES3::RasterizerSceneGLES3() { singleton = this; @@ -4052,6 +4065,7 @@ RasterizerSceneGLES3::RasterizerSceneGLES3() { positional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/positional_shadow/soft_shadow_filter_quality")); directional_soft_shadow_filter_set_quality((RS::ShadowQuality)(int)GLOBAL_GET("rendering/lights_and_shadows/directional_shadow/soft_shadow_filter_quality")); + lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter")); { // Setup Lights diff --git a/drivers/gles3/rasterizer_scene_gles3.h b/drivers/gles3/rasterizer_scene_gles3.h index 4c70c43244f..e4af8f99e92 100644 --- a/drivers/gles3/rasterizer_scene_gles3.h +++ b/drivers/gles3/rasterizer_scene_gles3.h @@ -680,6 +680,8 @@ protected: bool glow_bicubic_upscale = false; RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::ENV_SSR_ROUGHNESS_QUALITY_LOW; + bool lightmap_bicubic_upscale = false; + /* Sky */ struct SkyGlobals { @@ -863,6 +865,7 @@ public: void decals_set_filter(RS::DecalFilter p_filter) override; void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; + virtual void lightmaps_set_bicubic_filter(bool p_enable) override; RasterizerSceneGLES3(); ~RasterizerSceneGLES3(); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 6dd04af6b60..a2d29a94807 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -36,6 +36,7 @@ ADDITIVE_OMNI = false ADDITIVE_SPOT = false RENDER_MATERIAL = false SECOND_REFLECTION_PROBE = false +LIGHTMAP_BICUBIC_FILTER = false #[vertex] @@ -923,6 +924,10 @@ uniform lowp uint lightmap_slice; uniform highp vec4 lightmap_uv_scale; uniform float lightmap_exposure_normalization; +#ifdef LIGHTMAP_BICUBIC_FILTER +uniform highp vec2 lightmap_texture_size; +#endif + #ifdef USE_SH_LIGHTMAP uniform mediump mat3 lightmap_normal_xform; #endif // USE_SH_LIGHTMAP @@ -1414,6 +1419,67 @@ void reflection_process(samplerCube reflection_map, #endif // !MODE_RENDER_DEPTH +#ifdef LIGHTMAP_BICUBIC_FILTER +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(sampler2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(tex, vec3(p0, uv.z)) + g1x * texture(tex, vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(tex, vec3(p2, uv.z)) + g1x * texture(tex, vec3(p3, uv.z)))); +} +#endif //LIGHTMAP_BICUBIC_FILTER + void main() { //lay out everything, whatever is unused is optimized away anyway vec3 vertex = vertex_interp; @@ -1721,10 +1787,18 @@ void main() { #ifdef USE_SH_LIGHTMAP uvw.z *= 4.0; // SH textures use 4 times more data. + +#ifdef LIGHTMAP_BICUBIC_FILTER + vec3 lm_light_l0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), lightmap_texture_size).rgb; + vec3 lm_light_l1n1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), lightmap_texture_size).rgb; + vec3 lm_light_l1_0 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), lightmap_texture_size).rgb; + vec3 lm_light_l1p1 = textureArray_bicubic(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), lightmap_texture_size).rgb; +#else vec3 lm_light_l0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; vec3 lm_light_l1n1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; vec3 lm_light_l1_0 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; vec3 lm_light_l1p1 = textureLod(lightmap_textures, uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; +#endif vec3 n = normalize(lightmap_normal_xform * normal); @@ -1738,8 +1812,12 @@ void main() { specular_light += lm_light_l1_0 * 0.32573 * r.z * lightmap_exposure_normalization; specular_light += lm_light_l1p1 * 0.32573 * r.x * lightmap_exposure_normalization; } +#else +#ifdef LIGHTMAP_BICUBIC_FILTER + ambient_light += textureArray_bicubic(lightmap_textures, uvw, lightmap_texture_size).rgb * lightmap_exposure_normalization; #else ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; +#endif #endif } #endif // USE_LIGHTMAP diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index f9547502f4b..aab1aadf02d 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -1046,6 +1046,9 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use lightmap->light_texture = p_light; lightmap->uses_spherical_harmonics = p_uses_spherical_haromics; + Vector3i light_texture_size = GLES3::TextureStorage::get_singleton()->texture_get_size(lightmap->light_texture); + lightmap->light_texture_size = Vector2i(light_texture_size.x, light_texture_size.y); + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->light_texture); glBindTexture(GL_TEXTURE_2D_ARRAY, tex); glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index b6e64c9492b..81e7220439f 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -180,6 +180,7 @@ struct Lightmap { bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); float baked_exposure = 1.0; + Vector2i light_texture_size; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 3b1373c9287..8251c8f52ec 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1680,6 +1680,14 @@ uint32_t TextureStorage::texture_get_texid(RID p_texture) const { return texture->tex_id; } +Vector3i TextureStorage::texture_get_size(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_NULL_V(texture, Vector3i(0, 0, 0)); + + return Vector3i(texture->width, texture->height, texture->depth); +} + uint32_t TextureStorage::texture_get_width(RID p_texture) const { Texture *texture = texture_owner.get_or_null(p_texture); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index 1b83efee329..5569abcc732 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -553,6 +553,7 @@ public: void texture_set_data(RID p_texture, const Ref &p_image, int p_layer = 0); virtual Image::Format texture_get_format(RID p_texture) const override; uint32_t texture_get_texid(RID p_texture) const; + Vector3i texture_get_size(RID p_texture) const; uint32_t texture_get_width(RID p_texture) const; uint32_t texture_get_height(RID p_texture) const; uint32_t texture_get_depth(RID p_texture) const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index f24fa344ae5..4a8fa9540f2 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -429,6 +429,7 @@ void EditorNode::_update_from_settings() { RS::get_singleton()->decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter")))); RS::get_singleton()->light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter")))); + RS::get_singleton()->lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter")); SceneTree *tree = get_tree(); tree->set_debug_collisions_color(GLOBAL_GET("debug/shapes/collision/shape_color")); diff --git a/servers/rendering/dummy/rasterizer_scene_dummy.h b/servers/rendering/dummy/rasterizer_scene_dummy.h index 083493003f0..a699a58b1fb 100644 --- a/servers/rendering/dummy/rasterizer_scene_dummy.h +++ b/servers/rendering/dummy/rasterizer_scene_dummy.h @@ -186,6 +186,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) override {} virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override {} + virtual void lightmaps_set_bicubic_filter(bool p_enable) override {} RasterizerSceneDummy() {} ~RasterizerSceneDummy() {} diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index ad19f36257e..ed28e0e3628 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1103,9 +1103,17 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]); + // Transform (for directional lightmaps). Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + + // Light texture size. + Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap); + scene_state.lightmaps[i].texture_size[0] = lightmap_size[0]; + scene_state.lightmaps[i].texture_size[1] = lightmap_size[1]; + + // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); @@ -4242,6 +4250,11 @@ void RenderForwardClustered::_update_shader_quality_settings() { spec_constants.push_back(sc); + sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER; + sc.bool_value = lightmap_filter_bicubic_get(); + + spec_constants.push_back(sc); + scene_shader.set_default_specialization_constants(spec_constants); base_uniforms_changed(); //also need this diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 0aa4a0667ec..5d14653db6b 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -73,6 +73,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { SPEC_CONSTANT_DECAL_FILTER = 10, SPEC_CONSTANT_PROJECTOR_FILTER = 11, SPEC_CONSTANT_USE_DEPTH_FOG = 12, + SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 13, }; enum { @@ -235,8 +236,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { struct LightmapData { float normal_xform[12]; - float pad[3]; + float texture_size[2]; float exposure_normalization; + float pad; }; struct LightmapCaptureData { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index c03dd96062f..412fa643b1e 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -563,9 +563,17 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co RID lightmap = light_storage->lightmap_instance_get_lightmap(p_lightmaps[i]); + // Transform (for directional lightmaps). Basis to_lm = light_storage->lightmap_instance_get_transform(p_lightmaps[i]).basis.inverse() * p_cam_transform.basis; to_lm = to_lm.inverse().transposed(); //will transform normals RendererRD::MaterialStorage::store_transform_3x3(to_lm, scene_state.lightmaps[i].normal_xform); + + // Light texture size. + Vector2i lightmap_size = light_storage->lightmap_get_light_texture_size(lightmap); + scene_state.lightmaps[i].texture_size[0] = lightmap_size[0]; + scene_state.lightmaps[i].texture_size[1] = lightmap_size[1]; + + // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); @@ -2781,6 +2789,11 @@ void RenderForwardMobile::_update_shader_quality_settings() { spec_constants.push_back(sc); + sc.constant_id = SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER; + sc.bool_value = lightmap_filter_bicubic_get(); + + spec_constants.push_back(sc); + scene_shader.set_default_specialization_constants(spec_constants); base_uniforms_changed(); //also need this diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index 34260bd701f..fc60c770e8c 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -82,7 +82,7 @@ private: SPEC_CONSTANT_DISABLE_FOG = 14, SPEC_CONSTANT_USE_DEPTH_FOG = 16, SPEC_CONSTANT_IS_MULTIMESH = 17, - + SPEC_CONSTANT_USE_LIGHTMAP_BICUBIC_FILTER = 18, }; enum { @@ -208,8 +208,9 @@ private: struct LightmapData { float normal_xform[12]; - float pad[3]; + float texture_size[2]; float exposure_normalization; + float pad; }; struct LightmapCaptureData { diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 0ebed49ee9c..7d6d5018d07 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -1040,6 +1040,14 @@ void RendererSceneRenderRD::light_projectors_set_filter(RenderingServer::LightPr _update_shader_quality_settings(); } +void RendererSceneRenderRD::lightmaps_set_bicubic_filter(bool p_enable) { + if (lightmap_filter_bicubic == p_enable) { + return; + } + lightmap_filter_bicubic = p_enable; + _update_shader_quality_settings(); +} + int RendererSceneRenderRD::get_roughness_layers() const { return sky.roughness_layers; } @@ -1483,6 +1491,7 @@ void RendererSceneRenderRD::init() { decals_set_filter(RS::DecalFilter(int(GLOBAL_GET("rendering/textures/decals/filter")))); light_projectors_set_filter(RS::LightProjectorFilter(int(GLOBAL_GET("rendering/textures/light_projectors/filter")))); + lightmaps_set_bicubic_filter(GLOBAL_GET("rendering/lightmapping/lightmap_gi/use_bicubic_filter")); cull_argument.set_page_pool(&cull_argument_pool); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index a8e8e638cd2..022a4560f8d 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -133,6 +133,7 @@ private: float *directional_soft_shadow_kernel = nullptr; float *penumbra_shadow_kernel = nullptr; float *soft_shadow_kernel = nullptr; + bool lightmap_filter_bicubic = false; int directional_penumbra_shadow_samples = 0; int directional_soft_shadow_samples = 0; int penumbra_shadow_samples = 0; @@ -262,6 +263,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) override; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) override; + virtual void lightmaps_set_bicubic_filter(bool p_enable) override; _FORCE_INLINE_ RS::ShadowQuality shadows_quality_get() const { return shadows_quality; @@ -292,6 +294,9 @@ public: _FORCE_INLINE_ int directional_penumbra_shadow_samples_get() const { return directional_penumbra_shadow_samples; } + _FORCE_INLINE_ bool lightmap_filter_bicubic_get() const { + return lightmap_filter_bicubic; + } _FORCE_INLINE_ int directional_soft_shadow_samples_get() const { return directional_soft_shadow_samples; } diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl index 4a630b0b0a0..94d297644e6 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered.glsl @@ -657,6 +657,7 @@ layout(constant_id = 9) const uint sc_directional_penumbra_shadow_samples = 4; layout(constant_id = 10) const bool sc_decal_use_mipmaps = true; layout(constant_id = 11) const bool sc_projector_use_mipmaps = true; layout(constant_id = 12) const bool sc_use_depth_fog = false; +layout(constant_id = 13) const bool sc_use_lightmap_bicubic_filter = false; // not used in clustered renderer but we share some code with the mobile renderer that requires this. const float sc_luminance_multiplier = 1.0; @@ -701,6 +702,67 @@ layout(location = 9) in float dp_clip; layout(location = 10) in flat uint instance_index_interp; +#ifdef USE_LIGHTMAP +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z)))); +} +#endif //USE_LIGHTMAP + #ifdef USE_MULTIVIEW #ifdef has_VK_KHR_multiview #define ViewIndex gl_ViewIndex @@ -1439,10 +1501,23 @@ void fragment_shader(in SceneData scene_data) { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + + vec3 lm_light_l0; + vec3 lm_light_l1n1; + vec3 lm_light_l1_0; + vec3 lm_light_l1p1; + + if (sc_use_lightmap_bicubic_filter) { + lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb; + } else { + lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + } vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float en = lightmaps.data[ofs].exposure_normalization; @@ -1459,7 +1534,11 @@ void fragment_shader(in SceneData scene_data) { } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + if (sc_use_lightmap_bicubic_filter) { + ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + } else { + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + } } } #else diff --git a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl index 441cf3c80c9..18322a06199 100644 --- a/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_clustered/scene_forward_clustered_inc.glsl @@ -94,8 +94,9 @@ directional_lights; struct Lightmap { mat3 normal_xform; - vec3 pad; + vec2 light_texture_size; float exposure_normalization; + vec2 pad; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl index 530a7a37db8..5f2bee692fe 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile.glsl @@ -521,6 +521,7 @@ layout(constant_id = 9) const bool sc_disable_omni_lights = false; layout(constant_id = 10) const bool sc_disable_spot_lights = false; layout(constant_id = 11) const bool sc_disable_reflection_probes = false; layout(constant_id = 12) const bool sc_disable_directional_lights = false; +layout(constant_id = 18) const bool sc_use_lightmap_bicubic_filter = false; #endif //!MODE_UNSHADED @@ -567,6 +568,67 @@ layout(location = 9) highp in float dp_clip; #endif +#ifdef USE_LIGHTMAP +// w0, w1, w2, and w3 are the four cubic B-spline basis functions +float w0(float a) { + return (1.0 / 6.0) * (a * (a * (-a + 3.0) - 3.0) + 1.0); +} + +float w1(float a) { + return (1.0 / 6.0) * (a * a * (3.0 * a - 6.0) + 4.0); +} + +float w2(float a) { + return (1.0 / 6.0) * (a * (a * (-3.0 * a + 3.0) + 3.0) + 1.0); +} + +float w3(float a) { + return (1.0 / 6.0) * (a * a * a); +} + +// g0 and g1 are the two amplitude functions +float g0(float a) { + return w0(a) + w1(a); +} + +float g1(float a) { + return w2(a) + w3(a); +} + +// h0 and h1 are the two offset functions +float h0(float a) { + return -1.0 + w1(a) / (w0(a) + w1(a)); +} + +float h1(float a) { + return 1.0 + w3(a) / (w2(a) + w3(a)); +} + +vec4 textureArray_bicubic(texture2DArray tex, vec3 uv, vec2 texture_size) { + vec2 texel_size = vec2(1.0) / texture_size; + + uv.xy = uv.xy * texture_size + vec2(0.5); + + vec2 iuv = floor(uv.xy); + vec2 fuv = fract(uv.xy); + + float g0x = g0(fuv.x); + float g1x = g1(fuv.x); + float h0x = h0(fuv.x); + float h1x = h1(fuv.x); + float h0y = h0(fuv.y); + float h1y = h1(fuv.y); + + vec2 p0 = (vec2(iuv.x + h0x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p1 = (vec2(iuv.x + h1x, iuv.y + h0y) - vec2(0.5)) * texel_size; + vec2 p2 = (vec2(iuv.x + h0x, iuv.y + h1y) - vec2(0.5)) * texel_size; + vec2 p3 = (vec2(iuv.x + h1x, iuv.y + h1y) - vec2(0.5)) * texel_size; + + return (g0(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p0, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p1, uv.z)))) + + (g1(fuv.y) * (g0x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p2, uv.z)) + g1x * texture(sampler2DArray(tex, SAMPLER_LINEAR_CLAMP), vec3(p3, uv.z)))); +} +#endif //USE_LIGHTMAP + #ifdef USE_MULTIVIEW #ifdef has_VK_KHR_multiview #define ViewIndex gl_ViewIndex @@ -1199,10 +1261,22 @@ void main() { if (uses_sh) { uvw.z *= 4.0; //SH textures use 4 times more data - vec3 lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; - vec3 lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; - vec3 lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; - vec3 lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + vec3 lm_light_l0; + vec3 lm_light_l1n1; + vec3 lm_light_l1_0; + vec3 lm_light_l1p1; + + if (sc_use_lightmap_bicubic_filter) { + lm_light_l0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 0.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1n1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 1.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1_0 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 2.0), lightmaps.data[ofs].light_texture_size).rgb; + lm_light_l1p1 = textureArray_bicubic(lightmap_textures[ofs], uvw + vec3(0.0, 0.0, 3.0), lightmaps.data[ofs].light_texture_size).rgb; + } else { + lm_light_l0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 0.0), 0.0).rgb; + lm_light_l1n1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 1.0), 0.0).rgb; + lm_light_l1_0 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 2.0), 0.0).rgb; + lm_light_l1p1 = textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw + vec3(0.0, 0.0, 3.0), 0.0).rgb; + } vec3 n = normalize(lightmaps.data[ofs].normal_xform * normal); float exposure_normalization = lightmaps.data[ofs].exposure_normalization; @@ -1219,7 +1293,11 @@ void main() { } } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + if (sc_use_lightmap_bicubic_filter) { + ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization; + } else { + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + } } } diff --git a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl index 7674e905e1d..d971ff04c5e 100644 --- a/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl +++ b/servers/rendering/renderer_rd/shaders/forward_mobile/scene_forward_mobile_inc.glsl @@ -71,8 +71,9 @@ directional_lights; struct Lightmap { mediump mat3 normal_xform; - vec3 pad; + vec2 light_texture_size; float exposure_normalization; + float pad; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 3d294ca8cb1..c217c0fa9a5 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -1813,6 +1813,7 @@ void LightStorage::lightmap_set_textures(RID p_lightmap, RID p_light, bool p_use } t->lightmap_users.insert(p_lightmap); + lm->light_texture_size = Vector2i(t->width, t->height); if (using_lightmap_array) { if (lm->array_index < 0) { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index f152cc5dae6..94ab219dc21 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -332,6 +332,7 @@ private: bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); float baked_exposure = 1.0; + Vector2i light_texture_size; int32_t array_index = -1; //unassigned PackedVector3Array points; PackedColorArray point_sh; @@ -985,6 +986,10 @@ public: const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); return lm->uses_spherical_harmonics; } + _FORCE_INLINE_ Vector2i lightmap_get_light_texture_size(RID p_lightmap) const { + const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + return lm->light_texture_size; + } _FORCE_INLINE_ uint64_t lightmap_array_get_version() const { ERR_FAIL_COND_V(!using_lightmap_array, 0); //only for arrays return lightmap_array_version; diff --git a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h index 3cd397b8ed6..1312ad95f4d 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_data_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_data_rd.h @@ -76,6 +76,8 @@ public: uint32_t directional_light_count = 0; bool directional_light_soft_shadows = false; + bool lightmap_bicubic_filter = false; + RenderingMethod::RenderInfo *render_info = nullptr; /* Viewport data */ diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 53f1f2d2461..972f66d325f 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -1418,6 +1418,7 @@ public: PASS1(decals_set_filter, RS::DecalFilter) PASS1(light_projectors_set_filter, RS::LightProjectorFilter) + PASS1(lightmaps_set_bicubic_filter, bool) virtual void update(); diff --git a/servers/rendering/renderer_scene_render.h b/servers/rendering/renderer_scene_render.h index 719efa4df22..3446f5dd1bb 100644 --- a/servers/rendering/renderer_scene_render.h +++ b/servers/rendering/renderer_scene_render.h @@ -338,6 +338,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) = 0; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; virtual void update() = 0; virtual ~RendererSceneRender() {} diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h index 57fbf97d8cc..f6212faf085 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -349,6 +349,7 @@ public: virtual void decals_set_filter(RS::DecalFilter p_filter) = 0; virtual void light_projectors_set_filter(RS::LightProjectorFilter p_filter) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; virtual bool free(RID p_rid) = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 6e195a80020..406997f0a18 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -761,6 +761,7 @@ public: FUNC1(directional_soft_shadow_filter_set_quality, ShadowQuality); FUNC1(decals_set_filter, RS::DecalFilter); FUNC1(light_projectors_set_filter, RS::LightProjectorFilter); + FUNC1(lightmaps_set_bicubic_filter, bool); /* CAMERA ATTRIBUTES */ diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 9fc67b04b1d..23906c12b0b 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2477,6 +2477,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("light_directional_set_sky_mode", "light", "mode"), &RenderingServer::light_directional_set_sky_mode); ClassDB::bind_method(D_METHOD("light_projectors_set_filter", "filter"), &RenderingServer::light_projectors_set_filter); + ClassDB::bind_method(D_METHOD("lightmaps_set_bicubic_filter", "enable"), &RenderingServer::lightmaps_set_bicubic_filter); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_NEAREST); BIND_ENUM_CONSTANT(LIGHT_PROJECTOR_FILTER_LINEAR); @@ -3618,6 +3619,7 @@ void RenderingServer::init() { GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/probe_capture/update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"), 15); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/lightmapping/primitive_meshes/texel_size", PROPERTY_HINT_RANGE, "0.001,100,0.001"), 0.2); + GLOBAL_DEF("rendering/lightmapping/lightmap_gi/use_bicubic_filter", true); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)"), 1); GLOBAL_DEF(PropertyInfo(Variant::INT, "rendering/global_illumination/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)"), 5); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 8450cb0198a..5344bd0908c 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -687,6 +687,8 @@ public: virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0; + virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; + /* PARTICLES API */ virtual RID particles_create() = 0;