From 189c8eb67107b90f5635d825d9c6fcb2ae5c69c3 Mon Sep 17 00:00:00 2001 From: BlueCube3310 <53150244+BlueCube3310@users.noreply.github.com> Date: Thu, 30 Nov 2023 17:14:45 +0100 Subject: [PATCH] Implement shadowmasks for LightmapGI Co-authored-by: dearthdev --- doc/classes/LightmapGI.xml | 5 + doc/classes/LightmapGIData.xml | 14 + drivers/gles3/rasterizer_scene_gles3.cpp | 58 ++- drivers/gles3/shaders/scene.glsl | 223 ++++++---- drivers/gles3/storage/light_storage.cpp | 27 ++ drivers/gles3/storage/light_storage.h | 8 +- modules/lightmapper_rd/lightmapper_rd.cpp | 173 ++++++-- modules/lightmapper_rd/lightmapper_rd.h | 23 +- modules/lightmapper_rd/lm_common_inc.glsl | 3 + modules/lightmapper_rd/lm_compute.glsl | 31 +- scene/3d/lightmap_gi.cpp | 132 +++++- scene/3d/lightmap_gi.h | 27 +- scene/3d/lightmapper.h | 11 +- .../rendering/dummy/storage/light_storage.h | 4 + .../render_forward_clustered.cpp | 33 +- .../render_forward_clustered.h | 2 +- .../forward_mobile/render_forward_mobile.cpp | 31 +- .../forward_mobile/render_forward_mobile.h | 2 +- .../scene_forward_clustered.glsl | 404 ++++++++++-------- .../scene_forward_clustered_inc.glsl | 9 +- .../forward_mobile/scene_forward_mobile.glsl | 218 ++++++---- .../scene_forward_mobile_inc.glsl | 9 +- .../renderer_rd/storage_rd/light_storage.cpp | 68 ++- .../renderer_rd/storage_rd/light_storage.h | 14 + servers/rendering/rendering_server_default.h | 4 + servers/rendering/storage/light_storage.h | 4 + servers/rendering_server.h | 12 +- 27 files changed, 1098 insertions(+), 451 deletions(-) diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index 837da1794fd..a2491dac473 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -69,6 +69,11 @@ The quality preset to use when baking lightmaps. This affects bake times, but output file sizes remain mostly identical across quality levels. To further speed up bake times, decrease [member bounces], disable [member use_denoiser] and increase the lightmap texel size on 3D scenes in the Import dock. + + The shadowmasking policy to use for directional shadows on static objects that are baked with this [LightmapGI] instance. + Shadowmasking allows [DirectionalLight3D] nodes to cast shadows even outside the range defined by their [member DirectionalLight3D.directional_shadow_max_distance] property. This is done by baking a texture that contains a shadowmap for the directional light, then using this texture according to the current shadowmask mode. + [b]Note:[/b] The shadowmask texture is only created if [member shadowmask_mode] is not [constant LightmapGIData.SHADOWMASK_MODE_NONE]. To see a difference, you need to bake lightmaps again after switching from [constant LightmapGIData.SHADOWMASK_MODE_NONE] to any other mode. + Scales the lightmap texel density of all meshes for the current bake. This is a multiplier that builds upon the existing lightmap texel size defined in each imported 3D scene, along with the per-mesh density multiplier (which is designed to be used when the same mesh is used at different scales). Lower values will result in faster bake times. For example, doubling [member texel_scale] doubles the lightmap texture resolution for all objects [i]on each axis[/i], so it will [i]quadruple[/i] the texel count. diff --git a/doc/classes/LightmapGIData.xml b/doc/classes/LightmapGIData.xml index 76824f84a08..e7454f4ea81 100644 --- a/doc/classes/LightmapGIData.xml +++ b/doc/classes/LightmapGIData.xml @@ -60,5 +60,19 @@ The lightmap atlas textures generated by the lightmapper. + + The shadowmask atlas textures generated by the lightmapper. + + + + Shadowmasking is disabled. No shadowmask texture will be created when baking lightmaps. Existing shadowmask textures will be removed during baking. + + + Shadowmasking is enabled. Directional shadows that are outside the [member DirectionalLight3D.directional_shadow_max_distance] will be rendered using the shadowmask texture. Shadows that are inside the range will be rendered using real-time shadows exclusively. This mode allows for more precise real-time shadows up close, without the potential "smearing" effect that can occur when using lightmaps with a high texel size. The downside is that when the camera moves fast, the transition between the real-time light and shadowmask can be obvious. Also, objects that only have shadows baked in the shadowmask (and no real-time shadows) won't display any shadows up close. + + + Shadowmasking is enabled. Directional shadows will be rendered with real-time shadows overlaid on top of the shadowmask texture. This mode makes for smoother shadow transitions when the camera moves fast, at the cost of a potential smearing effect for directional shadows that are up close (due to the real-time shadow being mixed with a low-resolution shadowmask). Objects that only have shadows baked in the shadowmask (and no real-time shadows) will keep their shadows up close. + + diff --git a/drivers/gles3/rasterizer_scene_gles3.cpp b/drivers/gles3/rasterizer_scene_gles3.cpp index cd4c7dd82fb..907e57823b5 100644 --- a/drivers/gles3/rasterizer_scene_gles3.cpp +++ b/drivers/gles3/rasterizer_scene_gles3.cpp @@ -2660,14 +2660,14 @@ void RasterizerSceneGLES3::render_scene(const Ref &p_render_ glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); glBindTexture(GL_TEXTURE_2D, backbuffer); } if (scene_state.used_depth_texture) { glBlitFramebuffer(0, 0, size.x, size.y, 0, 0, size.x, size.y, GL_DEPTH_BUFFER_BIT, GL_NEAREST); - glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7); glBindTexture(GL_TEXTURE_2D, backbuffer_depth); } } @@ -3245,8 +3245,28 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; - spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE; + + bool disable_lightmaps = true; + + // Additive directional passes may use shadowmasks, so enable lightmaps for them. + if (pass >= int32_t(inst->light_passes.size()) && inst->lightmap_instance.is_valid()) { + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + if (lm->shadowmask_mode != RS::SHADOWMASK_MODE_NONE) { + spec_constants |= SceneShaderGLES3::USE_LIGHTMAP; + disable_lightmaps = false; + + if (lightmap_bicubic_upscale) { + spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER; + } + } + } + + if (disable_lightmaps) { + spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP; + } } if (uses_additive_lighting) { @@ -3341,6 +3361,33 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, GLuint tex = GLES3::LightStorage::get_singleton()->directional_shadow_get_texture(); glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 3); glBindTexture(GL_TEXTURE_2D, tex); + + if (inst->lightmap_instance.is_valid()) { + // Use shadowmasks for directional light passes. + GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance); + GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap); + + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SLICE, inst->lightmap_slice_index, shader->version, instance_variant, spec_constants); + + 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); + } + + material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SHADOWMASK_MODE, (uint32_t)lm->shadowmask_mode, shader->version, instance_variant, spec_constants); + + if (lm->shadow_texture.is_valid()) { + tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lm->shadow_texture); + } else { + tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(GLES3::TextureStorage::get_singleton()->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE)); + } + + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + } } } @@ -3399,6 +3446,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, }; glUniformMatrix3fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_NORMAL_XFORM, shader->version, instance_variant, spec_constants), 1, GL_FALSE, matrix); } + } else if (inst->lightmap_sh) { glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast(inst->lightmap_sh->sh)); } @@ -3430,7 +3478,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, 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); + 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[0])); } @@ -3448,7 +3496,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params, 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); + glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 9); glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[1])); spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE; diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index fd195fe7c43..f8b495b7fb3 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -809,10 +809,11 @@ void main() { 2-radiance 3-shadow 4-lightmap textures -5-screen -6-depth -7-reflection probe 1 -8-reflection probe 2 +5-shadowmask textures +6-screen +7-depth +8-reflection probe 1 +9-reflection probe 2 */ @@ -887,7 +888,7 @@ uniform float refprobe1_intensity; uniform int refprobe1_ambient_mode; uniform vec4 refprobe1_ambient_color; -uniform samplerCube refprobe1_texture; // texunit:-7 +uniform samplerCube refprobe1_texture; // texunit:-8 #ifdef SECOND_REFLECTION_PROBE @@ -900,7 +901,7 @@ uniform float refprobe2_intensity; uniform int refprobe2_ambient_mode; uniform vec4 refprobe2_ambient_color; -uniform samplerCube refprobe2_texture; // texunit:-8 +uniform samplerCube refprobe2_texture; // texunit:-9 #endif // SECOND_REFLECTION_PROBE @@ -1170,9 +1171,16 @@ float sample_shadow(highp sampler2DShadow shadow, float shadow_pixel_size, vec4 #ifndef DISABLE_LIGHTMAP #ifdef USE_LIGHTMAP uniform mediump sampler2DArray lightmap_textures; //texunit:-4 +uniform lowp sampler2DArray shadowmask_textures; //texunit:-5 uniform lowp uint lightmap_slice; uniform highp vec4 lightmap_uv_scale; uniform float lightmap_exposure_normalization; +uniform uint lightmap_shadowmask_mode; + +#define SHADOWMASK_MODE_NONE uint(0) +#define SHADOWMASK_MODE_REPLACE uint(1) +#define SHADOWMASK_MODE_OVERLAY uint(2) +#define SHADOWMASK_MODE_ONLY uint(3) #ifdef LIGHTMAP_BICUBIC_FILTER uniform highp vec2 lightmap_texture_size; @@ -1189,8 +1197,8 @@ uniform mediump vec4[9] lightmap_captures; #endif // !DISABLE_LIGHTMAP #ifdef USE_MULTIVIEW -uniform highp sampler2DArray depth_buffer; // texunit:-6 -uniform highp sampler2DArray color_buffer; // texunit:-5 +uniform highp sampler2DArray depth_buffer; // texunit:-7 +uniform highp sampler2DArray color_buffer; // texunit:-6 vec3 multiview_uv(vec2 uv) { return vec3(uv, ViewIndex); } @@ -1198,8 +1206,8 @@ ivec3 multiview_uv(ivec2 uv) { return ivec3(uv, int(ViewIndex)); } #else -uniform highp sampler2D depth_buffer; // texunit:-6 -uniform highp sampler2D color_buffer; // texunit:-5 +uniform highp sampler2D depth_buffer; // texunit:-7 +uniform highp sampler2D color_buffer; // texunit:-6 vec2 multiview_uv(vec2 uv) { return uv; } @@ -2278,111 +2286,146 @@ void main() { #if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) #ifndef SHADOWS_DISABLED +// Baked shadowmasks +#ifdef USE_LIGHTMAP + float shadowmask = 1.0f; + + if (lightmap_shadowmask_mode != SHADOWMASK_MODE_NONE) { + vec3 uvw; + uvw.xy = uv2 * lightmap_uv_scale.zw + lightmap_uv_scale.xy; + uvw.z = float(lightmap_slice); + +#ifdef LIGHTMAP_BICUBIC_FILTER + shadowmask = textureArray_bicubic(shadowmask_textures, uvw, lightmap_texture_size).x; +#else + shadowmask = textureLod(shadowmask_textures, uvw, 0.0).x; +#endif + } +#endif //USE_LIGHTMAP + + float directional_shadow = 1.0; + +#ifdef USE_LIGHTMAP + if (lightmap_shadowmask_mode != SHADOWMASK_MODE_ONLY) { +#endif // Orthogonal shadows #if !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4) - float directional_shadow = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); + directional_shadow = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); #endif // !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4) // PSSM2 shadows #ifdef LIGHT_USE_PSSM2 - float depth_z = -vertex.z; - vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets; - //take advantage of prefetch - float shadow1 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); - float shadow2 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord2); - float directional_shadow = 1.0; + float depth_z = -vertex.z; + vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets; + //take advantage of prefetch + float shadow1 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); + float shadow2 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord2); - if (depth_z < light_split_offsets.y) { - -#ifdef LIGHT_USE_PSSM_BLEND - float directional_shadow2 = 1.0; - float pssm_blend = 0.0; - bool use_blend = true; -#endif - if (depth_z < light_split_offsets.x) { - directional_shadow = shadow1; - -#ifdef LIGHT_USE_PSSM_BLEND - directional_shadow2 = shadow2; - pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); -#endif - } else { - directional_shadow = shadow2; -#ifdef LIGHT_USE_PSSM_BLEND - use_blend = false; -#endif - } -#ifdef LIGHT_USE_PSSM_BLEND - if (use_blend) { - directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend); - } -#endif - } - -#endif //LIGHT_USE_PSSM2 -// PSSM4 shadows -#ifdef LIGHT_USE_PSSM4 - float depth_z = -vertex.z; - vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets; - - float shadow1 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); - float shadow2 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord2); - float shadow3 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord3); - float shadow4 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord4); - float directional_shadow = 1.0; - - if (depth_z < light_split_offsets.w) { - -#ifdef LIGHT_USE_PSSM_BLEND - float directional_shadow2 = 1.0; - float pssm_blend = 0.0; - bool use_blend = true; -#endif if (depth_z < light_split_offsets.y) { + +#ifdef LIGHT_USE_PSSM_BLEND + float directional_shadow2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif if (depth_z < light_split_offsets.x) { directional_shadow = shadow1; #ifdef LIGHT_USE_PSSM_BLEND directional_shadow2 = shadow2; - pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); #endif } else { directional_shadow = shadow2; - #ifdef LIGHT_USE_PSSM_BLEND - directional_shadow2 = shadow3; - - pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); -#endif - } - } else { - if (depth_z < light_split_offsets.z) { - directional_shadow = shadow3; - -#if defined(LIGHT_USE_PSSM_BLEND) - directional_shadow2 = shadow4; - pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); -#endif - - } else { - directional_shadow = shadow4; - -#if defined(LIGHT_USE_PSSM_BLEND) use_blend = false; #endif } - } -#if defined(LIGHT_USE_PSSM_BLEND) - if (use_blend) { - directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend); - } +#ifdef LIGHT_USE_PSSM_BLEND + if (use_blend) { + directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend); + } #endif - } + } + +#endif //LIGHT_USE_PSSM2 +// PSSM4 shadows +#ifdef LIGHT_USE_PSSM4 + float depth_z = -vertex.z; + vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets; + + float shadow1 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); + float shadow2 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord2); + float shadow3 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord3); + float shadow4 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord4); + + if (depth_z < light_split_offsets.w) { +#ifdef LIGHT_USE_PSSM_BLEND + float directional_shadow2 = 1.0; + float pssm_blend = 0.0; + bool use_blend = true; +#endif + if (depth_z < light_split_offsets.y) { + if (depth_z < light_split_offsets.x) { + directional_shadow = shadow1; + +#ifdef LIGHT_USE_PSSM_BLEND + directional_shadow2 = shadow2; + + pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); +#endif + } else { + directional_shadow = shadow2; + +#ifdef LIGHT_USE_PSSM_BLEND + directional_shadow2 = shadow3; + + pssm_blend = smoothstep(light_split_offsets.x, light_split_offsets.y, depth_z); +#endif + } + } else { + if (depth_z < light_split_offsets.z) { + directional_shadow = shadow3; + +#if defined(LIGHT_USE_PSSM_BLEND) + directional_shadow2 = shadow4; + pssm_blend = smoothstep(light_split_offsets.y, light_split_offsets.z, depth_z); +#endif + + } else { + directional_shadow = shadow4; + +#if defined(LIGHT_USE_PSSM_BLEND) + use_blend = false; +#endif + } + } +#if defined(LIGHT_USE_PSSM_BLEND) + if (use_blend) { + directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend); + } +#endif + } #endif //LIGHT_USE_PSSM4 - directional_shadow = mix(directional_shadow, 1.0, smoothstep(directional_shadows[directional_shadow_index].fade_from, directional_shadows[directional_shadow_index].fade_to, vertex.z)); + +#ifdef USE_LIGHTMAP + if (lightmap_shadowmask_mode == SHADOWMASK_MODE_REPLACE) { + directional_shadow = mix(directional_shadow, shadowmask, smoothstep(directional_shadows[directional_shadow_index].fade_from, directional_shadows[directional_shadow_index].fade_to, vertex.z)); + } else if (lightmap_shadowmask_mode == SHADOWMASK_MODE_OVERLAY) { + directional_shadow = shadowmask * mix(directional_shadow, 1.0, smoothstep(directional_shadows[directional_shadow_index].fade_from, directional_shadows[directional_shadow_index].fade_to, vertex.z)); + } else { +#endif + directional_shadow = mix(directional_shadow, 1.0, smoothstep(directional_shadows[directional_shadow_index].fade_from, directional_shadows[directional_shadow_index].fade_to, vertex.z)); +#ifdef USE_LIGHTMAP + } + + } else { // lightmap_shadowmask_mode == SHADOWMASK_MODE_ONLY + directional_shadow = shadowmask; + } +#endif + directional_shadow = mix(1.0, directional_shadow, directional_lights[directional_shadow_index].shadow_opacity); #else diff --git a/drivers/gles3/storage/light_storage.cpp b/drivers/gles3/storage/light_storage.cpp index 886918a2f76..4ec37fe4ab2 100644 --- a/drivers/gles3/storage/light_storage.cpp +++ b/drivers/gles3/storage/light_storage.cpp @@ -1204,6 +1204,33 @@ float LightStorage::lightmap_get_probe_capture_update_speed() const { return lightmap_probe_capture_update_speed; } +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); + lightmap->shadow_texture = p_shadow; + + GLuint tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lightmap->shadow_texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); +} + +RS::ShadowmaskMode LightStorage::lightmap_get_shadowmask_mode(RID p_lightmap) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lightmap, RS::SHADOWMASK_MODE_NONE); + + return lightmap->shadowmask_mode; +} + +void LightStorage::lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) { + Lightmap *lightmap = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lightmap); + lightmap->shadowmask_mode = p_mode; +} + /* LIGHTMAP INSTANCE */ RID LightStorage::lightmap_instance_create(RID p_lightmap) { diff --git a/drivers/gles3/storage/light_storage.h b/drivers/gles3/storage/light_storage.h index 0695102640e..0bf410fac60 100644 --- a/drivers/gles3/storage/light_storage.h +++ b/drivers/gles3/storage/light_storage.h @@ -177,12 +177,14 @@ struct ReflectionProbeInstance { struct Lightmap { RID light_texture; + RID shadow_texture; bool uses_spherical_harmonics = false; 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 + RS::ShadowmaskMode shadowmask_mode = RS::SHADOWMASK_MODE_NONE; PackedVector3Array points; PackedColorArray point_sh; PackedInt32Array tetrahedra; @@ -231,8 +233,6 @@ private: mutable RID_Owner reflection_probe_instance_owner; /* LIGHTMAP */ - - Vector lightmap_textures; float lightmap_probe_capture_update_speed = 4; mutable RID_Owner lightmap_owner; @@ -737,6 +737,10 @@ public: 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_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override; + virtual RS::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override; + virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) override; + /* LIGHTMAP INSTANCE */ LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); } diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 9cfeff4f69e..a6074012de7 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -62,7 +62,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) { mesh_instances.push_back(mi); } -void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) { +void LightmapperRD::add_directional_light(const String &p_name, bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) { Light l; l.type = LIGHT_TYPE_DIRECTIONAL; l.direction[0] = p_direction.x; @@ -77,9 +77,10 @@ void LightmapperRD::add_directional_light(bool p_static, const Vector3 &p_direct l.size = Math::tan(Math::deg_to_rad(p_angular_distance)); l.shadow_blur = p_shadow_blur; lights.push_back(l); + light_names.push_back(p_name); } -void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) { +void LightmapperRD::add_omni_light(const String &p_name, bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) { Light l; l.type = LIGHT_TYPE_OMNI; l.position[0] = p_position.x; @@ -96,9 +97,10 @@ void LightmapperRD::add_omni_light(bool p_static, const Vector3 &p_position, con l.size = p_size; l.shadow_blur = p_shadow_blur; lights.push_back(l); + light_names.push_back(p_name); } -void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) { +void LightmapperRD::add_spot_light(const String &p_name, bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) { Light l; l.type = LIGHT_TYPE_SPOT; l.position[0] = p_position.x; @@ -120,6 +122,7 @@ void LightmapperRD::add_spot_light(bool p_static, const Vector3 &p_position, con l.size = p_size; l.shadow_blur = p_shadow_blur; lights.push_back(l); + light_names.push_back(p_name); } void LightmapperRD::add_probe(const Vector3 &p_position) { @@ -826,9 +829,9 @@ LightmapperRD::BakeError LightmapperRD::_pack_l1(RenderingDevice *rd, Ref data = p_rd->texture_get_data(p_atlas_tex, p_index); - Ref img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, Image::FORMAT_RGBAH, data); + Ref img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, p_shadowmask ? Image::FORMAT_RGBA8 : Image::FORMAT_RGBAH, data); img->convert(Image::FORMAT_RGBF); Vector data_float = img->get_data(); @@ -848,7 +851,7 @@ Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_in return OK; } -Ref LightmapperRD::_read_pfm(const String &p_name) { +Ref LightmapperRD::_read_pfm(const String &p_name, bool p_shadowmask) { Error err = OK; Ref file = FileAccess::open(p_name, FileAccess::READ, &err); ERR_FAIL_COND_V_MSG(err, Ref(), vformat("Can't load PFM at path: '%s'.", p_name)); @@ -881,23 +884,23 @@ Ref LightmapperRD::_read_pfm(const String &p_name) { } #endif Ref img = Image::create_from_data(new_width, new_height, false, Image::FORMAT_RGBF, new_data); - img->convert(Image::FORMAT_RGBAH); + img->convert(p_shadowmask ? Image::FORMAT_RGBA8 : Image::FORMAT_RGBAH); return img; } -LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, const String &p_exe) { +LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, bool p_shadowmask, const String &p_exe) { Ref da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); for (int i = 0; i < p_atlas_slices; i++) { String fname_norm_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_norm_%d.pfm", i)); - _store_pfm(p_rd, p_source_normal_tex, i, p_atlas_size, fname_norm_in); + _store_pfm(p_rd, p_source_normal_tex, i, p_atlas_size, fname_norm_in, false); for (int j = 0; j < (p_bake_sh ? 4 : 1); j++) { int index = i * (p_bake_sh ? 4 : 1) + j; String fname_light_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_light_%d.pfm", index)); String fname_out = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_denoised_%d.pfm", index)); - _store_pfm(p_rd, p_source_light_tex, index, p_atlas_size, fname_light_in); + _store_pfm(p_rd, p_source_light_tex, index, p_atlas_size, fname_light_in, p_shadowmask); List args; args.push_back("--device"); @@ -906,7 +909,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID args.push_back("--filter"); args.push_back("RTLightmap"); - args.push_back("--hdr"); + args.push_back(p_shadowmask ? "--ldr" : "--hdr"); args.push_back(fname_light_in); args.push_back("--nrm"); @@ -928,7 +931,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, vformat("OIDN denoiser failed, return code: %d", exitcode)); } - Ref img = _read_pfm(fname_out); + Ref img = _read_pfm(fname_out, p_shadowmask); da->remove(fname_out); ERR_FAIL_COND_V(img.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); @@ -1029,7 +1032,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) { +LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_bake_shadowmask, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function, void *p_bake_userdata, float p_exposure_normalization) { int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser"); String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path"); @@ -1050,7 +1053,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d if (p_step_function) { p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true); } - bake_textures.clear(); + lightmap_textures.clear(); + shadowmask_textures.clear(); int grid_size = 128; /* STEP 1: Fetch material textures and compute the bounds */ @@ -1066,6 +1070,35 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d return bake_error; } + // The index of the directional light used for shadowmasking. + int shadowmask_light_idx = -1; + + // If there are no valid directional lights for shadowmasking, the entire + // scene would be shadowed and this saves baking time. + if (p_bake_shadowmask) { + int shadowmask_lights_count = 0; + + for (int i = 0; i < lights.size(); i++) { + if (lights[i].type == LightType::LIGHT_TYPE_DIRECTIONAL && !lights[i].static_bake) { + if (shadowmask_light_idx < 0) { + shadowmask_light_idx = i; + } + + shadowmask_lights_count += 1; + } + } + + if (shadowmask_light_idx < 0) { + p_bake_shadowmask = false; + WARN_PRINT("Shadowmask disabled: no directional light with their bake mode set to dynamic exists."); + + } else if (shadowmask_lights_count > 1) { + WARN_PRINT( + vformat("%d directional lights detected for shadowmask baking. Only %s will be used.", + shadowmask_lights_count, light_names[shadowmask_light_idx])); + } + } + #ifdef DEBUG_TEXTURES for (int i = 0; i < atlas_slices; i++) { albedo_images[i]->save_png("res://0_albedo_" + itos(i) + ".png"); @@ -1119,17 +1152,23 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d RID light_accum_tex; RID light_accum_tex2; RID light_environment_tex; + RID shadowmask_tex; + RID shadowmask_tex2; -#define FREE_TEXTURES \ - rd->free(albedo_array_tex); \ - rd->free(emission_array_tex); \ - rd->free(normal_tex); \ - rd->free(position_tex); \ - rd->free(unocclude_tex); \ - rd->free(light_source_tex); \ - rd->free(light_accum_tex2); \ - rd->free(light_accum_tex); \ - rd->free(light_environment_tex); +#define FREE_TEXTURES \ + rd->free(albedo_array_tex); \ + rd->free(emission_array_tex); \ + rd->free(normal_tex); \ + rd->free(position_tex); \ + rd->free(unocclude_tex); \ + rd->free(light_source_tex); \ + rd->free(light_accum_tex2); \ + rd->free(light_accum_tex); \ + rd->free(light_environment_tex); \ + if (p_bake_shadowmask) { \ + rd->free(shadowmask_tex); \ + rd->free(shadowmask_tex2); \ + } { // create all textures @@ -1161,9 +1200,22 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d position_tex = rd->texture_create(tf, RD::TextureView()); unocclude_tex = rd->texture_create(tf, RD::TextureView()); - tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT; + // shadowmask + if (p_bake_shadowmask) { + tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + + shadowmask_tex = rd->texture_create(tf, RD::TextureView()); + rd->texture_clear(shadowmask_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices); + + shadowmask_tex2 = rd->texture_create(tf, RD::TextureView()); + rd->texture_clear(shadowmask_tex2, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices); + } + + // lightmap + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + light_source_tex = rd->texture_create(tf, RD::TextureView()); rd->texture_clear(light_source_tex, Color(0, 0, 0, 0), 0, 1, 0, atlas_slices); @@ -1266,6 +1318,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d bake_parameters.exposure_normalization = p_exposure_normalization; bake_parameters.bounces = p_bounces; bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy; + bake_parameters.shadowmask_light_idx = shadowmask_light_idx; bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters)); rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters); @@ -1463,6 +1516,10 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d defines += "\n#define USE_LIGHT_TEXTURE_FOR_BOUNCES\n"; } + if (p_bake_shadowmask) { + defines += "\n#define USE_SHADOWMASK\n"; + } + compute_shader.instantiate(); err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, defines); if (err != OK) { @@ -1634,6 +1691,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d u.append_id(light_accum_tex); uniforms.push_back(u); } + + if (p_bake_shadowmask) { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 5; + u.append_id(shadowmask_tex); + uniforms.push_back(u); + } } RID light_uniform_set = rd->uniform_set_create(uniforms, compute_shader_primary, 1); @@ -1945,7 +2010,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d BakeError error; if (denoiser == 1) { // OIDN (external). - error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh, oidn_path); + error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh, false, oidn_path); } else { // JNLM (built-in). SWAP(light_accum_tex, light_accum_tex2); @@ -1955,14 +2020,39 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d return error; } } + + if (p_bake_shadowmask) { + BakeError error; + if (denoiser == 1) { + // OIDN (external). + error = _denoise_oidn(rd, shadowmask_tex, normal_tex, shadowmask_tex, atlas_size, atlas_slices, false, true, oidn_path); + } else { + // JNLM (built-in). + SWAP(shadowmask_tex, shadowmask_tex2); + error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, normal_tex, shadowmask_tex, p_denoiser_strength, p_denoiser_range, atlas_size, atlas_slices, false, p_step_function, p_bake_userdata); + } + if (unlikely(error != BAKE_OK)) { + return error; + } + } } + /* DILATE */ + { SWAP(light_accum_tex, light_accum_tex2); BakeError error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, light_accum_tex, atlas_size, atlas_slices * (p_bake_sh ? 4 : 1)); if (unlikely(error != BAKE_OK)) { return error; } + + if (p_bake_shadowmask) { + SWAP(shadowmask_tex, shadowmask_tex2); + error = _dilate(rd, compute_shader, compute_base_uniform_set, push_constant, shadowmask_tex2, shadowmask_tex, atlas_size, atlas_slices); + if (unlikely(error != BAKE_OK)) { + return error; + } + } } #ifdef DEBUG_TEXTURES @@ -2139,6 +2229,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d img->save_exr("res://5_blendseams" + itos(i) + ".exr", false); } #endif + if (p_step_function) { p_step_function(0.9, RTR("Retrieving textures"), p_bake_userdata, true); } @@ -2147,7 +2238,16 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d Vector s = rd->texture_get_data(light_accum_tex, i); Ref img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); img->convert(Image::FORMAT_RGBH); //remove alpha - bake_textures.push_back(img); + lightmap_textures.push_back(img); + } + + if (p_bake_shadowmask) { + for (int i = 0; i < atlas_slices; i++) { + Vector s = rd->texture_get_data(shadowmask_tex, i); + Ref img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBA8, s); + img->convert(Image::FORMAT_R8); + shadowmask_textures.push_back(img); + } } if (probe_positions.size() > 0) { @@ -2180,12 +2280,21 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d } int LightmapperRD::get_bake_texture_count() const { - return bake_textures.size(); + return lightmap_textures.size(); } Ref LightmapperRD::get_bake_texture(int p_index) const { - ERR_FAIL_INDEX_V(p_index, bake_textures.size(), Ref()); - return bake_textures[p_index]; + ERR_FAIL_INDEX_V(p_index, lightmap_textures.size(), Ref()); + return lightmap_textures[p_index]; +} + +int LightmapperRD::get_shadowmask_texture_count() const { + return shadowmask_textures.size(); +} + +Ref LightmapperRD::get_shadowmask_texture(int p_index) const { + ERR_FAIL_INDEX_V(p_index, shadowmask_textures.size(), Ref()); + return shadowmask_textures[p_index]; } int LightmapperRD::get_bake_mesh_count() const { @@ -2198,9 +2307,9 @@ Variant LightmapperRD::get_bake_mesh_userdata(int p_index) const { } Rect2 LightmapperRD::get_bake_mesh_uv_scale(int p_index) const { - ERR_FAIL_COND_V(bake_textures.is_empty(), Rect2()); + ERR_FAIL_COND_V(lightmap_textures.is_empty(), Rect2()); Rect2 uv_ofs; - Vector2 atlas_size = Vector2(bake_textures[0]->get_width(), bake_textures[0]->get_height()); + Vector2 atlas_size = Vector2(lightmap_textures[0]->get_width(), lightmap_textures[0]->get_height()); uv_ofs.position = Vector2(mesh_instances[p_index].offset) / atlas_size; uv_ofs.size = Vector2(mesh_instances[p_index].data.albedo_on_uv2->get_width(), mesh_instances[p_index].data.albedo_on_uv2->get_height()) / atlas_size; return uv_ofs; diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index f43da396709..7e3efa71cca 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -57,7 +57,8 @@ class LightmapperRD : public Lightmapper { uint32_t bounces = 0; float bounce_indirect_energy = 0.0f; - uint32_t pad[3] = {}; + int shadowmask_light_idx = 0; + uint32_t pad[2] = {}; }; struct MeshInstance { @@ -202,6 +203,7 @@ class LightmapperRD : public Lightmapper { Vector mesh_instances; Vector lights; + Vector light_names; struct TriangleSort { uint32_t cell_index = 0; @@ -253,7 +255,8 @@ class LightmapperRD : public Lightmapper { uint32_t pad = 0; }; - Vector> bake_textures; + Vector> lightmap_textures; + Vector> shadowmask_textures; Vector probe_values; struct DenoiseParams { @@ -275,20 +278,22 @@ class LightmapperRD : public Lightmapper { BakeError _denoise(RenderingDevice *p_rd, Ref &p_compute_shader, const RID &p_compute_base_uniform_set, PushConstant &p_push_constant, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, float p_denoiser_strength, int p_denoiser_range, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function, void *p_bake_userdata); BakeError _pack_l1(RenderingDevice *rd, Ref &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices); - Error _store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name); - Ref _read_pfm(const String &p_name); - BakeError _denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, const String &p_exe); + Error _store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name, bool p_shadowmask); + Ref _read_pfm(const String &p_name, bool p_shadowmask); + BakeError _denoise_oidn(RenderingDevice *p_rd, RID p_source_light_tex, RID p_source_normal_tex, RID p_dest_light_tex, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, bool p_shadowmask, const String &p_exe); public: virtual void add_mesh(const MeshData &p_mesh) override; - virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) override; - virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override; - virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override; + virtual void add_directional_light(const String &p_name, bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) override; + virtual void add_omni_light(const String &p_name, bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override; + virtual void add_spot_light(const String &p_name, bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override; virtual void add_probe(const Vector3 &p_position) override; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_bake_shadowmask, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_bake_userdata = nullptr, float p_exposure_normalization = 1.0) override; int get_bake_texture_count() const override; Ref get_bake_texture(int p_index) const override; + int get_shadowmask_texture_count() const override; + Ref get_shadowmask_texture(int p_index) const override; int get_bake_mesh_count() const override; Variant get_bake_mesh_userdata(int p_index) const override; Rect2 get_bake_mesh_uv_scale(int p_index) const override; diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl index 962e4449115..954440dfe5f 100644 --- a/modules/lightmapper_rd/lm_common_inc.glsl +++ b/modules/lightmapper_rd/lm_common_inc.glsl @@ -17,6 +17,9 @@ layout(set = 0, binding = 0) uniform BakeParameters { uint bounces; float bounce_indirect_energy; + int shadowmask_light_idx; + uint pad0; + uint pad1; } bake_params; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 31b721bb20c..fd18d879c9b 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -60,7 +60,9 @@ layout(rgba16f, set = 1, binding = 4) uniform restrict image2DArray accum_light; #endif -#ifdef MODE_BOUNCE_LIGHT +#if defined(MODE_DIRECT_LIGHT) && defined(USE_SHADOWMASK) +layout(rgba8, set = 1, binding = 5) uniform restrict writeonly image2DArray shadowmask; +#elif defined(MODE_BOUNCE_LIGHT) layout(set = 1, binding = 5) uniform texture2D environment; #endif @@ -389,8 +391,9 @@ vec2 get_vogel_disk(float p_i, float p_rotation, float p_sample_count_sqrt) { return vec2(cos(theta), sin(theta)) * r; } -void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool p_soft_shadowing, out vec3 r_light, out vec3 r_light_dir, inout uint r_noise, float p_texel_size) { +void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool p_soft_shadowing, out vec3 r_light, out vec3 r_light_dir, inout uint r_noise, float p_texel_size, out float r_shadow) { r_light = vec3(0.0f); + r_shadow = 0.0f; vec3 light_pos; float dist; @@ -507,6 +510,7 @@ void trace_direct_light(vec3 p_position, vec3 p_normal, uint p_light_index, bool } } + r_shadow = penumbra; r_light = light_data.color * light_data.energy * attenuation * penumbra; } @@ -556,7 +560,8 @@ vec3 trace_indirect_light(vec3 p_position, vec3 p_ray_dir, inout uint r_noise, f for (uint i = 0; i < bake_params.light_count; i++) { vec3 light; vec3 light_dir; - trace_direct_light(position, normal, i, false, light, light_dir, r_noise, p_texel_size); + float shadow; + trace_direct_light(position, normal, i, false, light, light_dir, r_noise, p_texel_size, shadow); direct_light += light * lights.data[i].indirect_energy; } @@ -614,7 +619,6 @@ void main() { #endif #ifdef MODE_DIRECT_LIGHT - vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz; if (length(normal) < 0.5) { return; //empty texel, no process @@ -631,6 +635,10 @@ void main() { vec3 light_for_texture = vec3(0.0); vec3 light_for_bounces = vec3(0.0); +#ifdef USE_SHADOWMASK + float shadowmask_value = 0.0f; +#endif + #ifdef USE_SH_LIGHTMAPS vec4 sh_accum[4] = vec4[]( vec4(0.0, 0.0, 0.0, 1.0), @@ -644,7 +652,8 @@ void main() { for (uint i = 0; i < bake_params.light_count; i++) { vec3 light; vec3 light_dir; - trace_direct_light(position, normal, i, true, light, light_dir, noise, texel_size_world_space); + float shadow; + trace_direct_light(position, normal, i, true, light, light_dir, noise, texel_size_world_space, shadow); if (lights.data[i].static_bake) { light_for_texture += light; @@ -669,6 +678,12 @@ void main() { } light_for_bounces += light * lights.data[i].indirect_energy; + +#ifdef USE_SHADOWMASK + if (lights.data[i].type == LIGHT_TYPE_DIRECTIONAL && i == bake_params.shadowmask_light_idx) { + shadowmask_value = max(shadowmask_value, shadow); + } +#endif } light_for_bounces *= bake_params.exposure_normalization; @@ -685,6 +700,10 @@ void main() { imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_for_texture, 1.0)); #endif +#ifdef USE_SHADOWMASK + imageStore(shadowmask, ivec3(atlas_pos, params.atlas_slice), vec4(shadowmask_value, shadowmask_value, shadowmask_value, 1.0)); +#endif + #endif #ifdef MODE_BOUNCE_LIGHT @@ -850,7 +869,7 @@ void main() { #endif -#ifdef MODE_DILATE +#if defined(MODE_DILATE) vec4 c = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0); //sides first, as they are closer diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index aa4445a7ba8..4c3d1a82183 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -130,6 +130,52 @@ TypedArray LightmapGIData::get_lightmap_textures() const { return storage_light_textures; } +void LightmapGIData::set_shadowmask_textures(const TypedArray &p_data) { + storage_shadowmask_textures = p_data; + + if (p_data.is_empty()) { + combined_shadowmask_texture = Ref(); + _reset_shadowmask_textures(); + return; + } + + if (p_data.size() == 1) { + combined_shadowmask_texture = p_data[0]; + + } else { + Vector> images; + for (int i = 0; i < p_data.size(); i++) { + Ref texture = p_data[i]; + ERR_FAIL_COND_MSG(texture.is_null(), vformat("Invalid TextureLayered at index %d.", i)); + for (int j = 0; j < texture->get_layers(); j++) { + images.push_back(texture->get_layer_data(j)); + } + } + + Ref combined_texture; + combined_texture.instantiate(); + + combined_texture->create_from_images(images); + combined_shadowmask_texture = combined_texture; + } + + _reset_shadowmask_textures(); +} + +TypedArray LightmapGIData::get_shadowmask_textures() const { + return storage_shadowmask_textures; +} + +void LightmapGIData::clear_shadowmask_textures() { + RS::get_singleton()->lightmap_set_shadowmask_textures(lightmap, RID()); + storage_shadowmask_textures.clear(); + combined_shadowmask_texture.unref(); +} + +bool LightmapGIData::has_shadowmask_textures() { + return !storage_shadowmask_textures.is_empty() && combined_shadowmask_texture.is_valid(); +} + RID LightmapGIData::get_rid() const { return lightmap; } @@ -142,6 +188,10 @@ void LightmapGIData::_reset_lightmap_textures() { RS::get_singleton()->lightmap_set_textures(lightmap, combined_light_texture.is_valid() ? combined_light_texture->get_rid() : RID(), uses_spherical_harmonics); } +void LightmapGIData::_reset_shadowmask_textures() { + RS::get_singleton()->lightmap_set_shadowmask_textures(lightmap, combined_shadowmask_texture.is_valid() ? combined_shadowmask_texture->get_rid() : RID()); +} + void LightmapGIData::set_uses_spherical_harmonics(bool p_enable) { uses_spherical_harmonics = p_enable; _reset_lightmap_textures(); @@ -159,6 +209,14 @@ bool LightmapGIData::_is_using_packed_directional() const { return _uses_packed_directional; } +void LightmapGIData::update_shadowmask_mode(ShadowmaskMode p_mode) { + RS::get_singleton()->lightmap_set_shadowmask_mode(lightmap, (RS::ShadowmaskMode)p_mode); +} + +LightmapGIData::ShadowmaskMode LightmapGIData::get_shadowmask_mode() const { + return (ShadowmaskMode)RS::get_singleton()->lightmap_get_shadowmask_mode(lightmap); +} + void LightmapGIData::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) { if (p_points.size()) { int pc = p_points.size(); @@ -260,6 +318,9 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("set_lightmap_textures", "light_textures"), &LightmapGIData::set_lightmap_textures); ClassDB::bind_method(D_METHOD("get_lightmap_textures"), &LightmapGIData::get_lightmap_textures); + 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_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); @@ -275,6 +336,7 @@ void LightmapGIData::_bind_methods() { ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data); 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"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "user_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_user_data", "_get_user_data"); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data"); @@ -290,6 +352,10 @@ void LightmapGIData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered", PROPERTY_USAGE_NONE), "set_light_texture", "get_light_texture"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data"); #endif + + BIND_ENUM_CONSTANT(SHADOWMASK_MODE_NONE); + BIND_ENUM_CONSTANT(SHADOWMASK_MODE_REPLACE); + BIND_ENUM_CONSTANT(SHADOWMASK_MODE_OVERLAY); } LightmapGIData::LightmapGIData() { @@ -738,12 +804,12 @@ void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, f } } -LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref p_lightmapper, const String &p_base_name, TypedArray &r_textures, bool p_compress) const { +LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref p_lightmapper, const String &p_base_name, TypedArray &r_textures, bool p_is_shadowmask, bool p_compress) const { Vector> images; - images.resize(p_lightmapper->get_bake_texture_count()); + images.resize(p_is_shadowmask ? p_lightmapper->get_shadowmask_texture_count() : p_lightmapper->get_bake_texture_count()); for (int i = 0; i < images.size(); i++) { - images.set(i, p_lightmapper->get_bake_texture(i)); + images.set(i, p_is_shadowmask ? p_lightmapper->get_shadowmask_texture(i) : p_lightmapper->get_bake_texture(i)); } const int slice_count = images.size(); @@ -765,7 +831,7 @@ LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref
  • blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); } - const String atlas_path = (texture_count > 1 ? p_base_name + "_" + itos(i) : p_base_name) + ".exr"; + const String atlas_path = (texture_count > 1 ? p_base_name + "_" + itos(i) : p_base_name) + (p_is_shadowmask ? ".png" : ".exr"); const String config_path = atlas_path + ".import"; Ref config; @@ -790,7 +856,12 @@ LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref
  • save(config_path); // Save the file. - Error save_err = texture_image->save_exr(atlas_path, false); + Error save_err; + if (p_is_shadowmask) { + save_err = texture_image->save_png(atlas_path); + } else { + save_err = texture_image->save_exr(atlas_path, false); + } ERR_FAIL_COND_V(save_err, LightmapGI::BAKE_ERROR_CANT_CREATE_IMAGE); @@ -1104,20 +1175,20 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa if (Object::cast_to(light)) { DirectionalLight3D *l = Object::cast_to(light); if (l->get_sky_mode() != DirectionalLight3D::SKY_MODE_SKY_ONLY) { - lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_directional_light(light->get_name(), light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } } else if (Object::cast_to(light)) { OmniLight3D *l = Object::cast_to(light); if (use_physical_light_units) { energy *= (1.0 / (Math_PI * 4.0)); } - lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_omni_light(light->get_name(), light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } else if (Object::cast_to(light)) { SpotLight3D *l = Object::cast_to(light); if (use_physical_light_units) { energy *= (1.0 / Math_PI); } - lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); + lightmapper->add_spot_light(light->get_name(), light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_column(Vector3::AXIS_Z).normalized(), linear_color, energy, indirect_energy, l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE), l->get_param(Light3D::PARAM_SHADOW_BLUR)); } } for (int i = 0; i < probes_found.size(); i++) { @@ -1181,7 +1252,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } } - Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, denoiser_range, bounces, bounce_indirect_energy, bias, max_texture_size, directional, use_texture_for_bounces, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); + Lightmapper::BakeError bake_err = lightmapper->bake(Lightmapper::BakeQuality(bake_quality), use_denoiser, denoiser_strength, denoiser_range, bounces, bounce_indirect_energy, bias, max_texture_size, directional, shadowmask_mode != LightmapGIData::SHADOWMASK_MODE_NONE, use_texture_for_bounces, Lightmapper::GenerateProbes(gen_probes), environment_image, environment_transform, _lightmap_bake_step_function, &bsud, exposure_normalization); if (bake_err == Lightmapper::BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE) { return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL; @@ -1196,15 +1267,23 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa // POSTBAKE: Save Textures. TypedArray lightmap_textures; + TypedArray shadowmask_textures; const String texture_filename = p_image_data_path.get_basename(); + const int shadowmask_texture_count = lightmapper->get_shadowmask_texture_count(); + const bool save_shadowmask = shadowmask_mode != LightmapGIData::SHADOWMASK_MODE_NONE && shadowmask_texture_count > 0; // Save the lightmap atlases. - BakeError save_err = _save_and_reimport_atlas_textures(lightmapper, texture_filename, lightmap_textures, false); + BakeError save_err = _save_and_reimport_atlas_textures(lightmapper, texture_filename, lightmap_textures, false, false); ERR_FAIL_COND_V(save_err != BAKE_ERROR_OK, save_err); - // POSTBAKE: Save Light Data. + if (save_shadowmask) { + // Save the shadowmask atlases. + save_err = _save_and_reimport_atlas_textures(lightmapper, texture_filename + "_shadow", shadowmask_textures, true, true); + ERR_FAIL_COND_V(save_err != BAKE_ERROR_OK, save_err); + } + /* POSTBAKE: Save Light Data. */ Ref gi_data; if (get_light_data().is_valid()) { @@ -1217,6 +1296,13 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } gi_data->set_lightmap_textures(lightmap_textures); + + if (save_shadowmask) { + gi_data->set_shadowmask_textures(shadowmask_textures); + } else { + gi_data->clear_shadowmask_textures(); + } + gi_data->set_uses_spherical_harmonics(directional); gi_data->_set_uses_packed_directional(directional); // New SH lightmaps are packed automatically. @@ -1375,6 +1461,7 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa } set_light_data(gi_data); + update_configuration_warnings(); return BAKE_ERROR_OK; } @@ -1452,6 +1539,7 @@ void LightmapGI::set_light_data(const Ref &p_data) { if (is_inside_tree()) { _assign_lightmaps(); } + light_data->update_shadowmask_mode(shadowmask_mode); } update_gizmos(); @@ -1506,6 +1594,19 @@ bool LightmapGI::is_directional() const { return directional; } +void LightmapGI::set_shadowmask_mode(LightmapGIData::ShadowmaskMode p_mode) { + shadowmask_mode = p_mode; + if (light_data.is_valid()) { + light_data->update_shadowmask_mode(p_mode); + } + + update_configuration_warnings(); +} + +LightmapGIData::ShadowmaskMode LightmapGI::get_shadowmask_mode() const { + return shadowmask_mode; +} + void LightmapGI::set_use_texture_for_bounces(bool p_enable) { use_texture_for_bounces = p_enable; } @@ -1625,6 +1726,11 @@ PackedStringArray LightmapGI::get_configuration_warnings() const { warnings.push_back(vformat(RTR("Lightmaps can only be baked from a GPU that supports the RenderingDevice backends.\nYour GPU (%s) does not support RenderingDevice, as it does not support Vulkan, Direct3D 12, or Metal.\nLightmap baking will not be available on this device, although rendering existing baked lightmaps will work."), RenderingServer::get_singleton()->get_video_adapter_name())); return warnings; } + + if (shadowmask_mode != LightmapGIData::SHADOWMASK_MODE_NONE && light_data.is_valid() && !light_data->has_shadowmask_textures()) { + warnings.push_back(RTR("The lightmap has no baked shadowmask textures. Please rebake with the Shadowmask Mode set to anything other than None.")); + } + #elif defined(ANDROID_ENABLED) || defined(IOS_ENABLED) warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name())); #else @@ -1704,6 +1810,9 @@ void LightmapGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_directional", "directional"), &LightmapGI::set_directional); ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_directional); + ClassDB::bind_method(D_METHOD("set_shadowmask_mode", "mode"), &LightmapGI::set_shadowmask_mode); + ClassDB::bind_method(D_METHOD("get_shadowmask_mode"), &LightmapGI::get_shadowmask_mode); + ClassDB::bind_method(D_METHOD("set_use_texture_for_bounces", "use_texture_for_bounces"), &LightmapGI::set_use_texture_for_bounces); ClassDB::bind_method(D_METHOD("is_using_texture_for_bounces"), &LightmapGI::is_using_texture_for_bounces); @@ -1717,6 +1826,7 @@ void LightmapGI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "bounces", PROPERTY_HINT_RANGE, "0,6,1,or_greater"), "set_bounces", "get_bounces"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce_indirect_energy", PROPERTY_HINT_RANGE, "0,2,0.01"), "set_bounce_indirect_energy", "get_bounce_indirect_energy"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional"), "set_directional", "is_directional"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "shadowmask_mode", PROPERTY_HINT_ENUM, "None,Replace,Overlay"), "set_shadowmask_mode", "get_shadowmask_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_texture_for_bounces"), "set_use_texture_for_bounces", "is_using_texture_for_bounces"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser"); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index faa8b84fa17..6afd4ea5913 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -43,12 +43,23 @@ class LightmapGIData : public Resource { GDCLASS(LightmapGIData, Resource); RES_BASE_EXTENSION("lmbake") +public: + enum ShadowmaskMode { + SHADOWMASK_MODE_NONE, + SHADOWMASK_MODE_REPLACE, + SHADOWMASK_MODE_OVERLAY, + SHADOWMASK_MODE_ONLY, + }; + +private: // The 'merged' texture atlases actually used by the renderer. Ref combined_light_texture; + Ref combined_shadowmask_texture; // The temporary texture atlas arrays which are used for storage. // If a single atlas is too large, it's split and recombined during loading. TypedArray storage_light_textures; + TypedArray storage_shadowmask_textures; bool uses_spherical_harmonics = false; bool interior = false; @@ -74,6 +85,7 @@ class LightmapGIData : public Resource { Dictionary _get_probe_data() const; void _reset_lightmap_textures(); + void _reset_shadowmask_textures(); protected: static void _bind_methods(); @@ -101,6 +113,9 @@ public: void _set_uses_packed_directional(bool p_enable); bool _is_using_packed_directional() const; + void update_shadowmask_mode(ShadowmaskMode p_mode); + ShadowmaskMode get_shadowmask_mode() const; + bool is_interior() const; float get_baked_exposure() const; @@ -116,6 +131,11 @@ public: void set_lightmap_textures(const TypedArray &p_data); TypedArray get_lightmap_textures() const; + void set_shadowmask_textures(const TypedArray &p_data); + TypedArray get_shadowmask_textures() const; + void clear_shadowmask_textures(); + bool has_shadowmask_textures(); + virtual RID get_rid() const override; LightmapGIData(); ~LightmapGIData(); @@ -179,6 +199,7 @@ private: float environment_custom_energy = 1.0; bool directional = false; bool use_texture_for_bounces = true; + LightmapGIData::ShadowmaskMode shadowmask_mode = LightmapGIData::SHADOWMASK_MODE_NONE; GenerateProbes gen_probes = GENERATE_PROBES_SUBDIV_8; Ref camera_attributes; @@ -249,7 +270,7 @@ private: void _plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle); void _gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector &probe_positions, LocalVector &new_probe_positions, HashMap &positions_used, const AABB &p_bounds); - BakeError _save_and_reimport_atlas_textures(const Ref p_lightmapper, const String &p_base_name, TypedArray &r_textures, bool p_compress = false) const; + BakeError _save_and_reimport_atlas_textures(const Ref p_lightmapper, const String &p_base_name, TypedArray &r_textures, bool p_is_shadowmask = false, bool p_compress = false) const; protected: void _validate_property(PropertyInfo &p_property) const; @@ -275,6 +296,9 @@ public: void set_directional(bool p_enable); bool is_directional() const; + void set_shadowmask_mode(LightmapGIData::ShadowmaskMode p_mode); + LightmapGIData::ShadowmaskMode get_shadowmask_mode() const; + void set_use_texture_for_bounces(bool p_enable); bool is_using_texture_for_bounces() const; @@ -323,6 +347,7 @@ public: LightmapGI(); }; +VARIANT_ENUM_CAST(LightmapGIData::ShadowmaskMode); VARIANT_ENUM_CAST(LightmapGI::BakeQuality); VARIANT_ENUM_CAST(LightmapGI::GenerateProbes); VARIANT_ENUM_CAST(LightmapGI::BakeError); diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index 1228c63edce..03ef3f38060 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -133,7 +133,6 @@ public: GENERATE_PROBES_SUBDIV_8, GENERATE_PROBES_SUBDIV_16, GENERATE_PROBES_SUBDIV_32, - }; enum LightType { @@ -178,14 +177,16 @@ public: }; virtual void add_mesh(const MeshData &p_mesh) = 0; - virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) = 0; - virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0; - virtual void add_spot_light(bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0; + virtual void add_directional_light(const String &p_name, bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_angular_distance, float p_shadow_blur) = 0; + virtual void add_omni_light(const String &p_name, bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) = 0; + virtual void add_spot_light(const String &p_name, bool p_static, const Vector3 &p_position, const Vector3 p_direction, const Color &p_color, float p_energy, float p_indirect_energy, float p_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) = 0; virtual void add_probe(const Vector3 &p_position) = 0; - virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr, float p_exposure_normalization = 1.0) = 0; + virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_denoiser_range, int p_bounces, float p_bounce_indirect_energy, float p_bias, int p_max_texture_size, bool p_bake_sh, bool p_bake_shadowmask, bool p_texture_for_bounces, GenerateProbes p_generate_probes, const Ref &p_environment_panorama, const Basis &p_environment_transform, BakeStepFunc p_step_function = nullptr, void *p_step_userdata = nullptr, float p_exposure_normalization = 1.0) = 0; virtual int get_bake_texture_count() const = 0; virtual Ref get_bake_texture(int p_index) const = 0; + virtual int get_shadowmask_texture_count() const = 0; + virtual Ref get_shadowmask_texture(int p_index) const = 0; virtual int get_bake_mesh_count() const = 0; virtual Variant get_bake_mesh_userdata(int p_index) const = 0; virtual Rect2 get_bake_mesh_uv_scale(int p_index) const = 0; diff --git a/servers/rendering/dummy/storage/light_storage.h b/servers/rendering/dummy/storage/light_storage.h index d25523753c9..44cdffaedcc 100644 --- a/servers/rendering/dummy/storage/light_storage.h +++ b/servers/rendering/dummy/storage/light_storage.h @@ -191,6 +191,10 @@ public: 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_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override {} + virtual RS::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override { return RS::SHADOWMASK_MODE_NONE; } + virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) override {} + /* LIGHTMAP INSTANCE */ bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); } 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 43a50e97c4b..40844650335 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -1167,6 +1167,7 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; + scene_state.lightmaps[i].flags = light_storage->lightmap_get_shadowmask_mode(lightmap); if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); @@ -3223,15 +3224,29 @@ RID RenderForwardClustered::_setup_render_pass_uniform_set(RenderListType p_rend u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); - for (uint32_t i = 0; i < scene_state.max_lightmaps; i++) { - if (p_render_data && i < p_render_data->lightmaps->size()) { - RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[i]); - RID texture = light_storage->lightmap_get_texture(base); - RID rd_texture = texture_storage->texture_get_rd_texture(texture); - u.append_id(rd_texture); - } else { - u.append_id(default_tex); + for (uint32_t i = 0; i < scene_state.max_lightmaps * 2; i++) { + uint32_t current_lightmap_index = i < scene_state.max_lightmaps ? i : i - scene_state.max_lightmaps; + + if (p_render_data && current_lightmap_index < p_render_data->lightmaps->size()) { + RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[current_lightmap_index]); + RID texture; + + if (i < scene_state.max_lightmaps) { + // Lightmap + texture = light_storage->lightmap_get_texture(base); + } else { + // Shadowmask + texture = light_storage->shadowmask_get_texture(base); + } + + if (texture.is_valid()) { + RID rd_texture = texture_storage->texture_get_rd_texture(texture); + u.append_id(rd_texture); + continue; + } } + + u.append_id(default_tex); } uniforms.push_back(u); @@ -3535,7 +3550,7 @@ RID RenderForwardClustered::_setup_sdfgi_render_pass_uniform_set(RID p_albedo_te u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); - for (uint32_t i = 0; i < scene_state.max_lightmaps; i++) { + for (uint32_t i = 0; i < scene_state.max_lightmaps * 2; i++) { u.append_id(default_tex); } 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 728164c38e0..ac4b66e1dcf 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -237,7 +237,7 @@ private: float normal_xform[12]; float texture_size[2]; float exposure_normalization; - float pad; + uint32_t flags; }; 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 92d4fe499b2..20f8ef02c07 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -477,15 +477,29 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_ u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); - for (uint32_t i = 0; i < scene_state.max_lightmaps; i++) { - if (p_render_data && i < p_render_data->lightmaps->size()) { - RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[i]); - RID texture = light_storage->lightmap_get_texture(base); - RID rd_texture = texture_storage->texture_get_rd_texture(texture); - u.append_id(rd_texture); - } else { - u.append_id(default_tex); + for (uint32_t i = 0; i < scene_state.max_lightmaps * 2; i++) { + uint32_t current_lightmap_index = i < scene_state.max_lightmaps ? i : i - scene_state.max_lightmaps; + + if (p_render_data && current_lightmap_index < p_render_data->lightmaps->size()) { + RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[current_lightmap_index]); + RID texture; + + if (i < scene_state.max_lightmaps) { + // Lightmap + texture = light_storage->lightmap_get_texture(base); + } else { + // Shadowmask + texture = light_storage->shadowmask_get_texture(base); + } + + if (texture.is_valid()) { + RID rd_texture = texture_storage->texture_get_rd_texture(texture); + u.append_id(rd_texture); + continue; + } } + + u.append_id(default_tex); } uniforms.push_back(u); @@ -642,6 +656,7 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co // Exposure. scene_state.lightmaps[i].exposure_normalization = 1.0; + scene_state.lightmaps[i].flags = light_storage->lightmap_get_shadowmask_mode(lightmap); if (p_render_data->camera_attributes.is_valid()) { float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes); 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 5a4c1140418..0969dd0b508 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -184,7 +184,7 @@ private: float normal_xform[12]; float texture_size[2]; float exposure_normalization; - float pad; + uint32_t flags; }; struct LightmapCaptureData { 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 5b4d746b528..c14ea34c8f0 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 @@ -1978,9 +1978,34 @@ void fragment_shader(in SceneData scene_data) { uint shadow0 = 0; uint shadow1 = 0; + float shadowmask = 1.0; + +#ifdef USE_LIGHTMAP + uint shadowmask_mode = LIGHTMAP_SHADOWMASK_MODE_NONE; + + if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { + const uint ofs = instances.data[instance_index].gi_offset & 0xFFFF; + shadowmask_mode = lightmaps.data[ofs].flags; + + if (shadowmask_mode != LIGHTMAP_SHADOWMASK_MODE_NONE) { + const uint slice = instances.data[instance_index].gi_offset >> 16; + const vec2 scaled_uv = uv2 * instances.data[instance_index].lightmap_uv_scale.zw + instances.data[instance_index].lightmap_uv_scale.xy; + const vec3 uvw = vec3(scaled_uv, float(slice)); + + if (sc_use_lightmap_bicubic_filter()) { + shadowmask = textureArray_bicubic(lightmap_textures[MAX_LIGHTMAP_TEXTURES + ofs], uvw, lightmaps.data[ofs].light_texture_size).x; + } else { + shadowmask = textureLod(sampler2DArray(lightmap_textures[MAX_LIGHTMAP_TEXTURES + ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).x; + } + } + } + + if (shadowmask_mode != LIGHTMAP_SHADOWMASK_MODE_ONLY) { +#endif // USE_LIGHTMAP + #ifdef USE_VERTEX_LIGHTING - // Only process the first light's shadow for vertex lighting. - for (uint i = 0; i < 1; i++) { + // Only process the first light's shadow for vertex lighting. + for (uint i = 0; i < 1; i++) { #else for (uint i = 0; i < 8; i++) { if (i >= scene_data.directional_light_count) { @@ -1988,20 +2013,20 @@ void fragment_shader(in SceneData scene_data) { } #endif - if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) { - continue; //not masked - } + if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) { + continue; //not masked + } - if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { - continue; // Statically baked light and object uses lightmap, skip - } + if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { + continue; // Statically baked light and object uses lightmap, skip + } - float shadow = 1.0; + float shadow = 1.0; - if (directional_lights.data[i].shadow_opacity > 0.001) { - float depth_z = -vertex.z; - vec3 light_dir = directional_lights.data[i].direction; - vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))); + if (directional_lights.data[i].shadow_opacity > 0.001) { + float depth_z = -vertex.z; + vec3 light_dir = directional_lights.data[i].direction; + vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))); #define BIAS_FUNC(m_var, m_idx) \ m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[m_idx]; \ @@ -2009,195 +2034,218 @@ void fragment_shader(in SceneData scene_data) { normal_bias -= light_dir * dot(light_dir, normal_bias); \ m_var.xyz += normal_bias; - //version with soft shadows, more expensive - if (sc_use_directional_soft_shadows() && directional_lights.data[i].softshadow_angle > 0) { - uint blend_count = 0; - const uint blend_max = directional_lights.data[i].blend_splits ? 2 : 1; - - if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 0) - - vec4 pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); - pssm_coord /= pssm_coord.w; - - float range_pos = dot(directional_lights.data[i].direction, v.xyz); - float range_begin = directional_lights.data[i].shadow_range_begin.x; - float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; - vec2 tex_scale = directional_lights.data[i].uv_scale1 * test_radius; - shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); - blend_count++; - } - - if (blend_count < blend_max && depth_z < directional_lights.data[i].shadow_split_offsets.y) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 1) - - vec4 pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_coord /= pssm_coord.w; - - float range_pos = dot(directional_lights.data[i].direction, v.xyz); - float range_begin = directional_lights.data[i].shadow_range_begin.y; - float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; - vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius; - float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); - - if (blend_count == 0) { - shadow = s; - } else { - //blend - float blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); - shadow = mix(shadow, s, blend); - } - - blend_count++; - } - - if (blend_count < blend_max && depth_z < directional_lights.data[i].shadow_split_offsets.z) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 2) - - vec4 pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_coord /= pssm_coord.w; - - float range_pos = dot(directional_lights.data[i].direction, v.xyz); - float range_begin = directional_lights.data[i].shadow_range_begin.z; - float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; - vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius; - float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); - - if (blend_count == 0) { - shadow = s; - } else { - //blend - float blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); - shadow = mix(shadow, s, blend); - } - - blend_count++; - } - - if (blend_count < blend_max) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 3) - - vec4 pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_coord /= pssm_coord.w; - - float range_pos = dot(directional_lights.data[i].direction, v.xyz); - float range_begin = directional_lights.data[i].shadow_range_begin.w; - float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; - vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius; - float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); - - if (blend_count == 0) { - shadow = s; - } else { - //blend - float blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); - shadow = mix(shadow, s, blend); - } - } - - } else { //no soft shadows - - vec4 pssm_coord; - float blur_factor; - - if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 0) - - pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); - blur_factor = 1.0; - } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 1) - - pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; - } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 2) - - pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; - } else { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 3) - - pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; - } - - pssm_coord /= pssm_coord.w; - - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count); - - if (directional_lights.data[i].blend_splits) { - float pssm_blend; - float blur_factor2; + //version with soft shadows, more expensive + if (sc_use_directional_soft_shadows() && directional_lights.data[i].softshadow_angle > 0) { + uint blend_count = 0; + const uint blend_max = directional_lights.data[i].blend_splits ? 2 : 1; if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { vec4 v = vec4(vertex, 1.0); + + BIAS_FUNC(v, 0) + + vec4 pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); + pssm_coord /= pssm_coord.w; + + float range_pos = dot(directional_lights.data[i].direction, v.xyz); + float range_begin = directional_lights.data[i].shadow_range_begin.x; + float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; + vec2 tex_scale = directional_lights.data[i].uv_scale1 * test_radius; + shadow = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); + blend_count++; + } + + if (blend_count < blend_max && depth_z < directional_lights.data[i].shadow_split_offsets.y) { + vec4 v = vec4(vertex, 1.0); + BIAS_FUNC(v, 1) - pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; + + vec4 pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); + pssm_coord /= pssm_coord.w; + + float range_pos = dot(directional_lights.data[i].direction, v.xyz); + float range_begin = directional_lights.data[i].shadow_range_begin.y; + float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; + vec2 tex_scale = directional_lights.data[i].uv_scale2 * test_radius; + float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); + + if (blend_count == 0) { + shadow = s; + } else { + //blend + float blend = smoothstep(0.0, directional_lights.data[i].shadow_split_offsets.x, depth_z); + shadow = mix(shadow, s, blend); + } + + blend_count++; + } + + if (blend_count < blend_max && depth_z < directional_lights.data[i].shadow_split_offsets.z) { + vec4 v = vec4(vertex, 1.0); + + BIAS_FUNC(v, 2) + + vec4 pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); + pssm_coord /= pssm_coord.w; + + float range_pos = dot(directional_lights.data[i].direction, v.xyz); + float range_begin = directional_lights.data[i].shadow_range_begin.z; + float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; + vec2 tex_scale = directional_lights.data[i].uv_scale3 * test_radius; + float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); + + if (blend_count == 0) { + shadow = s; + } else { + //blend + float blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x, directional_lights.data[i].shadow_split_offsets.y, depth_z); + shadow = mix(shadow, s, blend); + } + + blend_count++; + } + + if (blend_count < blend_max) { + vec4 v = vec4(vertex, 1.0); + + BIAS_FUNC(v, 3) + + vec4 pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + pssm_coord /= pssm_coord.w; + + float range_pos = dot(directional_lights.data[i].direction, v.xyz); + float range_begin = directional_lights.data[i].shadow_range_begin.w; + float test_radius = (range_pos - range_begin) * directional_lights.data[i].softshadow_angle; + vec2 tex_scale = directional_lights.data[i].uv_scale4 * test_radius; + float s = sample_directional_soft_shadow(directional_shadow_atlas, pssm_coord.xyz, tex_scale * directional_lights.data[i].soft_shadow_scale, scene_data.taa_frame_count); + + if (blend_count == 0) { + shadow = s; + } else { + //blend + float blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y, directional_lights.data[i].shadow_split_offsets.z, depth_z); + shadow = mix(shadow, s, blend); + } + } + + } else { //no soft shadows + + vec4 pssm_coord; + float blur_factor; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + vec4 v = vec4(vertex, 1.0); + + BIAS_FUNC(v, 0) + + pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); + blur_factor = 1.0; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); - BIAS_FUNC(v, 2) - pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); + + BIAS_FUNC(v, 1) + + pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; + blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); - BIAS_FUNC(v, 3) - pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); + + BIAS_FUNC(v, 2) + + pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; + blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else { - pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached) - blur_factor2 = 1.0; + vec4 v = vec4(vertex, 1.0); + + BIAS_FUNC(v, 3) + + pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count); - shadow = mix(shadow, shadow2, pssm_blend); - } - } + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count); - shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance + if (directional_lights.data[i].blend_splits) { + float pssm_blend; + float blur_factor2; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + vec4 v = vec4(vertex, 1.0); + BIAS_FUNC(v, 1) + pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { + vec4 v = vec4(vertex, 1.0); + BIAS_FUNC(v, 2) + pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { + vec4 v = vec4(vertex, 1.0); + BIAS_FUNC(v, 3) + pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; + } else { + pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached) + blur_factor2 = 1.0; + } + + pssm_coord /= pssm_coord.w; + + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * float(directional_lights.data[i].blend_splits)), pssm_coord, scene_data.taa_frame_count); + shadow = mix(shadow, shadow2, pssm_blend); + } + } + +#ifdef USE_LIGHTMAP + if (shadowmask_mode == LIGHTMAP_SHADOWMASK_MODE_REPLACE) { + shadow = mix(shadow, shadowmask, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance + } else if (shadowmask_mode == LIGHTMAP_SHADOWMASK_MODE_OVERLAY) { + shadow = shadowmask * mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance + } else { +#endif + shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance +#ifdef USE_LIGHTMAP + } +#endif #ifdef USE_VERTEX_LIGHTING - diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a); - specular_light *= mix(1.0, shadow, specular_light_interp.a); + diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a); + specular_light *= mix(1.0, shadow, specular_light_interp.a); #endif #undef BIAS_FUNC - } // shadows + } // shadows - if (i < 4) { - shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8); - } else { - shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8); + if (i < 4) { + shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8); + } else { + shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8); + } } + +#ifdef USE_LIGHTMAP + } else { // shadowmask_mode == LIGHTMAP_SHADOWMASK_MODE_ONLY + +#ifdef USE_VERTEX_LIGHTING + diffuse_light *= mix(1.0, shadowmask, diffuse_light_interp.a); + specular_light *= mix(1.0, shadowmask, specular_light_interp.a); +#endif + + shadow0 |= uint(clamp(shadowmask * 255.0, 0.0, 255.0)); } +#endif // USE_LIGHTMAP + #endif // SHADOWS_DISABLED #ifndef USE_VERTEX_LIGHTING 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 8f153f7ed5d..3257a0d0563 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 @@ -200,11 +200,16 @@ directional_lights; #define LIGHTMAP_FLAG_USE_DIRECTION 1 #define LIGHTMAP_FLAG_USE_SPECULAR_DIRECTION 2 +#define LIGHTMAP_SHADOWMASK_MODE_NONE 0 +#define LIGHTMAP_SHADOWMASK_MODE_REPLACE 1 +#define LIGHTMAP_SHADOWMASK_MODE_OVERLAY 2 +#define LIGHTMAP_SHADOWMASK_MODE_ONLY 3 + struct Lightmap { mat3 normal_xform; vec2 light_texture_size; float exposure_normalization; - float pad; + uint flags; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { @@ -349,7 +354,7 @@ layout(set = 1, binding = 5) uniform texture2D shadow_atlas; layout(set = 1, binding = 6) uniform texture2D directional_shadow_atlas; -layout(set = 1, binding = 7) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES]; +layout(set = 1, binding = 7) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES * 2]; layout(set = 1, binding = 8) uniform texture3D voxel_gi_textures[MAX_VOXEL_GI_INSTANCES]; 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 dbca038d3a7..a3c264b8236 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 @@ -1434,30 +1434,54 @@ void main() { uint shadow0 = 0; uint shadow1 = 0; + float shadowmask = 1.0; + +#ifdef USE_LIGHTMAP + uint shadowmask_mode = LIGHTMAP_SHADOWMASK_MODE_NONE; + + if (bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { + const uint ofs = instances.data[draw_call.instance_index].gi_offset & 0xFFFF; + shadowmask_mode = lightmaps.data[ofs].flags; + + if (shadowmask_mode != LIGHTMAP_SHADOWMASK_MODE_NONE) { + const uint slice = instances.data[draw_call.instance_index].gi_offset >> 16; + const vec2 scaled_uv = uv2 * instances.data[draw_call.instance_index].lightmap_uv_scale.zw + instances.data[draw_call.instance_index].lightmap_uv_scale.xy; + const vec3 uvw = vec3(scaled_uv, float(slice)); + + if (sc_use_lightmap_bicubic_filter()) { + shadowmask = textureArray_bicubic(lightmap_textures[MAX_LIGHTMAP_TEXTURES + ofs], uvw, lightmaps.data[ofs].light_texture_size).x; + } else { + shadowmask = textureLod(sampler2DArray(lightmap_textures[MAX_LIGHTMAP_TEXTURES + ofs], SAMPLER_LINEAR_CLAMP), uvw, 0.0).x; + } + } + } + + if (shadowmask_mode != LIGHTMAP_SHADOWMASK_MODE_ONLY) { +#endif // USE_LIGHTMAP + #ifdef USE_VERTEX_LIGHTING - // Only process the first light's shadow for vertex lighting. - for (uint i = 0; i < 1; i++) { + // Only process the first light's shadow for vertex lighting. + for (uint i = 0; i < 1; i++) { #else for (uint i = 0; i < sc_directional_lights(); i++) { #endif + if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) { + continue; //not masked + } - if (!bool(directional_lights.data[i].mask & instances.data[draw_call.instance_index].layer_mask)) { - continue; //not masked - } + if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { + continue; // Statically baked light and object uses lightmap, skip. + } - if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { - continue; // Statically baked light and object uses lightmap, skip. - } + float shadow = 1.0; - float shadow = 1.0; + if (directional_lights.data[i].shadow_opacity > 0.001) { + float depth_z = -vertex.z; - if (directional_lights.data[i].shadow_opacity > 0.001) { - float depth_z = -vertex.z; - - vec4 pssm_coord; - float blur_factor; - vec3 light_dir = directional_lights.data[i].direction; - vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))); + vec4 pssm_coord; + float blur_factor; + vec3 light_dir = directional_lights.data[i].direction; + vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp)))); #define BIAS_FUNC(m_var, m_idx) \ m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[m_idx]; \ @@ -1465,97 +1489,119 @@ void main() { normal_bias -= light_dir * dot(light_dir, normal_bias); \ m_var.xyz += normal_bias; - if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 0) - - pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); - blur_factor = 1.0; - } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 1) - - pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; - ; - } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 2) - - pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; - } else { - vec4 v = vec4(vertex, 1.0); - - BIAS_FUNC(v, 3) - - pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; - } - - pssm_coord /= pssm_coord.w; - - bool blend_split = sc_directional_light_blend_split(i); - float blend_split_weight = blend_split ? 1.0f : 0.0f; - shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * blend_split_weight), pssm_coord, scene_data.taa_frame_count); - - if (blend_split) { - float pssm_blend; - float blur_factor2; - if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { vec4 v = vec4(vertex, 1.0); - BIAS_FUNC(v, 1) - pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); - // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; + + BIAS_FUNC(v, 0) + + pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); + blur_factor = 1.0; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { vec4 v = vec4(vertex, 1.0); - BIAS_FUNC(v, 2) - pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); + + BIAS_FUNC(v, 1) + + pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; + blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { vec4 v = vec4(vertex, 1.0); - BIAS_FUNC(v, 3) - pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); - pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); + + BIAS_FUNC(v, 2) + + pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. - blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; + blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; } else { - pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached) - blur_factor2 = 1.0; + vec4 v = vec4(vertex, 1.0); + + BIAS_FUNC(v, 3) + + pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; } pssm_coord /= pssm_coord.w; - float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * blend_split_weight), pssm_coord, scene_data.taa_frame_count); - shadow = mix(shadow, shadow2, pssm_blend); - } + bool blend_split = sc_directional_light_blend_split(i); + float blend_split_weight = blend_split ? 1.0f : 0.0f; + shadow = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor + (1.0 - blur_factor) * blend_split_weight), pssm_coord, scene_data.taa_frame_count); - shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance + if (blend_split) { + float pssm_blend; + float blur_factor2; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + vec4 v = vec4(vertex, 1.0); + BIAS_FUNC(v, 1) + pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.x - directional_lights.data[i].shadow_split_offsets.x * 0.1, directional_lights.data[i].shadow_split_offsets.x, depth_z); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { + vec4 v = vec4(vertex, 1.0); + BIAS_FUNC(v, 2) + pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.y - directional_lights.data[i].shadow_split_offsets.y * 0.1, directional_lights.data[i].shadow_split_offsets.y, depth_z); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.z; + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { + vec4 v = vec4(vertex, 1.0); + BIAS_FUNC(v, 3) + pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + pssm_blend = smoothstep(directional_lights.data[i].shadow_split_offsets.z - directional_lights.data[i].shadow_split_offsets.z * 0.1, directional_lights.data[i].shadow_split_offsets.z, depth_z); + // Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. + blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.w; + } else { + pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached) + blur_factor2 = 1.0; + } + + pssm_coord /= pssm_coord.w; + + float shadow2 = sample_directional_pcf_shadow(directional_shadow_atlas, scene_data.directional_shadow_pixel_size * directional_lights.data[i].soft_shadow_scale * (blur_factor2 + (1.0 - blur_factor2) * blend_split_weight), pssm_coord, scene_data.taa_frame_count); + shadow = mix(shadow, shadow2, pssm_blend); + } + +#ifdef USE_LIGHTMAP + if (shadowmask_mode == LIGHTMAP_SHADOWMASK_MODE_REPLACE) { + shadow = mix(shadow, shadowmask, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance + } else if (shadowmask_mode == LIGHTMAP_SHADOWMASK_MODE_OVERLAY) { + shadow = shadowmask * mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance + } else { +#endif + shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, vertex.z)); //done with negative values for performance +#ifdef USE_LIGHTMAP + } +#endif #ifdef USE_VERTEX_LIGHTING - diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a); - specular_light *= mix(1.0, shadow, specular_light_interp.a); + diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a); + specular_light *= mix(1.0, shadow, specular_light_interp.a); #endif #undef BIAS_FUNC + } + + if (i < 4) { + shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8); + } else { + shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8); + } } - if (i < 4) { - shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8); - } else { - shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8); - } +#ifdef USE_LIGHTMAP + } else { // shadowmask_mode == LIGHTMAP_SHADOWMASK_MODE_ONLY + +#ifdef USE_VERTEX_LIGHTING + diffuse_light *= mix(1.0, shadowmask, diffuse_light_interp.a); + specular_light *= mix(1.0, shadowmask, specular_light_interp.a); +#endif + + shadow0 |= uint(clamp(shadowmask * 255.0, 0.0, 255.0)); } +#endif // USE_LIGHTMAP + #endif // SHADOWS_DISABLED #ifndef USE_VERTEX_LIGHTING 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 5e4719c4988..49c8905dbf1 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 @@ -246,11 +246,16 @@ directional_lights; #define LIGHTMAP_FLAG_USE_DIRECTION 1 #define LIGHTMAP_FLAG_USE_SPECULAR_DIRECTION 2 +#define LIGHTMAP_SHADOWMASK_MODE_NONE 0 +#define LIGHTMAP_SHADOWMASK_MODE_REPLACE 1 +#define LIGHTMAP_SHADOWMASK_MODE_OVERLAY 2 +#define LIGHTMAP_SHADOWMASK_MODE_ONLY 3 + struct Lightmap { mediump mat3 normal_xform; vec2 light_texture_size; float exposure_normalization; - float pad; + uint flags; }; layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { @@ -330,7 +335,7 @@ layout(set = 1, binding = 4) uniform highp texture2D shadow_atlas; layout(set = 1, binding = 5) uniform highp texture2D directional_shadow_atlas; // this needs to change to providing just the lightmap we're using.. -layout(set = 1, binding = 6) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES]; +layout(set = 1, binding = 6) uniform texture2DArray lightmap_textures[MAX_LIGHTMAP_TEXTURES * 2]; #ifdef USE_MULTIVIEW layout(set = 1, binding = 9) uniform highp texture2DArray depth_buffer; diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index a812c894b92..5c1283c7624 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -55,12 +55,18 @@ LightStorage::LightStorage() { if (textures_per_stage <= 256) { lightmap_textures.resize(32); + shadowmask_textures.resize(32); } else { lightmap_textures.resize(1024); + shadowmask_textures.resize(1024); } - for (int i = 0; i < lightmap_textures.size(); i++) { - lightmap_textures.write[i] = texture_storage->texture_rd_get_default(TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); + for (RID &lightmap_texture : lightmap_textures) { + lightmap_texture = texture_storage->texture_rd_get_default(TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); + } + + for (RID &shadowmask_texture : shadowmask_textures) { + shadowmask_texture = texture_storage->texture_rd_get_default(TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); } } @@ -2003,6 +2009,64 @@ AABB LightStorage::lightmap_get_aabb(RID p_lightmap) const { return lm->bounds; } +void LightStorage::lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) { + TextureStorage *texture_storage = TextureStorage::get_singleton(); + + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + + // Erase lightmap users from shadow texture. + if (lm->shadow_texture.is_valid()) { + TextureStorage::Texture *t = texture_storage->get_texture(lm->shadow_texture); + if (t) { + t->lightmap_users.erase(p_lightmap); + } + } + + TextureStorage::Texture *t = texture_storage->get_texture(p_shadow); + lm->shadow_texture = p_shadow; + + RID default_2d_array = texture_storage->texture_rd_get_default(TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); + if (!t) { + if (lm->array_index >= 0) { + shadowmask_textures.write[lm->array_index] = default_2d_array; + lm->array_index = -1; + } + + return; + } + + t->lightmap_users.insert(p_lightmap); + + if (lm->array_index < 0) { + // Not in array, try to put in array. + for (int i = 0; i < shadowmask_textures.size(); i++) { + if (shadowmask_textures[i] == default_2d_array) { + lm->array_index = i; + break; + } + } + } + + ERR_FAIL_COND_MSG(lm->array_index < 0, vformat("Maximum amount of shadowmasks in use (%d) has been exceeded, shadowmask will not display properly.", shadowmask_textures.size())); + + shadowmask_textures.write[lm->array_index] = t->rd_texture; +} + +RS::ShadowmaskMode LightStorage::lightmap_get_shadowmask_mode(RID p_lightmap) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lm, RS::SHADOWMASK_MODE_NONE); + + return lm->shadowmask_mode; +} + +void LightStorage::lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) { + Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL(lm); + + lm->shadowmask_mode = p_mode; +} + /* LIGHTMAP INSTANCE */ RID LightStorage::lightmap_instance_create(RID p_lightmap) { diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.h b/servers/rendering/renderer_rd/storage_rd/light_storage.h index b5f1523123d..ccba9e4aa5b 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.h @@ -329,6 +329,8 @@ private: struct Lightmap { RID light_texture; + RID shadow_texture; + RS::ShadowmaskMode shadowmask_mode = RS::SHADOWMASK_MODE_NONE; bool uses_spherical_harmonics = false; bool interior = false; AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); @@ -356,6 +358,8 @@ private: mutable RID_Owner lightmap_owner; + Vector shadowmask_textures; + /* LIGHTMAP INSTANCE */ struct LightmapInstance { @@ -985,6 +989,10 @@ public: Dependency *lightmap_get_dependency(RID p_lightmap) const; + virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) override; + virtual RS::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) override; + virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) override; + virtual float lightmap_get_probe_capture_update_speed() const override { return lightmap_probe_capture_update_speed; } @@ -1027,6 +1035,12 @@ public: return lightmap_textures; } + _FORCE_INLINE_ RID shadowmask_get_texture(RID p_lightmap) const { + const Lightmap *lm = lightmap_owner.get_or_null(p_lightmap); + ERR_FAIL_NULL_V(lm, RID()); + return lm->shadow_texture; + } + /* LIGHTMAP INSTANCE */ bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); } diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 29b1e163c78..f0e6ef20115 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -489,6 +489,10 @@ public: FUNC1RC(PackedInt32Array, lightmap_get_probe_capture_bsp_tree, RID) FUNC1(lightmap_set_probe_capture_update_speed, float) + FUNC2(lightmap_set_shadowmask_textures, RID, RID) + FUNC1R(ShadowmaskMode, lightmap_get_shadowmask_mode, RID) + FUNC2(lightmap_set_shadowmask_mode, RID, ShadowmaskMode) + /* Shadow Atlas */ FUNC0R(RID, shadow_atlas_create) FUNC3(shadow_atlas_set_size, RID, int, bool) diff --git a/servers/rendering/storage/light_storage.h b/servers/rendering/storage/light_storage.h index 1e149e3a97c..7d6cfd94944 100644 --- a/servers/rendering/storage/light_storage.h +++ b/servers/rendering/storage/light_storage.h @@ -175,6 +175,10 @@ public: 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_set_shadowmask_textures(RID p_lightmap, RID p_shadow) = 0; + virtual RS::ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) = 0; + virtual void lightmap_set_shadowmask_mode(RID p_lightmap, RS::ShadowmaskMode p_mode) = 0; + /* LIGHTMAP INSTANCE */ virtual RID lightmap_instance_create(RID p_lightmap) = 0; diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 0917af73c6a..49990d72cdc 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -709,6 +709,13 @@ public: /* LIGHTMAP */ + enum ShadowmaskMode { + SHADOWMASK_MODE_NONE, + SHADOWMASK_MODE_REPLACE, + SHADOWMASK_MODE_OVERLAY, + SHADOWMASK_MODE_ONLY, + }; + virtual RID lightmap_create() = 0; virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) = 0; @@ -722,9 +729,12 @@ public: virtual PackedInt32Array lightmap_get_probe_capture_bsp_tree(RID p_lightmap) const = 0; virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0; - virtual void lightmaps_set_bicubic_filter(bool p_enable) = 0; + virtual void lightmap_set_shadowmask_textures(RID p_lightmap, RID p_shadow) = 0; + virtual ShadowmaskMode lightmap_get_shadowmask_mode(RID p_lightmap) = 0; + virtual void lightmap_set_shadowmask_mode(RID p_lightmap, ShadowmaskMode p_mode) = 0; + /* PARTICLES API */ virtual RID particles_create() = 0;