Skip to content

Commit 054340b

Browse files
GeometrorCalinoulander-vr
committed
Add a supersampling option to LightmapGI
This provides increased lightmap quality with less noise, smoother shadows and better small-scale shadow detail. The downside is that this significantly increases bake times and memory usage while baking lightmaps, so this option is disabled by default. Co-authored-by: Hugo Locurcio <[email protected]> Co-authored-by: landervr <[email protected]>
1 parent bdf625b commit 054340b

File tree

8 files changed

+114
-58
lines changed

8 files changed

+114
-58
lines changed

doc/classes/LightmapGI.xml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,10 @@
3939
If [code]true[/code], bakes lightmaps to contain directional information as spherical harmonics. This results in more realistic lighting appearance, especially with normal mapped materials and for lights that have their direct light baked ([member Light3D.light_bake_mode] set to [constant Light3D.BAKE_STATIC] and with [member Light3D.editor_only] set to [code]false[/code]). The directional information is also used to provide rough reflections for static and dynamic objects. This has a small run-time performance cost as the shader has to perform more work to interpret the direction information from the lightmap. Directional lightmaps also take longer to bake and result in larger file sizes.
4040
[b]Note:[/b] The property's name has no relationship with [DirectionalLight3D]. [member directional] works with all light types.
4141
</member>
42-
<member name="environment_custom_color" type="Color" setter="set_environment_custom_color" getter="get_environment_custom_color">
42+
<member name="environment_custom_color" type="Color" setter="set_environment_custom_color" getter="get_environment_custom_color" default="Color(1, 1, 1, 1)">
4343
The color to use for environment lighting. Only effective if [member environment_mode] is [constant ENVIRONMENT_MODE_CUSTOM_COLOR].
4444
</member>
45-
<member name="environment_custom_energy" type="float" setter="set_environment_custom_energy" getter="get_environment_custom_energy">
45+
<member name="environment_custom_energy" type="float" setter="set_environment_custom_energy" getter="get_environment_custom_energy" default="1.0">
4646
The color multiplier to use for environment lighting. Only effective if [member environment_mode] is [constant ENVIRONMENT_MODE_CUSTOM_COLOR].
4747
</member>
4848
<member name="environment_custom_sky" type="Sky" setter="set_environment_custom_sky" getter="get_environment_custom_sky">
@@ -67,13 +67,23 @@
6767
</member>
6868
<member name="quality" type="int" setter="set_bake_quality" getter="get_bake_quality" enum="LightmapGI.BakeQuality" default="1">
6969
The quality preset to use when baking lightmaps. This affects bake times, but output file sizes remain mostly identical across quality levels.
70-
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.
70+
To further speed up bake times, decrease [member bounces], disable [member use_denoiser] and/or decrease [member texel_scale].
71+
To further increase quality, enable [member supersampling] and/or increase [member texel_scale].
7172
</member>
7273
<member name="shadowmask_mode" type="int" setter="set_shadowmask_mode" getter="get_shadowmask_mode" enum="LightmapGIData.ShadowmaskMode" default="0" experimental="">
7374
The shadowmasking policy to use for directional shadows on static objects that are baked with this [LightmapGI] instance.
7475
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.
7576
[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.
7677
</member>
78+
<member name="supersampling" type="bool" setter="set_supersampling_enabled" getter="is_supersampling_enabled" default="false">
79+
If [code]true[/code], lightmaps are baked with the texel scale multiplied with [member supersampling_factor] and downsampled before saving the lightmap (so the effective texel density is identical to having supersampling disabled).
80+
Supersampling provides increased lightmap quality with less noise, smoother shadows and better shadowing of small-scale features in objects. However, it may result in significantly increased bake times and memory usage while baking lightmaps. Padding is automatically adjusted to avoid increasing light leaking.
81+
</member>
82+
<member name="supersampling_factor" type="float" setter="set_supersampling_factor" getter="get_supersampling_factor" default="2.0">
83+
The factor by which the texel density is multiplied for supersampling. For best results, use an integer value. While fractional values are allowed, they can result in increased light leaking and a blurry lightmap.
84+
Higher values may result in better quality, but also increase bake times and memory usage while baking.
85+
See [member supersampling] for more information.
86+
</member>
7787
<member name="texel_scale" type="float" setter="set_texel_scale" getter="get_texel_scale" default="1.0">
7888
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.
7989
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.

modules/lightmapper_rd/lightmapper_rd.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,14 @@ void LightmapperRD::_sort_triangle_clusters(uint32_t p_cluster_size, uint32_t p_
234234
}
235235
}
236236

