Skip to content

Commit

Permalink
Implement shadowmasks for LightmapGI
Browse files Browse the repository at this point in the history
Co-authored-by: dearthdev <nathandearthdev@gmail.com>
  • Loading branch information
BlueCube3310 and DearthDev committed Dec 11, 2024
1 parent a40fc23 commit 44cb13f
Show file tree
Hide file tree
Showing 27 changed files with 1,045 additions and 394 deletions.
5 changes: 5 additions & 0 deletions doc/classes/LightmapGI.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@
The quality preset to use when baking lightmaps. This affects bake times, but output file sizes remain mostly identical across quality levels.
To further speed up bake times, decrease [member bounces], disable [member use_denoiser] and increase the lightmap texel size on 3D scenes in the Import dock.
</member>
<member name="shadowmask_mode" type="int" setter="set_shadowmask_mode" getter="get_shadowmask_mode" enum="LightmapGIData.ShadowmaskMode" default="0">
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">
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.
Expand Down
17 changes: 17 additions & 0 deletions doc/classes/LightmapGIData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,22 @@
<member name="lightmap_textures" type="TextureLayered[]" setter="set_lightmap_textures" getter="get_lightmap_textures" default="[]">
The lightmap atlas textures generated by the lightmapper.
</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>
<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>
<constant name="SHADOWMASK_MODE_ONLY" value="3" enum="ShadowmaskMode">
Shadowmasking is enabled. Directional shadows will be rendered using the shadowmask texture regardless of their distance from the camera. This mode is faster than other shadowmasking modes (and can be faster than having shadowmasking disabled), but it can make shadows up close look blurry if the lightmap texel size is high. On the bright side, this mode prevents any visual transitions from occurring depending on the distance between the camera and the shadow.
</constant>
</constants>
</class>
58 changes: 53 additions & 5 deletions drivers/gles3/rasterizer_scene_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2660,14 +2660,14 @@ void RasterizerSceneGLES3::render_scene(const Ref<RenderSceneBuffers> &p_render_
glBlitFramebuffer(0, 0, size.x, size.y,
0, 0, size.x, size.y,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
glBindTexture(GL_TEXTURE_2D, backbuffer);
}
if (scene_state.used_depth_texture) {
glBlitFramebuffer(0, 0, size.x, size.y,
0, 0, size.x, size.y,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 6);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
glBindTexture(GL_TEXTURE_2D, backbuffer_depth);
}
}
Expand Down Expand Up @@ -3245,8 +3245,28 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_OMNI;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_SPOT;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHT_DIRECTIONAL;
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
spec_constants |= SceneShaderGLES3::DISABLE_REFLECTION_PROBE;

bool disable_lightmaps = true;

// Additive directional passes may use shadowmasks, so enable lightmaps for them.
if (pass >= int32_t(inst->light_passes.size()) && inst->lightmap_instance.is_valid()) {
GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);

if (lm->shadowmask_mode != RS::SHADOWMASK_MODE_NONE) {
spec_constants |= SceneShaderGLES3::USE_LIGHTMAP;
disable_lightmaps = false;

if (lightmap_bicubic_upscale) {
spec_constants |= SceneShaderGLES3::LIGHTMAP_BICUBIC_FILTER;
}
}
}

if (disable_lightmaps) {
spec_constants |= SceneShaderGLES3::DISABLE_LIGHTMAP;
}
}

if (uses_additive_lighting) {
Expand Down Expand Up @@ -3341,6 +3361,33 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
GLuint tex = GLES3::LightStorage::get_singleton()->directional_shadow_get_texture();
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 3);
glBindTexture(GL_TEXTURE_2D, tex);

if (inst->lightmap_instance.is_valid()) {
// Use shadowmasks for directional light passes.
GLES3::LightmapInstance *li = GLES3::LightStorage::get_singleton()->get_lightmap_instance(inst->lightmap_instance);
GLES3::Lightmap *lm = GLES3::LightStorage::get_singleton()->get_lightmap(li->lightmap);

material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SLICE, inst->lightmap_slice_index, shader->version, instance_variant, spec_constants);

Vector4 uv_scale(inst->lightmap_uv_scale.position.x, inst->lightmap_uv_scale.position.y, inst->lightmap_uv_scale.size.x, inst->lightmap_uv_scale.size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_UV_SCALE, uv_scale, shader->version, instance_variant, spec_constants);

if (lightmap_bicubic_upscale) {
Vector2 light_texture_size(lm->light_texture_size.x, lm->light_texture_size.y);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_TEXTURE_SIZE, light_texture_size, shader->version, instance_variant, spec_constants);
}

material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::LIGHTMAP_SHADOWMASK_MODE, (uint32_t)lm->shadowmask_mode, shader->version, instance_variant, spec_constants);

if (lm->shadow_texture.is_valid()) {
tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(lm->shadow_texture);
} else {
tex = GLES3::TextureStorage::get_singleton()->texture_get_texid(GLES3::TextureStorage::get_singleton()->texture_gl_get_default(GLES3::DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE));
}

glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 5);
glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
}
}
}

Expand Down Expand Up @@ -3399,6 +3446,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
};
glUniformMatrix3fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_NORMAL_XFORM, shader->version, instance_variant, spec_constants), 1, GL_FALSE, matrix);
}

} else if (inst->lightmap_sh) {
glUniform4fv(material_storage->shaders.scene_shader.version_get_uniform(SceneShaderGLES3::LIGHTMAP_CAPTURES, shader->version, instance_variant, spec_constants), 9, reinterpret_cast<const GLfloat *>(inst->lightmap_sh->sh));
}
Expand Down Expand Up @@ -3430,7 +3478,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE1_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[0], shader->version, instance_variant, spec_constants);

glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 7);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[0]));
}

Expand All @@ -3448,7 +3496,7 @@ void RasterizerSceneGLES3::_render_list_template(RenderListParameters *p_params,
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_AMBIENT_COLOR, probe->ambient_color * probe->ambient_color_energy, shader->version, instance_variant, spec_constants);
material_storage->shaders.scene_shader.version_set_uniform(SceneShaderGLES3::REFPROBE2_LOCAL_MATRIX, inst->reflection_probes_local_transform_cache[1], shader->version, instance_variant, spec_constants);

glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 8);
glActiveTexture(GL_TEXTURE0 + config->max_texture_image_units - 9);
glBindTexture(GL_TEXTURE_CUBE_MAP, light_storage->reflection_probe_instance_get_texture(inst->reflection_probe_rid_cache[1]));

spec_constants |= SceneShaderGLES3::SECOND_REFLECTION_PROBE;
Expand Down
Loading

0 comments on commit 44cb13f

Please sign in to comment.