1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Implement shadowmasks for LightmapGI

Co-authored-by: dearthdev <nathandearthdev@gmail.com>
This commit is contained in:
BlueCube3310
2023-11-30 17:14:45 +01:00
parent a40fc2354a
commit 189c8eb671
27 changed files with 1098 additions and 451 deletions

View File

@@ -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. 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. 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.
</member> </member>
<member name="shadowmask_mode" type="int" setter="set_shadowmask_mode" getter="get_shadowmask_mode" enum="LightmapGIData.ShadowmaskMode" default="0" experimental="">
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.
</member>
<member name="texel_scale" type="float" setter="set_texel_scale" getter="get_texel_scale" default="1.0"> <member name="texel_scale" type="float" setter="set_texel_scale" getter="get_texel_scale" default="1.0">
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. 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. 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.

View File

@@ -60,5 +60,19 @@
<member name="lightmap_textures" type="TextureLayered[]" setter="set_lightmap_textures" getter="get_lightmap_textures" default="[]"> <member name="lightmap_textures" type="TextureLayered[]" setter="set_lightmap_textures" getter="get_lightmap_textures" default="[]">
The lightmap atlas textures generated by the lightmapper. The lightmap atlas textures generated by the lightmapper.
</member> </member>
<member name="shadowmask_textures" type="TextureLayered[]" setter="set_shadowmask_textures" getter="get_shadowmask_textures" default="[]">
The shadowmask atlas textures generated by the lightmapper.
</member>
</members> </members>
<constants>
<constant name="SHADOWMASK_MODE_NONE" value="0" enum="ShadowmaskMode">
Shadowmasking is disabled. No shadowmask texture will be created when baking lightmaps. Existing shadowmask textures will be removed during baking.
</constant>
<constant name="SHADOWMASK_MODE_REPLACE" value="1" enum="ShadowmaskMode">
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.
</constant>
<constant name="SHADOWMASK_MODE_OVERLAY" value="2" enum="ShadowmaskMode">
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.
</constant>
</constants>
</class> </class>

View File

@@ -2660,14 +2660,14 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glBlitFramebuffer(0, 0, size.x, size.y, glBlitFramebuffer(0, 0, size.x, size.y,
0, 0, size.x, size.y, 0, 0, size.x, size.y,
GL_COLOR_BUFFER_BIT, GL_NEAREST); 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); glBindTexture(GL_TEXTURE_2D, backbuffer);
} }
if (scene_state.used_depth_texture) { if (scene_state.used_depth_texture) {
glBlitFramebuffer(0, 0, size.x, size.y, glBlitFramebuffer(0, 0, size.x, size.y,
0, 0, size.x, size.y, 0, 0, size.x, size.y,
GL_DEPTH_BUFFER_BIT, GL_NEAREST); 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); 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_OMNI;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL; spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE; 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) { 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(); GLuint tex = GLES3::LightStorage::get_singleton()->directional_shadow_get_texture();
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 3); glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 3);
glBindTexture(GL_TEXTURE_2D, tex); 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); 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) { } 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<const GLfloat *>(inst->lightmap_sh->sh)); glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast<const GLfloat *>(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_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); 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])); 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_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); 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])); glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[1]));
spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE; spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;

View File

@@ -809,10 +809,11 @@ void main() {
2-radiance 2-radiance
3-shadow 3-shadow
4-lightmap textures 4-lightmap textures
5-screen 5-shadowmask textures
6-depth 6-screen
7-reflection probe 1 7-depth
8-reflection probe 2 8-reflection probe 1
9-reflection probe 2
*/ */
@@ -887,7 +888,7 @@ uniform float refprobe1_intensity;
uniform int refprobe1_ambient_mode; uniform int refprobe1_ambient_mode;
uniform vec4 refprobe1_ambient_color; uniform vec4 refprobe1_ambient_color;
uniform samplerCube refprobe1_texture; // texunit:-7 uniform samplerCube refprobe1_texture; // texunit:-8
#ifdef SECOND_REFLECTION_PROBE #ifdef SECOND_REFLECTION_PROBE
@@ -900,7 +901,7 @@ uniform float refprobe2_intensity;
uniform int refprobe2_ambient_mode; uniform int refprobe2_ambient_mode;
uniform vec4 refprobe2_ambient_color; uniform vec4 refprobe2_ambient_color;
uniform samplerCube refprobe2_texture; // texunit:-8 uniform samplerCube refprobe2_texture; // texunit:-9
#endif // SECOND_REFLECTION_PROBE #endif // SECOND_REFLECTION_PROBE
@@ -1170,9 +1171,16 @@ float sample_shadow(highp sampler2DShadow shadow, float shadow_pixel_size, vec4
#ifndef DISABLE_LIGHTMAP #ifndef DISABLE_LIGHTMAP
#ifdef USE_LIGHTMAP #ifdef USE_LIGHTMAP
uniform mediump sampler2DArray lightmap_textures; //texunit:-4 uniform mediump sampler2DArray lightmap_textures; //texunit:-4
uniform lowp sampler2DArray shadowmask_textures; //texunit:-5
uniform lowp uint lightmap_slice; uniform lowp uint lightmap_slice;
uniform highp vec4 lightmap_uv_scale; uniform highp vec4 lightmap_uv_scale;
uniform float lightmap_exposure_normalization; 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 #ifdef LIGHTMAP_BICUBIC_FILTER
uniform highp vec2 lightmap_texture_size; uniform highp vec2 lightmap_texture_size;
@@ -1189,8 +1197,8 @@ uniform mediump vec4[9] lightmap_captures;
#endif // !DISABLE_LIGHTMAP #endif // !DISABLE_LIGHTMAP
#ifdef USE_MULTIVIEW #ifdef USE_MULTIVIEW
uniform highp sampler2DArray depth_buffer; // texunit:-6 uniform highp sampler2DArray depth_buffer; // texunit:-7
uniform highp sampler2DArray color_buffer; // texunit:-5 uniform highp sampler2DArray color_buffer; // texunit:-6
vec3 multiview_uv(vec2 uv) { vec3 multiview_uv(vec2 uv) {
return vec3(uv, ViewIndex); return vec3(uv, ViewIndex);
} }
@@ -1198,8 +1206,8 @@ ivec3 multiview_uv(ivec2 uv) {
return ivec3(uv, int(ViewIndex)); return ivec3(uv, int(ViewIndex));
} }
#else #else
uniform highp sampler2D depth_buffer; // texunit:-6 uniform highp sampler2D depth_buffer; // texunit:-7
uniform highp sampler2D color_buffer; // texunit:-5 uniform highp sampler2D color_buffer; // texunit:-6
vec2 multiview_uv(vec2 uv) { vec2 multiview_uv(vec2 uv) {
return uv; return uv;
} }
@@ -2278,111 +2286,146 @@ void main() {
#if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT) #if !defined(ADDITIVE_OMNI) && !defined(ADDITIVE_SPOT)
#ifndef SHADOWS_DISABLED #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 // Orthogonal shadows
#if !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4) #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) #endif // !defined(LIGHT_USE_PSSM2) && !defined(LIGHT_USE_PSSM4)
// PSSM2 shadows // PSSM2 shadows
#ifdef LIGHT_USE_PSSM2 #ifdef LIGHT_USE_PSSM2
float depth_z = -vertex.z; float depth_z = -vertex.z;
vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets; vec4 light_split_offsets = directional_shadows[directional_shadow_index].shadow_split_offsets;
//take advantage of prefetch //take advantage of prefetch
float shadow1 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord); 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 shadow2 = sample_shadow(directional_shadow_atlas, directional_shadows[directional_shadow_index].shadow_atlas_pixel_size, shadow_coord2);
float directional_shadow = 1.0;
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) { 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) { if (depth_z < light_split_offsets.x) {
directional_shadow = shadow1; directional_shadow = shadow1;
#ifdef LIGHT_USE_PSSM_BLEND #ifdef LIGHT_USE_PSSM_BLEND
directional_shadow2 = shadow2; directional_shadow2 = shadow2;
pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z); pssm_blend = smoothstep(0.0, light_split_offsets.x, depth_z);
#endif #endif
} else { } else {
directional_shadow = shadow2; directional_shadow = shadow2;
#ifdef LIGHT_USE_PSSM_BLEND #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; use_blend = false;
#endif #endif
} }
} #ifdef LIGHT_USE_PSSM_BLEND
#if defined(LIGHT_USE_PSSM_BLEND) if (use_blend) {
if (use_blend) { directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend);
directional_shadow = mix(directional_shadow, directional_shadow2, pssm_blend); }
}
#endif #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 #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); directional_shadow = mix(1.0, directional_shadow, directional_lights[directional_shadow_index].shadow_opacity);
#else #else

