diff options
23 files changed, 549 insertions, 68 deletions
diff --git a/core/image.cpp b/core/image.cpp index f2d78168f6..b126437fa9 100644 --- a/core/image.cpp +++ b/core/image.cpp @@ -330,6 +330,17 @@ int Image::get_mipmap_offset(int p_mipmap) const { return ofs; } +int Image::get_mipmap_byte_size(int p_mipmap) const { + + ERR_FAIL_INDEX_V(p_mipmap, get_mipmap_count() + 1, -1); + + int ofs, w, h; + _get_mipmap_offset_and_size(p_mipmap, ofs, w, h); + int ofs2; + _get_mipmap_offset_and_size(p_mipmap + 1, ofs2, w, h); + return ofs2 - ofs; +} + void Image::get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const { int ofs, w, h; @@ -1567,6 +1578,206 @@ Error Image::generate_mipmaps(bool p_renormalize) { return OK; } +Error Image::generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map) { + + Vector<double> normal_sat_vec; //summed area table + double *normal_sat = nullptr; //summed area table for normalmap + int normal_w = 0, normal_h = 0; + + ERR_FAIL_COND_V_MSG(p_normal_map.is_null() || p_normal_map->empty(), ERR_INVALID_PARAMETER, "Must provide a valid normalmap for roughness mipmaps"); + + Ref<Image> nm = p_normal_map->duplicate(); + if (nm->is_compressed()) { + nm->decompress(); + } + + normal_w = nm->get_width(); + normal_h = nm->get_height(); + + normal_sat_vec.resize(normal_w * normal_h * 3); + + normal_sat = normal_sat_vec.ptrw(); + + //create summed area table + nm->lock(); + + for (int y = 0; y < normal_h; y++) { + double line_sum[3] = { 0, 0, 0 }; + for (int x = 0; x < normal_w; x++) { + double normal[3]; + Color color = nm->get_pixel(x, y); + normal[0] = color.r * 2.0 - 1.0; + normal[1] = color.g * 2.0 - 1.0; + normal[2] = Math::sqrt(MAX(0.0, 1.0 - (normal[0] * normal[0] + normal[1] * normal[1]))); //reconstruct if missing + + line_sum[0] += normal[0]; + line_sum[1] += normal[1]; + line_sum[2] += normal[2]; + + uint32_t ofs = (y * normal_w + x) * 3; + + normal_sat[ofs + 0] = line_sum[0]; + normal_sat[ofs + 1] = line_sum[1]; + normal_sat[ofs + 2] = line_sum[2]; + + if (y > 0) { + uint32_t prev_ofs = ((y - 1) * normal_w + x) * 3; + normal_sat[ofs + 0] += normal_sat[prev_ofs + 0]; + normal_sat[ofs + 1] += normal_sat[prev_ofs + 1]; + normal_sat[ofs + 2] += normal_sat[prev_ofs + 2]; + } + } + } + +#if 0 + { + Vector3 beg(normal_sat_vec[0], normal_sat_vec[1], normal_sat_vec[2]); + Vector3 end(normal_sat_vec[normal_sat_vec.size() - 3], normal_sat_vec[normal_sat_vec.size() - 2], normal_sat_vec[normal_sat_vec.size() - 1]); + Vector3 avg = (end - beg) / (normal_w * normal_h); + print_line("average: " + avg); + } +#endif + + int mmcount; + + _get_dst_image_size(width, height, format, mmcount); + + lock(); + + uint8_t *base_ptr = write_lock.ptr(); + + for (int i = 1; i <= mmcount; i++) { + + int ofs, w, h; + _get_mipmap_offset_and_size(i, ofs, w, h); + uint8_t *ptr = &base_ptr[ofs]; + + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + int from_x = x * normal_w / w; + int from_y = y * normal_h / h; + int to_x = (x + 1) * normal_w / w; + int to_y = (y + 1) * normal_h / h; + to_x = MIN(to_x - 1, normal_w); + to_y = MIN(to_y - 1, normal_h); + + int size_x = (to_x - from_x) + 1; + int size_y = (to_y - from_y) + 1; + + //summed area table version (much faster) + + double avg[3] = { 0, 0, 0 }; + + if (from_x > 0 && from_y > 0) { + uint32_t tofs = ((from_y - 1) * normal_w + (from_x - 1)) * 3; + avg[0] += normal_sat[tofs + 0]; + avg[1] += normal_sat[tofs + 1]; + avg[2] += normal_sat[tofs + 2]; + } + + if (from_y > 0) { + uint32_t tofs = ((from_y - 1) * normal_w + to_x) * 3; + avg[0] -= normal_sat[tofs + 0]; + avg[1] -= normal_sat[tofs + 1]; + avg[2] -= normal_sat[tofs + 2]; + } + + if (from_x > 0) { + uint32_t tofs = (to_y * normal_w + (from_x - 1)) * 3; + avg[0] -= normal_sat[tofs + 0]; + avg[1] -= normal_sat[tofs + 1]; + avg[2] -= normal_sat[tofs + 2]; + } + + uint32_t tofs = (to_y * normal_w + to_x) * 3; + avg[0] += normal_sat[tofs + 0]; + avg[1] += normal_sat[tofs + 1]; + avg[2] += normal_sat[tofs + 2]; + + double div = double(size_x * size_y); + Vector3 vec(avg[0] / div, avg[1] / div, avg[2] / div); + + float r = vec.length(); + + int pixel_ofs = y * w + x; + Color c = _get_color_at_ofs(ptr, pixel_ofs); + + float roughness; + + switch (p_roughness_channel) { + case ROUGHNESS_CHANNEL_R: { + roughness = c.r; + } break; + case ROUGHNESS_CHANNEL_G: { + roughness = c.g; + } break; + case ROUGHNESS_CHANNEL_B: { + roughness = c.b; + } break; + case ROUGHNESS_CHANNEL_L: { + roughness = c.gray(); + } break; + case ROUGHNESS_CHANNEL_A: { + roughness = c.a; + } break; + } + + float variance = 0; + if (r < 1.0f) { + float r2 = r * r; + float kappa = (3.0f * r - r * r2) / (1.0f - r2); + variance = 0.25f / kappa; + } + + float threshold = 0.4; + roughness = Math::sqrt(roughness * roughness + MIN(3.0f * variance, threshold * threshold)); + + switch (p_roughness_channel) { + case ROUGHNESS_CHANNEL_R: { + c.r = roughness; + } break; + case ROUGHNESS_CHANNEL_G: { + c.g = roughness; + } break; + case ROUGHNESS_CHANNEL_B: { + c.b = roughness; + } break; + case ROUGHNESS_CHANNEL_L: { + c.r = roughness; + c.g = roughness; + c.b = roughness; + } break; + case ROUGHNESS_CHANNEL_A: { + c.a = roughness; + } break; + } + + _set_color_at_ofs(ptr, pixel_ofs, c); + } + } +#if 0 + { + int size = get_mipmap_byte_size(i); + print_line("size for mimpap " + itos(i) + ": " + itos(size)); + PoolVector<uint8_t> imgdata; + imgdata.resize(size); + PoolVector<uint8_t>::Write wr = imgdata.write(); + copymem(wr.ptr(), ptr, size); + wr = PoolVector<uint8_t>::Write(); + Ref<Image> im; + im.instance(); + im->create(w, h, false, format, imgdata); + im->save_png("res://mipmap_" + itos(i) + ".png"); + } +#endif + } + + unlock(); + nm->unlock(); + + return OK; +} + void Image::clear_mipmaps() { if (!mipmaps) @@ -2440,18 +2651,7 @@ Color Image::get_pixelv(const Point2 &p_src) const { return get_pixel(p_src.x, p_src.y); } -Color Image::get_pixel(int p_x, int p_y) const { - - uint8_t *ptr = write_lock.ptr(); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel()."); - - ERR_FAIL_INDEX_V(p_x, width, Color()); - ERR_FAIL_INDEX_V(p_y, height, Color()); - -#endif - - uint32_t ofs = p_y * width + p_x; +Color Image::_get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const { switch (format) { case FORMAT_L8: { @@ -2564,23 +2764,7 @@ Color Image::get_pixel(int p_x, int p_y) const { } } -void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) { - set_pixel(p_dst.x, p_dst.y, p_color); -} - -void Image::set_pixel(int p_x, int p_y, const Color &p_color) { - - uint8_t *ptr = write_lock.ptr(); -#ifdef DEBUG_ENABLED - ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel()."); - - ERR_FAIL_INDEX(p_x, width); - ERR_FAIL_INDEX(p_y, height); - -#endif - - uint32_t ofs = p_y * width + p_x; - +void Image::_set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color) { switch (format) { case FORMAT_L8: { ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255)); @@ -2688,6 +2872,40 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) { } } +Color Image::get_pixel(int p_x, int p_y) const { + + uint8_t *ptr = write_lock.ptr(); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_V_MSG(!ptr, Color(), "Image must be locked with 'lock()' before using get_pixel()."); + + ERR_FAIL_INDEX_V(p_x, width, Color()); + ERR_FAIL_INDEX_V(p_y, height, Color()); + +#endif + + uint32_t ofs = p_y * width + p_x; + return _get_color_at_ofs(ptr, ofs); +} + +void Image::set_pixelv(const Point2 &p_dst, const Color &p_color) { + set_pixel(p_dst.x, p_dst.y, p_color); +} + +void Image::set_pixel(int p_x, int p_y, const Color &p_color) { + + uint8_t *ptr = write_lock.ptr(); +#ifdef DEBUG_ENABLED + ERR_FAIL_COND_MSG(!ptr, "Image must be locked with 'lock()' before using set_pixel()."); + + ERR_FAIL_INDEX(p_x, width); + ERR_FAIL_INDEX(p_y, height); + +#endif + + uint32_t ofs = p_y * width + p_x; + _set_color_at_ofs(ptr, ofs, p_color); +} + Image::UsedChannels Image::detect_used_channels(CompressSource p_source) { ERR_FAIL_COND_V(data.size() == 0, USED_CHANNELS_RGBA); diff --git a/core/image.h b/core/image.h index 2f8b7fbe9c..1cc22420d5 100644 --- a/core/image.h +++ b/core/image.h @@ -159,6 +159,9 @@ public: PoolVector<uint8_t>::Write write_lock; + _FORCE_INLINE_ Color _get_color_at_ofs(uint8_t *ptr, uint32_t ofs) const; + _FORCE_INLINE_ void _set_color_at_ofs(uint8_t *ptr, uint32_t ofs, const Color &p_color); + protected: static void _bind_methods(); @@ -223,6 +226,7 @@ public: */ Format get_format() const; + int get_mipmap_byte_size(int p_mipmap) const; //get where the mipmap begins in data int get_mipmap_offset(int p_mipmap) const; //get where the mipmap begins in data void get_mipmap_offset_and_size(int p_mipmap, int &r_ofs, int &r_size) const; //get where the mipmap begins in data void get_mipmap_offset_size_and_dimensions(int p_mipmap, int &r_ofs, int &r_size, int &w, int &h) const; //get where the mipmap begins in data @@ -249,6 +253,16 @@ public: */ Error generate_mipmaps(bool p_renormalize = false); + enum RoughnessChannel { + ROUGHNESS_CHANNEL_R, + ROUGHNESS_CHANNEL_G, + ROUGHNESS_CHANNEL_B, + ROUGHNESS_CHANNEL_A, + ROUGHNESS_CHANNEL_L, + }; + + Error generate_mipmap_roughness(RoughnessChannel p_roughness_channel, const Ref<Image> &p_normal_map); + void clear_mipmaps(); void normalize(); //for normal maps @@ -385,5 +399,6 @@ VARIANT_ENUM_CAST(Image::CompressMode) VARIANT_ENUM_CAST(Image::CompressSource) VARIANT_ENUM_CAST(Image::UsedChannels) VARIANT_ENUM_CAST(Image::AlphaMode) +VARIANT_ENUM_CAST(Image::RoughnessChannel) #endif diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index a3d9b180f0..4552ce1348 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -361,6 +361,7 @@ void EditorNode::_notification(int p_what) { bool dof_jitter = GLOBAL_GET("rendering/quality/filters/depth_of_field_use_jitter"); VS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter); VS::get_singleton()->environment_set_ssao_quality(VS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); + VS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/filters/screen_space_roughness_limiter"), GLOBAL_GET("rendering/quality/filters/screen_space_roughness_limiter_curve")); } ResourceImporterTexture::get_singleton()->update_imports(); diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 9c443abd98..aa7346efe8 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -326,7 +326,7 @@ void ResourceImporterTexture::save_to_stex_format(FileAccess *f, const Ref<Image } } -void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap) { +void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_roughness, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel) { FileAccess *f = FileAccess::open(p_to_path, FileAccess::WRITE); f->store_8('G'); @@ -385,6 +385,10 @@ void ResourceImporterTexture::_save_stex(const Ref<Image> &p_image, const String image->clear_mipmaps(); } + if (image->has_mipmaps() && p_normal.is_valid()) { + image->generate_mipmap_roughness(p_roughness_channel, p_normal); + } + if (p_force_rgbe && image->get_format() >= Image::FORMAT_RF && image->get_format() < Image::FORMAT_RGBE9995) { image->convert(Image::FORMAT_RGBE9995); } @@ -421,7 +425,17 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String bool force_rgbe = int(p_options["compress/hdr_mode"]) == 1; int bptc_ldr = p_options["compress/bptc_ldr"]; int roughness = p_options["roughness/mode"]; + String normal_map = p_options["roughness/src_normal"]; + + Ref<Image> normal_image; + Image::RoughnessChannel roughness_channel; + if (mipmaps && roughness > 1 && FileAccess::exists(normal_map)) { + normal_image.instance(); + if (ImageLoader::load_image(normal_map, normal_image) == OK) { + roughness_channel = Image::RoughnessChannel(roughness - 2); + } + } Ref<Image> image; image.instance(); Error err = ImageLoader::load_image(p_source_file, image, NULL, hdr_as_srgb, scale); @@ -516,7 +530,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String } if (can_bptc || can_s3tc) { - _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit); + _save_stex(image, p_save_path + ".s3tc.stex", compress_mode, lossy, can_bptc ? Image::COMPRESS_BPTC : Image::COMPRESS_S3TC, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("s3tc"); formats_imported.push_back("s3tc"); ok_on_pc = true; @@ -524,20 +538,20 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc2")) { - _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit); + _save_stex(image, p_save_path + ".etc2.stex", compress_mode, lossy, Image::COMPRESS_ETC2, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("etc2"); formats_imported.push_back("etc2"); } if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_etc")) { - _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit); + _save_stex(image, p_save_path + ".etc.stex", compress_mode, lossy, Image::COMPRESS_ETC, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("etc"); formats_imported.push_back("etc"); } if (ProjectSettings::get_singleton()->get("rendering/vram_compression/import_pvrtc")) { - _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit); + _save_stex(image, p_save_path + ".pvrtc.stex", compress_mode, lossy, Image::COMPRESS_PVRTC4, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, true, mipmap_limit, normal_image, roughness_channel); r_platform_variants->push_back("pvrtc"); formats_imported.push_back("pvrtc"); } @@ -547,7 +561,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String } } else { //import normally - _save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit); + _save_stex(image, p_save_path + ".stex", compress_mode, lossy, Image::COMPRESS_S3TC /*this is ignored */, mipmaps, stream, detect_3d, detect_roughness, force_rgbe, detect_normal, force_normal, srgb_friendly_pack, false, mipmap_limit, normal_image, roughness_channel); } if (r_metadata) { diff --git a/editor/import/resource_importer_texture.h b/editor/import/resource_importer_texture.h index bd10924950..19d5498b4a 100644 --- a/editor/import/resource_importer_texture.h +++ b/editor/import/resource_importer_texture.h @@ -79,7 +79,7 @@ protected: static ResourceImporterTexture *singleton; static const char *compression_formats[]; - void _save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap); + void _save_stex(const Ref<Image> &p_image, const String &p_to_path, CompressMode p_compress_mode, float p_lossy_quality, Image::CompressMode p_vram_compression, bool p_mipmaps, bool p_streamable, bool p_detect_3d, bool p_detect_srgb, bool p_force_rgbe, bool p_detect_normal, bool p_force_normal, bool p_srgb_friendly, bool p_force_po2_for_compressed, uint32_t p_limit_mipmap, const Ref<Image> &p_normal, Image::RoughnessChannel p_roughness_channel); public: void save_to_stex_format(FileAccess *f, const Ref<Image> &p_image, CompressMode p_compress_mode, Image::UsedChannels p_channels, Image::CompressMode p_compress_format, float p_lossy_quality, bool p_force_rgbe); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 815b3be698..88376f6ce6 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -2764,13 +2764,15 @@ void SpatialEditorViewport::_menu_option(int p_option) { case VIEW_DISPLAY_OVERDRAW: case VIEW_DISPLAY_SHADELESS: case VIEW_DISPLAY_LIGHTING: + case VIEW_DISPLAY_NORMAL_BUFFER: case VIEW_DISPLAY_DEBUG_SHADOW_ATLAS: case VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS: case VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO: case VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING: case VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION: case VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE: - case VIEW_DISPLAY_DEBUG_SSAO: { + case VIEW_DISPLAY_DEBUG_SSAO: + case VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER: { static const int display_options[] = { VIEW_DISPLAY_NORMAL, @@ -2778,6 +2780,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { VIEW_DISPLAY_OVERDRAW, VIEW_DISPLAY_SHADELESS, VIEW_DISPLAY_LIGHTING, + VIEW_DISPLAY_NORMAL_BUFFER, VIEW_DISPLAY_WIREFRAME, VIEW_DISPLAY_DEBUG_SHADOW_ATLAS, VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS, @@ -2786,6 +2789,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION, VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, VIEW_DISPLAY_DEBUG_SSAO, + VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER, VIEW_MAX }; static const Viewport::DebugDraw debug_draw_modes[] = { @@ -2794,6 +2798,7 @@ void SpatialEditorViewport::_menu_option(int p_option) { Viewport::DEBUG_DRAW_OVERDRAW, Viewport::DEBUG_DRAW_UNSHADED, Viewport::DEBUG_DRAW_LIGHTING, + Viewport::DEBUG_DRAW_NORMAL_BUFFER, Viewport::DEBUG_DRAW_WIREFRAME, Viewport::DEBUG_DRAW_SHADOW_ATLAS, Viewport::DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS, @@ -2801,7 +2806,8 @@ void SpatialEditorViewport::_menu_option(int p_option) { Viewport::DEBUG_DRAW_GI_PROBE_LIGHTING, Viewport::DEBUG_DRAW_GI_PROBE_EMISSION, Viewport::DEBUG_DRAW_SCENE_LUMINANCE, - Viewport::DEBUG_DRAW_SSAO + Viewport::DEBUG_DRAW_SSAO, + Viewport::DEBUG_DRAW_ROUGHNESS_LIMITER, }; int idx = 0; @@ -3639,6 +3645,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_lighting", TTR("Display Lighting")), VIEW_DISPLAY_LIGHTING); view_menu->get_popup()->add_radio_check_shortcut(ED_SHORTCUT("spatial_editor/view_display_unshaded", TTR("Display Unshaded")), VIEW_DISPLAY_SHADELESS); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_DISPLAY_NORMAL), true); + display_submenu->add_radio_check_item(TTR("Normal Buffer"), VIEW_DISPLAY_NORMAL_BUFFER); + display_submenu->add_separator(); display_submenu->add_radio_check_item(TTR("Shadow Atlas"), VIEW_DISPLAY_DEBUG_SHADOW_ATLAS); display_submenu->add_radio_check_item(TTR("Directional Shadow"), VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS); display_submenu->add_separator(); @@ -3649,6 +3657,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed display_submenu->add_radio_check_item(TTR("Scene Luminance"), VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE); display_submenu->add_separator(); display_submenu->add_radio_check_item(TTR("SSAO"), VIEW_DISPLAY_DEBUG_SSAO); + display_submenu->add_separator(); + display_submenu->add_radio_check_item(TTR("Roughness Limiter"), VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER); display_submenu->set_name("display_advanced"); view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced"); view_menu->get_popup()->add_separator(); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 065a0396a8..a4d6b13389 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -168,6 +168,7 @@ class SpatialEditorViewport : public Control { VIEW_DISPLAY_OVERDRAW, VIEW_DISPLAY_SHADELESS, VIEW_DISPLAY_LIGHTING, + VIEW_DISPLAY_NORMAL_BUFFER, VIEW_DISPLAY_DEBUG_SHADOW_ATLAS, VIEW_DISPLAY_DEBUG_DIRECTIONAL_SHADOW_ATLAS, VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO, @@ -175,6 +176,7 @@ class SpatialEditorViewport : public Control { VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION, VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE, VIEW_DISPLAY_DEBUG_SSAO, + VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER, VIEW_LOCK_ROTATION, VIEW_CINEMATIC_PREVIEW, VIEW_MAX diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 6e77467f23..44d4c89ed1 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -131,6 +131,7 @@ public: DEBUG_DRAW_LIGHTING, DEBUG_DRAW_OVERDRAW, DEBUG_DRAW_WIREFRAME, + DEBUG_DRAW_NORMAL_BUFFER, DEBUG_DRAW_GI_PROBE_ALBEDO, DEBUG_DRAW_GI_PROBE_LIGHTING, DEBUG_DRAW_GI_PROBE_EMISSION, @@ -138,7 +139,7 @@ public: DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS, DEBUG_DRAW_SCENE_LUMINANCE, DEBUG_DRAW_SSAO, - + DEBUG_DRAW_ROUGHNESS_LIMITER }; enum ClearMode { diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 913bebd0f5..9959936212 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -260,6 +260,9 @@ public: virtual RID render_buffers_create() = 0; virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, VS::ViewportMSAA p_msaa) = 0; + virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) = 0; + virtual bool screen_space_roughness_limiter_is_active() const = 0; + virtual bool free(RID p_rid) = 0; virtual void update() = 0; diff --git a/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp index 4f8e8c6944..b775401496 100644 --- a/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp +++ b/servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp @@ -664,7 +664,7 @@ void RasterizerEffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, // Blur horizontal ssao.blur_push_constant.edge_sharpness = p_edge_sharpness; - ssao.blur_push_constant.filter_scale = p_blur + 1; + ssao.blur_push_constant.filter_scale = p_blur; ssao.blur_push_constant.screen_size[0] = ssao.gather_push_constant.screen_size[0]; ssao.blur_push_constant.screen_size[1] = ssao.gather_push_constant.screen_size[1]; ssao.blur_push_constant.z_far = p_projection.get_z_far(); @@ -673,33 +673,35 @@ void RasterizerEffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, ssao.blur_push_constant.axis[0] = 1; ssao.blur_push_constant.axis[1] = 0; - RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[p_half_size ? SSAO_BLUR_PASS_HALF : SSAO_BLUR_PASS]); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao1), 0); - if (p_half_size) { - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 1); - } else { - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 1); - } - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao2), 3); + if (p_blur != VS::ENV_SSAO_BLUR_DISABLED) { - RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[p_half_size ? SSAO_BLUR_PASS_HALF : SSAO_BLUR_PASS]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao1), 0); + if (p_half_size) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 1); + } else { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 1); + } + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao2), 3); - RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); - RD::get_singleton()->compute_list_add_barrier(compute_list); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); - /* THIRD PASS */ - // Blur vertical + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_add_barrier(compute_list); - ssao.blur_push_constant.axis[0] = 0; - ssao.blur_push_constant.axis[1] = 1; + /* THIRD PASS */ + // Blur vertical - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao2), 0); - RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao1), 3); + ssao.blur_push_constant.axis[0] = 0; + ssao.blur_push_constant.axis[1] = 1; - RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao2), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao1), 3); - RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + } if (p_half_size) { //must upscale /* FOURTH PASS */ @@ -727,6 +729,27 @@ void RasterizerEffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, RD::get_singleton()->compute_list_end(); } +void RasterizerEffectsRD::roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve) { + + roughness_limiter.push_constant.screen_size[0] = p_size.x; + roughness_limiter.push_constant.screen_size[1] = p_size.y; + roughness_limiter.push_constant.curve = p_curve; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, roughness_limiter.pipeline); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_normal), 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_roughness), 1); + + int x_groups = (p_size.x - 1) / 8 + 1; + int y_groups = (p_size.y - 1) / 8 + 1; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &roughness_limiter.push_constant, sizeof(RoughnessLimiterPushConstant)); //not used but set anyway + + RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1); + + RD::get_singleton()->compute_list_end(); +} + RasterizerEffectsRD::RasterizerEffectsRD() { { @@ -905,6 +928,19 @@ RasterizerEffectsRD::RasterizerEffectsRD() { ERR_FAIL_COND(pipeline != SSAO_MAX); } + + { + // Initialize copier + Vector<String> shader_modes; + shader_modes.push_back(""); + + roughness_limiter.shader.initialize(shader_modes); + + roughness_limiter.shader_version = roughness_limiter.shader.version_create(); + + roughness_limiter.pipeline = RD::get_singleton()->compute_pipeline_create(roughness_limiter.shader.version_get_shader(roughness_limiter.shader_version, 0)); + } + RD::SamplerState sampler; sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; diff --git a/servers/visual/rasterizer_rd/rasterizer_effects_rd.h b/servers/visual/rasterizer_rd/rasterizer_effects_rd.h index 7a7d127e38..3495c9dcfe 100644 --- a/servers/visual/rasterizer_rd/rasterizer_effects_rd.h +++ b/servers/visual/rasterizer_rd/rasterizer_effects_rd.h @@ -38,6 +38,7 @@ #include "servers/visual/rasterizer_rd/shaders/copy.glsl.gen.h" #include "servers/visual/rasterizer_rd/shaders/cubemap_roughness.glsl.gen.h" #include "servers/visual/rasterizer_rd/shaders/luminance_reduce.glsl.gen.h" +#include "servers/visual/rasterizer_rd/shaders/roughness_limiter.glsl.gen.h" #include "servers/visual/rasterizer_rd/shaders/sky.glsl.gen.h" #include "servers/visual/rasterizer_rd/shaders/ssao.glsl.gen.h" #include "servers/visual/rasterizer_rd/shaders/ssao_blur.glsl.gen.h" @@ -339,6 +340,21 @@ class RasterizerEffectsRD { RID pipelines[SSAO_MAX]; } ssao; + struct RoughnessLimiterPushConstant { + int32_t screen_size[2]; + float curve; + uint32_t pad; + }; + + struct RoughnessLimiter { + + RoughnessLimiterPushConstant push_constant; + RoughnessLimiterShaderRD shader; + RID shader_version; + RID pipeline; + + } roughness_limiter; + RID default_sampler; RID default_mipmap_sampler; RID index_buffer; @@ -407,6 +423,8 @@ public: void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, const Size2i &p_depth_buffer_size, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao1, bool p_half_size, RID p_ao2, RID p_upscale_buffer, float p_intensity, float p_radius, float p_bias, const CameraMatrix &p_projection, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_edge_sharpness); + void roughness_limit(RID p_source_normal, RID p_roughness, const Size2i &p_size, float p_curve); + RasterizerEffectsRD(); ~RasterizerEffectsRD(); }; diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.cpp index 29aef6916e..ad6d689b69 100644 --- a/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.cpp +++ b/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -260,6 +260,8 @@ void RasterizerSceneHighEndRD::ShaderData::set_code(const String &p_code) { blend_state_blend.attachments.push_back(blend_attachment); RD::PipelineColorBlendState blend_state_opaque = RD::PipelineColorBlendState::create_disabled(1); RD::PipelineColorBlendState blend_state_opaque_specular = RD::PipelineColorBlendState::create_disabled(2); + RD::PipelineColorBlendState blend_state_depth_normal = RD::PipelineColorBlendState::create_disabled(1); + RD::PipelineColorBlendState blend_state_depth_normal_roughness = RD::PipelineColorBlendState::create_disabled(2); //update pipelines @@ -326,8 +328,10 @@ void RasterizerSceneHighEndRD::ShaderData::set_code(const String &p_code) { blend_state = blend_state_opaque; } else if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { //none, leave empty - } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) { - blend_state = blend_state_opaque; //writes to normal and roughness in opaque way + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL) { + blend_state = blend_state_depth_normal; + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) { + blend_state = blend_state_depth_normal; } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way @@ -941,7 +945,7 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l } } -void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar) { +void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers) { //CameraMatrix projection = p_cam_projection; //projection.flip_y(); // Vulkan and modern APIs use Y-Down @@ -1031,7 +1035,7 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const Camer scene_state.ubo.use_reflection_cubemap = false; } - scene_state.ubo.ssao_enabled = environment_is_ssao_enabled(p_environment); + scene_state.ubo.ssao_enabled = p_opaque_render_buffers && environment_is_ssao_enabled(p_environment); scene_state.ubo.ssao_ao_affect = environment_get_ssao_ao_affect(p_environment); scene_state.ubo.ssao_light_affect = environment_get_ssao_light_affect(p_environment); @@ -1059,6 +1063,8 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const Camer scene_state.ubo.use_reflection_cubemap = false; } + scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active(); + RD::get_singleton()->buffer_update(scene_state.uniform_buffer, 0, sizeof(SceneState::UBO), &scene_state.ubo, true); } @@ -1723,9 +1729,15 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor screen_pixel_size.height = 1.0 / render_buffer->height; opaque_framebuffer = render_buffer->color_fb; + if (p_environment.is_valid() && environment_is_ssr_enabled(p_environment)) { depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; - } else if (p_environment.is_valid() && environment_is_ssao_enabled(p_environment)) { + } else if (screen_space_roughness_limiter_is_active()) { + depth_pass_mode = PASS_MODE_DEPTH_NORMAL; + //we need to allocate both these, if not allocated + _allocate_normal_texture(render_buffer); + _allocate_roughness_texture(render_buffer); + } else if (p_environment.is_valid() && (environment_is_ssao_enabled(p_environment) || get_debug_draw_mode() == VS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER)) { depth_pass_mode = PASS_MODE_DEPTH_NORMAL; } @@ -1773,7 +1785,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor _setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows); _setup_reflections(p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_cam_transform.affine_inverse(), p_environment); _setup_gi_probes(p_gi_probe_cull_result, p_gi_probe_cull_count, p_cam_transform); - _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far()); + _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); cluster_builder.bake_cluster(); //bake to cluster @@ -1855,12 +1867,18 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor _process_ssao(p_render_buffer, p_environment, render_buffer->normal_buffer, p_cam_projection); } + if (p_render_buffer.is_valid() && screen_space_roughness_limiter_is_active()) { + storage->get_effects()->roughness_limit(render_buffer->normal_buffer, render_buffer->roughness_buffer, Size2(render_buffer->width, render_buffer->height), screen_space_roughness_limiter_get_curve()); + } + if (p_render_buffer.is_valid()) { //update the render buffers uniform set in case it changed _update_render_buffers_uniform_set(p_render_buffer); render_buffers_uniform_set = render_buffer->uniform_set; } + _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), p_render_buffer.is_valid()); + RENDER_TIMESTAMP("Render Opaque Pass"); { @@ -1901,6 +1919,8 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor RENDER_TIMESTAMP("Render Transparent Pass"); + _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); + render_list.sort_by_reverse_depth_and_priority(true); _fill_instances(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false); @@ -2275,6 +2295,18 @@ void RasterizerSceneHighEndRD::_render_buffers_uniform_set_changed(RID p_render_ _render_buffers_clear_uniform_set(rb); } +RID RasterizerSceneHighEndRD::_render_buffers_get_roughness_texture(RID p_render_buffers) { + RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + + return rb->roughness_buffer; +} + +RID RasterizerSceneHighEndRD::_render_buffers_get_normal_texture(RID p_render_buffers) { + RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + + return rb->normal_buffer; +} + void RasterizerSceneHighEndRD::_update_render_buffers_uniform_set(RID p_render_buffers) { RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.h index e4349919f6..ef90d3a283 100644 --- a/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.h +++ b/servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.h @@ -229,6 +229,8 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { virtual void _base_uniforms_changed(); void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb); virtual void _render_buffers_uniform_set_changed(RID p_render_buffers); + virtual RID _render_buffers_get_roughness_texture(RID p_render_buffers); + virtual RID _render_buffers_get_normal_texture(RID p_render_buffers); void _update_render_base_uniform_set(); void _setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas); @@ -347,7 +349,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { uint32_t ssao_enabled; float ssao_light_affect; float ssao_ao_affect; - uint32_t pad_ssao; + uint32_t roughness_limiter_enabled; float ao_color[4]; }; @@ -555,7 +557,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { PASS_MODE_DEPTH_MATERIAL, }; - void _setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar); + void _setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers = false); void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows); void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment); void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform); diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp index 41acd05a30..a40211726c 100644 --- a/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp @@ -2640,6 +2640,16 @@ void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_s RID ao_buf = rb->ssao.ao_full.is_valid() ? rb->ssao.ao_full : rb->ssao.ao[0]; effects->copy_to_rect(ao_buf, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true); } + + if (debug_draw == VS::VIEWPORT_DEBUG_DRAW_ROUGHNESS_LIMITER && _render_buffers_get_roughness_texture(p_render_buffers).is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + effects->copy_to_rect(_render_buffers_get_roughness_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true); + } + + if (debug_draw == VS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(p_render_buffers).is_valid()) { + Size2 rtsize = storage->render_target_get_size(rb->render_target); + effects->copy_to_rect(_render_buffers_get_normal_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize)); + } } RID RasterizerSceneRD::render_buffers_get_back_buffer_texture(RID p_render_buffers) { @@ -2999,6 +3009,19 @@ void RasterizerSceneRD::set_time(double p_time, double p_step) { time_step = p_step; } +void RasterizerSceneRD::screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) { + screen_space_roughness_limiter = p_enable; + screen_space_roughness_limiter_curve = p_curve; +} + +bool RasterizerSceneRD::screen_space_roughness_limiter_is_active() const { + return screen_space_roughness_limiter; +} + +float RasterizerSceneRD::screen_space_roughness_limiter_get_curve() const { + return screen_space_roughness_limiter_curve; +} + RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { storage = p_storage; @@ -3096,6 +3119,8 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { camera_effects_set_dof_blur_bokeh_shape(VS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/filters/depth_of_field_bokeh_shape")))); camera_effects_set_dof_blur_quality(VS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/filters/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/quality/filters/depth_of_field_use_jitter")); environment_set_ssao_quality(VS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); + screen_space_roughness_limiter = GLOBAL_GET("rendering/quality/filters/screen_space_roughness_limiter"); + screen_space_roughness_limiter_curve = GLOBAL_GET("rendering/quality/filters/screen_space_roughness_limiter_curve"); } RasterizerSceneRD::~RasterizerSceneRD() { diff --git a/servers/visual/rasterizer_rd/rasterizer_scene_rd.h b/servers/visual/rasterizer_rd/rasterizer_scene_rd.h index cb917482cc..2b519ce2f5 100644 --- a/servers/visual/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/visual/rasterizer_rd/rasterizer_scene_rd.h @@ -64,6 +64,8 @@ protected: virtual void _base_uniforms_changed() = 0; virtual void _render_buffers_uniform_set_changed(RID p_render_buffers) = 0; + virtual RID _render_buffers_get_roughness_texture(RID p_render_buffers) = 0; + virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) = 0; void _process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection); @@ -575,6 +577,9 @@ private: } ssao; }; + bool screen_space_roughness_limiter = false; + float screen_space_roughness_limiter_curve = 1.0; + mutable RID_Owner<RenderBuffers> render_buffers_owner; void _free_render_buffer_data(RenderBuffers *rb); @@ -923,6 +928,10 @@ public: virtual void set_scene_pass(uint64_t p_pass) { scene_pass = p_pass; } _FORCE_INLINE_ uint64_t get_scene_pass() { return scene_pass; } + virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve); + virtual bool screen_space_roughness_limiter_is_active() const; + virtual float screen_space_roughness_limiter_get_curve() const; + int get_roughness_layers() const; bool is_using_radiance_cubemap_array() const; diff --git a/servers/visual/rasterizer_rd/shaders/SCsub b/servers/visual/rasterizer_rd/shaders/SCsub index 98c14144e9..9a28ef062c 100644 --- a/servers/visual/rasterizer_rd/shaders/SCsub +++ b/servers/visual/rasterizer_rd/shaders/SCsub @@ -19,3 +19,4 @@ if 'RD_GLSL' in env['BUILDERS']: env.RD_GLSL('ssao.glsl'); env.RD_GLSL('ssao_minify.glsl'); env.RD_GLSL('ssao_blur.glsl'); + env.RD_GLSL('roughness_limiter.glsl'); diff --git a/servers/visual/rasterizer_rd/shaders/roughness_limiter.glsl b/servers/visual/rasterizer_rd/shaders/roughness_limiter.glsl new file mode 100644 index 0000000000..5e0491c979 --- /dev/null +++ b/servers/visual/rasterizer_rd/shaders/roughness_limiter.glsl @@ -0,0 +1,73 @@ +/* clang-format off */ +[compute] +/* clang-format on */ + +#version 450 + +VERSION_DEFINES + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + + +layout(set = 0, binding = 0) uniform sampler2D source_normal; +layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_roughness; + +layout(push_constant, binding = 1, std430) uniform Params { + ivec2 screen_size; + float curve; + uint pad; +} params; + +#define HALF_PI 1.5707963267948966 + +void main() { + + // Pixel being shaded + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThan(pos,params.screen_size))) { //too large, do nothing + return; + } + + vec3 normal_accum = vec3(0.0); + float accum = 0.0; + for(int i=0;i<=1;i++) { + for(int j=0;j<=1;j++) { + normal_accum += normalize(texelFetch(source_normal,pos+ivec2(i,j),0).xyz * 2.0 - 1.0); + accum+=1.0; + } + } + + normal_accum /= accum; + + float r = length(normal_accum); + + float limit; + + if (r < 1.0) { + float threshold = 0.4; + +/* + //Formula from Filament, does not make sense to me. + + float r2 = r * r; + float kappa = (3.0f * r - r * r2) / (1.0f - r2); + float variance = 0.25f / kappa; + limit = sqrt(min(2.0f * variance, threshold * threshold)); +//*/ +/* + //Formula based on probability distribution graph + + float width = acos(max(0.0,r)); // convert to angle (width) + float roughness = pow(width,1.7)*0.854492; //approximate (crappy) formula to convert to roughness + limit = min(sqrt(roughness), threshold); //convert to perceptual roughness and apply threshold +//*/ + + limit = min(sqrt(pow(acos(max(0.0,r)) / HALF_PI ,params.curve)), threshold); //convert to perceptual roughness and apply threshold + + //limit = 0.5; + } else { + limit = 0.0; + } + + imageStore(dest_roughness,pos,vec4(limit)); +} diff --git a/servers/visual/rasterizer_rd/shaders/scene_high_end.glsl b/servers/visual/rasterizer_rd/shaders/scene_high_end.glsl index 66ce4b466f..8aee680c1e 100644 --- a/servers/visual/rasterizer_rd/shaders/scene_high_end.glsl +++ b/servers/visual/rasterizer_rd/shaders/scene_high_end.glsl @@ -1272,6 +1272,7 @@ FRAGMENT_SHADER_CODE #endif // !USE_SHADOW_TO_OPACITY + #if defined(NORMALMAP_USED) normalmap.xy = normalmap.xy * 2.0 - 1.0; @@ -1310,6 +1311,13 @@ FRAGMENT_SHADER_CODE #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) + if (scene_data.roughness_limiter_enabled) { + float limit = texelFetch(sampler2D(roughness_buffer, material_samplers[SAMPLER_NEAREST_CLAMP]),ivec2(gl_FragCoord.xy),0).r; + roughness = max(roughness,limit); + + } + + if (scene_data.use_reflection_cubemap) { vec3 ref_vec = reflect(-view, normal); diff --git a/servers/visual/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/visual/rasterizer_rd/shaders/scene_high_end_inc.glsl index 5504d0fc2f..384e51bbce 100644 --- a/servers/visual/rasterizer_rd/shaders/scene_high_end_inc.glsl +++ b/servers/visual/rasterizer_rd/shaders/scene_high_end_inc.glsl @@ -63,7 +63,7 @@ layout(set = 0, binding = 3, std140) uniform SceneData { bool ssao_enabled; float ssao_light_affect; float ssao_ao_affect; - uint pad_ssao; + bool roughness_limiter_enabled; vec4 ao_color; diff --git a/servers/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index bcb327a600..297f0727a0 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -538,6 +538,8 @@ public: BIND7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) BIND5(environment_set_fog_height, RID, bool, float, float, float) + BIND2(screen_space_roughness_limiter_set_active, bool, float) + /* CAMERA EFFECTS */ BIND0R(RID, camera_effects_create) diff --git a/servers/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index cb5fef3ba0..3ed3728757 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -453,6 +453,8 @@ public: FUNC7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) FUNC5(environment_set_fog_height, RID, bool, float, float, float) + FUNC2(screen_space_roughness_limiter_set_active, bool, float) + FUNCRID(camera_effects) FUNC2(camera_effects_set_dof_blur_quality, DOFBlurQuality, bool) diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp index a8511b12c7..2af6f7bfea 100644 --- a/servers/visual_server.cpp +++ b/servers/visual_server.cpp @@ -2326,6 +2326,11 @@ VisualServer::VisualServer() { GLOBAL_DEF("rendering/quality/ssao/quality", 1); ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/ssao/quality", PropertyInfo(Variant::INT, "rendering/quality/ssao/quality", PROPERTY_HINT_ENUM, "Low (Fast),Medium,High (Slow),Ultra (Very Slow)")); GLOBAL_DEF("rendering/quality/ssao/half_size", false); + + GLOBAL_DEF("rendering/quality/filters/screen_space_roughness_limiter", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/screen_space_roughness_limiter", PropertyInfo(Variant::INT, "rendering/quality/filters/screen_space_roughness_limiter", PROPERTY_HINT_ENUM, "Disabled,Enabled (Small Cost)")); + GLOBAL_DEF("rendering/quality/filters/screen_space_roughness_limiter_curve", 1.0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/screen_space_roughness_limiter_curve", PropertyInfo(Variant::REAL, "rendering/quality/filters/screen_space_roughness_limiter_curve", PROPERTY_HINT_EXP_EASING, "0.01,8,0.01")); } VisualServer::~VisualServer() { diff --git a/servers/visual_server.h b/servers/visual_server.h index 13ca4f4d1a..7ff28d9731 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -658,6 +658,7 @@ public: VIEWPORT_DEBUG_DRAW_LIGHTING, VIEWPORT_DEBUG_DRAW_OVERDRAW, VIEWPORT_DEBUG_DRAW_WIREFRAME, + VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER, VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO, VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING, VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION, @@ -665,6 +666,7 @@ public: VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS, VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE, VIEWPORT_DEBUG_DRAW_SSAO, + VIEWPORT_DEBUG_DRAW_ROUGHNESS_LIMITER, }; @@ -768,6 +770,8 @@ public: virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0; virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0; + virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) = 0; + /* CAMERA EFFECTS */ virtual RID camera_effects_create() = 0; |