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 8be2224
Show file tree
Hide file tree
Showing 27 changed files with 1,041 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" 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">
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
14 changes: 14 additions & 0 deletions doc/classes/LightmapGIData.xml
Original file line number Diff line number Diff line change
Expand Up @@ -60,5 +60,19 @@
<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>
</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 8be2224

Please sign in to comment.