237-
Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata) {
237+
Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, float p_supersampling_factor, BakeStepFunc p_step_function, void *p_bake_userdata) {
238238
Vector<Size2i> sizes;
239239

240240
for (int m_i = 0; m_i < mesh_instances.size(); m_i++) {
241241
MeshInstance &mi = mesh_instances.write[m_i];
242242
Size2i s = Size2i(mi.data.albedo_on_uv2->get_width(), mi.data.albedo_on_uv2->get_height());
243243
sizes.push_back(s);
244-
atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range));
244+
atlas_size = atlas_size.max(s + Size2i(2, 2).maxi(p_denoiser_range) * p_supersampling_factor);
245245
}
246246

247247
int max = nearest_power_of_2_templated(atlas_size.width);
@@ -271,7 +271,10 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_
271271
source_sizes.resize(sizes.size());
272272
source_indices.resize(sizes.size());
273273
for (int i = 0; i < source_indices.size(); i++) {
274-
source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range); // Add padding between lightmaps.
274+
// Add padding between lightmaps.
275+
// Scale the padding if the lightmap will be downsampled at the end of the baking process
276+
// Otherwise the padding would be insufficient.
277+
source_sizes.write[i] = sizes[i] + Vector2i(2, 2).maxi(p_denoiser_range) * p_supersampling_factor;
275278
source_indices.write[i] = i;
276279
}
277280
Vector<Vector3i> atlas_offsets;
@@ -1041,7 +1044,7 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh
10411044
return BAKE_OK;
10421045
}
10431046

