|
35 | 35 | #include "lm_raster.glsl.gen.h" |
36 | 36 |
|
37 | 37 | #include "core/config/project_settings.h" |
| 38 | +#include "core/io/dir_access.h" |
38 | 39 | #include "core/math/geometry_2d.h" |
| 40 | +#include "editor/editor_paths.h" |
| 41 | +#include "editor/editor_settings.h" |
39 | 42 | #include "servers/rendering/rendering_device_binds.h" |
40 | 43 |
|
41 | 44 | //uncomment this if you want to see textures from all the process saved |
@@ -671,6 +674,131 @@ LightmapperRD::BakeError LightmapperRD::_dilate(RenderingDevice *rd, Ref<RDShade |
671 | 674 | return BAKE_OK; |
672 | 675 | } |
673 | 676 |
|
| 677 | +Error LightmapperRD::_store_pfm(RenderingDevice *p_rd, RID p_atlas_tex, int p_index, const Size2i &p_atlas_size, const String &p_name) { |
| 678 | + Vector<uint8_t> data = p_rd->texture_get_data(p_atlas_tex, p_index); |
| 679 | + Ref<Image> img = Image::create_from_data(p_atlas_size.width, p_atlas_size.height, false, Image::FORMAT_RGBAH, data); |
| 680 | + img->convert(Image::FORMAT_RGBF); |
| 681 | + Vector<uint8_t> data_float = img->get_data(); |
| 682 | + |
| 683 | + Error err = OK; |
| 684 | + Ref<FileAccess> file = FileAccess::open(p_name, FileAccess::WRITE, &err); |
| 685 | + ERR_FAIL_COND_V_MSG(err, err, vformat("Can't save PFN at path: '%s'.", p_name)); |
| 686 | + file->store_line("PF"); |
| 687 | + file->store_line(vformat("%d %d", img->get_width(), img->get_height())); |
| 688 | +#ifdef BIG_ENDIAN_ENABLED |
| 689 | + file->store_line("1.0"); |
| 690 | +#else |
| 691 | + file->store_line("-1.0"); |
| 692 | +#endif |
| 693 | + file->store_buffer(data_float); |
| 694 | + file->close(); |
| 695 | + |
| 696 | + return OK; |
| 697 | +} |
| 698 | + |
| 699 | +Ref<Image> LightmapperRD::_read_pfm(const String &p_name) { |
| 700 | + Error err = OK; |
| 701 | + Ref<FileAccess> file = FileAccess::open(p_name, FileAccess::READ, &err); |
| 702 | + ERR_FAIL_COND_V_MSG(err, Ref<Image>(), vformat("Can't load PFM at path: '%s'.", p_name)); |
| 703 | + ERR_FAIL_COND_V(file->get_line() != "PF", Ref<Image>()); |
| 704 | + |
| 705 | + Vector<String> new_size = file->get_line().split(" "); |
| 706 | + ERR_FAIL_COND_V(new_size.size() != 2, Ref<Image>()); |
| 707 | + int new_width = new_size[0].to_int(); |
| 708 | + int new_height = new_size[1].to_int(); |
| 709 | + |
| 710 | + float endian = file->get_line().to_float(); |
| 711 | + Vector<uint8_t> new_data = file->get_buffer(file->get_length() - file->get_position()); |
| 712 | + file->close(); |
| 713 | + |
| 714 | +#ifdef BIG_ENDIAN_ENABLED |
| 715 | + if (unlikely(endian < 0.0)) { |
| 716 | + uint32_t count = new_data.size() / 4; |
| 717 | + uint16_t *dst = (uint16_t *)new_data.ptrw(); |
| 718 | + for (uint32_t j = 0; j < count; j++) { |
| 719 | + dst[j * 4] = BSWAP32(dst[j * 4]); |
| 720 | + } |
| 721 | + } |
| 722 | +#else |
| 723 | + if (unlikely(endian > 0.0)) { |
| 724 | + uint32_t count = new_data.size() / 4; |
| 725 | + uint16_t *dst = (uint16_t *)new_data.ptrw(); |
| 726 | + for (uint32_t j = 0; j < count; j++) { |
| 727 | + dst[j * 4] = BSWAP32(dst[j * 4]); |
| 728 | + } |
| 729 | + } |
| 730 | +#endif |
| 731 | + Ref<Image> img = Image::create_from_data(new_width, new_height, false, Image::FORMAT_RGBF, new_data); |
| 732 | + img->convert(Image::FORMAT_RGBAH); |
| 733 | + return img; |
| 734 | +} |
| 735 | + |
| 736 | +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) { |
| 737 | + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); |
| 738 | + |
| 739 | + for (int i = 0; i < p_atlas_slices; i++) { |
| 740 | + String fname_norm_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_norm_%d.pfm", i)); |
| 741 | + _store_pfm(p_rd, p_source_normal_tex, i, p_atlas_size, fname_norm_in); |
| 742 | + |
| 743 | + for (int j = 0; j < (p_bake_sh ? 4 : 1); j++) { |
| 744 | + int index = i * (p_bake_sh ? 4 : 1) + j; |
| 745 | + String fname_light_in = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_light_%d.pfm", index)); |
| 746 | + String fname_out = EditorPaths::get_singleton()->get_cache_dir().path_join(vformat("temp_denoised_%d.pfm", index)); |
| 747 | + |
| 748 | + _store_pfm(p_rd, p_source_light_tex, index, p_atlas_size, fname_light_in); |
| 749 | + |
| 750 | + List<String> args; |
| 751 | + args.push_back("--device"); |
| 752 | + args.push_back("default"); |
| 753 | + |
| 754 | + args.push_back("--filter"); |
| 755 | + args.push_back("RTLightmap"); |
| 756 | + |
| 757 | + args.push_back("--hdr"); |
| 758 | + args.push_back(fname_light_in); |
| 759 | + |
| 760 | + args.push_back("--nrm"); |
| 761 | + args.push_back(fname_norm_in); |
| 762 | + |
| 763 | + args.push_back("--output"); |
| 764 | + args.push_back(fname_out); |
| 765 | + |
| 766 | + String str; |
| 767 | + int exitcode = 0; |
| 768 | + |
| 769 | + Error err = OS::get_singleton()->execute(p_exe, args, &str, &exitcode, true); |
| 770 | + |
| 771 | + da->remove(fname_light_in); |
| 772 | + |
| 773 | + if (err != OK || exitcode != 0) { |
| 774 | + da->remove(fname_out); |
| 775 | + print_verbose(str); |
| 776 | + ERR_FAIL_V_MSG(BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, vformat(TTR("OIDN denoiser failed, return code: %d"), exitcode)); |
| 777 | + } |
| 778 | + |
| 779 | + Ref<Image> img = _read_pfm(fname_out); |
| 780 | + da->remove(fname_out); |
| 781 | + |
| 782 | + ERR_FAIL_COND_V(img.is_null(), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES); |
| 783 | + |
| 784 | + Vector<uint8_t> old_data = p_rd->texture_get_data(p_source_light_tex, index); |
| 785 | + Vector<uint8_t> new_data = img->get_data(); |
| 786 | + img.unref(); // Avoid copy on write. |
| 787 | + |
| 788 | + uint32_t count = old_data.size() / 2; |
| 789 | + const uint16_t *src = (const uint16_t *)old_data.ptr(); |
| 790 | + uint16_t *dst = (uint16_t *)new_data.ptrw(); |
| 791 | + for (uint32_t k = 0; k < count; k += 4) { |
| 792 | + dst[k + 3] = src[k + 3]; |
| 793 | + } |
| 794 | + |
| 795 | + p_rd->texture_update(p_dest_light_tex, index, new_data); |
| 796 | + } |
| 797 | + da->remove(fname_norm_in); |
| 798 | + } |
| 799 | + return BAKE_OK; |
| 800 | +} |
| 801 | + |
674 | 802 | 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 | 803 | RID denoise_params_buffer = p_rd->uniform_buffer_create(sizeof(DenoiseParams)); |
676 | 804 | DenoiseParams denoise_params; |
@@ -742,6 +870,23 @@ LightmapperRD::BakeError LightmapperRD::_denoise(RenderingDevice *p_rd, Ref<RDSh |
742 | 870 | } |
743 | 871 |
|
744 | 872 | 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) { |
| 873 | + int denoiser = GLOBAL_GET("rendering/lightmapping/denoising/denoiser"); |
| 874 | + String oidn_path = EDITOR_GET("filesystem/tools/oidn/oidn_denoise_path"); |
| 875 | + |
| 876 | + if (p_use_denoiser && denoiser == 1) { |
| 877 | + // OIDN (external). |
| 878 | + Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); |
| 879 | + |
| 880 | + if (da->dir_exists(oidn_path)) { |
| 881 | + if (OS::get_singleton()->get_name() == "Windows") { |
| 882 | + oidn_path = oidn_path.path_join("oidnDenoise.exe"); |
| 883 | + } else { |
| 884 | + oidn_path = oidn_path.path_join("oidnDenoise"); |
| 885 | + } |
| 886 | + } |
| 887 | + ERR_FAIL_COND_V_MSG(oidn_path.is_empty() || !da->file_exists(oidn_path), BAKE_ERROR_LIGHTMAP_CANT_PRE_BAKE_MESHES, TTR("OIDN denoiser is selected in the project settings, but no or invalid OIDN executable path is configured in the editor settings.")); |
| 888 | + } |
| 889 | + |
745 | 890 | if (p_step_function) { |
746 | 891 | p_step_function(0.0, RTR("Begin Bake"), p_bake_userdata, true); |
747 | 892 | } |
@@ -1501,8 +1646,15 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d |
1501 | 1646 | } |
1502 | 1647 |
|
1503 | 1648 | { |
1504 | | - SWAP(light_accum_tex, light_accum_tex2); |
1505 | | - 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); |
| 1649 | + BakeError error; |
| 1650 | + if (denoiser == 1) { |
| 1651 | + // OIDN (external). |
| 1652 | + error = _denoise_oidn(rd, light_accum_tex, normal_tex, light_accum_tex, atlas_size, atlas_slices, p_bake_sh, oidn_path); |
| 1653 | + } else { |
| 1654 | + // JNLM (built-in). |
| 1655 | + SWAP(light_accum_tex, light_accum_tex2); |
| 1656 | + 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); |
| 1657 | + } |
1506 | 1658 | if (unlikely(error != BAKE_OK)) { |
1507 | 1659 | return error; |
1508 | 1660 | } |
|
0 commit comments