From 3a16804276c71b68382c8352493bc8e816a257f5 Mon Sep 17 00:00:00 2001 From: skywolf285 Date: Wed, 6 May 2026 11:15:44 +0200 Subject: [PATCH] Add modulate property to lightmaps. --- doc/classes/LightmapGIData.xml | 3 +++ doc/classes/RenderingServer.xml | 15 +++++++++++ drivers/gles3/rasterizer_scene_gles3.cpp | 3 +++ drivers/gles3/shaders/scene.glsl | 16 +++++++----- drivers/gles3/storage/light_storage.cpp | 24 +++++++++++++++++- drivers/gles3/storage/light_storage.h | 6 +++++ scene/3d/lightmap_gi.cpp | 19 +++++++++++++- scene/3d/lightmap_gi.h | 4 +++ .../rendering/dummy/storage/light_storage.h | 4 +++ .../render_forward_clustered.cpp | 6 +++++ .../render_forward_clustered.h | 2 ++ .../forward_mobile/render_forward_mobile.cpp | 6 +++++ .../forward_mobile/render_forward_mobile.h | 2 ++ .../scene_forward_clustered.glsl | 15 ++++++----- .../scene_forward_clustered_inc.glsl | 2 ++ .../forward_mobile/scene_forward_mobile.glsl | 16 +++++++----- .../scene_forward_mobile_inc.glsl | 2 ++ .../renderer_rd/storage_rd/light_storage.cpp | 25 ++++++++++++++++++- .../renderer_rd/storage_rd/light_storage.h | 11 ++++++++ servers/rendering/renderer_scene_cull.cpp | 11 ++++++++ servers/rendering/renderer_scene_cull.h | 2 ++ servers/rendering/rendering_method.h | 2 ++ servers/rendering/rendering_server.cpp | 3 +++ servers/rendering/rendering_server.h | 3 +++ servers/rendering/rendering_server_default.h | 3 +++ servers/rendering/storage/light_storage.h | 4 +++ 26 files changed, 188 insertions(+), 21 deletions(-) diff --git a/doc/classes/LightmapGIData.xml b/doc/classes/LightmapGIData.xml index 5086814c4172..d6dd48d1e6fb 100644 --- a/doc/classes/LightmapGIData.xml +++ b/doc/classes/LightmapGIData.xml @@ -60,6 +60,9 @@ The lightmap atlas textures generated by the lightmapper. + + The color the lightmap is multiplied with. Gets applied in real-time without the need to re-bake the lightmap. + The shadowmask atlas textures generated by the lightmapper. diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 5a73ad34ba90..29eca79d09c6 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -1973,6 +1973,13 @@ Sets the visibility range values for the given geometry instance. Equivalent to [member GeometryInstance3D.visibility_range_begin] and related properties. + + + + + Updates the lightmap spherical harmonics captures of all [GeometryInstance3D] instances affected by this lightmap. + + @@ -2304,6 +2311,14 @@ Used to inform the renderer what exposure normalization value was used while baking the lightmap. This value will be used and modulated at run time to ensure that the lightmap maintains a consistent level of exposure even if the scene-wide exposure normalization is changed at run time. For more information see [method camera_attributes_set_exposure]. + + + + + + Sets a lightmap's modulate color. + + diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index a69e35c44711..7f2a4405c7b4 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -3799,6 +3799,9 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, } material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_EXPOSURE_NORMALIZATION, exposure_normalization, shader->version, instance_variant, spec_constants); + Vector3 lightmap_modulate(lm->modulate.r, lm->modulate.g, lm->modulate.b); + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_MODULATE, lightmap_modulate, shader->version, instance_variant, spec_constants); + if (lm->uses_spherical_harmonics) { Basis to_lm = li->transform.basis.inverse() * p_render_data->cam_transform.basis; to_lm = to_lm.inverse().transposed(); diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index cd210e8304c1..ac24f0de74c3 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -1441,6 +1441,7 @@ uniform lowp sampler2DArray shadowmask_textures; //texunit:-5 uniform lowp uint lightmap_slice; uniform highp vec4 lightmap_uv_scale; uniform float lightmap_exposure_normalization; +uniform vec3 lightmap_modulate; uniform uint lightmap_shadowmask_mode; #define SHADOWMASK_MODE_NONE uint(0) @@ -2543,15 +2544,18 @@ void main() { vec3 n = normalize(lightmap_normal_xform * indirect_normal); - ambient_light += lm_light_l0 * lightmap_exposure_normalization; - ambient_light += lm_light_l1n1 * n.y * (lm_light_l0 * lightmap_exposure_normalization * 4.0); - ambient_light += lm_light_l1_0 * n.z * (lm_light_l0 * lightmap_exposure_normalization * 4.0); - ambient_light += lm_light_l1p1 * n.x * (lm_light_l0 * lightmap_exposure_normalization * 4.0); + vec3 al = vec3(0.0, 0.0, 0.0); + al += lm_light_l0 * lightmap_exposure_normalization; + al += lm_light_l1n1 * n.y * (lm_light_l0 * lightmap_exposure_normalization * 4.0); + al += lm_light_l1_0 * n.z * (lm_light_l0 * lightmap_exposure_normalization * 4.0); + al += lm_light_l1p1 * n.x * (lm_light_l0 * lightmap_exposure_normalization * 4.0); + + ambient_light += al * lightmap_modulate; #else #ifdef LIGHTMAP_BICUBIC_FILTER - ambient_light += textureArray_bicubic(lightmap_textures, uvw, lightmap_texture_size).rgb * lightmap_exposure_normalization; + ambient_light += textureArray_bicubic(lightmap_textures, uvw, lightmap_texture_size).rgb * lightmap_exposure_normalization * lightmap_modulate; #else - ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization; + ambient_light += textureLod(lightmap_textures, uvw, 0.0).rgb * lightmap_exposure_normalization * lightmap_modulate; #endif #endif } diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 94c31aec6aa1..0e79ed6a5bc5 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -40,6 +40,7 @@ #include "drivers/gles3/rasterizer_util_gles3.h" #include "drivers/gles3/storage/render_scene_buffers_gles3.h" #include "drivers/gles3/storage/utilities.h" +#include "servers/rendering/rendering_server.h" using namespace GLES3; @@ -1185,6 +1186,16 @@ void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, flo lightmap->baked_exposure = p_exposure; } +void LightStorage::lightmap_set_modulate(RID p_lightmap, const Color &p_color) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + + lightmap->modulate = p_color; + for (RID r : lightmap->lightmap_instances) { + RS::get_singleton()->instance_lightmap_update_geometries_captures(r); + } +} + PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_NULL_V(lightmap, PackedVector3Array()); @@ -1260,7 +1271,7 @@ void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, for (int i = 0; i < 4; i++) { float c = CLAMP(barycentric[i], 0.0, 1.0); for (int j = 0; j < 9; j++) { - r_sh[j] += sh_colors[i][j] * c; + r_sh[j] += sh_colors[i][j] * c * lm->modulate; } } } @@ -1279,6 +1290,17 @@ float LightStorage::lightmap_get_probe_capture_update_speed() const { return lightmap_probe_capture_update_speed; } +void LightStorage::lightmap_insert_to_lightmap_instances(RID p_lightmap, RID p_instance) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + lm->lightmap_instances.insert(p_instance); +} +void LightStorage::lightmap_erase_from_lightmap_instances(RID p_lightmap, RID p_instance) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + lm->lightmap_instances.erase(p_instance); +} + void LightStorage::lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) { Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_NULL(lightmap); diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 10d31a788c65..0fed3f1bb7c8 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -179,10 +179,12 @@ struct ReflectionProbeInstance { struct Lightmap { RID light_texture; RID shadow_texture; + HashSet lightmap_instances; bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); float baked_exposure = 1.0; + Color modulate; // in linear space. Vector2i light_texture_size; int32_t array_index = -1; //unassigned RSE::ShadowmaskMode shadowmask_mode = RSE::SHADOWMASK_MODE_NONE; @@ -740,6 +742,7 @@ class LightStorage : public RendererLightStorage { virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override; + virtual void lightmap_set_modulate(RID p_lightmap, const Color &p_color) override; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; @@ -750,6 +753,9 @@ class LightStorage : public RendererLightStorage { virtual void lightmap_set_probe_capture_update_speed(float p_speed) override; virtual float lightmap_get_probe_capture_update_speed() const override; + virtual void lightmap_insert_to_lightmap_instances(RID p_lightmap, RID p_instance) override; + virtual void lightmap_erase_from_lightmap_instances(RID p_lightmap, RID p_instance) override; + virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override; virtual RSE::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override; virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RSE::ShadowmaskMode p_mode) override; diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 241bfe82d99f..ded0a051173f 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -47,6 +47,7 @@ #include "scene/resources/image_texture.h" #include "scene/resources/sky.h" #include "servers/rendering/rendering_server.h" +#include "servers/rendering/rendering_server_globals.h" #include "modules/modules_enabled.gen.h" // IWYU pragma: keep. For lightmapper_rd. @@ -287,6 +288,15 @@ float LightmapGIData::get_baked_exposure() const { return baked_exposure; } +void LightmapGIData::set_modulate(const Color &p_color) { + modulate = p_color; + RS::get_singleton()->lightmap_set_modulate(lightmap, p_color.srgb_to_linear()); +} + +Color LightmapGIData::get_modulate() const { + return modulate; +} + void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bounds")); ERR_FAIL_COND(!p_data.has("points")); @@ -348,6 +358,9 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadowmask_textures", "shadowmask_textures"), &LightmapGIData::set_shadowmask_textures); ClassDB::bind_method(D_METHOD("get_shadowmask_textures"), &LightmapGIData::get_shadowmask_textures); + ClassDB::bind_method(D_METHOD("set_modulate", "modulate"), &LightmapGIData::set_modulate); + ClassDB::bind_method(D_METHOD("get_modulate"), &LightmapGIData::get_modulate); + ClassDB::bind_method(D_METHOD("set_uses_spherical_harmonics", "uses_spherical_harmonics"), &LightmapGIData::set_uses_spherical_harmonics); ClassDB::bind_method(D_METHOD("is_using_spherical_harmonics"), &LightmapGIData::is_using_spherical_harmonics); @@ -362,6 +375,7 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_probe_data", "data"), &LightmapGIData::_set_probe_data); ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate", PROPERTY_HINT_COLOR_NO_ALPHA), "set_modulate", "get_modulate"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "lightmap_textures", PROPERTY_HINT_ARRAY_TYPE, "TextureLayered", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY), "set_lightmap_textures", "get_lightmap_textures"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "shadowmask_textures", PROPERTY_HINT_ARRAY_TYPE, "TextureLayered", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_READ_ONLY), "set_shadowmask_textures", "get_shadowmask_textures"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics"); @@ -1713,8 +1727,8 @@ void LightmapGI::_notification(int p_what) { if (last_owner && last_owner != get_owner()) { light_data->clear_users(); } - _assign_lightmaps(); + RSG::light_storage->lightmap_insert_to_lightmap_instances(light_data->get_rid(), get_instance()); } } break; @@ -1723,6 +1737,7 @@ void LightmapGI::_notification(int p_what) { if (light_data.is_valid()) { _clear_lightmaps(); + RSG::light_storage->lightmap_erase_from_lightmap_instances(light_data->get_rid(), get_instance()); } } break; } @@ -1791,6 +1806,7 @@ void LightmapGI::set_light_data(const Ref &p_data) { _clear_lightmaps(); } set_base(RID()); + RSG::light_storage->lightmap_erase_from_lightmap_instances(light_data->get_rid(), get_instance()); } light_data = p_data; @@ -1800,6 +1816,7 @@ void LightmapGI::set_light_data(const Ref &p_data) { _assign_lightmaps(); } light_data->update_shadowmask_mode(shadowmask_mode); + RSG::light_storage->lightmap_insert_to_lightmap_instances(light_data->get_rid(), get_instance()); } update_gizmos(); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index aa1dcc36eba8..c8c103356f1e 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -70,6 +70,7 @@ class LightmapGIData : public Resource { RID lightmap; AABB bounds; float baked_exposure = 1.0; + Color modulate = Color(1, 1, 1, 1); uint32_t lightprobe_hash = 0; struct User { @@ -121,6 +122,9 @@ class LightmapGIData : public Resource { bool is_interior() const; float get_baked_exposure() const; + void set_modulate(const Color &p_color); + Color get_modulate() const; + void set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree, float p_baked_exposure, uint32_t p_lightprobe_hash); PackedVector3Array get_capture_points() const; PackedColorArray get_capture_sh() const; diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index 1f722816c7b2..adf018e2a40b 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -192,6 +192,7 @@ class LightStorage : public RendererLightStorage { virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override {} virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override {} virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override {} + virtual void lightmap_set_modulate(RID p_lightmap, const Color &p_color) override {} virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override { return PackedVector3Array(); } virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override { return PackedColorArray(); } virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override { return PackedInt32Array(); } @@ -202,6 +203,9 @@ class LightStorage : public RendererLightStorage { virtual void lightmap_set_probe_capture_update_speed(float p_speed) override {} virtual float lightmap_get_probe_capture_update_speed() const override { return 0; } + virtual void lightmap_insert_to_lightmap_instances(RID p_lightmap, RID p_instance) override {} + virtual void lightmap_erase_from_lightmap_instances(RID p_lightmap, RID p_instance) override {} + virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override {} virtual RSE::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override { return RSE::SHADOWMASK_MODE_NONE; } virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RSE::ShadowmaskMode p_mode) override {} 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 92ed65df5508..fa98e817088f 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1240,6 +1240,12 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, scene_state.lightmaps[i].exposure_normalization = enf / baked_exposure; } + // Modulate + Color m = light_storage->lightmap_get_modulate(lightmap); + scene_state.lightmaps[i].modulate[0] = m.r; + scene_state.lightmaps[i].modulate[1] = m.g; + scene_state.lightmaps[i].modulate[2] = m.b; + scene_state.lightmap_ids[i] = p_lightmaps[i]; scene_state.lightmap_has_sh[i] = light_storage->lightmap_uses_spherical_harmonics(lightmap); 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 be97ee2d96af..b7e6f891bf3c 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -262,6 +262,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { float normal_xform[12]; float texture_size[2]; float exposure_normalization; + float pad; + float modulate[3]; uint32_t flags; }; 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 ad6566357fdb..7b0c443c993b 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -754,6 +754,12 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co scene_state.lightmaps[i].exposure_normalization = enf / baked_exposure; } + // Modulate + Color m = light_storage->lightmap_get_modulate(lightmap); + scene_state.lightmaps[i].modulate[0] = m.r; + scene_state.lightmaps[i].modulate[1] = m.g; + scene_state.lightmaps[i].modulate[2] = m.b; + scene_state.lightmap_ids[i] = p_lightmaps[i]; scene_state.lightmap_has_sh[i] = light_storage->lightmap_uses_spherical_harmonics(lightmap); 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 0b156b05f1a7..ce865b4043c4 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -189,6 +189,8 @@ class RenderForwardMobile : public RendererSceneRenderRD { float normal_xform[12]; float texture_size[2]; float exposure_normalization; + float pad; + float modulate[3]; uint32_t flags; }; 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 431f4800b9ad..1cda59bf9156 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 @@ -1857,16 +1857,19 @@ void fragment_shader(in SceneData scene_data) { vec3 n = normalize(lightmaps.data[ofs].normal_xform * indirect_normal); float en = lightmaps.data[ofs].exposure_normalization; - ambient_light += lm_light_l0 * en; - ambient_light += lm_light_l1n1 * n.y * (lm_light_l0 * en * 4.0); - ambient_light += lm_light_l1_0 * n.z * (lm_light_l0 * en * 4.0); - ambient_light += lm_light_l1p1 * n.x * (lm_light_l0 * en * 4.0); + vec3 al = vec3(0.0, 0.0, 0.0); + al += lm_light_l0 * en; + al += lm_light_l1n1 * n.y * (lm_light_l0 * en * 4.0); + al += lm_light_l1_0 * n.z * (lm_light_l0 * en * 4.0); + al += lm_light_l1p1 * n.x * (lm_light_l0 * en * 4.0); + + ambient_light += al * lightmaps.data[ofs].modulate; } else { 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; + ambient_light += textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization * lightmaps.data[ofs].modulate; } else { - ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization; + ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization * lightmaps.data[ofs].modulate; } } } 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 4fc3ce2bc739..e0d78dab5d36 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 @@ -231,6 +231,8 @@ struct Lightmap { mat3 normal_xform; vec2 light_texture_size; float exposure_normalization; + float pad; + vec3 modulate; uint flags; }; 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 7845f5df4643..a4a215641bc3 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 @@ -1745,15 +1745,19 @@ void main() { hvec3 n = hvec3(normalize(lightmaps.data[ofs].normal_xform * indirect_normal)); half exposure_normalization = half(lightmaps.data[ofs].exposure_normalization); - ambient_light += lm_light_l0 * exposure_normalization; - ambient_light += lm_light_l1n1 * n.y * lm_light_l0 * exposure_normalization * half(4.0); - ambient_light += lm_light_l1_0 * n.z * lm_light_l0 * exposure_normalization * half(4.0); - ambient_light += lm_light_l1p1 * n.x * lm_light_l0 * exposure_normalization * half(4.0); + hvec3 al = hvec3(0.0, 0.0, 0.0); + al += lm_light_l0 * exposure_normalization; + al += lm_light_l1n1 * n.y * lm_light_l0 * exposure_normalization * half(4.0); + al += lm_light_l1_0 * n.z * lm_light_l0 * exposure_normalization * half(4.0); + al += lm_light_l1p1 * n.x * lm_light_l0 * exposure_normalization * half(4.0); + + ambient_light += al * hvec3(lightmaps.data[ofs].modulate); + } else { if (sc_use_lightmap_bicubic_filter()) { - ambient_light += hvec3(textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization); + ambient_light += hvec3(textureArray_bicubic(lightmap_textures[ofs], uvw, lightmaps.data[ofs].light_texture_size).rgb * lightmaps.data[ofs].exposure_normalization * lightmaps.data[ofs].modulate); } else { - ambient_light += hvec3(textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization); + ambient_light += hvec3(textureLod(sampler2DArray(lightmap_textures[ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).rgb * lightmaps.data[ofs].exposure_normalization * lightmaps.data[ofs].modulate); } } } 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 99ecf3a385a0..0792c1d6a420 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 @@ -295,6 +295,8 @@ struct Lightmap { mat3 normal_xform; vec2 light_texture_size; float exposure_normalization; + float pad; + vec3 modulate; uint flags; }; diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 48ec43438328..95e13125f8fa 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -35,6 +35,7 @@ #include "core/os/os.h" #include "servers/rendering/renderer_rd/renderer_scene_render_rd.h" #include "servers/rendering/renderer_rd/storage_rd/texture_storage.h" +#include "servers/rendering/rendering_server.h" #include "servers/rendering/rendering_server_globals.h" using namespace RendererRD; @@ -2164,6 +2165,16 @@ void LightStorage::lightmap_set_baked_exposure_normalization(RID p_lightmap, flo lm->baked_exposure = p_exposure; } +void LightStorage::lightmap_set_modulate(RID p_lightmap, const Color &p_color) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + + lm->modulate = p_color; + for (RID r : lm->lightmap_instances) { + RS::get_singleton()->instance_lightmap_update_geometries_captures(r); + } +} + PackedVector3Array LightStorage::lightmap_get_probe_capture_points(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_NULL_V(lm, PackedVector3Array()); @@ -2193,6 +2204,18 @@ void LightStorage::lightmap_set_probe_capture_update_speed(float p_speed) { lightmap_probe_capture_update_speed = p_speed; } +void LightStorage::lightmap_insert_to_lightmap_instances(RID p_lightmap, RID p_instance) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + lm->lightmap_instances.insert(p_instance); +} + +void LightStorage::lightmap_erase_from_lightmap_instances(RID p_lightmap, RID p_instance) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + lm->lightmap_instances.erase(p_instance); +} + Dependency *LightStorage::lightmap_get_dependency(RID p_lightmap) const { Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); ERR_FAIL_NULL_V(lm, nullptr); @@ -2245,7 +2268,7 @@ void LightStorage::lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, for (int i = 0; i < 4; i++) { float c = CLAMP(barycentric[i], 0.0, 1.0); for (int j = 0; j < 9; j++) { - r_sh[j] += sh_colors[i][j] * c; + r_sh[j] += sh_colors[i][j] * c * lm->modulate; } } } diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index 71043e449bd9..638c45917b7c 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -351,11 +351,13 @@ class LightStorage : public RendererLightStorage { struct Lightmap { RID light_texture; RID shadow_texture; + HashSet lightmap_instances; RSE::ShadowmaskMode shadowmask_mode = RSE::SHADOWMASK_MODE_NONE; bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); float baked_exposure = 1.0; + Color modulate = Color(1, 1, 1, 1); // in linear space. Vector2i light_texture_size; int32_t array_index = -1; //unassigned PackedVector3Array points; @@ -1023,6 +1025,7 @@ class LightStorage : public RendererLightStorage { virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) override; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) override; virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) override; + virtual void lightmap_set_modulate(RID p_lightmap, const Color &p_color) override; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const override; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const override; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const override; @@ -1032,6 +1035,9 @@ class LightStorage : public RendererLightStorage { virtual void lightmap_tap_sh_light(RID p_lightmap, const Vector3 &p_point, Color *r_sh) override; virtual void lightmap_set_probe_capture_update_speed(float p_speed) override; + virtual void lightmap_insert_to_lightmap_instances(RID p_lightmap, RID p_instance) override; + virtual void lightmap_erase_from_lightmap_instances(RID p_lightmap, RID p_instance) override; + Dependency *lightmap_get_dependency(RID p_lightmap) const; virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override; @@ -1051,6 +1057,11 @@ class LightStorage : public RendererLightStorage { ERR_FAIL_NULL_V(lm, 1.0); return lm->baked_exposure; } + _FORCE_INLINE_ Color lightmap_get_modulate(RID p_lightmap) const { + const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lm, Color(1, 1, 1, 1)); + return lm->modulate; + } _FORCE_INLINE_ int32_t lightmap_get_array_index(RID p_lightmap) const { ERR_FAIL_COND_V(!using_lightmap_array, -1); //only for arrays const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); diff --git a/servers/rendering/renderer_scene_cull.cpp b/servers/rendering/renderer_scene_cull.cpp index efe4de27205f..5c93ef974cb2 100644 --- a/servers/rendering/renderer_scene_cull.cpp +++ b/servers/rendering/renderer_scene_cull.cpp @@ -1602,6 +1602,17 @@ void RendererSceneCull::instance_geometry_get_shader_parameter_list(RID p_instan instance->instance_uniforms.get_property_list(*p_parameters); } +void RendererSceneCull::instance_lightmap_update_geometries_captures(RID p_lightmap) { + Instance *lightmap_instance = instance_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap_instance); + if (lightmap_instance->base_type == RSE::INSTANCE_LIGHTMAP) { + InstanceLightmapData *lightmap_data = static_cast(lightmap_instance->base_data); + for (Instance *geom : lightmap_data->geometries) { + _instance_queue_update(geom, false, false); + } + } +} + void RendererSceneCull::_update_instance(Instance *p_instance) const { p_instance->version++; diff --git a/servers/rendering/renderer_scene_cull.h b/servers/rendering/renderer_scene_cull.h index 6d9279889d0c..4856e57667dd 100644 --- a/servers/rendering/renderer_scene_cull.h +++ b/servers/rendering/renderer_scene_cull.h @@ -1070,6 +1070,8 @@ class RendererSceneCull : public RenderingMethod { virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const; virtual Variant instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const; + virtual void instance_lightmap_update_geometries_captures(RID p_lightmap); + virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation); virtual uint32_t get_pipeline_compilations(RSE::PipelineSource p_source); diff --git a/servers/rendering/rendering_method.h b/servers/rendering/rendering_method.h index c06adc8b8c8a..e34ee8e5ef05 100644 --- a/servers/rendering/rendering_method.h +++ b/servers/rendering/rendering_method.h @@ -121,6 +121,8 @@ class RenderingMethod { virtual Variant instance_geometry_get_shader_parameter(RID p_instance, const StringName &p_parameter) const = 0; virtual Variant instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &p_parameter) const = 0; + virtual void instance_lightmap_update_geometries_captures(RID p_lightmap) = 0; + /* PIPELINES */ virtual void mesh_generate_pipelines(RID p_mesh, bool p_background_compilation) = 0; diff --git a/servers/rendering/rendering_server.cpp b/servers/rendering/rendering_server.cpp index b0a4f2a98602..2c68ee01be5b 100644 --- a/servers/rendering/rendering_server.cpp +++ b/servers/rendering/rendering_server.cpp @@ -2708,6 +2708,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_tetrahedra", "lightmap"), &RenderingServer::lightmap_get_probe_capture_tetrahedra); ClassDB::bind_method(D_METHOD("lightmap_get_probe_capture_bsp_tree", "lightmap"), &RenderingServer::lightmap_get_probe_capture_bsp_tree); ClassDB::bind_method(D_METHOD("lightmap_set_baked_exposure_normalization", "lightmap", "baked_exposure"), &RenderingServer::lightmap_set_baked_exposure_normalization); + ClassDB::bind_method(D_METHOD("lightmap_set_modulate", "lightmap", "energy_color"), &RenderingServer::lightmap_set_modulate); ClassDB::bind_method(D_METHOD("lightmap_set_probe_capture_update_speed", "speed"), &RenderingServer::lightmap_set_probe_capture_update_speed); @@ -3255,6 +3256,8 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("instances_cull_ray", "from", "to", "scenario"), &RenderingServer::_instances_cull_ray_bind, DEFVAL(RID())); ClassDB::bind_method(D_METHOD("instances_cull_convex", "convex", "scenario"), &RenderingServer::_instances_cull_convex_bind, DEFVAL(RID())); + ClassDB::bind_method(D_METHOD("instance_lightmap_update_geometries_captures", "lightmap"), &RenderingServer::instance_lightmap_update_geometries_captures); + BIND_ENUM_CONSTANT(RSE::INSTANCE_NONE); BIND_ENUM_CONSTANT(RSE::INSTANCE_MESH); BIND_ENUM_CONSTANT(RSE::INSTANCE_MULTIMESH); diff --git a/servers/rendering/rendering_server.h b/servers/rendering/rendering_server.h index 78f63933c993..8851fb1e7139 100644 --- a/servers/rendering/rendering_server.h +++ b/servers/rendering/rendering_server.h @@ -423,6 +423,7 @@ class RenderingServer : public Object { virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) = 0; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) = 0; virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) = 0; + virtual void lightmap_set_modulate(RID p_lightmap, const Color &p_color) = 0; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const = 0; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const = 0; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const = 0; @@ -774,6 +775,8 @@ class RenderingServer : public Object { virtual Variant instance_geometry_get_shader_parameter_default_value(RID p_instance, const StringName &) const = 0; virtual void instance_geometry_get_shader_parameter_list(RID p_instance, List *p_parameters) const = 0; + virtual void instance_lightmap_update_geometries_captures(RID p_lightmap) = 0; + /* BAKE API */ virtual TypedArray bake_render_uv2(RID p_base, const TypedArray &p_material_overrides, const Size2i &p_image_size) = 0; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index e91b58e7486c..de09f754ec59 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -540,6 +540,7 @@ class RenderingServerDefault : public RenderingServer { FUNC2(lightmap_set_probe_interior, RID, bool) FUNC5(lightmap_set_probe_capture_data, RID, const PackedVector3Array &, const PackedColorArray &, const PackedInt32Array &, const PackedInt32Array &) FUNC2(lightmap_set_baked_exposure_normalization, RID, float) + FUNC2(lightmap_set_modulate, RID, const Color &) FUNC1RC(PackedVector3Array, lightmap_get_probe_capture_points, RID) FUNC1RC(PackedColorArray, lightmap_get_probe_capture_sh, RID) FUNC1RC(PackedInt32Array, lightmap_get_probe_capture_tetrahedra, RID) @@ -979,6 +980,8 @@ class RenderingServerDefault : public RenderingServer { FUNC2RC(Variant, instance_geometry_get_shader_parameter_default_value, RID, const StringName &) FUNC2C(instance_geometry_get_shader_parameter_list, RID, List *) + FUNC1(instance_lightmap_update_geometries_captures, RID) + FUNC3R(TypedArray, bake_render_uv2, RID, const TypedArray &, const Size2i &) FUNC4R(PackedByteArray, bake_render_area_light_atlas, const TypedArray &, const TypedArray &, const Size2i &, int) diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h index fe2f4db53a78..93917e0ed50d 100644 --- a/servers/rendering/storage/light_storage.h +++ b/servers/rendering/storage/light_storage.h @@ -176,6 +176,7 @@ class RendererLightStorage { virtual void lightmap_set_probe_interior(RID p_lightmap, bool p_interior) = 0; virtual void lightmap_set_probe_capture_data(RID p_lightmap, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) = 0; virtual void lightmap_set_baked_exposure_normalization(RID p_lightmap, float p_exposure) = 0; + virtual void lightmap_set_modulate(RID p_lightmap, const Color &p_color) = 0; virtual PackedVector3Array lightmap_get_probe_capture_points(RID p_lightmap) const = 0; virtual PackedColorArray lightmap_get_probe_capture_sh(RID p_lightmap) const = 0; virtual PackedInt32Array lightmap_get_probe_capture_tetrahedra(RID p_lightmap) const = 0; @@ -186,6 +187,9 @@ class RendererLightStorage { virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0; virtual float lightmap_get_probe_capture_update_speed() const = 0; + virtual void lightmap_insert_to_lightmap_instances(RID p_lightmap, RID p_instance) = 0; + virtual void lightmap_erase_from_lightmap_instances(RID p_lightmap, RID p_instance) = 0; + virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) = 0; virtual RSE::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) = 0; virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RSE::ShadowmaskMode p_mode) = 0;