1044-
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) {
1047+
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, float p_supersampling_factor) {
10451048
int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser");
10461049
String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path");
10471050

@@ -1074,7 +1077,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
10741077
Vector<Ref<Image>> albedo_images;
10751078
Vector<Ref<Image>> emission_images;
10761079

1077-
BakeError bake_error = _blit_meshes_into_atlas(p_max_texture_size, p_denoiser_range, albedo_images, emission_images, bounds, atlas_size, atlas_slices, p_step_function, p_bake_userdata);
1080+
BakeError bake_error = _blit_meshes_into_atlas(p_max_texture_size, p_denoiser_range, albedo_images, emission_images, bounds, atlas_size, atlas_slices, p_supersampling_factor, p_step_function, p_bake_userdata);
10781081
if (bake_error != BAKE_OK) {
10791082
return bake_error;
10801083
}
@@ -1330,6 +1333,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
13301333
bake_parameters.shadowmask_light_idx = shadowmask_light_idx;
13311334
// Same number of rays for transparency regardless of quality (it's more of a retry rather than shooting new ones).
13321335
bake_parameters.transparency_rays = GLOBAL_GET("rendering/lightmapping/bake_performance/max_transparency_rays");
1336+
bake_parameters.supersampling_factor = p_supersampling_factor;
13331337

13341338
bake_parameters_buffer = rd->uniform_buffer_create(sizeof(BakeParameters));
13351339
rd->buffer_update(bake_parameters_buffer, 0, sizeof(BakeParameters), &bake_parameters);

modules/lightmapper_rd/lightmapper_rd.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class LightmapperRD : public Lightmapper {
5959
float bounce_indirect_energy = 0.0f;
6060
uint32_t shadowmask_light_idx = 0;
6161
uint32_t transparency_rays = 0;
62-
uint32_t pad[1] = {};
62+
float supersampling_factor = 0.0f;
6363
};
6464

6565
struct MeshInstance {
@@ -260,6 +260,11 @@ class LightmapperRD : public Lightmapper {
260260
Vector<Ref<Image>> shadowmask_textures;
261261
Vector<Color> probe_values;
262262

263+
struct DilateParams {
264+
uint32_t radius;
265+
uint32_t pad[3];
266+
};
267+
263268
struct DenoiseParams {
264269
float spatial_bandwidth;
265270
float light_bandwidth;
@@ -271,7 +276,7 @@ class LightmapperRD : public Lightmapper {
271276
float pad[2];
272277
};
273278

274-
BakeError _blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata);
279+
BakeError _blit_meshes_into_atlas(int p_max_texture_size, int p_denoiser_range, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, float p_supersampling_factor, BakeStepFunc p_step_function, void *p_bake_userdata);
275280
void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, uint32_t p_cluster_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &r_triangle_indices_buffer, RID &r_cluster_indices_buffer, RID &r_cluster_aabbs_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
276281
void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform);
277282

@@ -289,7 +294,7 @@ class LightmapperRD : public Lightmapper {
289294
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;
290295
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;
291296
virtual void add_probe(const Vector3 &p_position) override;
292-
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;
297+
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, float p_supersampling_factor = 1.0f) override;
293298

294299
int get_bake_texture_count() const override;
295300
Ref<Image> get_bake_texture(int p_index) const override;

modules/lightmapper_rd/lm_common_inc.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ layout(set = 0, binding = 0) uniform BakeParameters {
1818
float bounce_indirect_energy;
1919
int shadowmask_light_idx;
2020
uint transparency_rays;
21-
uint pad0;
21+
float supersampling_factor;
2222
}
2323
bake_params;
2424

modules/lightmapper_rd/lm_compute.glsl

Lines changed: 30 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ void main() {
820820
// Empty texel, try again.
821821
neighbor_position.xyz = texelFetch(sampler2DArray(source_position, linear_sampler), ivec3(atlas_pos + ivec2(-1, 0), params.atlas_slice), 0).xyz;
822822
}
823-
float texel_size_world_space = distance(position, neighbor_position.xyz);
823+
float texel_size_world_space = distance(position, neighbor_position.xyz) * bake_params.supersampling_factor;
824824

825825
vec3 light_for_texture = vec3(0.0);
826826
vec3 light_for_bounces = vec3(0.0);
@@ -1059,43 +1059,35 @@ void main() {
10591059

10601060
#endif
10611061

1062-
#if defined(MODE_DILATE)
1063-
1064-
vec4 c = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0);
1065-
//sides first, as they are closer
1066-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 0), params.atlas_slice), 0);
1067-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, 1), params.atlas_slice), 0);
1068-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 0), params.atlas_slice), 0);
1069-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, -1), params.atlas_slice), 0);
1070-
//endpoints second
1071-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, -1), params.atlas_slice), 0);
1072-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 1), params.atlas_slice), 0);
1073-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, -1), params.atlas_slice), 0);
1074-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 1), params.atlas_slice), 0);
1075-
1076-
//far sides third
1077-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 0), params.atlas_slice), 0);
1078-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, 2), params.atlas_slice), 0);
1079-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 0), params.atlas_slice), 0);
1080-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(0, -2), params.atlas_slice), 0);
1081-
1082-
//far-mid endpoints
1083-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, -1), params.atlas_slice), 0);
1084-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 1), params.atlas_slice), 0);
1085-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, -1), params.atlas_slice), 0);
1086-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 1), params.atlas_slice), 0);
1087-
1088-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, -2), params.atlas_slice), 0);
1089-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-1, 2), params.atlas_slice), 0);
1090-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, -2), params.atlas_slice), 0);
1091-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(1, 2), params.atlas_slice), 0);
1092-
//far endpoints
1093-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, -2), params.atlas_slice), 0);
1094-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(-2, 2), params.atlas_slice), 0);
1095-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, -2), params.atlas_slice), 0);
1096-
c = c.a > 0.5 ? c : texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos + ivec2(2, 2), params.atlas_slice), 0);
1097-
1098-
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), c);
1062+
#ifdef MODE_DILATE
1063+
1064+
const int max_radius = int(4.0 * bake_params.supersampling_factor);
1065+
const ivec2 directions[8] = ivec2[8](ivec2(-1, 0), ivec2(0, 1), ivec2(1, 0), ivec2(0, -1), ivec2(-1, -1), ivec2(-1, 1), ivec2(1, -1), ivec2(1, 1));
1066+
1067+
vec4 texel_color = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(atlas_pos, params.atlas_slice), 0);
1068+
1069+
for (int radius = 1; radius <= max_radius; radius++) {
1070+
for (uint i = 0; i < 8; i++) {
1071+
const ivec2 sample_pos = atlas_pos + directions[i] * radius;
1072+
// Texture bounds check for robustness.
1073+
if (any(lessThan(sample_pos, ivec2(0))) ||
1074+
any(greaterThanEqual(sample_pos, textureSize(source_light, 0).xy))) {
1075+
continue;
1076+
}
1077+
1078+
vec4 neighbor_color = texelFetch(sampler2DArray(source_light, linear_sampler), ivec3(sample_pos, params.atlas_slice), 0);
1079+
if (neighbor_color.a > 0.5) {
1080+
texel_color = neighbor_color;
1081+
break;
1082+
}
1083+
}
1084+
1085+
if (texel_color.a > 0.5) {
1086+
break;
1087+
}
1088+
}
1089+
1090+
imageStore(dest_light, ivec3(atlas_pos, params.atlas_slice), texel_color);
10991091

11001092
#endif
11011093

0 commit comments

Comments
 (0)