View File

@@ -1204,6 +1204,33 @@ float LightStorage::lightmap_get_probe_capture_update_speed() const {
return lightmap_probe_capture_update_speed; 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 */ /* LIGHTMAP INSTANCE */
RID LightStorage::lightmap_instance_create(RID p_lightmap) { RID LightStorage::lightmap_instance_create(RID p_lightmap) {

View File

@@ -177,12 +177,14 @@ struct ReflectionProbeInstance {
struct Lightmap { struct Lightmap {
RID light_texture; RID light_texture;
RID shadow_texture;
bool uses_spherical_harmonics = false; bool uses_spherical_harmonics = false;
bool interior = false; bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
float baked_exposure = 1.0; float baked_exposure = 1.0;
Vector2i light_texture_size; Vector2i light_texture_size;
int32_t array_index = -1; //unassigned int32_t array_index = -1; //unassigned
RS::ShadowmaskMode shadowmask_mode = RS::SHADOWMASK_MODE_NONE;
PackedVector3Array points; PackedVector3Array points;
PackedColorArray point_sh; PackedColorArray point_sh;
PackedInt32Array tetrahedra; PackedInt32Array tetrahedra;
@@ -231,8 +233,6 @@ private:
mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner; mutable RID_Owner<ReflectionProbeInstance> reflection_probe_instance_owner;
/* LIGHTMAP */ /* LIGHTMAP */
Vector<RID> lightmap_textures;
float lightmap_probe_capture_update_speed = 4; float lightmap_probe_capture_update_speed = 4;
mutable RID_Owner<Lightmap, true> lightmap_owner; mutable RID_Owner<Lightmap, true> lightmap_owner;
@@ -737,6 +737,10 @@ public:
virtual void lightmap_set_probe_capture_update_speed(float p_speed) override; virtual void lightmap_set_probe_capture_update_speed(float p_speed) override;
virtual float lightmap_get_probe_capture_update_speed() const 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 */ /* LIGHTMAP INSTANCE */
LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); } LightmapInstance *get_lightmap_instance(RID p_rid) { return lightmap_instance_owner.get_or_null(p_rid); }

View File

@@ -62,7 +62,7 @@ void LightmapperRD::add_mesh(const MeshData &p_mesh) {
mesh_instances.push_back(mi); 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; Light l;
l.type = LIGHT_TYPE_DIRECTIONAL; l.type = LIGHT_TYPE_DIRECTIONAL;
l.direction[0] = p_direction.x; 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.size = Math::tan(Math::deg_to_rad(p_angular_distance));
l.shadow_blur = p_shadow_blur; l.shadow_blur = p_shadow_blur;
lights.push_back(l); 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; Light l;
l.type = LIGHT_TYPE_OMNI; l.type = LIGHT_TYPE_OMNI;
l.position[0] = p_position.x; 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.size = p_size;
l.shadow_blur = p_shadow_blur; l.shadow_blur = p_shadow_blur;
lights.push_back(l); 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; Light l;
l.type = LIGHT_TYPE_SPOT; l.type = LIGHT_TYPE_SPOT;
l.position[0] = p_position.x; 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.size = p_size;
l.shadow_blur = p_shadow_blur; l.shadow_blur = p_shadow_blur;
lights.push_back(l); lights.push_back(l);
light_names.push_back(p_name);
} }
void LightmapperRD::add_probe(const Vector3 &p_position) { void LightmapperRD::add_probe(const Vector3 &p_position) {
@@ -826,9 +829,9 @@ LightmapperRD::BakeError LightmapperRD::_pack_l1(RenderingDevice *rd, Ref<RDShad
return BAKE_OK; return BAKE_OK;
} }
Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name) { Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name, bool p_shadowmask) {
Vector<uint8_t> data = p_rd->texture_get_data(p_atlas_tex, p_index); Vector<uint8_t> data = p_rd->texture_get_data(p_atlas_tex, p_index);
Ref<Image> img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, Image::FORMAT_RGBAH, data); Ref<Image> 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); img->convert(Image::FORMAT_RGBF);
Vector<uint8_t> data_float = img->get_data(); Vector<uint8_t> 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; return OK;
} }
Ref<Image> LightmapperRD::_read_pfm(const String &p_name) { Ref<Image> LightmapperRD::_read_pfm(const String &p_name, bool p_shadowmask) {
Error err = OK; Error err = OK;
Ref<FileAccess> file = FileAccess::open(p_name, FileAccess::READ, &err); Ref<FileAccess> file = FileAccess::open(p_name, FileAccess::READ, &err);
ERR_FAIL_COND_V_MSG(err, Ref<Image>(), vformat("Can't load PFM at path: '%s'.", p_name)); ERR_FAIL_COND_V_MSG(err, Ref<Image>(), vformat("Can't load PFM at path: '%s'.", p_name));
@@ -881,23 +884,23 @@ Ref<Image> LightmapperRD::_read_pfm(const String &p_name) {
} }
#endif #endif
Ref<Image> img = Image::create_from_data(new_width, new_height, false, Image::FORMAT_RGBF, new_data); Ref<Image> 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; 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<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
for (int i = 0; i < p_atlas_slices; i++) { 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)); 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++) { for (int j = 0; j < (p_bake_sh ? 4 : 1); j++) {
int index = i * (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_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)); 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<String> args; List<String> args;
args.push_back("--device"); args.push_back("--device");
@@ -906,7 +909,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise_oidn(RenderingDevice *p_rd, RID
args.push_back("--filter"); args.push_back("--filter");
args.push_back("RTLightmap"); args.push_back("RTLightmap");
args.push_back("--hdr"); args.push_back(p_shadowmask ? "--ldr" : "--hdr");
args.push_back(fname_light_in); args.push_back(fname_light_in);
args.push_back("--nrm"); 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)); ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, vformat("OIDN denoiser failed, return code: %d", exitcode));
} }
Ref<Image> img = _read_pfm(fname_out); Ref<Image> img = _read_pfm(fname_out, p_shadowmask);
da->remove(fname_out); da->remove(fname_out);
ERR_FAIL_COND_V(img.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); 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<RDSh
return BAKE_OK; return BAKE_OK;
} }
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_texture_for_bounces, GenerateProbes p_generate_probes, const Ref<Image> &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<Image> &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"); int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser");
String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path"); 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) { if (p_step_function) {
p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true); 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; int grid_size = 128;
/* STEP 1: Fetch material textures and compute the bounds */ /* 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; 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 #ifdef DEBUG_TEXTURES
for (int i = 0; i < atlas_slices; i++) { for (int i = 0; i < atlas_slices; i++) {
albedo_images[i]->save_png("res://0_albedo_" + itos(i) + ".png"); 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_tex;
RID light_accum_tex2; RID light_accum_tex2;
RID light_environment_tex; RID light_environment_tex;
RID shadowmask_tex;
RID shadowmask_tex2;
#define FREE_TEXTURES \ #define FREE_TEXTURES \
rd->free(albedo_array_tex); \ rd->free(albedo_array_tex); \
rd->free(emission_array_tex); \ rd->free(emission_array_tex); \
rd->free(normal_tex); \ rd->free(normal_tex); \
rd->free(position_tex); \ rd->free(position_tex); \
rd->free(unocclude_tex); \ rd->free(unocclude_tex); \
rd->free(light_source_tex); \ rd->free(light_source_tex); \
rd->free(light_accum_tex2); \ rd->free(light_accum_tex2); \
rd->free(light_accum_tex); \ rd->free(light_accum_tex); \
rd->free(light_environment_tex); rd->free(light_environment_tex); \
if (p_bake_shadowmask) { \
rd->free(shadowmask_tex); \
rd->free(shadowmask_tex2); \
}
{ // create all textures { // 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()); position_tex = rd->texture_create(tf, RD::TextureView());
unocclude_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; 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()); 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); 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.exposure_normalization = p_exposure_normalization;
bake_parameters.bounces = p_bounces; bake_parameters.bounces = p_bounces;
bake_parameters.bounce_indirect_energy = p_bounce_indirect_energy; 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)); bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters); 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"; defines += "\n#define USE_LIGHT_TEXTURE_FOR_BOUNCES\n";
} }
if (p_bake_shadowmask) {
defines += "\n#define USE_SHADOWMASK\n";
}
compute_shader.instantiate(); compute_shader.instantiate();
err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, defines); err = compute_shader->parse_versions_from_text(lm_compute_shader_glsl, defines);
if (err != OK) { if (err != OK) {
@@ -1634,6 +1691,14 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
u.append_id(light_accum_tex); u.append_id(light_accum_tex);
uniforms.push_back(u); 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); 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; BakeError error;
if (denoiser == 1) { if (denoiser == 1) {
// OIDN (external). // 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 { } else {
// JNLM (built-in). // JNLM (built-in).
SWAP(light_accum_tex, light_accum_tex2); SWAP(light_accum_tex, light_accum_tex2);
@@ -1955,14 +2020,39 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
return error; 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); 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)); 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)) { if (unlikely(error != BAKE_OK)) {
return error; 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 #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); img->save_exr("res://5_blendseams" + itos(i) + ".exr", false);
} }
#endif #endif
if (p_step_function) { if (p_step_function) {
p_step_function(0.9, RTR("Retrieving textures"), p_bake_userdata, true); 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<uint8_t> s = rd->texture_get_data(light_accum_tex, i); Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s); Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
img->convert(Image::FORMAT_RGBH); //remove alpha 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<uint8_t> s = rd->texture_get_data(shadowmask_tex, i);
Ref<Image> 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) { 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 { int LightmapperRD::get_bake_texture_count() const {
return bake_textures.size(); return lightmap_textures.size();
} }
Ref<Image> LightmapperRD::get_bake_texture(int p_index) const { Ref<Image> LightmapperRD::get_bake_texture(int p_index) const {
ERR_FAIL_INDEX_V(p_index, bake_textures.size(), Ref<Image>()); ERR_FAIL_INDEX_V(p_index, lightmap_textures.size(), Ref<Image>());
return bake_textures[p_index]; return lightmap_textures[p_index];
}
int LightmapperRD::get_shadowmask_texture_count() const {
return shadowmask_textures.size();
}
Ref<Image> LightmapperRD::get_shadowmask_texture(int p_index) const {
ERR_FAIL_INDEX_V(p_index, shadowmask_textures.size(), Ref<Image>());
return shadowmask_textures[p_index];
} }
int LightmapperRD::get_bake_mesh_count() const { 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 { 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; 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.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; 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; return uv_ofs;

View File

@@ -57,7 +57,8 @@ class LightmapperRD : public Lightmapper {
uint32_t bounces = 0; uint32_t bounces = 0;
float bounce_indirect_energy = 0.0f; float bounce_indirect_energy = 0.0f;
uint32_t pad[3] = {}; int shadowmask_light_idx = 0;
uint32_t pad[2] = {};
}; };
struct MeshInstance { struct MeshInstance {
@@ -202,6 +203,7 @@ class LightmapperRD : public Lightmapper {
Vector<MeshInstance> mesh_instances; Vector<MeshInstance> mesh_instances;
Vector<Light> lights; Vector<Light> lights;
Vector<String> light_names;
struct TriangleSort { struct TriangleSort {
uint32_t cell_index = 0; uint32_t cell_index = 0;
@@ -253,7 +255,8 @@ class LightmapperRD : public Lightmapper {
uint32_t pad = 0; uint32_t pad = 0;
}; };
Vector<Ref<Image>> bake_textures; Vector<Ref<Image>> lightmap_textures;
Vector<Ref<Image>> shadowmask_textures;
Vector<Color> probe_values; Vector<Color> probe_values;
struct DenoiseParams { struct DenoiseParams {
@@ -275,20 +278,22 @@ class LightmapperRD : public Lightmapper {
BakeError _denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &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 _denoise(RenderingDevice *p_rd, Ref<RDShaderFile> &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<RDShaderFile> &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); BakeError _pack_l1(RenderingDevice *rd, Ref<RDShaderFile> &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); 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<Image> _read_pfm(const String &p_name); Ref<Image> _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, const String &p_exe); 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: public:
virtual void add_mesh(const MeshData &p_mesh) override; 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_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(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_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(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_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 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<Image> &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<Image> &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; int get_bake_texture_count() const override;
Ref<Image> get_bake_texture(int p_index) const override; Ref<Image> get_bake_texture(int p_index) const override;
int get_shadowmask_texture_count() const override;
Ref<Image> get_shadowmask_texture(int p_index) const override;
int get_bake_mesh_count() const override; int get_bake_mesh_count() const override;
Variant get_bake_mesh_userdata(int p_index) const override; Variant get_bake_mesh_userdata(int p_index) const override;
Rect2 get_bake_mesh_uv_scale(int p_index) const override; Rect2 get_bake_mesh_uv_scale(int p_index) const override;

View File

@@ -17,6 +17,9 @@ layout(set = 0, binding = 0) uniform BakeParameters {
uint bounces; uint bounces;
float bounce_indirect_energy; float bounce_indirect_energy;
int shadowmask_light_idx;
uint pad0;
uint pad1;
} }
bake_params; bake_params;

View File

@@ -60,7 +60,9 @@ layout(rgba16f, set = 1, binding = 4) uniform restrict image2DArray accum_light;
#endif #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; layout(set = 1, binding = 5) uniform texture2D environment;
#endif #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; 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_light = vec3(0.0f);
r_shadow = 0.0f;
vec3 light_pos; vec3 light_pos;
float dist; 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; 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++) { for (uint i = 0; i < bake_params.light_count; i++) {
vec3 light; vec3 light;
vec3 light_dir; 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; direct_light += light * lights.data[i].indirect_energy;
} }
@@ -614,7 +619,6 @@ void main() {
#endif #endif
#ifdef MODE_DIRECT_LIGHT #ifdef MODE_DIRECT_LIGHT
vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz; vec3 normal = texelFetch(sampler2DArray(source_normal, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0).xyz;
if (length(normal) < 0.5) { if (length(normal) < 0.5) {
return; //empty texel, no process return; //empty texel, no process
@@ -631,6 +635,10 @@ void main() {
vec3 light_for_texture = vec3(0.0); vec3 light_for_texture = vec3(0.0);
vec3 light_for_bounces = vec3(0.0); vec3 light_for_bounces = vec3(0.0);
#ifdef USE_SHADOWMASK
float shadowmask_value = 0.0f;
#endif
#ifdef USE_SH_LIGHTMAPS #ifdef USE_SH_LIGHTMAPS
vec4 sh_accum[4] = vec4[]( vec4 sh_accum[4] = vec4[](
vec4(0.0, 0.0, 0.0, 1.0), 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++) { for (uint i = 0; i < bake_params.light_count; i++) {
vec3 light; vec3 light;
vec3 light_dir; 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) { if (lights.data[i].static_bake) {
light_for_texture += light; light_for_texture += light;
@@ -669,6 +678,12 @@ void main() {
} }
light_for_bounces += light * lights.data[i].indirect_energy; 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; 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)); imageStore(accum_light, ivec3(atlas_pos, params.atlas_slice), vec4(light_for_texture, 1.0));
#endif #endif
#ifdef USE_SHADOWMASK
imageStore(shadowmask, ivec3(atlas_pos, params.atlas_slice), vec4(shadowmask_value, shadowmask_value, shadowmask_value, 1.0));
#endif
#endif #endif
#ifdef MODE_BOUNCE_LIGHT #ifdef MODE_BOUNCE_LIGHT
@@ -850,7 +869,7 @@ void main() {
#endif #endif
#ifdef MODE_DILATE #if defined(MODE_DILATE)
vec4 c = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0); vec4 c = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0);
//sides first, as they are closer //sides first, as they are closer

View File

@@ -130,6 +130,52 @@ TypedArray<TextureLayered> LightmapGIData::get_lightmap_textures() const {
return storage_light_textures; return storage_light_textures;
} }
void LightmapGIData::set_shadowmask_textures(const TypedArray<TextureLayered> &p_data) {
storage_shadowmask_textures = p_data;
if (p_data.is_empty()) {
combined_shadowmask_texture = Ref<TextureLayered>();
_reset_shadowmask_textures();
return;
}
if (p_data.size() == 1) {
combined_shadowmask_texture = p_data[0];
} else {
Vector<Ref<Image>> images;
for (int i = 0; i < p_data.size(); i++) {
Ref<TextureLayered> 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<Texture2DArray> combined_texture;
combined_texture.instantiate();
combined_texture->create_from_images(images);
combined_shadowmask_texture = combined_texture;
}
_reset_shadowmask_textures();
}
TypedArray<TextureLayered> 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 { RID LightmapGIData::get_rid() const {
return lightmap; 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); 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) { void LightmapGIData::set_uses_spherical_harmonics(bool p_enable) {
uses_spherical_harmonics = p_enable; uses_spherical_harmonics = p_enable;
_reset_lightmap_textures(); _reset_lightmap_textures();
@@ -159,6 +209,14 @@ bool LightmapGIData::_is_using_packed_directional() const {
return _uses_packed_directional; 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) { 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()) { if (p_points.size()) {
int pc = 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("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("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("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); 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); 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, "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::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::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"); 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::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"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "light_textures", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_INTERNAL), "_set_light_textures_data", "_get_light_textures_data");
#endif #endif
BIND_ENUM_CONSTANT(SHADOWMASK_MODE_NONE);
BIND_ENUM_CONSTANT(SHADOWMASK_MODE_REPLACE);
BIND_ENUM_CONSTANT(SHADOWMASK_MODE_OVERLAY);
} }
LightmapGIData::LightmapGIData() { 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<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_compress) const { LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_is_shadowmask, bool p_compress) const {
Vector<Ref<Image>> images; Vector<Ref<Image>> 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++) { 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(); const int slice_count = images.size();
@@ -765,7 +831,7 @@ LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref<Li
texture_image->blit_rect(images[i * slices_per_texture + j], Rect2i(0, 0, slice_width, slice_height), Point2i(0, slice_height * j)); texture_image->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"; const String config_path = atlas_path + ".import";
Ref<ConfigFile> config; Ref<ConfigFile> config;
@@ -790,7 +856,12 @@ LightmapGI::BakeError LightmapGI::_save_and_reimport_atlas_textures(const Ref<Li
config->save(config_path); config->save(config_path);
// Save the file. // 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); 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<DirectionalLight3D>(light)) { if (Object::cast_to<DirectionalLight3D>(light)) {
DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light);
if (l->get_sky_mode() != DirectionalLight3D::SKY_MODE_SKY_ONLY) { 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<OmniLight3D>(light)) { } else if (Object::cast_to<OmniLight3D>(light)) {
OmniLight3D *l = Object::cast_to<OmniLight3D>(light); OmniLight3D *l = Object::cast_to<OmniLight3D>(light);
if (use_physical_light_units) { if (use_physical_light_units) {
energy *= (1.0 / (Math_PI * 4.0)); 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<SpotLight3D>(light)) { } else if (Object::cast_to<SpotLight3D>(light)) {
SpotLight3D *l = Object::cast_to<SpotLight3D>(light); SpotLight3D *l = Object::cast_to<SpotLight3D>(light);
if (use_physical_light_units) { if (use_physical_light_units) {
energy *= (1.0 / Math_PI); 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++) { 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) { if (bake_err == Lightmapper::BAKE_ERROR_TEXTURE_EXCEEDS_MAX_SIZE) {
return BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL; 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. // POSTBAKE: Save Textures.
TypedArray<TextureLayered> lightmap_textures; TypedArray<TextureLayered> lightmap_textures;
TypedArray<TextureLayered> shadowmask_textures;
const String texture_filename = p_image_data_path.get_basename(); 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. // 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); 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<LightmapGIData> gi_data; Ref<LightmapGIData> gi_data;
if (get_light_data().is_valid()) { 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); 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_spherical_harmonics(directional);
gi_data->_set_uses_packed_directional(directional); // New SH lightmaps are packed automatically. 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); set_light_data(gi_data);
update_configuration_warnings();
return BAKE_ERROR_OK; return BAKE_ERROR_OK;
} }
@@ -1452,6 +1539,7 @@ void LightmapGI::set_light_data(const Ref<LightmapGIData> &p_data) {
if (is_inside_tree()) { if (is_inside_tree()) {
_assign_lightmaps(); _assign_lightmaps();
} }
light_data->update_shadowmask_mode(shadowmask_mode);
} }
update_gizmos(); update_gizmos();
@@ -1506,6 +1594,19 @@ bool LightmapGI::is_directional() const {
return directional; 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) { void LightmapGI::set_use_texture_for_bounces(bool p_enable) {
use_texture_for_bounces = 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())); 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; 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) #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())); warnings.push_back(vformat(RTR("Lightmaps cannot be baked on %s. Rendering existing baked lightmaps will still work."), OS::get_singleton()->get_name()));
#else #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("set_directional", "directional"), &LightmapGI::set_directional);
ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_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("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); 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::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::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::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, "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, "interior"), "set_interior", "is_interior");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser");

View File

@@ -43,12 +43,23 @@ class LightmapGIData : public Resource {
GDCLASS(LightmapGIData, Resource); GDCLASS(LightmapGIData, Resource);
RES_BASE_EXTENSION("lmbake") 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. // The 'merged' texture atlases actually used by the renderer.
Ref<TextureLayered> combined_light_texture; Ref<TextureLayered> combined_light_texture;
Ref<TextureLayered> combined_shadowmask_texture;
// The temporary texture atlas arrays which are used for storage. // The temporary texture atlas arrays which are used for storage.
// If a single atlas is too large, it's split and recombined during loading. // If a single atlas is too large, it's split and recombined during loading.
TypedArray<TextureLayered> storage_light_textures; TypedArray<TextureLayered> storage_light_textures;
TypedArray<TextureLayered> storage_shadowmask_textures;
bool uses_spherical_harmonics = false; bool uses_spherical_harmonics = false;
bool interior = false; bool interior = false;
@@ -74,6 +85,7 @@ class LightmapGIData : public Resource {
Dictionary _get_probe_data() const; Dictionary _get_probe_data() const;
void _reset_lightmap_textures(); void _reset_lightmap_textures();
void _reset_shadowmask_textures();
protected: protected:
static void _bind_methods(); static void _bind_methods();
@@ -101,6 +113,9 @@ public:
void _set_uses_packed_directional(bool p_enable); void _set_uses_packed_directional(bool p_enable);
bool _is_using_packed_directional() const; bool _is_using_packed_directional() const;
void update_shadowmask_mode(ShadowmaskMode p_mode);
ShadowmaskMode get_shadowmask_mode() const;
bool is_interior() const; bool is_interior() const;
float get_baked_exposure() const; float get_baked_exposure() const;
@@ -116,6 +131,11 @@ public:
void set_lightmap_textures(const TypedArray<TextureLayered> &p_data); void set_lightmap_textures(const TypedArray<TextureLayered> &p_data);
TypedArray<TextureLayered> get_lightmap_textures() const; TypedArray<TextureLayered> get_lightmap_textures() const;
void set_shadowmask_textures(const TypedArray<TextureLayered> &p_data);
TypedArray<TextureLayered> get_shadowmask_textures() const;
void clear_shadowmask_textures();
bool has_shadowmask_textures();
virtual RID get_rid() const override; virtual RID get_rid() const override;
LightmapGIData(); LightmapGIData();
~LightmapGIData(); ~LightmapGIData();
@@ -179,6 +199,7 @@ private:
float environment_custom_energy = 1.0; float environment_custom_energy = 1.0;
bool directional = false; bool directional = false;
bool use_texture_for_bounces = true; bool use_texture_for_bounces = true;
LightmapGIData::ShadowmaskMode shadowmask_mode = LightmapGIData::SHADOWMASK_MODE_NONE;
GenerateProbes gen_probes = GENERATE_PROBES_SUBDIV_8; GenerateProbes gen_probes = GENERATE_PROBES_SUBDIV_8;
Ref<CameraAttributes> camera_attributes; Ref<CameraAttributes> camera_attributes;
@@ -249,7 +270,7 @@ private:
void _plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle); 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<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds); void _gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool> &positions_used, const AABB &p_bounds);
BakeError _save_and_reimport_atlas_textures(const Ref<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_compress = false) const; BakeError _save_and_reimport_atlas_textures(const Ref<Lightmapper> p_lightmapper, const String &p_base_name, TypedArray<TextureLayered> &r_textures, bool p_is_shadowmask = false, bool p_compress = false) const;
protected: protected:
void _validate_property(PropertyInfo &p_property) const; void _validate_property(PropertyInfo &p_property) const;
@@ -275,6 +296,9 @@ public:
void set_directional(bool p_enable); void set_directional(bool p_enable);
bool is_directional() const; 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); void set_use_texture_for_bounces(bool p_enable);
bool is_using_texture_for_bounces() const; bool is_using_texture_for_bounces() const;
@@ -323,6 +347,7 @@ public:
LightmapGI(); LightmapGI();
}; };
VARIANT_ENUM_CAST(LightmapGIData::ShadowmaskMode);
VARIANT_ENUM_CAST(LightmapGI::BakeQuality); VARIANT_ENUM_CAST(LightmapGI::BakeQuality);
VARIANT_ENUM_CAST(LightmapGI::GenerateProbes); VARIANT_ENUM_CAST(LightmapGI::GenerateProbes);
VARIANT_ENUM_CAST(LightmapGI::BakeError); VARIANT_ENUM_CAST(LightmapGI::BakeError);

View File

@@ -133,7 +133,6 @@ public:
GENERATE_PROBES_SUBDIV_8, GENERATE_PROBES_SUBDIV_8,
GENERATE_PROBES_SUBDIV_16, GENERATE_PROBES_SUBDIV_16,
GENERATE_PROBES_SUBDIV_32, GENERATE_PROBES_SUBDIV_32,
}; };
enum LightType { enum LightType {
@@ -178,14 +177,16 @@ public:
}; };
virtual void add_mesh(const MeshData &p_mesh) = 0; 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_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(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_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(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_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 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<Image> &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<Image> &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 int get_bake_texture_count() const = 0;
virtual Ref<Image> get_bake_texture(int p_index) const = 0; virtual Ref<Image> get_bake_texture(int p_index) const = 0;
virtual int get_shadowmask_texture_count() const = 0;
virtual Ref<Image> get_shadowmask_texture(int p_index) const = 0;
virtual int get_bake_mesh_count() const = 0; virtual int get_bake_mesh_count() const = 0;
virtual Variant get_bake_mesh_userdata(int p_index) 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; virtual Rect2 get_bake_mesh_uv_scale(int p_index) const = 0;

View File

@@ -191,6 +191,10 @@ public:
virtual void lightmap_set_probe_capture_update_speed(float p_speed) override {} 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 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 */ /* LIGHTMAP INSTANCE */
bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); } bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }

View File

@@ -1167,6 +1167,7 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data,
// Exposure. // Exposure.
scene_state.lightmaps[i].exposure_normalization = 1.0; 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()) { if (p_render_data->camera_attributes.is_valid()) {
float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); 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); 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; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); 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++) {
if (p_render_data && i < p_render_data->lightmaps->size()) { uint32_t current_lightmap_index = i < scene_state.max_lightmaps ? i : i - scene_state.max_lightmaps;
RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[i]);
RID texture = light_storage->lightmap_get_texture(base); if (p_render_data && current_lightmap_index < p_render_data->lightmaps->size()) {
RID rd_texture = texture_storage->texture_get_rd_texture(texture); RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[current_lightmap_index]);
u.append_id(rd_texture); RID texture;
} else {
u.append_id(default_tex); 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); 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; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); 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); u.append_id(default_tex);
} }

View File

@@ -237,7 +237,7 @@ private:
float normal_xform[12]; float normal_xform[12];
float texture_size[2]; float texture_size[2];
float exposure_normalization; float exposure_normalization;
float pad; uint32_t flags;
}; };
struct LightmapCaptureData { struct LightmapCaptureData {

View File

@@ -477,15 +477,29 @@ RID RenderForwardMobile::_setup_render_pass_uniform_set(RenderListType p_render_
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE; u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
RID default_tex = texture_storage->texture_rd_get_default(RendererRD::TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); 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++) {
if (p_render_data && i < p_render_data->lightmaps->size()) { uint32_t current_lightmap_index = i < scene_state.max_lightmaps ? i : i - scene_state.max_lightmaps;
RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[i]);
RID texture = light_storage->lightmap_get_texture(base); if (p_render_data && current_lightmap_index < p_render_data->lightmaps->size()) {
RID rd_texture = texture_storage->texture_get_rd_texture(texture); RID base = light_storage->lightmap_instance_get_lightmap((*p_render_data->lightmaps)[current_lightmap_index]);
u.append_id(rd_texture); RID texture;
} else {
u.append_id(default_tex); 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); uniforms.push_back(u);
@@ -642,6 +656,7 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co
// Exposure. // Exposure.
scene_state.lightmaps[i].exposure_normalization = 1.0; 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()) { if (p_render_data->camera_attributes.is_valid()) {
float baked_exposure = light_storage->lightmap_get_baked_exposure_normalization(lightmap); 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); float enf = RSG::camera_attributes->camera_attributes_get_exposure_normalization_factor(p_render_data->camera_attributes);

View File

@@ -184,7 +184,7 @@ private:
float normal_xform[12]; float normal_xform[12];
float texture_size[2]; float texture_size[2];
float exposure_normalization; float exposure_normalization;
float pad; uint32_t flags;
}; };
struct LightmapCaptureData { struct LightmapCaptureData {

View File

@@ -1978,9 +1978,34 @@ void fragment_shader(in SceneData scene_data) {
uint shadow0 = 0; uint shadow0 = 0;
uint shadow1 = 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 #ifdef USE_VERTEX_LIGHTING
// Only process the first light's shadow for vertex lighting. // Only process the first light's shadow for vertex lighting.
for (uint i = 0; i < 1; i++) { for (uint i = 0; i < 1; i++) {
#else #else
for (uint i = 0; i < 8; i++) { for (uint i = 0; i < 8; i++) {
if (i >= scene_data.directional_light_count) { if (i >= scene_data.directional_light_count) {
@@ -1988,20 +2013,20 @@ void fragment_shader(in SceneData scene_data) {
} }
#endif #endif
if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) { if (!bool(directional_lights.data[i].mask & instances.data[instance_index].layer_mask)) {
continue; //not masked continue; //not masked
} }
if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) { 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 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) { if (directional_lights.data[i].shadow_opacity > 0.001) {
float depth_z = -vertex.z; float depth_z = -vertex.z;
vec3 light_dir = directional_lights.data[i].direction; 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)))); 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) \ #define BIAS_FUNC(m_var, m_idx) \
m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[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); \ normal_bias -= light_dir * dot(light_dir, normal_bias); \
m_var.xyz += normal_bias; m_var.xyz += normal_bias;
//version with soft shadows, more expensive //version with soft shadows, more expensive
if (sc_use_directional_soft_shadows() && directional_lights.data[i].softshadow_angle > 0) { if (sc_use_directional_soft_shadows() && directional_lights.data[i].softshadow_angle > 0) {
uint blend_count = 0; uint blend_count = 0;
const uint blend_max = directional_lights.data[i].blend_splits ? 2 : 1; 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;
if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
vec4 v = vec4(vertex, 1.0); 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) 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); vec4 pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
// Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. pssm_coord /= pssm_coord.w;
blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y;
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) { } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) {
vec4 v = vec4(vertex, 1.0); vec4 v = vec4(vertex, 1.0);
BIAS_FUNC(v, 2)
pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); BIAS_FUNC(v, 1)
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);
pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
// Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. // 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) { } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
vec4 v = vec4(vertex, 1.0); vec4 v = vec4(vertex, 1.0);
BIAS_FUNC(v, 3)
pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); BIAS_FUNC(v, 2)
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);
pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
// Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. // 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 { } else {
pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached) vec4 v = vec4(vertex, 1.0);
blur_factor2 = 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; 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 = 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, shadow2, pssm_blend);
}
}
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 #ifdef USE_VERTEX_LIGHTING
diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a); diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
specular_light *= mix(1.0, shadow, specular_light_interp.a); specular_light *= mix(1.0, shadow, specular_light_interp.a);
#endif #endif
#undef BIAS_FUNC #undef BIAS_FUNC
} // shadows } // shadows
if (i < 4) { if (i < 4) {
shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8); shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8);
} else { } else {
shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8); 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 #endif // SHADOWS_DISABLED
#ifndef USE_VERTEX_LIGHTING #ifndef USE_VERTEX_LIGHTING

