Skip to content

Commit 1b2b726

Browse files
committed
Replace OIDN denoiser with a JNLM denoiser compute shader implementation.
1 parent fcbc50e commit 1b2b726

File tree

8 files changed

+304
-43
lines changed

8 files changed

+304
-43
lines changed

COPYRIGHT.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ Copyright: 2011, Ole Kniemeyer, MAXON, www.maxon.net
6363
2007-2014, Juan Linietsky, Ariel Manzur
6464
License: Expat and Zlib
6565

66+
Files: ./modules/lightmapper_rd/lm_compute.glsl
67+
Comment: Joint Non-Local Means (JNLM) denoiser
68+
Copyright: 2020, Manuel Prandini
69+
2014-present, Godot Engine contributors
70+
2007-2014, Juan Linietsky, Ariel Manzur
71+
License: Expat
72+
6673
Files: ./platform/android/java/lib/aidl/com/android/*
6774
./platform/android/java/lib/res/layout/status_bar_ongoing_event_progress_bar.xml
6875
./platform/android/java/lib/src/com/google/android/*

doc/classes/LightmapGI.xml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
<member name="camera_attributes" type="CameraAttributes" setter="set_camera_attributes" getter="get_camera_attributes">
2525
The [CameraAttributes] resource that specifies exposure levels to bake at. Auto-exposure and non exposure properties will be ignored. Exposure settings should be used to reduce the dynamic range present when baking. If exposure is too high, the [LightmapGI] will have banding artifacts or may have over-exposure artifacts.
2626
</member>
27+
<member name="denoiser_strength" type="float" setter="set_denoiser_strength" getter="get_denoiser_strength" default="0.1">
28+
The strength of denoising step applied to the generated lightmaps. Only effective if [member use_denoiser] is [code]true[/code].
29+
</member>
2730
<member name="directional" type="bool" setter="set_directional" getter="is_directional" default="false">
2831
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]). 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.
2932
[b]Note:[/b] The property's name has no relationship with [DirectionalLight3D]. [member directional] works with all light types.
@@ -59,8 +62,7 @@
5962
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 doc.
6063
</member>
6164
<member name="use_denoiser" type="bool" setter="set_use_denoiser" getter="is_using_denoiser" default="true">
62-
If [code]true[/code], uses a CPU-based denoising algorithm on the generated lightmap. This eliminates most noise within the generated lightmap at the cost of longer bake times. File sizes are generally not impacted significantly by the use of a denoiser, although lossless compression may do a better job at compressing a denoised image.
63-
[b]Note:[/b] The built-in denoiser (OpenImageDenoise) may crash when denoising lightmaps in large scenes. If you encounter a crash at the end of lightmap baking, try disabling [member use_denoiser].
65+
If [code]true[/code], uses a GPU-based denoising algorithm on the generated lightmap. This eliminates most noise within the generated lightmap at the cost of longer bake times. File sizes are generally not impacted significantly by the use of a denoiser, although lossless compression may do a better job at compressing a denoised image.
6466
</member>
6567
</members>
6668
<constants>

modules/lightmapper_rd/lightmapper_rd.cpp

Lines changed: 95 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -614,25 +614,29 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int
614614
}
615615
}
616616

617-
LightmapperRD::BakeError LightmapperRD::_dilate(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) {
617+
static Vector<RD::Uniform> dilate_or_denoise_common_uniforms(RID &p_source_light_tex, RID &p_dest_light_tex) {
618618
Vector<RD::Uniform> uniforms;
619619
{
620-
{
621-
RD::Uniform u;
622-
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
623-
u.binding = 0;
624-
u.append_id(dest_light_tex);
625-
uniforms.push_back(u);
626-
}
627-
{
628-
RD::Uniform u;
629-
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
630-
u.binding = 1;
631-
u.append_id(source_light_tex);
632-
uniforms.push_back(u);
633-
}
620+
RD::Uniform u;
621+
u.uniform_type = RD::UNIFORM_TYPE_IMAGE;
622+
u.binding = 0;
623+
u.append_id(p_dest_light_tex);
624+
uniforms.push_back(u);
625+
}
626+
{
627+
RD::Uniform u;
628+
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
629+
u.binding = 1;
630+
u.append_id(p_source_light_tex);
631+
uniforms.push_back(u);
634632
}
635633

634+
return uniforms;
635+
}
636+
637+
LightmapperRD::BakeError LightmapperRD::_dilate(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) {
638+
Vector<RD::Uniform> uniforms = dilate_or_denoise_common_uniforms(source_light_tex, dest_light_tex);
639+
636640
RID compute_shader_dilate = rd->shader_create_from_spirv(compute_shader->get_spirv_stages("dilate"));
637641
ERR_FAIL_COND_V(compute_shader_dilate.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); //internal check, should not happen
638642
RID compute_shader_dilate_pipeline = rd->compute_pipeline_create(compute_shader_dilate);
@@ -667,7 +671,77 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade
667671
return BAKE_OK;
668672
}
669673

670-
LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, 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) {
674+
LightmapperRD::BakeError LightmapperRD::_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, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function) {
675+
RID denoise_params_buffer = p_rd->uniform_buffer_create(sizeof(DenoiseParams));
676+
DenoiseParams denoise_params;
677+
denoise_params.spatial_bandwidth = 5.0f;
678+
denoise_params.light_bandwidth = p_denoiser_strength;
679+
denoise_params.albedo_bandwidth = 1.0f;
680+
denoise_params.normal_bandwidth = 0.1f;
681+
denoise_params.filter_strength = 10.0f;
682+
p_rd->buffer_update(denoise_params_buffer, 0, sizeof(DenoiseParams), &denoise_params);
683+
684+
Vector<RD::Uniform> uniforms = dilate_or_denoise_common_uniforms(p_source_light_tex, p_dest_light_tex);
685+
{
686+
RD::Uniform u;
687+
u.uniform_type = RD::UNIFORM_TYPE_TEXTURE;
688+
u.binding = 2;
689+
u.append_id(p_source_normal_tex);
690+
uniforms.push_back(u);
691+
}
692+
{
693+
RD::Uniform u;
694+
u.uniform_type = RD::UNIFORM_TYPE_UNIFORM_BUFFER;
695+
u.binding = 3;
696+
u.append_id(denoise_params_buffer);
697+
uniforms.push_back(u);
698+
}
699+
700+
RID compute_shader_denoise = p_rd->shader_create_from_spirv(p_compute_shader->get_spirv_stages("denoise"));
701+
ERR_FAIL_COND_V(compute_shader_denoise.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES);
702+
703+
RID compute_shader_denoise_pipeline = p_rd->compute_pipeline_create(compute_shader_denoise);
704+
RID denoise_uniform_set = p_rd->uniform_set_create(uniforms, compute_shader_denoise, 1);
705+
706+
// We denoise in fixed size regions and synchronize execution to avoid GPU timeouts.
707+
// We use a region with 1/4 the amount of pixels if we're denoising SH lightmaps, as
708+
// all four of them are denoised in the shader in one dispatch.
709+
const int max_region_size = p_bake_sh ? 512 : 1024;
710+
int x_regions = (p_atlas_size.width - 1) / max_region_size + 1;
711+
int y_regions = (p_atlas_size.height - 1) / max_region_size + 1;
712+
for (int s = 0; s < p_atlas_slices; s++) {
713+
p_push_constant.atlas_slice = s;
714+
715+
for (int i = 0; i < x_regions; i++) {
716+
for (int j = 0; j < y_regions; j++) {
717+
int x = i * max_region_size;
718+
int y = j * max_region_size;
719+
int w = MIN((i + 1) * max_region_size, p_atlas_size.width) - x;
720+
int h = MIN((j + 1) * max_region_size, p_atlas_size.height) - y;
721+
p_push_constant.region_ofs[0] = x;
722+
p_push_constant.region_ofs[1] = y;
723+
724+
RD::ComputeListID compute_list = p_rd->compute_list_begin();
725+
p_rd->compute_list_bind_compute_pipeline(compute_list, compute_shader_denoise_pipeline);
726+
p_rd->compute_list_bind_uniform_set(compute_list, p_compute_base_uniform_set, 0);
727+
p_rd->compute_list_bind_uniform_set(compute_list, denoise_uniform_set, 1);
728+
p_rd->compute_list_set_push_constant(compute_list, &p_push_constant, sizeof(PushConstant));
729+
p_rd->compute_list_dispatch(compute_list, (w - 1) / 8 + 1, (h - 1) / 8 + 1, 1);
730+
p_rd->compute_list_end();
731+
732+
p_rd->submit();
733+
p_rd->sync();
734+
}
735+
}
736+
}
737+
738+
p_rd->free(compute_shader_denoise);
739+
p_rd->free(denoise_params_buffer);
740+
741+
return BAKE_OK;
742+
}
743+
744+
LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, 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) {
671745
if (p_step_function) {
672746
p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true);
673747
}
@@ -1434,27 +1508,11 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d
14341508
p_step_function(0.8, RTR("Denoising"), p_bake_userdata, true);
14351509
}
14361510

1437-
Ref<LightmapDenoiser> denoiser = LightmapDenoiser::create();
1438-
if (denoiser.is_valid()) {
1439-
for (int i = 0; i < atlas_slices * (p_bake_sh ? 4 : 1); i++) {
1440-
Vector<uint8_t> s = rd->texture_get_data(light_accum_tex, i);
1441-
Ref<Image> img = Image::create_from_data(atlas_size.width, atlas_size.height, false, Image::FORMAT_RGBAH, s);
1442-
1443-
Ref<Image> denoised = denoiser->denoise_image(img);
1444-
if (denoised != img) {
1445-
denoised->convert(Image::FORMAT_RGBAH);
1446-
Vector<uint8_t> ds = denoised->get_data();
1447-
denoised.unref(); //avoid copy on write
1448-
{ //restore alpha
1449-
uint32_t count = s.size() / 2; //uint16s
1450-
const uint16_t *src = (const uint16_t *)s.ptr();
1451-
uint16_t *dst = (uint16_t *)ds.ptrw();
1452-
for (uint32_t j = 0; j < count; j += 4) {
1453-
dst[j + 3] = src[j + 3];
1454-
}
1455-
}
1456-
rd->texture_update(light_accum_tex, i, ds);
1457-
}
1511+
{
1512+
SWAP(light_accum_tex, light_accum_tex2);
1513+
BakeError error = _denoise(rd, compute_shader, compute_base_uniform_set, push_constant, light_accum_tex2, normal_tex, light_accum_tex, p_denoiser_strength, atlas_size, atlas_slices, p_bake_sh, p_step_function);
1514+
if (unlikely(error != BAKE_OK)) {
1515+
return error;
14581516
}
14591517
}
14601518

modules/lightmapper_rd/lightmapper_rd.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,19 +229,30 @@ class LightmapperRD : public Lightmapper {
229229
Vector<Ref<Image>> bake_textures;
230230
Vector<Color> probe_values;
231231

232+
struct DenoiseParams {
233+
float spatial_bandwidth;
234+
float light_bandwidth;
235+
float albedo_bandwidth;
236+
float normal_bandwidth;
237+
238+
float filter_strength;
239+
float pad[3];
240+
};
241+
232242
BakeError _blit_meshes_into_atlas(int p_max_texture_size, 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);
233243
void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_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 &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata);
234244
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);
235245

236246
BakeError _dilate(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);
247+
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, const Size2i &p_atlas_size, int p_atlas_slices, bool p_bake_sh, BakeStepFunc p_step_function);
237248

238249
public:
239250
virtual void add_mesh(const MeshData &p_mesh) override;
240251
virtual void add_directional_light(bool p_static, const Vector3 &p_direction, const Color &p_color, float p_energy, float p_angular_distance, float p_shadow_blur) override;
241252
virtual void add_omni_light(bool p_static, const Vector3 &p_position, const Color &p_color, float p_energy, float p_range, float p_attenuation, float p_size, float p_shadow_blur) override;
242253
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_range, float p_attenuation, float p_spot_angle, float p_spot_attenuation, float p_size, float p_shadow_blur) override;
243254
virtual void add_probe(const Vector3 &p_position) override;
244-
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, 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;
255+
virtual BakeError bake(BakeQuality p_quality, bool p_use_denoiser, float p_denoiser_strength, int p_bounces, float p_bias, int p_max_texture_size, bool p_bake_sh, 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;
245256

246257
int get_bake_texture_count() const override;
247258
Ref<Image> get_bake_texture(int p_index) const override;

0 commit comments

Comments
 (0)