소스 검색

Merge pull request #41213 from reduz/volumetric-fog

Added volumetric fog effect.
Juan Linietsky 5 년 전
부모
커밋
d84b28b94f
29개의 변경된 파일1978개의 추가작업 그리고 198개의 파일을 삭제
  1. 2 3
      core/math/camera_matrix.cpp
  2. 1 1
      core/math/camera_matrix.h
  3. 4 0
      editor/editor_node.cpp
  4. 1 0
      glsl_builders.py
  5. 3 0
      scene/3d/light_3d.cpp
  6. 1 0
      scene/3d/light_3d.h
  7. 102 2
      scene/resources/environment.cpp
  8. 37 0
      scene/resources/environment.h
  9. 7 0
      servers/rendering/rasterizer.h
  10. 59 0
      servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
  11. 16 0
      servers/rendering/rasterizer_rd/rasterizer_effects_rd.h
  12. 26 1
      servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
  13. 5 0
      servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
  14. 755 78
      servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
  15. 123 4
      servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
  16. 1 0
      servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
  17. 7 0
      servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
  18. 2 0
      servers/rendering/rasterizer_rd/shaders/SCsub
  19. 95 0
      servers/rendering/rasterizer_rd/shaders/cluster_data_inc.glsl
  20. 1 1
      servers/rendering/rasterizer_rd/shaders/gi.glsl
  21. 37 13
      servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl
  22. 9 95
      servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl
  23. 12 0
      servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl
  24. 105 0
      servers/rendering/rasterizer_rd/shaders/shadow_reduce.glsl
  25. 530 0
      servers/rendering/rasterizer_rd/shaders/volumetric_fog.glsl
  26. 6 0
      servers/rendering/rendering_server_raster.h
  27. 6 0
      servers/rendering/rendering_server_wrap_mt.h
  28. 11 0
      servers/rendering_server.cpp
  29. 14 0
      servers/rendering_server.h

+ 2 - 3
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 {

+ 1 - 1
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;

+ 4 - 0
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();

+ 1 - 0
glsl_builders.py

@@ -14,6 +14,7 @@ class RDHeaderStruct:
 
         self.vertex_included_files = []
         self.fragment_included_files = []
+        self.compute_included_files = []
 
         self.reading = ""
         self.line_offset = 0

+ 3 - 0
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);
 }

+ 1 - 0
scene/3d/light_3d.h

@@ -58,6 +58,7 @@ public:
 		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
 	};

+ 102 - 2
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();
 }
 

+ 37 - 0
scene/resources/environment.h

@@ -97,6 +97,13 @@ public:
 		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 @@ private:
 	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 @@ public:
 	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

+ 7 - 0
servers/rendering/rasterizer.h

@@ -88,6 +88,13 @@ public:
 	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;
 

+ 59 - 0
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<String> copy_modes;
@@ -1560,6 +1604,20 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
 		}
 	}
 
+	{
+		Vector<String> 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);
 }

+ 16 - 0
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 @@ public:
 
 	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();
 };

+ 26 - 1
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);
 	}

+ 5 - 0
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;

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 755 - 78
servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp


+ 123 - 4
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 @@ protected:
 	};
 	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 @@ private:
 
 	/* SHADOW ATLAS */
 
+	struct ShadowShrinkStage {
+		RID texture;
+		RID filter_texture;
+		uint32_t size;
+	};
+
 	struct ShadowAtlas {
 		enum {
 			QUADRANT_SHIFT = 27,
@@ -503,10 +511,12 @@ private:
 			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 @@ private:
 		RID fb; //for copying
 
 		Map<RID, uint32_t> shadow_owners;
+
+		Vector<ShadowShrinkStage> shrink_stages;
 	};
 
 	RID_Owner<ShadowAtlas> shadow_atlas_owner;
@@ -556,8 +568,14 @@ private:
 		int light_count = 0;
 		int size = 0;
 		int current_light = 0;
+
+		Vector<ShadowShrinkStage> shrink_stages;
+
 	} directional_shadow;
 
+	void _allocate_shadow_shrink_stages(RID p_base, int p_base_size, Vector<ShadowShrinkStage> &shrink_stages, uint32_t p_target_size);
+	void _clear_shadow_shrink_stages(Vector<ShadowShrinkStage> &shrink_stages);
+
 	/* SHADOW CUBEMAPS */
 
 	struct ShadowCubemap {
@@ -656,6 +674,17 @@ private:
 		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 @@ private:
 	/* RENDER BUFFERS */
 
 	struct SDFGI;
+	struct VolumetricFog;
 
 	struct RenderBuffers {
 		enum {
@@ -759,6 +789,7 @@ private:
 
 		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 @@ private:
 		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 @@ private:
 
 			float sky_color[3];
 			float y_mult;
+
+			uint32_t store_ambient_texture;
+			uint32_t pad[3];
 		};
 
 		SdfgiIntegrateShaderRD integrate;
@@ -1141,7 +1176,7 @@ private:
 			float anisotropy_strength;
 			float ao;
 			float ao_size;
-			uint32_t pad[1];
+			uint32_t mipmaps;
 		};
 
 		struct PushConstant {
@@ -1219,7 +1254,8 @@ private:
 			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 @@ private:
 			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 @@ private:
 		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 @@ private:
 
 	} 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 @@ public:
 	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 @@ public:
 	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);

+ 1 - 0
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);
 }

+ 7 - 0
servers/rendering/rasterizer_rd/rasterizer_storage_rd.h

@@ -1184,6 +1184,13 @@ public:
 		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;

+ 2 - 0
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")

+ 95 - 0
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;
+};

+ 1 - 1
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 {

+ 37 - 13
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

+ 9 - 95
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) */

+ 12 - 0
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
 

+ 105 - 0
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
+}

+ 530 - 0
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
+}

+ 6 - 0
servers/rendering/rendering_server_raster.h

@@ -565,6 +565,12 @@ public:
 	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)

+ 6 - 0
servers/rendering/rendering_server_wrap_mt.h

@@ -482,6 +482,12 @@ public:
 	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<Image>, environment_bake_panorama, RID, bool, const Size2i &)
 

+ 11 - 0
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() {

+ 14 - 0
servers/rendering_server.h

@@ -390,6 +390,7 @@ public:
 		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 @@ public:
 	virtual void environment_set_fog_depth(RID p_env, bool p_enable, float p_depth_begin, float p_depth_end, float p_depth_curve, bool p_transmit, float p_transmit_curve) = 0;
 	virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0;
 
+	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<Image> 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;

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.