View File

@@ -200,11 +200,16 @@ directional_lights;
#define LIGHTMAP_FLAG_USE_DIRECTION 1 #define LIGHTMAP_FLAG_USE_DIRECTION 1
#define LIGHTMAP_FLAG_USE_SPECULAR_DIRECTION 2 #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 { struct Lightmap {
mat3 normal_xform; mat3 normal_xform;
vec2 light_texture_size; vec2 light_texture_size;
float exposure_normalization; float exposure_normalization;
float pad; uint flags;
}; };
layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { 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 = 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]; layout(set = 1, binding = 8) uniform texture3D voxel_gi_textures[MAX_VOXEL_GI_INSTANCES];

View File

@@ -1434,30 +1434,54 @@ void main() {
uint shadow0 = 0; uint shadow0 = 0;
uint shadow1 = 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 #ifdef USE_VERTEX_LIGHTING
// Only process the first light's shadow for vertex lighting. // Only process the first light's shadow for vertex lighting.
for (uint i = 0; i < 1; i++) { for (uint i = 0; i < 1; i++) {
#else #else
for (uint i = 0; i < sc_directional_lights(); i++) { for (uint i = 0; i < sc_directional_lights(); i++) {
#endif #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)) { if (directional_lights.data[i].bake_mode == LIGHT_BAKE_STATIC && bool(instances.data[draw_call.instance_index].flags & INSTANCE_FLAGS_USE_LIGHTMAP)) {
continue; //not masked 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)) { float shadow = 1.0;
continue; // Statically baked light and object uses lightmap, skip.
}
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) { vec4 pssm_coord;
float depth_z = -vertex.z; float blur_factor;
vec3 light_dir = directional_lights.data[i].direction;
vec4 pssm_coord; vec3 base_normal_bias = normalize(normal_interp) * (1.0 - max(0.0, dot(light_dir, -normalize(normal_interp))));
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) \ #define BIAS_FUNC(m_var, m_idx) \
m_var.xyz += light_dir * directional_lights.data[i].shadow_bias[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); \ normal_bias -= light_dir * dot(light_dir, normal_bias); \
m_var.xyz += 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) { if (depth_z < directional_lights.data[i].shadow_split_offsets.x) {
vec4 v = vec4(vertex, 1.0); vec4 v = vec4(vertex, 1.0);
BIAS_FUNC(v, 1)
pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); BIAS_FUNC(v, 0)
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. pssm_coord = (directional_lights.data[i].shadow_matrix1 * v);
blur_factor2 = directional_lights.data[i].shadow_split_offsets.x / directional_lights.data[i].shadow_split_offsets.y; blur_factor = 1.0;
} else if (depth_z < 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); vec4 v = vec4(vertex, 1.0);
BIAS_FUNC(v, 2)
pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); BIAS_FUNC(v, 1)
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);
pssm_coord = (directional_lights.data[i].shadow_matrix2 * v);
// Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. // 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) { } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) {
vec4 v = vec4(vertex, 1.0); vec4 v = vec4(vertex, 1.0);
BIAS_FUNC(v, 3)
pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); BIAS_FUNC(v, 2)
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);
pssm_coord = (directional_lights.data[i].shadow_matrix3 * v);
// Adjust shadow blur with reference to the first split to reduce discrepancy between shadow splits. // 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 { } else {
pssm_blend = 0.0; //if no blend, same coord will be used (divide by z will result in same value, and already cached) vec4 v = vec4(vertex, 1.0);
blur_factor2 = 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; 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); bool blend_split = sc_directional_light_blend_split(i);
shadow = mix(shadow, shadow2, pssm_blend); 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 #ifdef USE_VERTEX_LIGHTING
diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a); diffuse_light *= mix(1.0, shadow, diffuse_light_interp.a);
specular_light *= mix(1.0, shadow, specular_light_interp.a); specular_light *= mix(1.0, shadow, specular_light_interp.a);
#endif #endif
#undef BIAS_FUNC #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) { #ifdef USE_LIGHTMAP
shadow0 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << (i * 8); } else { // shadowmask_mode == LIGHTMAP_SHADOWMASK_MODE_ONLY
} else {
shadow1 |= uint(clamp(shadow * 255.0, 0.0, 255.0)) << ((i - 4) * 8); #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 #endif // SHADOWS_DISABLED
#ifndef USE_VERTEX_LIGHTING #ifndef USE_VERTEX_LIGHTING

View File

@@ -246,11 +246,16 @@ directional_lights;
#define LIGHTMAP_FLAG_USE_DIRECTION 1 #define LIGHTMAP_FLAG_USE_DIRECTION 1
#define LIGHTMAP_FLAG_USE_SPECULAR_DIRECTION 2 #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 { struct Lightmap {
mediump mat3 normal_xform; mediump mat3 normal_xform;
vec2 light_texture_size; vec2 light_texture_size;
float exposure_normalization; float exposure_normalization;
float pad; uint flags;
}; };
layout(set = 0, binding = 7, std140) restrict readonly buffer Lightmaps { 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; layout(set = 1, binding = 5) uniform highp texture2D directional_shadow_atlas;
// this needs to change to providing just the lightmap we're using.. // 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 #ifdef USE_MULTIVIEW
layout(set = 1, binding = 9) uniform highp texture2DArray depth_buffer; layout(set = 1, binding = 9) uniform highp texture2DArray depth_buffer;

View File

@@ -55,12 +55,18 @@ LightStorage::LightStorage() {
if (textures_per_stage <= 256) { if (textures_per_stage <= 256) {
lightmap_textures.resize(32); lightmap_textures.resize(32);
shadowmask_textures.resize(32);
} else { } else {
lightmap_textures.resize(1024); lightmap_textures.resize(1024);
shadowmask_textures.resize(1024);
} }
for (int i = 0; i < lightmap_textures.size(); i++) { for (RID &lightmap_texture : lightmap_textures) {
lightmap_textures.write[i] = texture_storage->texture_rd_get_default(TextureStorage::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); 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; 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 */ /* LIGHTMAP INSTANCE */
RID LightStorage::lightmap_instance_create(RID p_lightmap) { RID LightStorage::lightmap_instance_create(RID p_lightmap) {

View File

@@ -329,6 +329,8 @@ private:
struct Lightmap { struct Lightmap {
RID light_texture; RID light_texture;
RID shadow_texture;
RS::ShadowmaskMode shadowmask_mode = RS::SHADOWMASK_MODE_NONE;
bool uses_spherical_harmonics = false; bool uses_spherical_harmonics = false;
bool interior = false; bool interior = false;
AABB bounds = AABB(Vector3(), Vector3(1, 1, 1)); AABB bounds = AABB(Vector3(), Vector3(1, 1, 1));
@@ -356,6 +358,8 @@ private:
mutable RID_Owner<Lightmap, true> lightmap_owner; mutable RID_Owner<Lightmap, true> lightmap_owner;
Vector<RID> shadowmask_textures;
/* LIGHTMAP INSTANCE */ /* LIGHTMAP INSTANCE */
struct LightmapInstance { struct LightmapInstance {
@@ -985,6 +989,10 @@ public:
Dependency *lightmap_get_dependency(RID p_lightmap) const; 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 { virtual float lightmap_get_probe_capture_update_speed() const override {
return lightmap_probe_capture_update_speed; return lightmap_probe_capture_update_speed;
} }
@@ -1027,6 +1035,12 @@ public:
return lightmap_textures; 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 */ /* LIGHTMAP INSTANCE */
bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); } bool owns_lightmap_instance(RID p_rid) { return lightmap_instance_owner.owns(p_rid); }

View File

@@ -489,6 +489,10 @@ public:
FUNC1RC(PackedInt32Array, lightmap_get_probe_capture_bsp_tree, RID) FUNC1RC(PackedInt32Array, lightmap_get_probe_capture_bsp_tree, RID)
FUNC1(lightmap_set_probe_capture_update_speed, float) 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 */ /* Shadow Atlas */
FUNC0R(RID, shadow_atlas_create) FUNC0R(RID, shadow_atlas_create)
FUNC3(shadow_atlas_set_size, RID, int, bool) FUNC3(shadow_atlas_set_size, RID, int, bool)

View File

@@ -175,6 +175,10 @@ public:
virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0; virtual void lightmap_set_probe_capture_update_speed(float p_speed) = 0;
virtual float lightmap_get_probe_capture_update_speed() const = 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 */ /* LIGHTMAP INSTANCE */
virtual RID lightmap_instance_create(RID p_lightmap) = 0; virtual RID lightmap_instance_create(RID p_lightmap) = 0;

View File

@@ -709,6 +709,13 @@ public:
/* LIGHTMAP */ /* LIGHTMAP */
enum ShadowmaskMode {
SHADOWMASK_MODE_NONE,
SHADOWMASK_MODE_REPLACE,
SHADOWMASK_MODE_OVERLAY,
SHADOWMASK_MODE_ONLY,
};
virtual RID lightmap_create() = 0; virtual RID lightmap_create() = 0;
virtual void lightmap_set_textures(RID p_lightmap, RID p_light, bool p_uses_spherical_haromics) = 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 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 lightmap_set_probe_capture_update_speed(float p_speed) = 0;
virtual void lightmaps_set_bicubic_filter(bool p_enable) = 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 */ /* PARTICLES API */
virtual RID particles_create() = 0; virtual RID particles_create() = 0;