From 079ca220e14669ef7c31c399985cd2c733af15bd Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Wed, 12 Aug 2020 22:21:01 -0300 Subject: [PATCH] Added volumetric fog effect. --- core/math/camera_matrix.cpp | 5 +- core/math/camera_matrix.h | 2 +- editor/editor_node.cpp | 4 + glsl_builders.py | 1 + scene/3d/light_3d.cpp | 3 + scene/3d/light_3d.h | 1 + scene/resources/environment.cpp | 104 ++- scene/resources/environment.h | 37 + servers/rendering/rasterizer.h | 7 + .../rasterizer_rd/rasterizer_effects_rd.cpp | 59 ++ .../rasterizer_rd/rasterizer_effects_rd.h | 16 + .../rasterizer_scene_high_end_rd.cpp | 27 +- .../rasterizer_scene_high_end_rd.h | 5 + .../rasterizer_rd/rasterizer_scene_rd.cpp | 839 ++++++++++++++++-- .../rasterizer_rd/rasterizer_scene_rd.h | 127 ++- .../rasterizer_rd/rasterizer_storage_rd.cpp | 1 + .../rasterizer_rd/rasterizer_storage_rd.h | 7 + servers/rendering/rasterizer_rd/shaders/SCsub | 2 + .../shaders/cluster_data_inc.glsl | 95 ++ .../rendering/rasterizer_rd/shaders/gi.glsl | 2 +- .../rasterizer_rd/shaders/scene_high_end.glsl | 50 +- .../shaders/scene_high_end_inc.glsl | 104 +-- .../shaders/sdfgi_integrate.glsl | 12 + .../rasterizer_rd/shaders/shadow_reduce.glsl | 105 +++ .../rasterizer_rd/shaders/volumetric_fog.glsl | 530 +++++++++++ servers/rendering/rendering_server_raster.h | 6 + servers/rendering/rendering_server_wrap_mt.h | 6 + servers/rendering_server.cpp | 11 + servers/rendering_server.h | 14 + 29 files changed, 1984 insertions(+), 198 deletions(-) create mode 100644 servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl create mode 100644 servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl create mode 100644 servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl diff --git a/core/math/camera_matrix.cpp b/core/math/camera_matrix.cpp index 22ab83f3589f..c154d57a13b9 100644 --- a/core/math/camera_matrix.cpp +++ b/core/math/camera_matrix.cpp @@ -278,7 +278,7 @@ Vector2 CameraMatrix::get_viewport_half_extents() const { return Vector2(res.x, res.y); } -void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const { +Vector2 CameraMatrix::get_far_plane_half_extents() const { const real_t *matrix = (const real_t *)this->matrix; ///////--- Far Plane ---/////// Plane far_plane = Plane(matrix[3] - matrix[2], @@ -303,8 +303,7 @@ void CameraMatrix::get_far_plane_size(real_t &r_width, real_t &r_height) const { Vector3 res; far_plane.intersect_3(right_plane, top_plane, &res); - r_width = res.x; - r_height = res.y; + return Vector2(res.x, res.y); } bool CameraMatrix::get_endpoints(const Transform &p_transform, Vector3 *p_8points) const { diff --git a/core/math/camera_matrix.h b/core/math/camera_matrix.h index 49fdecae029c..c5cdd98377b0 100644 --- a/core/math/camera_matrix.h +++ b/core/math/camera_matrix.h @@ -74,7 +74,7 @@ struct CameraMatrix { bool get_endpoints(const Transform &p_transform, Vector3 *p_8points) const; Vector2 get_viewport_half_extents() const; - void get_far_plane_size(real_t &r_width, real_t &r_height) const; + Vector2 get_far_plane_half_extents() const; void invert(); CameraMatrix inverse() const; diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index dabee6703326..381ff888909a 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -498,6 +498,10 @@ void EditorNode::_notification(int p_what) { RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count); RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/quality/gi_probes/quality"))); RS::get_singleton()->gi_probe_set_quality(gi_probe_quality); + RS::get_singleton()->environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/volumetric_fog/volume_size"), GLOBAL_GET("rendering/volumetric_fog/volume_depth")); + RS::get_singleton()->environment_set_volumetric_fog_filter_active(bool(GLOBAL_GET("rendering/volumetric_fog/use_filter"))); + RS::get_singleton()->environment_set_volumetric_fog_directional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/directional_shadow_shrink")); + RS::get_singleton()->environment_set_volumetric_fog_positional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/positional_shadow_shrink")); } ResourceImporterTexture::get_singleton()->update_imports(); diff --git a/glsl_builders.py b/glsl_builders.py index 9391fa1b6557..af9afcae706d 100644 --- a/glsl_builders.py +++ b/glsl_builders.py @@ -14,6 +14,7 @@ def __init__(self): self.vertex_included_files = [] self.fragment_included_files = [] + self.compute_included_files = [] self.reading = "" self.line_offset = 0 diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index cc1622722edf..2eb12d10faac 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -270,6 +270,7 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_normal_bias", PROPERTY_HINT_RANGE, "0,10,0.001"), "set_param", "get_param", PARAM_SHADOW_NORMAL_BIAS); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_reverse_cull_face"), "set_shadow_reverse_cull_face", "get_shadow_reverse_cull_face"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_transmittance_bias", PROPERTY_HINT_RANGE, "-16,16,0.01"), "set_param", "get_param", PARAM_TRANSMITTANCE_BIAS); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_fog_fade", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_param", "get_param", PARAM_SHADOW_VOLUMETRIC_FOG_FADE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "shadow_blur", PROPERTY_HINT_RANGE, "0.1,8,0.01"), "set_param", "get_param", PARAM_SHADOW_BLUR); ADD_GROUP("Editor", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_only"), "set_editor_only", "is_editor_only"); @@ -292,6 +293,7 @@ void Light3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_SHADOW_BIAS); BIND_ENUM_CONSTANT(PARAM_SHADOW_PANCAKE_SIZE); BIND_ENUM_CONSTANT(PARAM_SHADOW_BLUR); + BIND_ENUM_CONSTANT(PARAM_SHADOW_VOLUMETRIC_FOG_FADE); BIND_ENUM_CONSTANT(PARAM_TRANSMITTANCE_BIAS); BIND_ENUM_CONSTANT(PARAM_MAX); @@ -345,6 +347,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_SHADOW_BIAS, 0.02); set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); + set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 1.0); set_param(PARAM_SHADOW_FADE_START, 1); set_disable_scale(true); } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index f106151472ae..69c0478b8660 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -58,6 +58,7 @@ class Light3D : public VisualInstance3D { PARAM_SHADOW_BIAS = RS::LIGHT_PARAM_SHADOW_BIAS, PARAM_SHADOW_PANCAKE_SIZE = RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE, PARAM_SHADOW_BLUR = RS::LIGHT_PARAM_SHADOW_BLUR, + PARAM_SHADOW_VOLUMETRIC_FOG_FADE = RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE, PARAM_TRANSMITTANCE_BIAS = RS::LIGHT_PARAM_TRANSMITTANCE_BIAS, PARAM_MAX = RS::LIGHT_PARAM_MAX }; diff --git a/scene/resources/environment.cpp b/scene/resources/environment.cpp index 94629a77b989..526d10138929 100644 --- a/scene/resources/environment.cpp +++ b/scene/resources/environment.cpp @@ -840,6 +840,74 @@ void Environment::_update_fog_height() { fog_height_curve); } +// Volumetric Fog + +void Environment::_update_volumetric_fog() { + RS::get_singleton()->environment_set_volumetric_fog(environment, volumetric_fog_enabled, volumetric_fog_density, volumetric_fog_light, volumetric_fog_light_energy, volumetric_fog_length, volumetric_fog_detail_spread, volumetric_fog_gi_inject, RS::EnvVolumetricFogShadowFilter(volumetric_fog_shadow_filter)); +} + +void Environment::set_volumetric_fog_enabled(bool p_enable) { + volumetric_fog_enabled = p_enable; + _update_volumetric_fog(); +} + +bool Environment::is_volumetric_fog_enabled() const { + return volumetric_fog_enabled; +} +void Environment::set_volumetric_fog_density(float p_density) { + p_density = CLAMP(p_density, 0.0000001, 1.0); + volumetric_fog_density = p_density; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_density() const { + return volumetric_fog_density; +} +void Environment::set_volumetric_fog_light(Color p_color) { + volumetric_fog_light = p_color; + _update_volumetric_fog(); +} +Color Environment::get_volumetric_fog_light() const { + return volumetric_fog_light; +} +void Environment::set_volumetric_fog_light_energy(float p_begin) { + volumetric_fog_light_energy = p_begin; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_light_energy() const { + return volumetric_fog_light_energy; +} +void Environment::set_volumetric_fog_length(float p_length) { + volumetric_fog_length = p_length; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_length() const { + return volumetric_fog_length; +} +void Environment::set_volumetric_fog_detail_spread(float p_detail_spread) { + volumetric_fog_detail_spread = p_detail_spread; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_detail_spread() const { + return volumetric_fog_detail_spread; +} + +void Environment::set_volumetric_fog_gi_inject(float p_gi_inject) { + volumetric_fog_gi_inject = p_gi_inject; + _update_volumetric_fog(); +} +float Environment::get_volumetric_fog_gi_inject() const { + return volumetric_fog_gi_inject; +} + +void Environment::set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter) { + volumetric_fog_shadow_filter = p_filter; + _update_volumetric_fog(); +} + +Environment::VolumetricFogShadowFilter Environment::get_volumetric_fog_shadow_filter() const { + return volumetric_fog_shadow_filter; +} + // Adjustment void Environment::set_adjustment_enabled(bool p_enabled) { @@ -1251,7 +1319,7 @@ void Environment::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fog_height_curve", "curve"), &Environment::set_fog_height_curve); ClassDB::bind_method(D_METHOD("get_fog_height_curve"), &Environment::get_fog_height_curve); - ADD_GROUP("Fog", "fog_"); + ADD_GROUP("Fixed Fog", "fog_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fog_enabled"), "set_fog_enabled", "is_fog_enabled"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_color"), "set_fog_color", "get_fog_color"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "fog_sun_color"), "set_fog_sun_color", "get_fog_sun_color"); @@ -1269,6 +1337,33 @@ void Environment::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_max", PROPERTY_HINT_RANGE, "-4000,4000,0.1,or_lesser,or_greater"), "set_fog_height_max", "get_fog_height_max"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fog_height_curve", PROPERTY_HINT_EXP_EASING), "set_fog_height_curve", "get_fog_height_curve"); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_enabled", "enabled"), &Environment::set_volumetric_fog_enabled); + ClassDB::bind_method(D_METHOD("is_volumetric_fog_enabled"), &Environment::is_volumetric_fog_enabled); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_light", "color"), &Environment::set_volumetric_fog_light); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_light"), &Environment::get_volumetric_fog_light); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_density", "density"), &Environment::set_volumetric_fog_density); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_density"), &Environment::get_volumetric_fog_density); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_light_energy", "begin"), &Environment::set_volumetric_fog_light_energy); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_light_energy"), &Environment::get_volumetric_fog_light_energy); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_length", "length"), &Environment::set_volumetric_fog_length); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_length"), &Environment::get_volumetric_fog_length); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_detail_spread", "detail_spread"), &Environment::set_volumetric_fog_detail_spread); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_detail_spread"), &Environment::get_volumetric_fog_detail_spread); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_gi_inject", "gi_inject"), &Environment::set_volumetric_fog_gi_inject); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_gi_inject"), &Environment::get_volumetric_fog_gi_inject); + ClassDB::bind_method(D_METHOD("set_volumetric_fog_shadow_filter", "shadow_filter"), &Environment::set_volumetric_fog_shadow_filter); + ClassDB::bind_method(D_METHOD("get_volumetric_fog_shadow_filter"), &Environment::get_volumetric_fog_shadow_filter); + + ADD_GROUP("Volumetric Fog", "volumetric_fog_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "volumetric_fog_enabled"), "set_volumetric_fog_enabled", "is_volumetric_fog_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_density", PROPERTY_HINT_RANGE, "0,1,0.0001,or_greater"), "set_volumetric_fog_density", "get_volumetric_fog_density"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "volumetric_fog_light", PROPERTY_HINT_COLOR_NO_ALPHA), "set_volumetric_fog_light", "get_volumetric_fog_light"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_light_energy", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_light_energy", "get_volumetric_fog_light_energy"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_gi_inject", PROPERTY_HINT_EXP_RANGE, "0.00,16,0.01"), "set_volumetric_fog_gi_inject", "get_volumetric_fog_gi_inject"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_length", PROPERTY_HINT_RANGE, "0,1024,0.01,or_greater"), "set_volumetric_fog_length", "get_volumetric_fog_length"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volumetric_fog_detail_spread", PROPERTY_HINT_EXP_EASING, "0.01,16,0.01"), "set_volumetric_fog_detail_spread", "get_volumetric_fog_detail_spread"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "volumetric_fog_shadow_filter", PROPERTY_HINT_ENUM, "Disabled,Low,Medium,High"), "set_volumetric_fog_shadow_filter", "get_volumetric_fog_shadow_filter"); + // Adjustment ClassDB::bind_method(D_METHOD("set_adjustment_enabled", "enabled"), &Environment::set_adjustment_enabled); @@ -1331,6 +1426,11 @@ void Environment::_bind_methods() { BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_DISABLED); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_75_PERCENT); BIND_ENUM_CONSTANT(SDFGI_Y_SCALE_50_PERCENT); + + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED); + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_LOW); + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM); + BIND_ENUM_CONSTANT(VOLUMETRIC_FOG_SHADOW_FILTER_HIGH); } Environment::Environment() { @@ -1347,7 +1447,7 @@ Environment::Environment() { _update_fog_depth(); _update_fog_height(); _update_adjustment(); - + _update_volumetric_fog(); _change_notify(); } diff --git a/scene/resources/environment.h b/scene/resources/environment.h index f334d22115db..6edb54b187c3 100644 --- a/scene/resources/environment.h +++ b/scene/resources/environment.h @@ -97,6 +97,13 @@ class Environment : public Resource { GLOW_BLEND_MODE_MIX, }; + enum VolumetricFogShadowFilter { + VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED, + VOLUMETRIC_FOG_SHADOW_FILTER_LOW, + VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM, + VOLUMETRIC_FOG_SHADOW_FILTER_HIGH, + }; + private: RID environment; @@ -196,6 +203,17 @@ class Environment : public Resource { float fog_height_curve = 1.0; void _update_fog_height(); + // Volumetric Fog + bool volumetric_fog_enabled = false; + float volumetric_fog_density = 0.01; + Color volumetric_fog_light = Color(0.0, 0.0, 0.0); + float volumetric_fog_light_energy = 1.0; + float volumetric_fog_length = 64.0; + float volumetric_fog_detail_spread = 2.0; + VolumetricFogShadowFilter volumetric_fog_shadow_filter = VOLUMETRIC_FOG_SHADOW_FILTER_LOW; + float volumetric_fog_gi_inject = 0.0; + void _update_volumetric_fog(); + // Adjustment bool adjustment_enabled = false; float adjustment_brightness = 1.0; @@ -375,6 +393,24 @@ class Environment : public Resource { void set_fog_height_curve(float p_distance); float get_fog_height_curve() const; + // Volumetric Fog + void set_volumetric_fog_enabled(bool p_enable); + bool is_volumetric_fog_enabled() const; + void set_volumetric_fog_density(float p_density); + float get_volumetric_fog_density() const; + void set_volumetric_fog_light(Color p_color); + Color get_volumetric_fog_light() const; + void set_volumetric_fog_light_energy(float p_begin); + float get_volumetric_fog_light_energy() const; + void set_volumetric_fog_length(float p_length); + float get_volumetric_fog_length() const; + void set_volumetric_fog_detail_spread(float p_detail_spread); + float get_volumetric_fog_detail_spread() const; + void set_volumetric_fog_shadow_filter(VolumetricFogShadowFilter p_filter); + VolumetricFogShadowFilter get_volumetric_fog_shadow_filter() const; + void set_volumetric_fog_gi_inject(float p_gi_inject); + float get_volumetric_fog_gi_inject() const; + // Adjustment void set_adjustment_enabled(bool p_enabled); bool is_adjustment_enabled() const; @@ -399,5 +435,6 @@ VARIANT_ENUM_CAST(Environment::SSAOBlur) VARIANT_ENUM_CAST(Environment::SDFGICascades) VARIANT_ENUM_CAST(Environment::SDFGIYScale) VARIANT_ENUM_CAST(Environment::GlowBlendMode) +VARIANT_ENUM_CAST(Environment::VolumetricFogShadowFilter) #endif // ENVIRONMENT_H diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 348fc423bba4..d7292972097e 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -88,6 +88,13 @@ class RasterizerScene { virtual void environment_glow_set_use_bicubic_upscale(bool p_enable) = 0; virtual void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) = 0; + virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter) = 0; + + virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) = 0; + virtual void environment_set_volumetric_fog_filter_active(bool p_enable) = 0; + virtual void environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size) = 0; + virtual void environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size) = 0; + virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) = 0; virtual void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality) = 0; diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp index e620ad954d78..390272b4b8a9 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp @@ -1229,6 +1229,50 @@ void RasterizerEffectsRD::resolve_gi(RID p_source_depth, RID p_source_normal_rou RD::get_singleton()->compute_list_end(); } +void RasterizerEffectsRD::reduce_shadow(RID p_source_shadow, RID p_dest_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, int p_shrink_limit, RD::ComputeListID compute_list) { + uint32_t push_constant[8] = { (uint32_t)p_source_size.x, (uint32_t)p_source_size.y, (uint32_t)p_source_rect.position.x, (uint32_t)p_source_rect.position.y, (uint32_t)p_shrink_limit, 0, 0, 0 }; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shadow_reduce.pipelines[SHADOW_REDUCE_REDUCE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_source_shadow, p_dest_shadow), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(uint32_t) * 8); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_source_rect.size.width, p_source_rect.size.height, 1, 8, 8, 1); +} +void RasterizerEffectsRD::filter_shadow(RID p_shadow, RID p_backing_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, RenderingServer::EnvVolumetricFogShadowFilter p_filter, RD::ComputeListID compute_list, bool p_vertical, bool p_horizontal) { + uint32_t push_constant[8] = { (uint32_t)p_source_size.x, (uint32_t)p_source_size.y, (uint32_t)p_source_rect.position.x, (uint32_t)p_source_rect.position.y, 0, 0, 0, 0 }; + + switch (p_filter) { + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED: + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_LOW: { + push_constant[5] = 0; + } break; + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM: { + push_constant[5] = 9; + } break; + case RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_HIGH: { + push_constant[5] = 18; + } break; + } + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, shadow_reduce.pipelines[SHADOW_REDUCE_FILTER]); + if (p_vertical) { + push_constant[6] = 1; + push_constant[7] = 0; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_shadow, p_backing_shadow), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(uint32_t) * 8); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_source_rect.size.width, p_source_rect.size.height, 1, 8, 8, 1); + } + if (p_vertical && p_horizontal) { + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + if (p_horizontal) { + push_constant[6] = 0; + push_constant[7] = 1; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_backing_shadow, p_shadow), 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(uint32_t) * 8); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_source_rect.size.width, p_source_rect.size.height, 1, 8, 8, 1); + } +} RasterizerEffectsRD::RasterizerEffectsRD() { { // Initialize copy Vector copy_modes; @@ -1560,6 +1604,20 @@ RasterizerEffectsRD::RasterizerEffectsRD() { } } + { + Vector shadow_reduce_modes; + shadow_reduce_modes.push_back("\n#define MODE_REDUCE\n"); + shadow_reduce_modes.push_back("\n#define MODE_FILTER\n"); + + shadow_reduce.shader.initialize(shadow_reduce_modes); + + shadow_reduce.shader_version = shadow_reduce.shader.version_create(); + + for (int i = 0; i < SHADOW_REDUCE_MAX; i++) { + shadow_reduce.pipelines[i] = RD::get_singleton()->compute_pipeline_create(shadow_reduce.shader.version_get_shader(shadow_reduce.shader_version, i)); + } + } + RD::SamplerState sampler; sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; @@ -1624,4 +1682,5 @@ RasterizerEffectsRD::~RasterizerEffectsRD() { ssr_scale.shader.version_free(ssr_scale.shader_version); sss.shader.version_free(sss.shader_version); tonemap.shader.version_free(tonemap.shader_version); + shadow_reduce.shader.version_free(shadow_reduce.shader_version); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h index 80849654de54..2ba96104982c 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h @@ -46,6 +46,7 @@ #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection_filter.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection_scale.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/specular_merge.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/ssao.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/ssao_blur.glsl.gen.h" @@ -534,6 +535,18 @@ class RasterizerEffectsRD { RID pipelines[RESOLVE_MODE_MAX]; //3 quality levels } resolve; + enum ShadowReduceMode { + SHADOW_REDUCE_REDUCE, + SHADOW_REDUCE_FILTER, + SHADOW_REDUCE_MAX + }; + + struct ShadowReduce { + ShadowReduceShaderRD shader; + RID shader_version; + RID pipelines[2]; + } shadow_reduce; + RID default_sampler; RID default_mipmap_sampler; RID index_buffer; @@ -633,6 +646,9 @@ class RasterizerEffectsRD { void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_giprobe, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_giprobe, Vector2i p_screen_size, int p_samples); + void reduce_shadow(RID p_source_shadow, RID p_dest_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, int p_shrink_limit, RenderingDevice::ComputeListID compute_list); + void filter_shadow(RID p_shadow, RID p_backing_shadow, const Size2i &p_source_size, const Rect2i &p_source_rect, RS::EnvVolumetricFogShadowFilter p_filter, RenderingDevice::ComputeListID compute_list, bool p_vertical = true, bool p_horizontal = true); + RasterizerEffectsRD(); ~RasterizerEffectsRD(); }; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp index 8c122983da96..78509f964223 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -1145,12 +1145,19 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, RID p_rende scene_state.ubo.time = time; scene_state.ubo.gi_upscale_for_msaa = false; + scene_state.ubo.volumetric_fog_enabled = false; if (p_render_buffers.is_valid()) { RenderBufferDataHighEnd *render_buffers = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); if (render_buffers->msaa != RS::VIEWPORT_MSAA_DISABLED) { scene_state.ubo.gi_upscale_for_msaa = true; } + + if (render_buffers_has_volumetric_fog(p_render_buffers)) { + scene_state.ubo.volumetric_fog_enabled = true; + scene_state.ubo.volumetric_fog_inv_length = 1.0 / render_buffers_get_volumetric_fog_end(p_render_buffers); + scene_state.ubo.volumetric_fog_detail_spread = 1.0 / render_buffers_get_volumetric_fog_detail_spread(p_render_buffers); //reverse lookup + } } #if 0 if (p_render_buffers.is_valid() && render_buffers_is_sdfgi_enabled(p_render_buffers)) { @@ -1754,6 +1761,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor RD::get_singleton()->draw_list_end(); if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { + RENDER_TIMESTAMP("Resolve Depth Pre-Pass"); if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE) { static int texture_samples[RS::VIEWPORT_MSAA_MAX] = { 1, 2, 4, 8, 16 }; storage->get_effects()->resolve_gi(render_buffer->depth_msaa, render_buffer->normal_roughness_buffer_msaa, using_giprobe ? render_buffer->giprobe_buffer_msaa : RID(), render_buffer->depth, render_buffer->normal_roughness_buffer, using_giprobe ? render_buffer->giprobe_buffer : RID(), Vector2i(render_buffer->width, render_buffer->height), texture_samples[render_buffer->msaa]); @@ -2502,7 +2510,17 @@ void RasterizerSceneHighEndRD::_update_render_buffers_uniform_set(RID p_render_b u.ids.push_back(render_buffers_get_gi_probe_buffer(p_render_buffers)); uniforms.push_back(u); } - + { + RD::Uniform u; + u.binding = 10; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID vfog = render_buffers_get_volumetric_fog_texture(p_render_buffers); + if (vfog.is_null()) { + vfog = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + } + u.ids.push_back(vfog); + uniforms.push_back(u); + } rb->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); } } @@ -2815,6 +2833,13 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag u.ids.push_back(render_buffers_get_default_gi_probe_buffer()); uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 10; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + uniforms.push_back(u); + } default_render_buffers_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h index a49173de98c3..1fb59453b562 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h @@ -359,6 +359,11 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD { int32_t sdf_size[3]; uint32_t gi_upscale_for_msaa; + + uint32_t volumetric_fog_enabled; + float volumetric_fog_inv_length; + float volumetric_fog_detail_spread; + uint32_t volumetric_fog_pad; }; UBO ubo; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp index bdf9b71c56b9..040124d5ab4c 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp @@ -227,6 +227,7 @@ void RasterizerSceneRD::_sdfgi_erase(RenderBuffers *rb) { RD::get_singleton()->free(rb->sdfgi->lightprobe_data); RD::get_singleton()->free(rb->sdfgi->lightprobe_history_scroll); RD::get_singleton()->free(rb->sdfgi->occlusion_data); + RD::get_singleton()->free(rb->sdfgi->ambient_texture); RD::get_singleton()->free(rb->sdfgi->cascades_ubo); @@ -371,6 +372,16 @@ void RasterizerSceneRD::sdfgi_update(RID p_render_buffers, RID p_environment, co RD::TextureView tv; tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; sdfgi->lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, sdfgi->lightprobe_data); + + //texture handling ambient data, to integrate with volumetric foc + RD::TextureFormat tf_ambient = tf_probes; + tf_ambient.array_layers = sdfgi->cascades.size(); + tf_ambient.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; //pack well with RGBE + tf_ambient.width = sdfgi->probe_axis_count * sdfgi->probe_axis_count; + tf_ambient.height = sdfgi->probe_axis_count; + tf_ambient.type = RD::TEXTURE_TYPE_2D_ARRAY; + //lightprobe texture is an octahedral texture + sdfgi->ambient_texture = RD::get_singleton()->texture_create(tf_ambient, RD::TextureView()); } sdfgi->cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES); @@ -930,6 +941,13 @@ void RasterizerSceneRD::sdfgi_update(RID p_render_buffers, RID p_environment, co u.ids.push_back(parent_average); uniforms.push_back(u); } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 14; + u.ids.push_back(sdfgi->ambient_texture); + uniforms.push_back(u); + } sdfgi->cascades[i].integrate_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 0); } @@ -1282,6 +1300,7 @@ void RasterizerSceneRD::sdfgi_update_probes(RID p_render_buffers, RID p_environm push_constant.ray_bias = rb->sdfgi->probe_bias; push_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count; push_constant.image_size[1] = rb->sdfgi->probe_axis_count; + push_constant.store_ambient_texture = env->volumetric_fog_enabled; RID sky_uniform_set = sdfgi_shader.integrate_default_sky_uniform_set; push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_DISABLED; @@ -1375,6 +1394,96 @@ void RasterizerSceneRD::sdfgi_update_probes(RID p_render_buffers, RID p_environm RENDER_TIMESTAMP("texture; + GI::GIProbeData &gipd = gi_probe_data[i]; + + RID base_probe = gipi->probe; + + Transform to_cell = storage->gi_probe_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera; + + gipd.xform[0] = to_cell.basis.elements[0][0]; + gipd.xform[1] = to_cell.basis.elements[1][0]; + gipd.xform[2] = to_cell.basis.elements[2][0]; + gipd.xform[3] = 0; + gipd.xform[4] = to_cell.basis.elements[0][1]; + gipd.xform[5] = to_cell.basis.elements[1][1]; + gipd.xform[6] = to_cell.basis.elements[2][1]; + gipd.xform[7] = 0; + gipd.xform[8] = to_cell.basis.elements[0][2]; + gipd.xform[9] = to_cell.basis.elements[1][2]; + gipd.xform[10] = to_cell.basis.elements[2][2]; + gipd.xform[11] = 0; + gipd.xform[12] = to_cell.origin.x; + gipd.xform[13] = to_cell.origin.y; + gipd.xform[14] = to_cell.origin.z; + gipd.xform[15] = 1; + + Vector3 bounds = storage->gi_probe_get_octree_size(base_probe); + + gipd.bounds[0] = bounds.x; + gipd.bounds[1] = bounds.y; + gipd.bounds[2] = bounds.z; + + gipd.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe); + gipd.bias = storage->gi_probe_get_bias(base_probe); + gipd.normal_bias = storage->gi_probe_get_normal_bias(base_probe); + gipd.blend_ambient = !storage->gi_probe_is_interior(base_probe); + gipd.anisotropy_strength = 0; + gipd.ao = storage->gi_probe_get_ao(base_probe); + gipd.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f); + gipd.mipmaps = gipi->mipmaps.size(); + } + + r_gi_probes_used++; + } + + if (texture == RID()) { + texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + } + + if (texture != rb->giprobe_textures[i]) { + giprobes_changed = true; + rb->giprobe_textures[i] = texture; + } + } + + if (giprobes_changed) { + RD::get_singleton()->free(rb->gi_uniform_set); + rb->gi_uniform_set = RID(); + if (rb->volumetric_fog) { + if (RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + RD::get_singleton()->free(rb->volumetric_fog->uniform_set); + RD::get_singleton()->free(rb->volumetric_fog->uniform_set2); + } + rb->volumetric_fog->uniform_set = RID(); + rb->volumetric_fog->uniform_set2 = RID(); + } + } + + if (p_gi_probe_cull_count > 0) { + RD::get_singleton()->buffer_update(gi_probe_buffer, 0, sizeof(GI::GIProbeData) * MIN(RenderBuffers::MAX_GIPROBES, p_gi_probe_cull_count), gi_probe_data, true); + } +} + void RasterizerSceneRD::_process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_ambient_buffer, RID p_reflection_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count) { RENDER_TIMESTAMP("Render GI"); @@ -1490,81 +1599,6 @@ void RasterizerSceneRD::_process_gi(RID p_render_buffers, RID p_normal_roughness RD::get_singleton()->buffer_update(gi.sdfgi_ubo, 0, sizeof(GI::SDFGIData), &sdfgi_data, true); } - { - RID gi_probe_buffer = render_buffers_get_gi_probe_buffer(p_render_buffers); - GI::GIProbeData gi_probe_data[RenderBuffers::MAX_GIPROBES]; - - bool giprobes_changed = false; - - Transform to_camera; - to_camera.origin = p_transform.origin; //only translation, make local - - for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) { - RID texture; - if (i < p_gi_probe_cull_count) { - GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_gi_probe_cull_result[i]); - - if (gipi) { - texture = gipi->texture; - GI::GIProbeData &gipd = gi_probe_data[i]; - - RID base_probe = gipi->probe; - - Transform to_cell = storage->gi_probe_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera; - - gipd.xform[0] = to_cell.basis.elements[0][0]; - gipd.xform[1] = to_cell.basis.elements[1][0]; - gipd.xform[2] = to_cell.basis.elements[2][0]; - gipd.xform[3] = 0; - gipd.xform[4] = to_cell.basis.elements[0][1]; - gipd.xform[5] = to_cell.basis.elements[1][1]; - gipd.xform[6] = to_cell.basis.elements[2][1]; - gipd.xform[7] = 0; - gipd.xform[8] = to_cell.basis.elements[0][2]; - gipd.xform[9] = to_cell.basis.elements[1][2]; - gipd.xform[10] = to_cell.basis.elements[2][2]; - gipd.xform[11] = 0; - gipd.xform[12] = to_cell.origin.x; - gipd.xform[13] = to_cell.origin.y; - gipd.xform[14] = to_cell.origin.z; - gipd.xform[15] = 1; - - Vector3 bounds = storage->gi_probe_get_octree_size(base_probe); - - gipd.bounds[0] = bounds.x; - gipd.bounds[1] = bounds.y; - gipd.bounds[2] = bounds.z; - - gipd.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe); - gipd.bias = storage->gi_probe_get_bias(base_probe); - gipd.normal_bias = storage->gi_probe_get_normal_bias(base_probe); - gipd.blend_ambient = !storage->gi_probe_is_interior(base_probe); - gipd.anisotropy_strength = 0; - gipd.ao = storage->gi_probe_get_ao(base_probe); - gipd.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f); - } - } - - if (texture == RID()) { - texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); - } - - if (texture != rb->giprobe_textures[i]) { - giprobes_changed = true; - rb->giprobe_textures[i] = texture; - } - } - - if (giprobes_changed) { - RD::get_singleton()->free(rb->gi_uniform_set); - rb->gi_uniform_set = RID(); - } - - if (p_gi_probe_cull_count > 0) { - RD::get_singleton()->buffer_update(gi_probe_buffer, 0, sizeof(GI::GIProbeData) * MIN(RenderBuffers::MAX_GIPROBES, p_gi_probe_cull_count), gi_probe_data, true); - } - } - if (rb->gi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) { Vector uniforms; { @@ -2880,6 +2914,48 @@ void RasterizerSceneRD::environment_set_sdfgi(RID p_env, bool p_enable, RS::Envi env->sdfgi_y_scale = p_y_scale; } +void RasterizerSceneRD::environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RenderingServer::EnvVolumetricFogShadowFilter p_shadow_filter) { + Environment *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->volumetric_fog_enabled = p_enable; + env->volumetric_fog_density = p_density; + env->volumetric_fog_light = p_light; + env->volumetric_fog_light_energy = p_light_energy; + env->volumetric_fog_length = p_lenght; + env->volumetric_fog_detail_spread = p_detail_spread; + env->volumetric_fog_shadow_filter = p_shadow_filter; + env->volumetric_fog_gi_inject = p_gi_inject; +} + +void RasterizerSceneRD::environment_set_volumetric_fog_volume_size(int p_size, int p_depth) { + volumetric_fog_size = p_size; + volumetric_fog_depth = p_depth; +} + +void RasterizerSceneRD::environment_set_volumetric_fog_filter_active(bool p_enable) { + volumetric_fog_filter_active = p_enable; +} +void RasterizerSceneRD::environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size) { + p_shrink_size = nearest_power_of_2_templated(p_shrink_size); + if (volumetric_fog_directional_shadow_shrink == (uint32_t)p_shrink_size) { + return; + } + + _clear_shadow_shrink_stages(directional_shadow.shrink_stages); +} +void RasterizerSceneRD::environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size) { + p_shrink_size = nearest_power_of_2_templated(p_shrink_size); + if (volumetric_fog_positional_shadow_shrink == (uint32_t)p_shrink_size) { + return; + } + + for (uint32_t i = 0; i < shadow_atlas_owner.get_rid_count(); i++) { + ShadowAtlas *sa = shadow_atlas_owner.get_ptr_by_index(i); + _clear_shadow_shrink_stages(sa->shrink_stages); + } +} + void RasterizerSceneRD::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { sdfgi_ray_count = p_ray_count; } @@ -3286,6 +3362,7 @@ void RasterizerSceneRD::shadow_atlas_set_size(RID p_atlas, int p_size) { if (shadow_atlas->depth.is_valid()) { RD::get_singleton()->free(shadow_atlas->depth); shadow_atlas->depth = RID(); + _clear_shadow_shrink_stages(shadow_atlas->shrink_stages); } for (int i = 0; i < 4; i++) { //clear subdivisions @@ -3579,6 +3656,7 @@ void RasterizerSceneRD::directional_shadow_atlas_set_size(int p_size) { if (directional_shadow.depth.is_valid()) { RD::get_singleton()->free(directional_shadow.depth); + _clear_shadow_shrink_stages(directional_shadow.shrink_stages); directional_shadow.depth = RID(); } @@ -4951,6 +5029,8 @@ void RasterizerSceneRD::_process_ssao(RID p_render_buffers, RID p_environment, R Environment *env = environment_owner.getornull(p_environment); ERR_FAIL_COND(!env); + RENDER_TIMESTAMP("Process SSAO"); + if (rb->ssao.ao[0].is_valid() && rb->ssao.ao_full.is_valid() != ssao_half_size) { RD::get_singleton()->free(rb->ssao.depth); RD::get_singleton()->free(rb->ssao.ao[0]); @@ -5463,6 +5543,30 @@ RID RasterizerSceneRD::render_buffers_get_sdfgi_occlusion_texture(RID p_render_b return rb->sdfgi->occlusion_texture; } +bool RasterizerSceneRD::render_buffers_has_volumetric_fog(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, false); + + return rb->volumetric_fog != nullptr; +} +RID RasterizerSceneRD::render_buffers_get_volumetric_fog_texture(RID p_render_buffers) { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, RID()); + + return rb->volumetric_fog->fog_map; +} + +float RasterizerSceneRD::render_buffers_get_volumetric_fog_end(RID p_render_buffers) { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, 0); + return rb->volumetric_fog->length; +} +float RasterizerSceneRD::render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers) { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb || !rb->volumetric_fog, 0); + return rb->volumetric_fog->spread; +} + void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); rb->width = p_width; @@ -5679,9 +5783,10 @@ void RasterizerSceneRD::_setup_reflections(RID *p_reflection_probe_cull_result, } } -void RasterizerSceneRD::_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, uint32_t &r_directional_light_count) { +void RasterizerSceneRD::_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, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count) { uint32_t light_count = 0; r_directional_light_count = 0; + r_positional_light_count = 0; sky_scene_state.directional_light_count = 0; for (int i = 0; i < p_light_cull_count; i++) { @@ -5797,7 +5902,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull light_data.shadow_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BIAS) * bias_scale; light_data.shadow_normal_bias[j] = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS) * light_instance_get_directional_shadow_texel_size(li, j); light_data.shadow_transmittance_bias[j] = storage->light_get_transmittance_bias(base) * bias_scale; - light_data.shadow_transmittance_z_scale[j] = light_instance_get_shadow_range(li, j); + light_data.shadow_z_range[j] = light_instance_get_shadow_range(li, j); light_data.shadow_range_begin[j] = light_instance_get_shadow_range_begin(li, j); RasterizerStorageRD::store_camera(shadow_mtx, light_data.shadow_matrices[j]); @@ -5826,6 +5931,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull float fade_start = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_FADE_START); light_data.fade_from = -light_data.shadow_split_offsets[3] * MIN(fade_start, 0.999); //using 1.0 would break smoothstep light_data.fade_to = -light_data.shadow_split_offsets[3]; + light_data.shadow_volumetric_fog_fade = 1.0 / storage->light_get_shadow_volumetric_fog_fade(base); light_data.soft_shadow_scale = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR); light_data.softshadow_angle = angular_diameter; @@ -5867,6 +5973,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull Transform light_transform = light_instance_get_base_transform(li); Cluster::LightData &light_data = cluster.lights[light_count]; + cluster.lights_instances[light_count] = li; float sign = storage->light_is_negative(base) ? -1 : 1; Color linear_col = storage->light_get_color(base).to_linear(); @@ -5965,6 +6072,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull light_data.atlas_rect[3] = rect.size.height; light_data.soft_shadow_scale = storage->light_get_param(base, RS::LIGHT_PARAM_SHADOW_BLUR); + light_data.shadow_volumetric_fog_fade = 1.0 / storage->light_get_shadow_volumetric_fog_fade(base); if (type == RS::LIGHT_OMNI) { light_data.atlas_rect[3] *= 0.5; //one paraboloid on top of another @@ -6005,6 +6113,7 @@ void RasterizerSceneRD::_setup_lights(RID *p_light_cull_result, int p_light_cull cluster.builder.add_light(type == RS::LIGHT_SPOT ? LightClusterBuilder::LIGHT_TYPE_SPOT : LightClusterBuilder::LIGHT_TYPE_OMNI, light_transform, radius, spot_angle); light_count++; + r_positional_light_count++; } break; } @@ -6152,6 +6261,526 @@ void RasterizerSceneRD::_setup_decals(const RID *p_decal_instances, int p_decal_ } } +void RasterizerSceneRD::_volumetric_fog_erase(RenderBuffers *rb) { + ERR_FAIL_COND(!rb->volumetric_fog); + + RD::get_singleton()->free(rb->volumetric_fog->light_density_map); + RD::get_singleton()->free(rb->volumetric_fog->fog_map); + memdelete(rb->volumetric_fog); + rb->volumetric_fog = nullptr; +} + +void RasterizerSceneRD::_allocate_shadow_shrink_stages(RID p_base, int p_base_size, Vector &shrink_stages, uint32_t p_target_size) { + //create fog mipmaps + uint32_t fog_texture_size = p_target_size; + uint32_t base_texture_size = p_base_size; + + ShadowShrinkStage first; + first.size = base_texture_size; + first.texture = p_base; + shrink_stages.push_back(first); //put depth first in case we dont find smaller ones + + while (fog_texture_size < base_texture_size) { + base_texture_size = MAX(base_texture_size / 8, fog_texture_size); + + ShadowShrinkStage s; + s.size = base_texture_size; + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + tf.width = base_texture_size; + tf.height = base_texture_size; + tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + + if (base_texture_size == fog_texture_size) { + s.filter_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + } + + s.texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + shrink_stages.push_back(s); + } +} + +void RasterizerSceneRD::_clear_shadow_shrink_stages(Vector &shrink_stages) { + for (int i = 1; i < shrink_stages.size(); i++) { + RD::get_singleton()->free(shrink_stages[i].texture); + if (shrink_stages[i].filter_texture.is_valid()) { + RD::get_singleton()->free(shrink_stages[i].filter_texture); + } + } + shrink_stages.clear(); +} + +void RasterizerSceneRD::_update_volumetric_fog(RID p_render_buffers, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_gi_probe_count) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + Environment *env = environment_owner.getornull(p_environment); + + float ratio = float(rb->width) / float((rb->width + rb->height) / 2); + uint32_t target_width = uint32_t(float(volumetric_fog_size) * ratio); + uint32_t target_height = uint32_t(float(volumetric_fog_size) / ratio); + + if (rb->volumetric_fog) { + //validate + if (!env || !env->volumetric_fog_enabled || rb->volumetric_fog->width != target_width || rb->volumetric_fog->height != target_height || rb->volumetric_fog->depth != volumetric_fog_depth) { + _volumetric_fog_erase(rb); + _render_buffers_uniform_set_changed(p_render_buffers); + } + } + + if (!env || !env->volumetric_fog_enabled) { + //no reason to enable or update, bye + return; + } + + if (env && env->volumetric_fog_enabled && !rb->volumetric_fog) { + //required volumetric fog but not existing, create + rb->volumetric_fog = memnew(VolumetricFog); + rb->volumetric_fog->width = target_width; + rb->volumetric_fog->height = target_height; + rb->volumetric_fog->depth = volumetric_fog_depth; + + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.width = target_width; + tf.height = target_height; + tf.depth = volumetric_fog_depth; + tf.type = RD::TEXTURE_TYPE_3D; + tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT; + + rb->volumetric_fog->light_density_map = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT; + + rb->volumetric_fog->fog_map = RD::get_singleton()->texture_create(tf, RD::TextureView()); + _render_buffers_uniform_set_changed(p_render_buffers); + } + + //update directional shadow + + if (p_use_directional_shadows) { + if (directional_shadow.shrink_stages.empty()) { + if (rb->volumetric_fog->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + //invalidate uniform set, we will need a new one + RD::get_singleton()->free(rb->volumetric_fog->uniform_set); + rb->volumetric_fog->uniform_set = RID(); + } + _allocate_shadow_shrink_stages(directional_shadow.depth, directional_shadow.size, directional_shadow.shrink_stages, volumetric_fog_directional_shadow_shrink); + } + + if (directional_shadow.shrink_stages.size() > 1) { + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + for (int i = 1; i < directional_shadow.shrink_stages.size(); i++) { + int32_t src_size = directional_shadow.shrink_stages[i - 1].size; + int32_t dst_size = directional_shadow.shrink_stages[i].size; + Rect2i r(0, 0, src_size, src_size); + int32_t shrink_limit = 8 / (src_size / dst_size); + + storage->get_effects()->reduce_shadow(directional_shadow.shrink_stages[i - 1].texture, directional_shadow.shrink_stages[i].texture, Size2i(src_size, src_size), r, shrink_limit, compute_list); + RD::get_singleton()->compute_list_add_barrier(compute_list); + if (env->volumetric_fog_shadow_filter != RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED && directional_shadow.shrink_stages[i].filter_texture.is_valid()) { + Rect2i rf(0, 0, dst_size, dst_size); + storage->get_effects()->filter_shadow(directional_shadow.shrink_stages[i].texture, directional_shadow.shrink_stages[i].filter_texture, Size2i(dst_size, dst_size), rf, env->volumetric_fog_shadow_filter, compute_list); + } + } + RD::get_singleton()->compute_list_end(); + } + } + + ShadowAtlas *shadow_atlas = shadow_atlas_owner.getornull(p_shadow_atlas); + + if (shadow_atlas) { + //shrink shadows that need to be shrunk + + bool force_shrink_shadows = false; + + if (shadow_atlas->shrink_stages.empty()) { + if (rb->volumetric_fog->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + //invalidate uniform set, we will need a new one + RD::get_singleton()->free(rb->volumetric_fog->uniform_set); + rb->volumetric_fog->uniform_set = RID(); + } + _allocate_shadow_shrink_stages(shadow_atlas->depth, shadow_atlas->size, shadow_atlas->shrink_stages, volumetric_fog_positional_shadow_shrink); + force_shrink_shadows = true; + } + + if (rb->volumetric_fog->last_shadow_filter != env->volumetric_fog_shadow_filter) { + //if shadow filter changed, invalidate caches + rb->volumetric_fog->last_shadow_filter = env->volumetric_fog_shadow_filter; + force_shrink_shadows = true; + } + + cluster.lights_shadow_rect_cache_count = 0; + + for (int i = 0; i < p_positional_light_count; i++) { + if (cluster.lights[i].shadow_color_enabled[3] > 127) { + RID li = cluster.lights_instances[i]; + + ERR_CONTINUE(!shadow_atlas->shadow_owners.has(li)); + + uint32_t key = shadow_atlas->shadow_owners[li]; + + uint32_t quadrant = (key >> ShadowAtlas::QUADRANT_SHIFT) & 0x3; + uint32_t shadow = key & ShadowAtlas::SHADOW_INDEX_MASK; + + ERR_CONTINUE((int)shadow >= shadow_atlas->quadrants[quadrant].shadows.size()); + + ShadowAtlas::Quadrant::Shadow &s = shadow_atlas->quadrants[quadrant].shadows.write[shadow]; + + if (!force_shrink_shadows && s.fog_version == s.version) { + continue; //do not update, no need + } + + s.fog_version = s.version; + + uint32_t quadrant_size = shadow_atlas->size >> 1; + + Rect2i atlas_rect; + + atlas_rect.position.x = (quadrant & 1) * quadrant_size; + atlas_rect.position.y = (quadrant >> 1) * quadrant_size; + + uint32_t shadow_size = (quadrant_size / shadow_atlas->quadrants[quadrant].subdivision); + atlas_rect.position.x += (shadow % shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + atlas_rect.position.y += (shadow / shadow_atlas->quadrants[quadrant].subdivision) * shadow_size; + + atlas_rect.size.x = shadow_size; + atlas_rect.size.y = shadow_size; + + cluster.lights_shadow_rect_cache[cluster.lights_shadow_rect_cache_count] = atlas_rect; + + cluster.lights_shadow_rect_cache_count++; + + if (cluster.lights_shadow_rect_cache_count == cluster.max_lights) { + break; //light limit reached + } + } + } + + if (cluster.lights_shadow_rect_cache_count > 0) { + //there are shadows to be shrunk, try to do them in parallel + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + for (int i = 1; i < shadow_atlas->shrink_stages.size(); i++) { + int32_t base_size = shadow_atlas->shrink_stages[0].size; + int32_t src_size = shadow_atlas->shrink_stages[i - 1].size; + int32_t dst_size = shadow_atlas->shrink_stages[i].size; + + uint32_t rect_divisor = base_size / src_size; + + int32_t shrink_limit = 8 / (src_size / dst_size); + + //shrink in parallel for more performance + for (uint32_t j = 0; j < cluster.lights_shadow_rect_cache_count; j++) { + Rect2i src_rect = cluster.lights_shadow_rect_cache[j]; + + src_rect.position /= rect_divisor; + src_rect.size /= rect_divisor; + + storage->get_effects()->reduce_shadow(shadow_atlas->shrink_stages[i - 1].texture, shadow_atlas->shrink_stages[i].texture, Size2i(src_size, src_size), src_rect, shrink_limit, compute_list); + } + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + if (env->volumetric_fog_shadow_filter != RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED && shadow_atlas->shrink_stages[i].filter_texture.is_valid()) { + uint32_t filter_divisor = base_size / dst_size; + + //filter in parallel for more performance + for (uint32_t j = 0; j < cluster.lights_shadow_rect_cache_count; j++) { + Rect2i dst_rect = cluster.lights_shadow_rect_cache[j]; + + dst_rect.position /= filter_divisor; + dst_rect.size /= filter_divisor; + + storage->get_effects()->filter_shadow(shadow_atlas->shrink_stages[i].texture, shadow_atlas->shrink_stages[i].filter_texture, Size2i(dst_size, dst_size), dst_rect, env->volumetric_fog_shadow_filter, compute_list, true, false); + } + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + for (uint32_t j = 0; j < cluster.lights_shadow_rect_cache_count; j++) { + Rect2i dst_rect = cluster.lights_shadow_rect_cache[j]; + + dst_rect.position /= filter_divisor; + dst_rect.size /= filter_divisor; + + storage->get_effects()->filter_shadow(shadow_atlas->shrink_stages[i].texture, shadow_atlas->shrink_stages[i].filter_texture, Size2i(dst_size, dst_size), dst_rect, env->volumetric_fog_shadow_filter, compute_list, false, true); + } + } + } + + RD::get_singleton()->compute_list_end(); + } + } + + //update volumetric fog + + if (rb->volumetric_fog->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->uniform_set)) { + //re create uniform set if needed + + Vector uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; + if (shadow_atlas == nullptr || shadow_atlas->shrink_stages.size() == 0) { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK)); + } else { + u.ids.push_back(shadow_atlas->shrink_stages[shadow_atlas->shrink_stages.size() - 1].texture); + } + + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; + if (directional_shadow.shrink_stages.size() == 0) { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK)); + } else { + u.ids.push_back(directional_shadow.shrink_stages[directional_shadow.shrink_stages.size() - 1].texture); + } + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 3; + u.ids.push_back(get_positional_light_buffer()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 4; + u.ids.push_back(get_directional_light_buffer()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 5; + u.ids.push_back(get_cluster_builder_texture()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 6; + u.ids.push_back(get_cluster_builder_indices_buffer()); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 7; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 8; + u.ids.push_back(rb->volumetric_fog->light_density_map); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 9; + u.ids.push_back(rb->volumetric_fog->fog_map); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 10; + u.ids.push_back(shadow_sampler); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 11; + u.ids.push_back(render_buffers_get_gi_probe_buffer(p_render_buffers)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 12; + for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) { + u.ids.push_back(rb->giprobe_textures[i]); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 13; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + rb->volumetric_fog->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, 0), 0); + + SWAP(uniforms.write[7].ids.write[0], uniforms.write[8].ids.write[0]); + + rb->volumetric_fog->uniform_set2 = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, 0), 0); + } + + bool using_sdfgi = env->volumetric_fog_gi_inject > 0.0001 && env->sdfgi_enabled && (rb->sdfgi != nullptr); + + if (using_sdfgi) { + if (rb->volumetric_fog->sdfgi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->volumetric_fog->sdfgi_uniform_set)) { + Vector uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 0; + u.ids.push_back(gi.sdfgi_ubo); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 1; + u.ids.push_back(rb->sdfgi->ambient_texture); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 2; + u.ids.push_back(rb->sdfgi->occlusion_texture); + uniforms.push_back(u); + } + + rb->volumetric_fog->sdfgi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, VOLUMETRIC_FOG_SHADER_DENSITY_WITH_SDFGI), 1); + } + } + + rb->volumetric_fog->length = env->volumetric_fog_length; + rb->volumetric_fog->spread = env->volumetric_fog_detail_spread; + + VolumetricFogShader::PushConstant push_constant; + + Vector2 frustum_near_size = p_cam_projection.get_viewport_half_extents(); + Vector2 frustum_far_size = p_cam_projection.get_far_plane_half_extents(); + float z_near = p_cam_projection.get_z_near(); + float z_far = p_cam_projection.get_z_far(); + float fog_end = env->volumetric_fog_length; + + Vector2 fog_far_size = frustum_near_size.lerp(frustum_far_size, (fog_end - z_near) / (z_far - z_near)); + Vector2 fog_near_size; + if (p_cam_projection.is_orthogonal()) { + fog_near_size = fog_far_size; + } else { + fog_near_size = Vector2(); + } + + push_constant.fog_frustum_size_begin[0] = fog_near_size.x; + push_constant.fog_frustum_size_begin[1] = fog_near_size.y; + + push_constant.fog_frustum_size_end[0] = fog_far_size.x; + push_constant.fog_frustum_size_end[1] = fog_far_size.y; + + push_constant.z_near = z_near; + push_constant.z_far = z_far; + + push_constant.fog_frustum_end = fog_end; + + push_constant.fog_volume_size[0] = rb->volumetric_fog->width; + push_constant.fog_volume_size[1] = rb->volumetric_fog->height; + push_constant.fog_volume_size[2] = rb->volumetric_fog->depth; + + push_constant.directional_light_count = p_directional_light_count; + + Color light = env->volumetric_fog_light.to_linear(); + push_constant.light_energy[0] = light.r * env->volumetric_fog_light_energy; + push_constant.light_energy[1] = light.g * env->volumetric_fog_light_energy; + push_constant.light_energy[2] = light.b * env->volumetric_fog_light_energy; + push_constant.base_density = env->volumetric_fog_density; + + push_constant.detail_spread = env->volumetric_fog_detail_spread; + push_constant.gi_inject = env->volumetric_fog_gi_inject; + + push_constant.cam_rotation[0] = p_cam_transform.basis[0][0]; + push_constant.cam_rotation[1] = p_cam_transform.basis[1][0]; + push_constant.cam_rotation[2] = p_cam_transform.basis[2][0]; + push_constant.cam_rotation[3] = 0; + push_constant.cam_rotation[4] = p_cam_transform.basis[0][1]; + push_constant.cam_rotation[5] = p_cam_transform.basis[1][1]; + push_constant.cam_rotation[6] = p_cam_transform.basis[2][1]; + push_constant.cam_rotation[7] = 0; + push_constant.cam_rotation[8] = p_cam_transform.basis[0][2]; + push_constant.cam_rotation[9] = p_cam_transform.basis[1][2]; + push_constant.cam_rotation[10] = p_cam_transform.basis[2][2]; + push_constant.cam_rotation[11] = 0; + push_constant.filter_axis = 0; + push_constant.max_gi_probes = env->volumetric_fog_gi_inject > 0.001 ? p_gi_probe_count : 0; + + /* Vector2 dssize = directional_shadow_get_size(); + push_constant.directional_shadow_pixel_size[0] = 1.0 / dssize.x; + push_constant.directional_shadow_pixel_size[1] = 1.0 / dssize.y; +*/ + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + bool use_filter = volumetric_fog_filter_active; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.pipelines[using_sdfgi ? VOLUMETRIC_FOG_SHADER_DENSITY_WITH_SDFGI : VOLUMETRIC_FOG_SHADER_DENSITY]); + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set, 0); + if (using_sdfgi) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->sdfgi_uniform_set, 1); + } + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, rb->volumetric_fog->depth, 4, 4, 4); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + if (use_filter) { + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.pipelines[VOLUMETRIC_FOG_SHADER_FILTER]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set, 0); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, rb->volumetric_fog->depth, 8, 8, 1); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + push_constant.filter_axis = 1; + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set2, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, rb->volumetric_fog->depth, 8, 8, 1); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, volumetric_fog.pipelines[VOLUMETRIC_FOG_SHADER_FOG]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->volumetric_fog->uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(VolumetricFogShader::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->volumetric_fog->width, rb->volumetric_fog->height, 1, 8, 8, 1); + + RD::get_singleton()->compute_list_end(); +} + void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) { Color clear_color; if (p_render_buffers.is_valid()) { @@ -6190,10 +6819,25 @@ void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_ca } uint32_t directional_light_count = 0; - _setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows, directional_light_count); + uint32_t positional_light_count = 0; + _setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows, directional_light_count, positional_light_count); _setup_decals(p_decal_cull_result, p_decal_cull_count, p_cam_transform.affine_inverse()); cluster.builder.bake_cluster(); //bake to cluster + uint32_t gi_probe_count = 0; + _setup_giprobes(p_render_buffers, p_cam_transform, p_gi_probe_cull_result, p_gi_probe_cull_count, gi_probe_count); + + if (p_render_buffers.is_valid()) { + bool directional_shadows = false; + for (uint32_t i = 0; i < directional_light_count; i++) { + if (cluster.directional_lights[i].shadow_enabled) { + directional_shadows = true; + break; + } + } + _update_volumetric_fog(p_render_buffers, p_environment, p_cam_projection, p_cam_transform, p_shadow_atlas, directional_light_count, directional_shadows, positional_light_count, gi_probe_count); + } + _render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, directional_light_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_lightmap_cull_result, p_lightmap_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color); if (p_render_buffers.is_valid()) { @@ -6481,6 +7125,7 @@ void RasterizerSceneRD::render_sdfgi(RID p_render_buffers, int p_region, Instanc ipush_constant.sky_color[1] = 0; ipush_constant.sky_color[2] = 0; ipush_constant.y_mult = rb->sdfgi->y_mult; + ipush_constant.store_ambient_texture = false; ipush_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count; ipush_constant.image_size[1] = rb->sdfgi->probe_axis_count; @@ -6836,6 +7481,9 @@ bool RasterizerSceneRD::free(RID p_rid) { if (rb->sdfgi) { _sdfgi_erase(rb); } + if (rb->volumetric_fog) { + _volumetric_fog_erase(rb); + } render_buffers_owner.free(p_rid); } else if (environment_owner.owns(p_rid)) { //not much to delete, just free it @@ -7406,6 +8054,8 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { cluster.lights = memnew_arr(Cluster::LightData, cluster.max_lights); cluster.light_buffer = RD::get_singleton()->storage_buffer_create(light_buffer_size); //defines += "\n#define MAX_LIGHT_DATA_STRUCTS " + itos(cluster.max_lights) + "\n"; + cluster.lights_instances = memnew_arr(RID, cluster.max_lights); + cluster.lights_shadow_rect_cache = memnew_arr(Rect2i, cluster.max_lights); cluster.max_directional_lights = 8; uint32_t directional_light_buffer_size = cluster.max_directional_lights * sizeof(Cluster::DirectionalLightData); @@ -7422,8 +8072,30 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { cluster.builder.setup(16, 8, 24); + { + String defines = "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(cluster.max_directional_lights) + "\n"; + Vector volumetric_fog_modes; + volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n"); + volumetric_fog_modes.push_back("\n#define MODE_DENSITY\n#define ENABLE_SDFGI\n"); + volumetric_fog_modes.push_back("\n#define MODE_FILTER\n"); + volumetric_fog_modes.push_back("\n#define MODE_FOG\n"); + volumetric_fog.shader.initialize(volumetric_fog_modes, defines); + volumetric_fog.shader_version = volumetric_fog.shader.version_create(); + for (int i = 0; i < VOLUMETRIC_FOG_SHADER_MAX; i++) { + volumetric_fog.pipelines[i] = RD::get_singleton()->compute_pipeline_create(volumetric_fog.shader.version_get_shader(volumetric_fog.shader_version, i)); + } + } default_giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES); + { + RD::SamplerState sampler; + sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.min_filter = RD::SAMPLER_FILTER_LINEAR; + sampler.enable_compare = true; + sampler.compare_op = RD::COMPARE_OP_LESS; + shadow_sampler = RD::get_singleton()->sampler_create(sampler); + } + camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_shape")))); camera_effects_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_use_jitter")); environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); @@ -7441,6 +8113,11 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { soft_shadow_kernel = memnew_arr(float, 128); shadows_quality_set(RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/shadows/soft_shadow_quality")))); directional_shadow_quality_set(RS::ShadowQuality(int(GLOBAL_GET("rendering/quality/directional_shadow/soft_shadow_quality")))); + + environment_set_volumetric_fog_volume_size(GLOBAL_GET("rendering/volumetric_fog/volume_size"), GLOBAL_GET("rendering/volumetric_fog/volume_depth")); + environment_set_volumetric_fog_filter_active(GLOBAL_GET("rendering/volumetric_fog/use_filter")); + environment_set_volumetric_fog_directional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/directional_shadow_shrink")); + environment_set_volumetric_fog_positional_shadow_shrink_size(GLOBAL_GET("rendering/volumetric_fog/positional_shadow_shrink")); } RasterizerSceneRD::~RasterizerSceneRD() { @@ -7491,7 +8168,13 @@ RasterizerSceneRD::~RasterizerSceneRD() { RD::get_singleton()->free(cluster.decal_buffer); memdelete_arr(cluster.directional_lights); memdelete_arr(cluster.lights); + memdelete_arr(cluster.lights_shadow_rect_cache); + memdelete_arr(cluster.lights_instances); memdelete_arr(cluster.reflections); memdelete_arr(cluster.decals); } + + RD::get_singleton()->free(shadow_sampler); + + directional_shadow_atlas_set_size(0); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h index 27eec44ec33d..7c58a410d096 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h @@ -45,6 +45,7 @@ #include "servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl.gen.h" #include "servers/rendering/rasterizer_rd/shaders/sky.glsl.gen.h" +#include "servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl.gen.h" #include "servers/rendering/rendering_device.h" class RasterizerSceneRD : public RasterizerScene { @@ -78,9 +79,10 @@ class RasterizerSceneRD : public RasterizerScene { }; virtual RenderBufferData *_create_render_buffer_data() = 0; - 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, uint32_t &r_directional_light_count); + 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, uint32_t &r_directional_light_count, uint32_t &r_positional_light_count); void _setup_decals(const RID *p_decal_instances, int p_decal_count, const Transform &p_camera_inverse_xform); 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_giprobes(RID p_render_buffers, const Transform &p_transform, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, uint32_t &r_gi_probes_used); virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, int p_directional_light_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_color) = 0; virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip, bool p_use_pancake) = 0; @@ -490,6 +492,12 @@ class RasterizerSceneRD : public RasterizerScene { /* SHADOW ATLAS */ + struct ShadowShrinkStage { + RID texture; + RID filter_texture; + uint32_t size; + }; + struct ShadowAtlas { enum { QUADRANT_SHIFT = 27, @@ -503,10 +511,12 @@ class RasterizerSceneRD : public RasterizerScene { struct Shadow { RID owner; uint64_t version; + uint64_t fog_version; // used for fog uint64_t alloc_tick; Shadow() { version = 0; + fog_version = 0; alloc_tick = 0; } }; @@ -528,6 +538,8 @@ class RasterizerSceneRD : public RasterizerScene { RID fb; //for copying Map shadow_owners; + + Vector shrink_stages; }; RID_Owner shadow_atlas_owner; @@ -556,8 +568,14 @@ class RasterizerSceneRD : public RasterizerScene { int light_count = 0; int size = 0; int current_light = 0; + + Vector shrink_stages; + } directional_shadow; + void _allocate_shadow_shrink_stages(RID p_base, int p_base_size, Vector &shrink_stages, uint32_t p_target_size); + void _clear_shadow_shrink_stages(Vector &shrink_stages); + /* SHADOW CUBEMAPS */ struct ShadowCubemap { @@ -656,6 +674,17 @@ class RasterizerSceneRD : public RasterizerScene { float auto_exp_scale = 0.5; uint64_t auto_exposure_version = 0; + /// Volumetric Fog + /// + bool volumetric_fog_enabled = false; + float volumetric_fog_density = 0.01; + Color volumetric_fog_light = Color(0, 0, 0); + float volumetric_fog_light_energy = 0.0; + float volumetric_fog_length = 64.0; + float volumetric_fog_detail_spread = 2.0; + RS::EnvVolumetricFogShadowFilter volumetric_fog_shadow_filter = RS::ENV_VOLUMETRIC_FOG_SHADOW_FILTER_LOW; + float volumetric_fog_gi_inject = 0.0; + /// Glow bool glow_enabled = false; @@ -739,6 +768,7 @@ class RasterizerSceneRD : public RasterizerScene { /* RENDER BUFFERS */ struct SDFGI; + struct VolumetricFog; struct RenderBuffers { enum { @@ -759,6 +789,7 @@ class RasterizerSceneRD : public RasterizerScene { RID gi_uniform_set; SDFGI *sdfgi = nullptr; + VolumetricFog *volumetric_fog = nullptr; //built-in textures used for ping pong image processing and blurring struct Blur { @@ -885,6 +916,7 @@ class RasterizerSceneRD : public RasterizerScene { RID lightprobe_data; RID occlusion_texture; RID occlusion_data; + RID ambient_texture; //integrates with volumetric fog RID lightprobe_history_scroll; //used for scrolling lightprobes RID lightprobe_average_scroll; //used for scrolling lightprobes @@ -1077,6 +1109,9 @@ class RasterizerSceneRD : public RasterizerScene { float sky_color[3]; float y_mult; + + uint32_t store_ambient_texture; + uint32_t pad[3]; }; SdfgiIntegrateShaderRD integrate; @@ -1141,7 +1176,7 @@ class RasterizerSceneRD : public RasterizerScene { float anisotropy_strength; float ao; float ao_size; - uint32_t pad[1]; + uint32_t mipmaps; }; struct PushConstant { @@ -1219,7 +1254,8 @@ class RasterizerSceneRD : public RasterizerScene { float soft_shadow_size; float soft_shadow_scale; uint32_t mask; - uint32_t pad[2]; + float shadow_volumetric_fog_fade; + uint32_t pad; float projector_rect[4]; }; @@ -1236,10 +1272,12 @@ class RasterizerSceneRD : public RasterizerScene { uint32_t shadow_enabled; float fade_from; float fade_to; + uint32_t pad[3]; + float shadow_volumetric_fog_fade; float shadow_bias[4]; float shadow_normal_bias[4]; float shadow_transmittance_bias[4]; - float shadow_transmittance_z_scale[4]; + float shadow_z_range[4]; float shadow_range_begin[4]; float shadow_split_offsets[4]; float shadow_matrices[4][16]; @@ -1283,6 +1321,9 @@ class RasterizerSceneRD : public RasterizerScene { LightData *lights; uint32_t max_lights; RID light_buffer; + RID *lights_instances; + Rect2i *lights_shadow_rect_cache; + uint32_t lights_shadow_rect_cache_count = 0; DirectionalLightData *directional_lights; uint32_t max_directional_lights; @@ -1292,6 +1333,73 @@ class RasterizerSceneRD : public RasterizerScene { } cluster; + struct VolumetricFog { + uint32_t width = 0; + uint32_t height = 0; + uint32_t depth = 0; + + float length; + float spread; + + RID light_density_map; + RID fog_map; + RID uniform_set; + RID uniform_set2; + RID sdfgi_uniform_set; + + int last_shadow_filter = -1; + }; + + enum { + VOLUMETRIC_FOG_SHADER_DENSITY, + VOLUMETRIC_FOG_SHADER_DENSITY_WITH_SDFGI, + VOLUMETRIC_FOG_SHADER_FILTER, + VOLUMETRIC_FOG_SHADER_FOG, + VOLUMETRIC_FOG_SHADER_MAX, + }; + + struct VolumetricFogShader { + struct PushConstant { + float fog_frustum_size_begin[2]; + float fog_frustum_size_end[2]; + + float fog_frustum_end; + float z_near; + float z_far; + uint32_t filter_axis; + + int32_t fog_volume_size[3]; + uint32_t directional_light_count; + + float light_energy[3]; + float base_density; + + float detail_spread; + float gi_inject; + uint32_t max_gi_probes; + uint32_t pad; + + float cam_rotation[12]; + }; + + VolumetricFogShaderRD shader; + + RID shader_version; + RID pipelines[VOLUMETRIC_FOG_SHADER_MAX]; + + } volumetric_fog; + + uint32_t volumetric_fog_depth = 128; + uint32_t volumetric_fog_size = 128; + bool volumetric_fog_filter_active = false; + uint32_t volumetric_fog_directional_shadow_shrink = 512; + uint32_t volumetric_fog_positional_shadow_shrink = 512; + + void _volumetric_fog_erase(RenderBuffers *rb); + void _update_volumetric_fog(RID p_render_buffers, RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_shadow_atlas, int p_directional_light_count, bool p_use_directional_shadows, int p_positional_light_count, int p_gi_probe_count); + + RID shadow_sampler; + uint64_t scene_pass = 0; uint64_t shadow_atlas_realloc_tolerance_msec = 500; @@ -1391,6 +1499,12 @@ class RasterizerSceneRD : public RasterizerScene { void environment_glow_set_use_bicubic_upscale(bool p_enable); void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {} + void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, RS::EnvVolumetricFogShadowFilter p_shadow_filter); + + virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth); + virtual void environment_set_volumetric_fog_filter_active(bool p_enable); + virtual void environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size); + virtual void environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size); void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance); void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_bias, float p_light_affect, float p_ao_channel_affect, RS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness); @@ -1708,6 +1822,11 @@ class RasterizerSceneRD : public RasterizerScene { float render_buffers_get_sdfgi_energy(RID p_render_buffers) const; RID render_buffers_get_sdfgi_occlusion_texture(RID p_render_buffers) const; + bool render_buffers_has_volumetric_fog(RID p_render_buffers) const; + RID render_buffers_get_volumetric_fog_texture(RID p_render_buffers); + float render_buffers_get_volumetric_fog_end(RID p_render_buffers); + float render_buffers_get_volumetric_fog_detail_spread(RID p_render_buffers); + void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass); void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count); diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index 102e0e2eed37..f3ba57e733f9 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp @@ -3290,6 +3290,7 @@ RID RasterizerStorageRD::light_create(RS::LightType p_type) { light.param[RS::LIGHT_PARAM_SHADOW_NORMAL_BIAS] = 1.0; light.param[RS::LIGHT_PARAM_SHADOW_PANCAKE_SIZE] = 20.0; light.param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS] = 0.05; + light.param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE] = 1.0; return light_owner.make_rid(light); } diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index b7aedf871774..6e5923953bdd 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h @@ -1184,6 +1184,13 @@ class RasterizerStorageRD : public RasterizerStorage { return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS]; } + _FORCE_INLINE_ float light_get_shadow_volumetric_fog_fade(RID p_light) const { + const Light *light = light_owner.getornull(p_light); + ERR_FAIL_COND_V(!light, 0.0); + + return light->param[RS::LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE]; + } + RS::LightBakeMode light_get_bake_mode(RID p_light); uint32_t light_get_max_sdfgi_cascade(RID p_light); uint64_t light_get_version(RID p_light) const; diff --git a/servers/rendering/rasterizer_rd/shaders/SCsub b/servers/rendering/rasterizer_rd/shaders/SCsub index 67f4edc626a7..3aa863be98b5 100644 --- a/servers/rendering/rasterizer_rd/shaders/SCsub +++ b/servers/rendering/rasterizer_rd/shaders/SCsub @@ -35,3 +35,5 @@ if "RD_GLSL" in env["BUILDERS"]: env.RD_GLSL("sdfgi_direct_light.glsl") env.RD_GLSL("sdfgi_debug.glsl") env.RD_GLSL("sdfgi_debug_probes.glsl") + env.RD_GLSL("volumetric_fog.glsl") + env.RD_GLSL("shadow_reduce.glsl") diff --git a/servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl b/servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl new file mode 100644 index 000000000000..e723468dd838 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl @@ -0,0 +1,95 @@ + +#define CLUSTER_COUNTER_SHIFT 20 +#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1) +#define CLUSTER_COUNTER_MASK 0xfff + +struct LightData { //this structure needs to be as packed as possible + vec3 position; + float inv_radius; + vec3 direction; + float size; + uint attenuation_energy; //attenuation + uint color_specular; //rgb color, a specular (8 bit unorm) + uint cone_attenuation_angle; // attenuation and angle, (16bit float) + uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm) + vec4 atlas_rect; // rect in the shadow atlas + mat4 shadow_matrix; + float shadow_bias; + float shadow_normal_bias; + float transmittance_bias; + float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle + float soft_shadow_scale; // scales the shadow kernel for blurrier shadows + uint mask; + float shadow_volumetric_fog_fade; + uint pad; + vec4 projector_rect; //projector rect in srgb decal atlas +}; + +#define REFLECTION_AMBIENT_DISABLED 0 +#define REFLECTION_AMBIENT_ENVIRONMENT 1 +#define REFLECTION_AMBIENT_COLOR 2 + +struct ReflectionData { + vec3 box_extents; + float index; + vec3 box_offset; + uint mask; + vec4 params; // intensity, 0, interior , boxproject + vec3 ambient; // ambient color + uint ambient_mode; + mat4 local_matrix; // up to here for spot and omni, rest is for directional + // notes: for ambientblend, use distance to edge to blend between already existing global environment +}; + +struct DirectionalLightData { + vec3 direction; + float energy; + vec3 color; + float size; + float specular; + uint mask; + float softshadow_angle; + float soft_shadow_scale; + bool blend_splits; + bool shadow_enabled; + float fade_from; + float fade_to; + uvec3 pad; + float shadow_volumetric_fog_fade; + vec4 shadow_bias; + vec4 shadow_normal_bias; + vec4 shadow_transmittance_bias; + vec4 shadow_z_range; + vec4 shadow_range_begin; + vec4 shadow_split_offsets; + mat4 shadow_matrix1; + mat4 shadow_matrix2; + mat4 shadow_matrix3; + mat4 shadow_matrix4; + vec4 shadow_color1; + vec4 shadow_color2; + vec4 shadow_color3; + vec4 shadow_color4; + vec2 uv_scale1; + vec2 uv_scale2; + vec2 uv_scale3; + vec2 uv_scale4; +}; + +struct DecalData { + mat4 xform; //to decal transform + vec3 inv_extents; + float albedo_mix; + vec4 albedo_rect; + vec4 normal_rect; + vec4 orm_rect; + vec4 emission_rect; + vec4 modulate; + float emission_energy; + uint mask; + float upper_fade; + float lower_fade; + mat3x4 normal_xform; + vec3 normal; + float normal_fade; +}; diff --git a/servers/rendering/rasterizer_rd/shaders/gi.glsl b/servers/rendering/rasterizer_rd/shaders/gi.glsl index a1939f75ad05..8011dadc72d6 100644 --- a/servers/rendering/rasterizer_rd/shaders/gi.glsl +++ b/servers/rendering/rasterizer_rd/shaders/gi.glsl @@ -80,7 +80,7 @@ struct GIProbeData { float anisotropy_strength; float ambient_occlusion; float ambient_occlusion_size; - uint pad2; + uint mipmaps; }; layout(set = 0, binding = 16, std140) uniform GIProbes { diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl index 792a1aa05f57..837d7a3a5d7a 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl @@ -1237,7 +1237,7 @@ void light_process_spot(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v float shadow_z = textureLod(sampler2D(shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), splane.xy, 0.0).r; //reconstruct depth - shadow_z / lights.data[idx].inv_radius; + shadow_z /= lights.data[idx].inv_radius; //distance to light plane float z = dot(spot_dir, -light_rel_vec); transmittance_z = z - shadow_z; @@ -1601,6 +1601,21 @@ void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED) +#ifndef MODE_RENDER_DEPTH + +vec4 volumetric_fog_process(vec2 screen_uv, float z) { + vec3 fog_pos = vec3(screen_uv, z * scene_data.volumetric_fog_inv_length); + if (fog_pos.z < 0.0) { + return vec4(0.0); + } else if (fog_pos.z < 1.0) { + fog_pos.z = pow(fog_pos.z, scene_data.volumetric_fog_detail_spread); + } + + return texture(sampler3D(volumetric_fog_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), fog_pos); +} + +#endif + void main() { #ifdef MODE_DUAL_PARABOLOID @@ -2187,8 +2202,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.x; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.x; + shadow_z *= directional_lights.data[i].shadow_z_range.x; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.x; transmittance_z = z - shadow_z; } @@ -2219,8 +2234,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.y; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.y; + shadow_z *= directional_lights.data[i].shadow_z_range.y; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.y; transmittance_z = z - shadow_z; } @@ -2251,8 +2266,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.z; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.z; + shadow_z *= directional_lights.data[i].shadow_z_range.z; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.z; transmittance_z = z - shadow_z; } @@ -2285,8 +2300,8 @@ FRAGMENT_SHADER_CODE trans_coord /= trans_coord.w; float shadow_z = textureLod(sampler2D(directional_shadow_atlas, material_samplers[SAMPLER_LINEAR_CLAMP]), trans_coord.xy, 0.0).r; - shadow_z *= directional_lights.data[i].shadow_transmittance_z_scale.w; - float z = trans_coord.z * directional_lights.data[i].shadow_transmittance_z_scale.w; + shadow_z *= directional_lights.data[i].shadow_z_range.w; + float z = trans_coord.z * directional_lights.data[i].shadow_z_range.w; transmittance_z = z - shadow_z; } @@ -2662,8 +2677,6 @@ FRAGMENT_SHADER_CODE diffuse_light *= 1.0 - metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point ambient_light *= 1.0 - metallic; - //fog - #ifdef MODE_MULTIPLE_RENDER_TARGETS #ifdef MODE_UNSHADED @@ -2679,16 +2692,27 @@ FRAGMENT_SHADER_CODE specular_buffer = vec4(specular_light, metallic); #endif + if (scene_data.volumetric_fog_enabled) { + vec4 fog = volumetric_fog_process(screen_uv, -vertex.z); + diffuse_buffer.rgb = mix(diffuse_buffer.rgb, fog.rgb, fog.a); + specular_buffer.rgb = mix(specular_buffer.rgb, vec3(0.0), fog.a); + ; + } + #else //MODE_MULTIPLE_RENDER_TARGETS #ifdef MODE_UNSHADED frag_color = vec4(albedo, alpha); #else frag_color = vec4(emission + ambient_light + diffuse_light + specular_light, alpha); - //frag_color = vec4(1.0);;; - + //frag_color = vec4(1.0); #endif //USE_NO_SHADING + if (scene_data.volumetric_fog_enabled) { + vec4 fog = volumetric_fog_process(screen_uv, -vertex.z); + frag_color.rgb = mix(frag_color.rgb, fog.rgb, fog.a); + } + #endif //MODE_MULTIPLE_RENDER_TARGETS #endif //MODE_RENDER_DEPTH diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl index c4dc7bd6751b..756cab73509e 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl +++ b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl @@ -3,6 +3,8 @@ #define MAX_GI_PROBES 8 +#include "cluster_data_inc.glsl" + layout(push_constant, binding = 0, std430) uniform DrawCall { uint instance_index; uint pad; //16 bits minimum size @@ -94,6 +96,10 @@ layout(set = 0, binding = 3, std140) uniform SceneData { ivec3 sdf_size; bool gi_upscale_for_msaa; + bool volumetric_fog_enabled; + float volumetric_fog_inv_length; + float volumetric_fog_detail_spread; + uint volumetric_fog_pad; #if 0 vec4 ambient_light_color; vec4 bg_color; @@ -163,86 +169,16 @@ layout(set = 0, binding = 4, std430) restrict readonly buffer Instances { } instances; -struct LightData { //this structure needs to be as packed as possible - vec3 position; - float inv_radius; - vec3 direction; - float size; - uint attenuation_energy; //attenuation - uint color_specular; //rgb color, a specular (8 bit unorm) - uint cone_attenuation_angle; // attenuation and angle, (16bit float) - uint shadow_color_enabled; //shadow rgb color, a>0.5 enabled (8bit unorm) - vec4 atlas_rect; // rect in the shadow atlas - mat4 shadow_matrix; - float shadow_bias; - float shadow_normal_bias; - float transmittance_bias; - float soft_shadow_size; // for spot, it's the size in uv coordinates of the light, for omni it's the span angle - float soft_shadow_scale; // scales the shadow kernel for blurrier shadows - uint mask; - uint pad[2]; - vec4 projector_rect; //projector rect in srgb decal atlas -}; - layout(set = 0, binding = 5, std430) restrict readonly buffer Lights { LightData data[]; } lights; -#define REFLECTION_AMBIENT_DISABLED 0 -#define REFLECTION_AMBIENT_ENVIRONMENT 1 -#define REFLECTION_AMBIENT_COLOR 2 - -struct ReflectionData { - vec3 box_extents; - float index; - vec3 box_offset; - uint mask; - vec4 params; // intensity, 0, interior , boxproject - vec3 ambient; // ambient color - uint ambient_mode; - mat4 local_matrix; // up to here for spot and omni, rest is for directional - // notes: for ambientblend, use distance to edge to blend between already existing global environment -}; - layout(set = 0, binding = 6) buffer restrict readonly ReflectionProbeData { ReflectionData data[]; } reflections; -struct DirectionalLightData { - vec3 direction; - float energy; - vec3 color; - float size; - float specular; - uint mask; - float softshadow_angle; - float soft_shadow_scale; - bool blend_splits; - bool shadow_enabled; - float fade_from; - float fade_to; - vec4 shadow_bias; - vec4 shadow_normal_bias; - vec4 shadow_transmittance_bias; - vec4 shadow_transmittance_z_scale; - vec4 shadow_range_begin; - vec4 shadow_split_offsets; - mat4 shadow_matrix1; - mat4 shadow_matrix2; - mat4 shadow_matrix3; - mat4 shadow_matrix4; - vec4 shadow_color1; - vec4 shadow_color2; - vec4 shadow_color3; - vec4 shadow_color4; - vec2 uv_scale1; - vec2 uv_scale2; - vec2 uv_scale3; - vec2 uv_scale4; -}; - layout(set = 0, binding = 7, std140) uniform DirectionalLights { DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; } @@ -271,31 +207,9 @@ layout(set = 0, binding = 12, std140) restrict readonly buffer LightmapCaptures } lightmap_captures; -#define CLUSTER_COUNTER_SHIFT 20 -#define CLUSTER_POINTER_MASK ((1 << CLUSTER_COUNTER_SHIFT) - 1) -#define CLUSTER_COUNTER_MASK 0xfff - layout(set = 0, binding = 13) uniform texture2D decal_atlas; layout(set = 0, binding = 14) uniform texture2D decal_atlas_srgb; -struct DecalData { - mat4 xform; //to decal transform - vec3 inv_extents; - float albedo_mix; - vec4 albedo_rect; - vec4 normal_rect; - vec4 orm_rect; - vec4 emission_rect; - vec4 modulate; - float emission_energy; - uint mask; - float upper_fade; - float lower_fade; - mat3x4 normal_xform; - vec3 normal; - float normal_fade; -}; - layout(set = 0, binding = 15, std430) restrict readonly buffer Decals { DecalData data[]; } @@ -394,9 +308,7 @@ layout(set = 3, binding = 2) uniform texture2D normal_roughness_buffer; layout(set = 3, binding = 4) uniform texture2D ao_buffer; layout(set = 3, binding = 5) uniform texture2D ambient_buffer; layout(set = 3, binding = 6) uniform texture2D reflection_buffer; - layout(set = 3, binding = 7) uniform texture2DArray sdfgi_lightprobe_texture; - layout(set = 3, binding = 8) uniform texture3D sdfgi_occlusion_cascades; struct GIProbeData { @@ -412,7 +324,7 @@ struct GIProbeData { float anisotropy_strength; float ambient_occlusion; float ambient_occlusion_size; - uint pad2; + uint mipmaps; }; layout(set = 3, binding = 9, std140) uniform GIProbes { @@ -420,6 +332,8 @@ layout(set = 3, binding = 9, std140) uniform GIProbes { } gi_probes; +layout(set = 3, binding = 10) uniform texture3D volumetric_fog_texture; + #endif /* Set 4 Skeleton & Instancing (Multimesh) */ diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl index e4779aafaf1c..1ec471d20468 100644 --- a/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl +++ b/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl @@ -37,6 +37,8 @@ layout(rgba32i, set = 0, binding = 12) uniform restrict iimage2D lightprobe_aver layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_average_parent_texture; +layout(rgba16f, set = 0, binding = 14) uniform restrict writeonly image2DArray lightprobe_ambient_texture; + layout(set = 1, binding = 0) uniform textureCube sky_irradiance; layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps; @@ -68,6 +70,9 @@ layout(push_constant, binding = 0, std430) uniform Params { vec3 sky_color; float y_mult; + + bool store_ambient_texture; + uint pad[3]; } params; @@ -319,6 +324,13 @@ void main() { imageStore(lightprobe_history_texture, prev_pos, ivalue); imageStore(lightprobe_average_texture, average_pos, average); + + if (params.store_ambient_texture && i == 0) { + ivec3 ambient_pos = ivec3(pos, int(params.cascade)); + vec4 ambient_light = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS); + ambient_light *= 0.88622; // SHL0 + imageStore(lightprobe_ambient_texture, ambient_pos, ambient_light); + } } #endif // MODE PROCESS diff --git a/servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl b/servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl new file mode 100644 index 000000000000..29443ae7db35 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl @@ -0,0 +1,105 @@ +#[compute] + +#version 450 + +VERSION_DEFINES + +#define BLOCK_SIZE 8 + +layout(local_size_x = BLOCK_SIZE, local_size_y = BLOCK_SIZE, local_size_z = 1) in; + +#ifdef MODE_REDUCE + +shared float tmp_data[BLOCK_SIZE * BLOCK_SIZE]; +const uint swizzle_table[BLOCK_SIZE] = uint[](0, 4, 2, 6, 1, 5, 3, 7); +const uint unswizzle_table[BLOCK_SIZE] = uint[](0, 0, 0, 1, 0, 2, 1, 3); + +#endif + +layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_depth; +layout(r32f, set = 0, binding = 1) uniform restrict writeonly image2D dst_depth; + +layout(push_constant, binding = 1, std430) uniform Params { + ivec2 source_size; + ivec2 source_offset; + uint min_size; + uint gaussian_kernel_version; + ivec2 filter_dir; +} +params; + +void main() { +#ifdef MODE_REDUCE + + uvec2 pos = gl_LocalInvocationID.xy; + + ivec2 image_offset = params.source_offset; + ivec2 image_pos = image_offset + ivec2(gl_GlobalInvocationID.xy); + uint dst_t = swizzle_table[pos.y] * BLOCK_SIZE + swizzle_table[pos.x]; + tmp_data[dst_t] = imageLoad(source_depth, min(image_pos, params.source_size - ivec2(1))).r; + ivec2 image_size = params.source_size; + + uint t = pos.y * BLOCK_SIZE + pos.x; + + //neighbours + uint size = BLOCK_SIZE; + + do { + groupMemoryBarrier(); + barrier(); + + size >>= 1; + image_size >>= 1; + image_offset >>= 1; + + if (all(lessThan(pos, uvec2(size)))) { + uint nx = t + size; + uint ny = t + (BLOCK_SIZE * size); + uint nxy = ny + size; + + tmp_data[t] += tmp_data[nx]; + tmp_data[t] += tmp_data[ny]; + tmp_data[t] += tmp_data[nxy]; + tmp_data[t] /= 4.0; + } + + } while (size > params.min_size); + + if (all(lessThan(pos, uvec2(size)))) { + image_pos = ivec2(unswizzle_table[size + pos.x], unswizzle_table[size + pos.y]); + image_pos += image_offset + ivec2(gl_WorkGroupID.xy) * int(size); + + image_size = max(ivec2(1), image_size); //in case image size became 0 + + if (all(lessThan(image_pos, uvec2(image_size)))) { + imageStore(dst_depth, image_pos, vec4(tmp_data[t])); + } + } +#endif + +#ifdef MODE_FILTER + + ivec2 image_pos = params.source_offset + ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(image_pos, params.source_size))) { + return; + } + + ivec2 clamp_min = ivec2(params.source_offset); + ivec2 clamp_max = ivec2(params.source_size) - 1; + + //gaussian kernel, size 9, sigma 4 + const int kernel_size = 9; + const float gaussian_kernel[kernel_size * 3] = float[]( + 0.000229, 0.005977, 0.060598, 0.241732, 0.382928, 0.241732, 0.060598, 0.005977, 0.000229, + 0.028532, 0.067234, 0.124009, 0.179044, 0.20236, 0.179044, 0.124009, 0.067234, 0.028532, + 0.081812, 0.101701, 0.118804, 0.130417, 0.134535, 0.130417, 0.118804, 0.101701, 0.081812); + float accum = 0.0; + for (int i = 0; i < kernel_size; i++) { + ivec2 ofs = clamp(image_pos + params.filter_dir * (i - kernel_size / 2), clamp_min, clamp_max); + accum += imageLoad(source_depth, ofs).r * gaussian_kernel[params.gaussian_kernel_version + i]; + } + + imageStore(dst_depth, image_pos, vec4(accum)); + +#endif +} diff --git a/servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl b/servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl new file mode 100644 index 000000000000..cb19fb0b6975 --- /dev/null +++ b/servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl @@ -0,0 +1,530 @@ +#[compute] + +#version 450 + +VERSION_DEFINES + +#if defined(MODE_FOG) || defined(MODE_FILTER) + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +#endif + +#if defined(MODE_DENSITY) + +layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in; + +#endif + +#include "cluster_data_inc.glsl" + +#define M_PI 3.14159265359 + +layout(set = 0, binding = 1) uniform texture2D shadow_atlas; +layout(set = 0, binding = 2) uniform texture2D directional_shadow_atlas; + +layout(set = 0, binding = 3, std430) restrict readonly buffer Lights { + LightData data[]; +} +lights; + +layout(set = 0, binding = 4, std140) uniform DirectionalLights { + DirectionalLightData data[MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS]; +} +directional_lights; + +layout(set = 0, binding = 5) uniform utexture3D cluster_texture; + +layout(set = 0, binding = 6, std430) restrict readonly buffer ClusterData { + uint indices[]; +} +cluster_data; + +layout(set = 0, binding = 7) uniform sampler linear_sampler; + +#ifdef MODE_DENSITY +layout(rgba16f, set = 0, binding = 8) uniform restrict writeonly image3D density_map; +layout(rgba16f, set = 0, binding = 9) uniform restrict readonly image3D fog_map; //unused +#endif + +#ifdef MODE_FOG +layout(rgba16f, set = 0, binding = 8) uniform restrict readonly image3D density_map; +layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image3D fog_map; +#endif + +#ifdef MODE_FILTER +layout(rgba16f, set = 0, binding = 8) uniform restrict readonly image3D source_map; +layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image3D dest_map; +#endif + +layout(set = 0, binding = 10) uniform sampler shadow_sampler; + +#define MAX_GI_PROBES 8 + +struct GIProbeData { + mat4 xform; + vec3 bounds; + float dynamic_range; + + float bias; + float normal_bias; + bool blend_ambient; + uint texture_slot; + + float anisotropy_strength; + float ambient_occlusion; + float ambient_occlusion_size; + uint mipmaps; +}; + +layout(set = 0, binding = 11, std140) uniform GIProbes { + GIProbeData data[MAX_GI_PROBES]; +} +gi_probes; + +layout(set = 0, binding = 12) uniform texture3D gi_probe_textures[MAX_GI_PROBES]; + +layout(set = 0, binding = 13) uniform sampler linear_sampler_with_mipmaps; + +#ifdef ENABLE_SDFGI + +// SDFGI Integration on set 1 +#define SDFGI_MAX_CASCADES 8 + +struct SDFGIProbeCascadeData { + vec3 position; + float to_probe; + ivec3 probe_world_offset; + float to_cell; // 1/bounds * grid_size +}; + +layout(set = 1, binding = 0, std140) uniform SDFGI { + vec3 grid_size; + uint max_cascades; + + bool use_occlusion; + int probe_axis_size; + float probe_to_uvw; + float normal_bias; + + vec3 lightprobe_tex_pixel_size; + float energy; + + vec3 lightprobe_uv_offset; + float y_mult; + + vec3 occlusion_clamp; + uint pad3; + + vec3 occlusion_renormalize; + uint pad4; + + vec3 cascade_probe_size; + uint pad5; + + SDFGIProbeCascadeData cascades[SDFGI_MAX_CASCADES]; +} +sdfgi; + +layout(set = 1, binding = 1) uniform texture2DArray sdfgi_ambient_texture; + +layout(set = 1, binding = 2) uniform texture3D sdfgi_occlusion_texture; + +#endif //SDFGI + +layout(push_constant, binding = 0, std430) uniform Params { + vec2 fog_frustum_size_begin; + vec2 fog_frustum_size_end; + + float fog_frustum_end; + float z_near; + float z_far; + int filter_axis; + + ivec3 fog_volume_size; + uint directional_light_count; + + vec3 light_color; + float base_density; + + float detail_spread; + float gi_inject; + uint max_gi_probes; + uint pad; + + mat3x4 cam_rotation; +} +params; + +float get_depth_at_pos(float cell_depth_size, int z) { + float d = float(z) * cell_depth_size + cell_depth_size * 0.5; //center of voxels + d = pow(d, params.detail_spread); + return params.fog_frustum_end * d; +} + +vec3 hash3f(uvec3 x) { + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return vec3(x & 0xFFFFF) / vec3(float(0xFFFFF)); +} + +void main() { + vec3 fog_cell_size = 1.0 / vec3(params.fog_volume_size); + +#ifdef MODE_DENSITY + + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + if (any(greaterThanEqual(pos, params.fog_volume_size))) { + return; //do not compute + } + + vec3 posf = vec3(pos); + + //posf += mix(vec3(0.0),vec3(1.0),0.3) * hash3f(uvec3(pos)) * 2.0 - 1.0; + + vec3 fog_unit_pos = posf * fog_cell_size + fog_cell_size * 0.5; //center of voxels + fog_unit_pos.z = pow(fog_unit_pos.z, params.detail_spread); + + vec3 view_pos; + view_pos.xy = (fog_unit_pos.xy * 2.0 - 1.0) * mix(params.fog_frustum_size_begin, params.fog_frustum_size_end, vec2(fog_unit_pos.z)); + view_pos.z = -params.fog_frustum_end * fog_unit_pos.z; + view_pos.y = -view_pos.y; + + vec3 total_light = params.light_color; + + float total_density = params.base_density; + float cell_depth_size = abs(view_pos.z - get_depth_at_pos(fog_cell_size.z, pos.z + 1)); + //compute directional lights + + for (uint i = 0; i < params.directional_light_count; i++) { + vec3 shadow_attenuation = vec3(1.0); + + if (directional_lights.data[i].shadow_enabled) { + float depth_z = -view_pos.z; + + vec4 pssm_coord; + vec3 shadow_color = directional_lights.data[i].shadow_color1.rgb; + vec3 light_dir = directional_lights.data[i].direction; + vec4 v = vec4(view_pos, 1.0); + float z_range; + + if (depth_z < directional_lights.data[i].shadow_split_offsets.x) { + pssm_coord = (directional_lights.data[i].shadow_matrix1 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.x; + + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.y) { + pssm_coord = (directional_lights.data[i].shadow_matrix2 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.y; + + } else if (depth_z < directional_lights.data[i].shadow_split_offsets.z) { + pssm_coord = (directional_lights.data[i].shadow_matrix3 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.z; + + } else { + pssm_coord = (directional_lights.data[i].shadow_matrix4 * v); + pssm_coord /= pssm_coord.w; + z_range = directional_lights.data[i].shadow_z_range.w; + } + + float depth = texture(sampler2D(directional_shadow_atlas, linear_sampler), pssm_coord.xy).r; + float shadow = exp(min(0.0, (depth - pssm_coord.z)) * z_range * directional_lights.data[i].shadow_volumetric_fog_fade); + + /* + //float shadow = textureProj(sampler2DShadow(directional_shadow_atlas,shadow_sampler),pssm_coord); + float shadow = 0.0; + for(float xi=-1;xi<=1;xi++) { + for(float yi=-1;yi<=1;yi++) { + vec2 ofs = vec2(xi,yi) * 1.5 * params.directional_shadow_pixel_size; + shadow += textureProj(sampler2DShadow(directional_shadow_atlas,shadow_sampler),pssm_coord + vec4(ofs,0.0,0.0)); + } + + } + + shadow /= 3.0 * 3.0; + +*/ + shadow = mix(shadow, 1.0, smoothstep(directional_lights.data[i].fade_from, directional_lights.data[i].fade_to, view_pos.z)); //done with negative values for performance + + shadow_attenuation = mix(shadow_color, vec3(1.0), shadow); + } + + total_light += shadow_attenuation * directional_lights.data[i].color * directional_lights.data[i].energy / M_PI; + } + + //compute lights from cluster + + vec3 cluster_pos; + cluster_pos.xy = fog_unit_pos.xy; + cluster_pos.z = clamp((abs(view_pos.z) - params.z_near) / (params.z_far - params.z_near), 0.0, 1.0); + + uvec4 cluster_cell = texture(usampler3D(cluster_texture, linear_sampler), cluster_pos); + + uint omni_light_count = cluster_cell.x >> CLUSTER_COUNTER_SHIFT; + uint omni_light_pointer = cluster_cell.x & CLUSTER_POINTER_MASK; + + for (uint i = 0; i < omni_light_count; i++) { + uint light_index = cluster_data.indices[omni_light_pointer + i]; + + vec3 light_pos = lights.data[i].position; + float d = distance(lights.data[i].position, view_pos) * lights.data[i].inv_radius; + vec3 shadow_attenuation = vec3(1.0); + + if (d < 1.0) { + vec2 attenuation_energy = unpackHalf2x16(lights.data[i].attenuation_energy); + vec4 color_specular = unpackUnorm4x8(lights.data[i].color_specular); + + float attenuation = pow(max(1.0 - d, 0.0), attenuation_energy.x); + + vec3 light = attenuation_energy.y * color_specular.rgb / M_PI; + + vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[i].shadow_color_enabled); + + if (shadow_color_enabled.a > 0.5) { + //has shadow + vec4 v = vec4(view_pos, 1.0); + + vec4 splane = (lights.data[i].shadow_matrix * v); + float shadow_len = length(splane.xyz); //need to remember shadow len from here + + splane.xyz = normalize(splane.xyz); + vec4 clamp_rect = lights.data[i].atlas_rect; + + if (splane.z >= 0.0) { + splane.z += 1.0; + + clamp_rect.y += clamp_rect.w; + + } else { + splane.z = 1.0 - splane.z; + } + + splane.xy /= splane.z; + + splane.xy = splane.xy * 0.5 + 0.5; + splane.z = shadow_len * lights.data[i].inv_radius; + splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw; + splane.w = 1.0; //needed? i think it should be 1 already + + float depth = texture(sampler2D(shadow_atlas, linear_sampler), splane.xy).r; + float shadow = exp(min(0.0, (depth - splane.z)) / lights.data[i].inv_radius * lights.data[i].shadow_volumetric_fog_fade); + + shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + } + total_light += light * attenuation * shadow_attenuation; + } + } + + uint spot_light_count = cluster_cell.y >> CLUSTER_COUNTER_SHIFT; + uint spot_light_pointer = cluster_cell.y & CLUSTER_POINTER_MASK; + + for (uint i = 0; i < spot_light_count; i++) { + uint light_index = cluster_data.indices[spot_light_pointer + i]; + + vec3 light_pos = lights.data[i].position; + vec3 light_rel_vec = lights.data[i].position - view_pos; + float d = length(light_rel_vec) * lights.data[i].inv_radius; + vec3 shadow_attenuation = vec3(1.0); + + if (d < 1.0) { + vec2 attenuation_energy = unpackHalf2x16(lights.data[i].attenuation_energy); + vec4 color_specular = unpackUnorm4x8(lights.data[i].color_specular); + + float attenuation = pow(max(1.0 - d, 0.0), attenuation_energy.x); + + vec3 spot_dir = lights.data[i].direction; + vec2 spot_att_angle = unpackHalf2x16(lights.data[i].cone_attenuation_angle); + float scos = max(dot(-normalize(light_rel_vec), spot_dir), spot_att_angle.y); + float spot_rim = max(0.0001, (1.0 - scos) / (1.0 - spot_att_angle.y)); + attenuation *= 1.0 - pow(spot_rim, spot_att_angle.x); + + vec3 light = attenuation_energy.y * color_specular.rgb / M_PI; + + vec4 shadow_color_enabled = unpackUnorm4x8(lights.data[i].shadow_color_enabled); + + if (shadow_color_enabled.a > 0.5) { + //has shadow + vec4 v = vec4(view_pos, 1.0); + + vec4 splane = (lights.data[i].shadow_matrix * v); + splane /= splane.w; + + float depth = texture(sampler2D(shadow_atlas, linear_sampler), splane.xy).r; + float shadow = exp(min(0.0, (depth - splane.z)) / lights.data[i].inv_radius * lights.data[i].shadow_volumetric_fog_fade); + + shadow_attenuation = mix(shadow_color_enabled.rgb, vec3(1.0), shadow); + } + + total_light += light * attenuation * shadow_attenuation; + } + } + + vec3 world_pos = mat3(params.cam_rotation) * view_pos; + + for (uint i = 0; i < params.max_gi_probes; i++) { + vec3 position = (gi_probes.data[i].xform * vec4(world_pos, 1.0)).xyz; + + //this causes corrupted pixels, i have no idea why.. + if (all(bvec2(all(greaterThanEqual(position, vec3(0.0))), all(lessThan(position, gi_probes.data[i].bounds))))) { + position /= gi_probes.data[i].bounds; + + vec4 light = vec4(0.0); + for (uint j = 0; j < gi_probes.data[i].mipmaps; j++) { + vec4 slight = textureLod(sampler3D(gi_probe_textures[i], linear_sampler_with_mipmaps), position, float(j)); + float a = (1.0 - light.a); + light += a * slight; + } + + light.rgb *= gi_probes.data[i].dynamic_range * params.gi_inject; + + total_light += light.rgb; + } + } + + //sdfgi +#ifdef ENABLE_SDFGI + + { + float blend = -1.0; + vec3 ambient_total = vec3(0.0); + + for (uint i = 0; i < sdfgi.max_cascades; i++) { + vec3 cascade_pos = (world_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe; + + if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) { + continue; //skip cascade + } + + vec3 base_pos = floor(cascade_pos); + ivec3 probe_base_pos = ivec3(base_pos); + + vec4 ambient_accum = vec4(0.0); + + ivec3 tex_pos = ivec3(probe_base_pos.xy, int(i)); + tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size; + + for (uint j = 0; j < 8; j++) { + ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1); + ivec3 probe_posi = probe_base_pos; + probe_posi += offset; + + // Compute weight + + vec3 probe_pos = vec3(probe_posi); + vec3 probe_to_pos = cascade_pos - probe_pos; + + vec3 trilinear = vec3(1.0) - abs(probe_to_pos); + float weight = trilinear.x * trilinear.y * trilinear.z; + + // Compute lightprobe occlusion + + if (sdfgi.use_occlusion) { + ivec3 occ_indexv = abs((sdfgi.cascades[i].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4); + vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3))); + + vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw; + occ_pos.z += float(i); + if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures + occ_pos.x += 1.0; + } + + occ_pos *= sdfgi.occlusion_renormalize; + float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask); + + weight *= max(occlusion, 0.01); + } + + // Compute ambient texture position + + ivec3 uvw = tex_pos; + uvw.xy += offset.xy; + uvw.x += offset.z * sdfgi.probe_axis_size; + + vec3 ambient = texelFetch(sampler2DArray(sdfgi_ambient_texture, linear_sampler), uvw, 0).rgb; + + ambient_accum.rgb += ambient * weight; + ambient_accum.a += weight; + } + + if (ambient_accum.a > 0) { + ambient_accum.rgb /= ambient_accum.a; + } + ambient_total = ambient_accum.rgb; + break; + } + + total_light += ambient_total * params.gi_inject; + } + +#endif + + imageStore(density_map, pos, vec4(total_light, total_density)); +#endif + +#ifdef MODE_FOG + + ivec3 pos = ivec3(gl_GlobalInvocationID.xy, 0); + + if (any(greaterThanEqual(pos, params.fog_volume_size))) { + return; //do not compute + } + + vec4 fog_accum = vec4(0.0); + float prev_z = 0.0; + + float t = 1.0; + + for (int i = 0; i < params.fog_volume_size.z; i++) { + //compute fog position + ivec3 fog_pos = pos + ivec3(0, 0, i); + //get fog value + vec4 fog = imageLoad(density_map, fog_pos); + + //get depth at cell pos + float z = get_depth_at_pos(fog_cell_size.z, i); + //get distance from previos pos + float d = abs(prev_z - z); + //compute exinction based on beer's + float extinction = t * exp(-d * fog.a); + //compute alpha based on different of extinctions + float alpha = t - extinction; + //update extinction + t = extinction; + + fog_accum += vec4(fog.rgb * alpha, alpha); + prev_z = z; + + vec4 fog_value; + + if (fog_accum.a > 0.0) { + fog_value = vec4(fog_accum.rgb / fog_accum.a, 1.0 - t); + } else { + fog_value = vec4(0.0); + } + + imageStore(fog_map, fog_pos, fog_value); + } + +#endif + +#ifdef MODE_FILTER + + ivec3 pos = ivec3(gl_GlobalInvocationID.xyz); + + const float gauss[7] = float[](0.071303, 0.131514, 0.189879, 0.214607, 0.189879, 0.131514, 0.071303); + + const ivec3 filter_dir[3] = ivec3[](ivec3(1, 0, 0), ivec3(0, 1, 0), ivec3(0, 0, 1)); + ivec3 offset = filter_dir[params.filter_axis]; + + vec4 accum = vec4(0.0); + for (int i = -3; i <= 3; i++) { + accum += imageLoad(source_map, clamp(pos + offset * i, ivec3(0), params.fog_volume_size - ivec3(1))) * gauss[i + 3]; + } + + imageStore(dest_map, pos, accum); + +#endif +} diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 706912b3532f..75d3165c948d 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -565,6 +565,12 @@ class RenderingServerRaster : public RenderingServer { BIND5(environment_set_fog, RID, bool, const Color &, const Color &, float) BIND7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) BIND5(environment_set_fog_height, RID, bool, float, float, float) + BIND9(environment_set_volumetric_fog, RID, bool, float, const Color &, float, float, float, float, EnvVolumetricFogShadowFilter) + + BIND2(environment_set_volumetric_fog_volume_size, int, int) + BIND1(environment_set_volumetric_fog_filter_active, bool) + BIND1(environment_set_volumetric_fog_directional_shadow_shrink_size, int) + BIND1(environment_set_volumetric_fog_positional_shadow_shrink_size, int) BIND11(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, bool, bool, float, float, float) BIND1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount) diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 60a694eed56d..d7d0d968a2dd 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -482,6 +482,12 @@ class RenderingServerWrapMT : public RenderingServer { FUNC5(environment_set_fog, RID, bool, const Color &, const Color &, float) FUNC7(environment_set_fog_depth, RID, bool, float, float, float, bool, float) FUNC5(environment_set_fog_height, RID, bool, float, float, float) + FUNC9(environment_set_volumetric_fog, RID, bool, float, const Color &, float, float, float, float, EnvVolumetricFogShadowFilter) + + FUNC2(environment_set_volumetric_fog_volume_size, int, int) + FUNC1(environment_set_volumetric_fog_filter_active, bool) + FUNC1(environment_set_volumetric_fog_directional_shadow_shrink_size, int) + FUNC1(environment_set_volumetric_fog_positional_shadow_shrink_size, int) FUNC3R(Ref, environment_bake_panorama, RID, bool, const Size2i &) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index c156313c1575..714fe71d08b2 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2411,6 +2411,17 @@ RenderingServer::RenderingServer() { ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/probe_ray_count", PropertyInfo(Variant::INT, "rendering/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)")); GLOBAL_DEF("rendering/sdfgi/frames_to_converge", 1); ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/frames_to_converge", PropertyInfo(Variant::INT, "rendering/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)")); + + GLOBAL_DEF("rendering/volumetric_fog/volume_size", 64); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/volume_size", PropertyInfo(Variant::INT, "rendering/volumetric_fog/volume_size", PROPERTY_HINT_RANGE, "16,512,1")); + GLOBAL_DEF("rendering/volumetric_fog/volume_depth", 128); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/volume_depth", PropertyInfo(Variant::INT, "rendering/volumetric_fog/volume_depth", PROPERTY_HINT_RANGE, "16,512,1")); + GLOBAL_DEF("rendering/volumetric_fog/use_filter", 0); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/use_filter", PropertyInfo(Variant::INT, "rendering/volumetric_fog/use_filter", PROPERTY_HINT_ENUM, "No (Faster),Yes (Higher Quality)")); + GLOBAL_DEF("rendering/volumetric_fog/directional_shadow_shrink", 512); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/directional_shadow_shrink", PropertyInfo(Variant::INT, "rendering/volumetric_fog/directional_shadow_shrink", PROPERTY_HINT_RANGE, "32,2048,1")); + GLOBAL_DEF("rendering/volumetric_fog/positional_shadow_shrink", 512); + ProjectSettings::get_singleton()->set_custom_property_info("rendering/volumetric_fog/positional_shadow_shrink", PropertyInfo(Variant::INT, "rendering/volumetric_fog/positional_shadow_shrink", PROPERTY_HINT_RANGE, "32,2048,1")); } RenderingServer::~RenderingServer() { diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 109e9e53c560..57af53f0c022 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -390,6 +390,7 @@ class RenderingServer : public Object { LIGHT_PARAM_SHADOW_BIAS, LIGHT_PARAM_SHADOW_PANCAKE_SIZE, LIGHT_PARAM_SHADOW_BLUR, + LIGHT_PARAM_SHADOW_VOLUMETRIC_FOG_FADE, LIGHT_PARAM_TRANSMITTANCE_BIAS, LIGHT_PARAM_MAX }; @@ -865,6 +866,19 @@ class RenderingServer : public Object { 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; + enum EnvVolumetricFogShadowFilter { + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_DISABLED, + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_LOW, + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_MEDIUM, + ENV_VOLUMETRIC_FOG_SHADOW_FILTER_HIGH, + }; + + virtual void environment_set_volumetric_fog(RID p_env, bool p_enable, float p_density, const Color &p_light, float p_light_energy, float p_lenght, float p_detail_spread, float p_gi_inject, EnvVolumetricFogShadowFilter p_shadow_filter) = 0; + virtual void environment_set_volumetric_fog_volume_size(int p_size, int p_depth) = 0; + virtual void environment_set_volumetric_fog_filter_active(bool p_enable) = 0; + virtual void environment_set_volumetric_fog_directional_shadow_shrink_size(int p_shrink_size) = 0; + virtual void environment_set_volumetric_fog_positional_shadow_shrink_size(int p_shrink_size) = 0; + virtual Ref environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) = 0; virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0;