瀏覽代碼

Re-implemented screen space ambient occlusion

Juan Linietsky 5 年之前
父節點
當前提交
bed8980ca5
共有 28 個文件被更改,包括 1468 次插入263 次删除
  1. 1 0
      editor/editor_node.cpp
  2. 7 2
      editor/plugins/spatial_editor_plugin.cpp
  3. 1 0
      editor/plugins/spatial_editor_plugin.h
  4. 1 0
      scene/main/viewport.cpp
  5. 1 0
      scene/main/viewport.h
  6. 21 73
      scene/resources/environment.cpp
  7. 3 22
      scene/resources/environment.h
  8. 4 2
      servers/visual/rasterizer.h
  9. 213 1
      servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp
  10. 78 2
      servers/visual/rasterizer_rd/rasterizer_effects_rd.h
  11. 298 101
      servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
  12. 40 6
      servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.h
  13. 189 14
      servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp
  14. 38 5
      servers/visual/rasterizer_rd/rasterizer_scene_rd.h
  15. 1 1
      servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp
  16. 3 0
      servers/visual/rasterizer_rd/shaders/SCsub
  17. 4 0
      servers/visual/rasterizer_rd/shaders/blur.glsl
  18. 1 0
      servers/visual/rasterizer_rd/shaders/blur_inc.glsl
  19. 40 8
      servers/visual/rasterizer_rd/shaders/scene_high_end.glsl
  20. 28 13
      servers/visual/rasterizer_rd/shaders/scene_high_end_inc.glsl
  21. 257 0
      servers/visual/rasterizer_rd/shaders/ssao.glsl
  22. 163 0
      servers/visual/rasterizer_rd/shaders/ssao_blur.glsl
  23. 49 0
      servers/visual/rasterizer_rd/shaders/ssao_minify.glsl
  24. 3 2
      servers/visual/visual_server_raster.h
  25. 3 0
      servers/visual/visual_server_viewport.cpp
  26. 4 2
      servers/visual/visual_server_wrap_mt.h
  27. 5 1
      servers/visual_server.cpp
  28. 12 8
      servers/visual_server.h

+ 1 - 0
editor/editor_node.cpp

@@ -360,6 +360,7 @@ void EditorNode::_notification(int p_what) {
 				VS::DOFBlurQuality dof_quality = VS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/filters/depth_of_field_bokeh_quality")));
 				bool dof_jitter = GLOBAL_GET("rendering/quality/filters/depth_of_field_use_jitter");
 				VS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter);
+				VS::get_singleton()->environment_set_ssao_quality(VS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size"));
 			}
 
 			ResourceImporterTexture::get_singleton()->update_imports();

+ 7 - 2
editor/plugins/spatial_editor_plugin.cpp

@@ -2769,7 +2769,8 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 		case VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO:
 		case VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING:
 		case VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION:
-		case VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE: {
+		case VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE:
+		case VIEW_DISPLAY_DEBUG_SSAO: {
 
 			static const int display_options[] = {
 				VIEW_DISPLAY_NORMAL,
@@ -2784,6 +2785,7 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 				VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING,
 				VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION,
 				VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE,
+				VIEW_DISPLAY_DEBUG_SSAO,
 				VIEW_MAX
 			};
 			static const Viewport::DebugDraw debug_draw_modes[] = {
@@ -2798,7 +2800,8 @@ void SpatialEditorViewport::_menu_option(int p_option) {
 				Viewport::DEBUG_DRAW_GI_PROBE_ALBEDO,
 				Viewport::DEBUG_DRAW_GI_PROBE_LIGHTING,
 				Viewport::DEBUG_DRAW_GI_PROBE_EMISSION,
-				Viewport::DEBUG_DRAW_SCENE_LUMINANCE
+				Viewport::DEBUG_DRAW_SCENE_LUMINANCE,
+				Viewport::DEBUG_DRAW_SSAO
 			};
 
 			int idx = 0;
@@ -3644,6 +3647,8 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed
 	display_submenu->add_radio_check_item(TTR("GIProbe Emission"), VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION);
 	display_submenu->add_separator();
 	display_submenu->add_radio_check_item(TTR("Scene Luminance"), VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE);
+	display_submenu->add_separator();
+	display_submenu->add_radio_check_item(TTR("SSAO"), VIEW_DISPLAY_DEBUG_SSAO);
 	display_submenu->set_name("display_advanced");
 	view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced");
 	view_menu->get_popup()->add_separator();

+ 1 - 0
editor/plugins/spatial_editor_plugin.h

@@ -174,6 +174,7 @@ class SpatialEditorViewport : public Control {
 		VIEW_DISPLAY_DEBUG_GIPROBE_LIGHTING,
 		VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION,
 		VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE,
+		VIEW_DISPLAY_DEBUG_SSAO,
 		VIEW_LOCK_ROTATION,
 		VIEW_CINEMATIC_PREVIEW,
 		VIEW_MAX

+ 1 - 0
scene/main/viewport.cpp

@@ -3269,6 +3269,7 @@ void Viewport::_bind_methods() {
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_SHADOW_ATLAS);
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS);
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_SCENE_LUMINANCE);
+	BIND_ENUM_CONSTANT(DEBUG_DRAW_SSAO);
 
 	BIND_ENUM_CONSTANT(MSAA_DISABLED);
 	BIND_ENUM_CONSTANT(MSAA_2X);

+ 1 - 0
scene/main/viewport.h

@@ -137,6 +137,7 @@ public:
 		DEBUG_DRAW_SHADOW_ATLAS,
 		DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
 		DEBUG_DRAW_SCENE_LUMINANCE,
+		DEBUG_DRAW_SSAO,
 
 	};
 

+ 21 - 73
scene/resources/environment.cpp

@@ -79,17 +79,17 @@ void Environment::set_canvas_max_layer(int p_max_layer) {
 void Environment::set_ambient_light_color(const Color &p_color) {
 
 	ambient_color = p_color;
-	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source));
+	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source), ao_color);
 }
 void Environment::set_ambient_light_energy(float p_energy) {
 
 	ambient_energy = p_energy;
-	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source));
+	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source), ao_color);
 }
 void Environment::set_ambient_light_sky_contribution(float p_energy) {
 
 	ambient_sky_contribution = p_energy;
-	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source));
+	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source), ao_color);
 }
 
 void Environment::set_camera_feed_id(int p_camera_feed_id) {
@@ -102,7 +102,7 @@ void Environment::set_camera_feed_id(int p_camera_feed_id) {
 
 void Environment::set_ambient_source(AmbientSource p_source) {
 	ambient_source = p_source;
-	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source));
+	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source), ao_color);
 }
 
 Environment::AmbientSource Environment::get_ambient_source() const {
@@ -110,7 +110,7 @@ Environment::AmbientSource Environment::get_ambient_source() const {
 }
 void Environment::set_reflection_source(ReflectionSource p_source) {
 	reflection_source = p_source;
-	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source));
+	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source), ao_color);
 }
 Environment::ReflectionSource Environment::get_reflection_source() const {
 	return reflection_source;
@@ -451,7 +451,7 @@ bool Environment::is_ssr_rough() const {
 void Environment::set_ssao_enabled(bool p_enable) {
 
 	ssao_enabled = p_enable;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 	_change_notify();
 }
 
@@ -463,7 +463,7 @@ bool Environment::is_ssao_enabled() const {
 void Environment::set_ssao_radius(float p_radius) {
 
 	ssao_radius = p_radius;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 }
 float Environment::get_ssao_radius() const {
 
@@ -473,7 +473,7 @@ float Environment::get_ssao_radius() const {
 void Environment::set_ssao_intensity(float p_intensity) {
 
 	ssao_intensity = p_intensity;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 }
 
 float Environment::get_ssao_intensity() const {
@@ -481,30 +481,10 @@ float Environment::get_ssao_intensity() const {
 	return ssao_intensity;
 }
 
-void Environment::set_ssao_radius2(float p_radius) {
-
-	ssao_radius2 = p_radius;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
-}
-float Environment::get_ssao_radius2() const {
-
-	return ssao_radius2;
-}
-
-void Environment::set_ssao_intensity2(float p_intensity) {
-
-	ssao_intensity2 = p_intensity;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
-}
-float Environment::get_ssao_intensity2() const {
-
-	return ssao_intensity2;
-}
-
 void Environment::set_ssao_bias(float p_bias) {
 
 	ssao_bias = p_bias;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 }
 float Environment::get_ssao_bias() const {
 
@@ -514,7 +494,7 @@ float Environment::get_ssao_bias() const {
 void Environment::set_ssao_direct_light_affect(float p_direct_light_affect) {
 
 	ssao_direct_light_affect = p_direct_light_affect;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 }
 float Environment::get_ssao_direct_light_affect() const {
 
@@ -524,49 +504,38 @@ float Environment::get_ssao_direct_light_affect() const {
 void Environment::set_ssao_ao_channel_affect(float p_ao_channel_affect) {
 
 	ssao_ao_channel_affect = p_ao_channel_affect;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 }
 float Environment::get_ssao_ao_channel_affect() const {
 
 	return ssao_ao_channel_affect;
 }
 
-void Environment::set_ssao_color(const Color &p_color) {
+void Environment::set_ao_color(const Color &p_color) {
 
-	ssao_color = p_color;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	ao_color = p_color;
+	VS::get_singleton()->environment_set_ambient_light(environment, ambient_color, VS::EnvironmentAmbientSource(ambient_source), ambient_energy, ambient_sky_contribution, VS::EnvironmentReflectionSource(reflection_source), ao_color);
 }
 
-Color Environment::get_ssao_color() const {
+Color Environment::get_ao_color() const {
 
-	return ssao_color;
+	return ao_color;
 }
 
 void Environment::set_ssao_blur(SSAOBlur p_blur) {
 
 	ssao_blur = p_blur;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 }
 Environment::SSAOBlur Environment::get_ssao_blur() const {
 
 	return ssao_blur;
 }
 
-void Environment::set_ssao_quality(SSAOQuality p_quality) {
-
-	ssao_quality = p_quality;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
-}
-
-Environment::SSAOQuality Environment::get_ssao_quality() const {
-
-	return ssao_quality;
-}
-
 void Environment::set_ssao_edge_sharpness(float p_edge_sharpness) {
 
 	ssao_edge_sharpness = p_edge_sharpness;
-	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_radius2, ssao_intensity2, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, ssao_color, VS::EnvironmentSSAOQuality(ssao_quality), VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
+	VS::get_singleton()->environment_set_ssao(environment, ssao_enabled, ssao_radius, ssao_intensity, ssao_bias, ssao_direct_light_affect, ssao_ao_channel_affect, VS::EnvironmentSSAOBlur(ssao_blur), ssao_edge_sharpness);
 }
 
 float Environment::get_ssao_edge_sharpness() const {
@@ -888,6 +857,8 @@ void Environment::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &Environment::get_camera_feed_id);
 	ClassDB::bind_method(D_METHOD("get_ambient_source"), &Environment::get_ambient_source);
 	ClassDB::bind_method(D_METHOD("get_reflection_source"), &Environment::get_reflection_source);
+	ClassDB::bind_method(D_METHOD("set_ao_color", "color"), &Environment::set_ao_color);
+	ClassDB::bind_method(D_METHOD("get_ao_color"), &Environment::get_ao_color);
 
 	ADD_GROUP("Background", "background_");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "background_mode", PROPERTY_HINT_ENUM, "Clear Color,Custom Color,Sky,Canvas,Keep,Camera Feed"), "set_background", "get_background");
@@ -904,6 +875,7 @@ void Environment::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_color"), "set_ambient_light_color", "get_ambient_light_color");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ambient_light_sky_contribution", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ambient_light_sky_contribution", "get_ambient_light_sky_contribution");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ambient_light_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_light_energy", "get_ambient_light_energy");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_light_occlusion_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ao_color", "get_ao_color");
 	ADD_GROUP("Reflected Light", "reflected_light_");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "reflected_light_source", PROPERTY_HINT_ENUM, "Background,Disabled,Sky"), "set_reflection_source", "get_reflection_source");
 
@@ -1035,12 +1007,6 @@ void Environment::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_ssao_intensity", "intensity"), &Environment::set_ssao_intensity);
 	ClassDB::bind_method(D_METHOD("get_ssao_intensity"), &Environment::get_ssao_intensity);
 
-	ClassDB::bind_method(D_METHOD("set_ssao_radius2", "radius"), &Environment::set_ssao_radius2);
-	ClassDB::bind_method(D_METHOD("get_ssao_radius2"), &Environment::get_ssao_radius2);
-
-	ClassDB::bind_method(D_METHOD("set_ssao_intensity2", "intensity"), &Environment::set_ssao_intensity2);
-	ClassDB::bind_method(D_METHOD("get_ssao_intensity2"), &Environment::get_ssao_intensity2);
-
 	ClassDB::bind_method(D_METHOD("set_ssao_bias", "bias"), &Environment::set_ssao_bias);
 	ClassDB::bind_method(D_METHOD("get_ssao_bias"), &Environment::get_ssao_bias);
 
@@ -1050,15 +1016,9 @@ void Environment::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_ssao_ao_channel_affect", "amount"), &Environment::set_ssao_ao_channel_affect);
 	ClassDB::bind_method(D_METHOD("get_ssao_ao_channel_affect"), &Environment::get_ssao_ao_channel_affect);
 
-	ClassDB::bind_method(D_METHOD("set_ssao_color", "color"), &Environment::set_ssao_color);
-	ClassDB::bind_method(D_METHOD("get_ssao_color"), &Environment::get_ssao_color);
-
 	ClassDB::bind_method(D_METHOD("set_ssao_blur", "mode"), &Environment::set_ssao_blur);
 	ClassDB::bind_method(D_METHOD("get_ssao_blur"), &Environment::get_ssao_blur);
 
-	ClassDB::bind_method(D_METHOD("set_ssao_quality", "quality"), &Environment::set_ssao_quality);
-	ClassDB::bind_method(D_METHOD("get_ssao_quality"), &Environment::get_ssao_quality);
-
 	ClassDB::bind_method(D_METHOD("set_ssao_edge_sharpness", "edge_sharpness"), &Environment::set_ssao_edge_sharpness);
 	ClassDB::bind_method(D_METHOD("get_ssao_edge_sharpness"), &Environment::get_ssao_edge_sharpness);
 
@@ -1066,13 +1026,9 @@ void Environment::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ssao_enabled"), "set_ssao_enabled", "is_ssao_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_radius", PROPERTY_HINT_RANGE, "0.1,128,0.1"), "set_ssao_radius", "get_ssao_radius");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_intensity", PROPERTY_HINT_RANGE, "0.0,128,0.1"), "set_ssao_intensity", "get_ssao_intensity");
-	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_radius2", PROPERTY_HINT_RANGE, "0.0,128,0.1"), "set_ssao_radius2", "get_ssao_radius2");
-	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_intensity2", PROPERTY_HINT_RANGE, "0.0,128,0.1"), "set_ssao_intensity2", "get_ssao_intensity2");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_bias", PROPERTY_HINT_RANGE, "0.001,8,0.001"), "set_ssao_bias", "get_ssao_bias");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_light_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_direct_light_affect", "get_ssao_direct_light_affect");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_ao_channel_affect", PROPERTY_HINT_RANGE, "0.00,1,0.01"), "set_ssao_ao_channel_affect", "get_ssao_ao_channel_affect");
-	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ssao_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ssao_color", "get_ssao_color");
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_quality", PROPERTY_HINT_ENUM, "Low,Medium,High"), "set_ssao_quality", "get_ssao_quality");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_blur", PROPERTY_HINT_ENUM, "Disabled,1x1,2x2,3x3"), "set_ssao_blur", "get_ssao_blur");
 	ADD_PROPERTY(PropertyInfo(Variant::REAL, "ssao_edge_sharpness", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_ssao_edge_sharpness", "get_ssao_edge_sharpness");
 
@@ -1183,17 +1139,12 @@ void Environment::_bind_methods() {
 	BIND_ENUM_CONSTANT(SSAO_BLUR_1x1);
 	BIND_ENUM_CONSTANT(SSAO_BLUR_2x2);
 	BIND_ENUM_CONSTANT(SSAO_BLUR_3x3);
-
-	BIND_ENUM_CONSTANT(SSAO_QUALITY_LOW);
-	BIND_ENUM_CONSTANT(SSAO_QUALITY_MEDIUM);
-	BIND_ENUM_CONSTANT(SSAO_QUALITY_HIGH);
 }
 
 Environment::Environment() :
 		bg_mode(BG_CLEAR_COLOR),
 		tone_mapper(TONE_MAPPER_LINEAR),
 		ssao_blur(SSAO_BLUR_3x3),
-		ssao_quality(SSAO_QUALITY_MEDIUM),
 		glow_blend_mode(GLOW_BLEND_MODE_ADDITIVE) {
 
 	environment = VS::get_singleton()->environment_create();
@@ -1237,14 +1188,11 @@ Environment::Environment() :
 	ssao_enabled = false;
 	ssao_radius = 1;
 	ssao_intensity = 1;
-	ssao_radius2 = 0;
-	ssao_intensity2 = 1;
 	ssao_bias = 0.01;
 	ssao_direct_light_affect = 0.0;
 	ssao_ao_channel_affect = 0.0;
 	ssao_blur = SSAO_BLUR_3x3;
 	set_ssao_edge_sharpness(4);
-	set_ssao_quality(SSAO_QUALITY_MEDIUM);
 
 	glow_enabled = false;
 	glow_levels = (1 << 2) | (1 << 4);

+ 3 - 22
scene/resources/environment.h

@@ -87,12 +87,6 @@ public:
 		SSAO_BLUR_3x3
 	};
 
-	enum SSAOQuality {
-		SSAO_QUALITY_LOW,
-		SSAO_QUALITY_MEDIUM,
-		SSAO_QUALITY_HIGH
-	};
-
 private:
 	RID environment;
 
@@ -105,6 +99,7 @@ private:
 	int bg_canvas_max_layer;
 	Color ambient_color;
 	float ambient_energy;
+	Color ao_color;
 	float ambient_sky_contribution;
 	int camera_feed_id;
 	AmbientSource ambient_source;
@@ -135,15 +130,11 @@ private:
 	bool ssao_enabled;
 	float ssao_radius;
 	float ssao_intensity;
-	float ssao_radius2;
-	float ssao_intensity2;
 	float ssao_bias;
 	float ssao_direct_light_affect;
 	float ssao_ao_channel_affect;
-	Color ssao_color;
 	SSAOBlur ssao_blur;
 	float ssao_edge_sharpness;
-	SSAOQuality ssao_quality;
 
 	bool glow_enabled;
 	int glow_levels;
@@ -278,12 +269,6 @@ public:
 	void set_ssao_intensity(float p_intensity);
 	float get_ssao_intensity() const;
 
-	void set_ssao_radius2(float p_radius);
-	float get_ssao_radius2() const;
-
-	void set_ssao_intensity2(float p_intensity);
-	float get_ssao_intensity2() const;
-
 	void set_ssao_bias(float p_bias);
 	float get_ssao_bias() const;
 
@@ -293,15 +278,12 @@ public:
 	void set_ssao_ao_channel_affect(float p_ao_channel_affect);
 	float get_ssao_ao_channel_affect() const;
 
-	void set_ssao_color(const Color &p_color);
-	Color get_ssao_color() const;
+	void set_ao_color(const Color &p_color);
+	Color get_ao_color() const;
 
 	void set_ssao_blur(SSAOBlur p_blur);
 	SSAOBlur get_ssao_blur() const;
 
-	void set_ssao_quality(SSAOQuality p_quality);
-	SSAOQuality get_ssao_quality() const;
-
 	void set_ssao_edge_sharpness(float p_edge_sharpness);
 	float get_ssao_edge_sharpness() const;
 
@@ -391,7 +373,6 @@ VARIANT_ENUM_CAST(Environment::AmbientSource)
 VARIANT_ENUM_CAST(Environment::ReflectionSource)
 VARIANT_ENUM_CAST(Environment::ToneMapper)
 VARIANT_ENUM_CAST(Environment::GlowBlendMode)
-VARIANT_ENUM_CAST(Environment::SSAOQuality)
 VARIANT_ENUM_CAST(Environment::SSAOBlur)
 
 class CameraEffects : public Resource {

+ 4 - 2
servers/visual/rasterizer.h

@@ -69,7 +69,7 @@ public:
 	virtual void environment_set_bg_color(RID p_env, const Color &p_color) = 0;
 	virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0;
 	virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0;
-	virtual void environment_set_ambient_light(RID p_env, const Color &p_color, VS::EnvironmentAmbientSource p_ambient = VS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, VS::EnvironmentReflectionSource p_reflection_source = VS::ENV_REFLECTION_SOURCE_BG) = 0;
+	virtual void environment_set_ambient_light(RID p_env, const Color &p_color, VS::EnvironmentAmbientSource p_ambient = VS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, VS::EnvironmentReflectionSource p_reflection_source = VS::ENV_REFLECTION_SOURCE_BG, const Color &p_ao_color = Color()) = 0;
 // FIXME: Disabled during Vulkan refactoring, should be ported.
 #if 0
 	virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0;
@@ -79,7 +79,9 @@ public:
 	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_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance, bool p_roughness) = 0;
-	virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
+	virtual 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, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
+
+	virtual void environment_set_ssao_quality(VS::EnvironmentSSAOQuality p_quality, bool p_half_size) = 0;
 
 	virtual void environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) = 0;
 

+ 213 - 1
servers/visual/rasterizer_rd/rasterizer_effects_rd.cpp

@@ -116,12 +116,15 @@ RID RasterizerEffectsRD::_get_compute_uniform_set_from_texture(RID p_texture, bo
 	return uniform_set;
 }
 
-void RasterizerEffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, bool p_flip_y) {
+void RasterizerEffectsRD::copy_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, bool p_flip_y, bool p_force_luminance) {
 
 	zeromem(&blur.push_constant, sizeof(BlurPushConstant));
 	if (p_flip_y) {
 		blur.push_constant.flags |= BLUR_FLAG_FLIP_Y;
 	}
+	if (p_force_luminance) {
+		blur.push_constant.flags |= BLUR_COPY_FORCE_LUMINANCE;
+	}
 
 	RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect);
 	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur.pipelines[BLUR_MODE_SIMPLY_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
@@ -570,6 +573,160 @@ void RasterizerEffectsRD::bokeh_dof(RID p_base_texture, RID p_depth_texture, con
 	RD::get_singleton()->compute_list_end();
 }
 
+void RasterizerEffectsRD::generate_ssao(RID p_depth_buffer, RID p_normal_buffer, const Size2i &p_depth_buffer_size, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao1, bool p_half_size, RID p_ao2, RID p_upscale_buffer, float p_intensity, float p_radius, float p_bias, const CameraMatrix &p_projection, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_edge_sharpness) {
+
+	//minify first
+	ssao.minify_push_constant.orthogonal = p_projection.is_orthogonal();
+	ssao.minify_push_constant.z_near = p_projection.get_z_near();
+	ssao.minify_push_constant.z_far = p_projection.get_z_far();
+	ssao.minify_push_constant.pixel_size[0] = 1.0 / p_depth_buffer_size.x;
+	ssao.minify_push_constant.pixel_size[1] = 1.0 / p_depth_buffer_size.y;
+	ssao.minify_push_constant.source_size[0] = p_depth_buffer_size.x;
+	ssao.minify_push_constant.source_size[1] = p_depth_buffer_size.y;
+
+	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+
+	/* FIRST PASS */
+	// Minify the depth buffer.
+
+	for (int i = 0; i < depth_mipmaps.size(); i++) {
+
+		if (i == 0) {
+			RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_MINIFY_FIRST]);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 0);
+		} else {
+			if (i == 1) {
+				RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_MINIFY_MIPMAP]);
+			}
+
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(depth_mipmaps[i - 1]), 0);
+		}
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(depth_mipmaps[i]), 1);
+
+		RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.minify_push_constant, sizeof(SSAOMinifyPushConstant));
+		// shrink after set
+		ssao.minify_push_constant.source_size[0] = MAX(1, ssao.minify_push_constant.source_size[0] >> 1);
+		ssao.minify_push_constant.source_size[1] = MAX(1, ssao.minify_push_constant.source_size[1] >> 1);
+
+		int x_groups = (ssao.minify_push_constant.source_size[0] - 1) / 8 + 1;
+		int y_groups = (ssao.minify_push_constant.source_size[1] - 1) / 8 + 1;
+
+		RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
+		RD::get_singleton()->compute_list_add_barrier(compute_list);
+	}
+
+	/* SECOND PASS */
+	// Gather samples
+
+	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[(SSAO_GATHER_LOW + p_quality) + (p_half_size ? 4 : 0)]);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 0);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao1), 1);
+	if (!p_half_size) {
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 2);
+	}
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_normal_buffer), 3);
+
+	ssao.gather_push_constant.screen_size[0] = p_depth_buffer_size.x;
+	ssao.gather_push_constant.screen_size[1] = p_depth_buffer_size.y;
+	if (p_half_size) {
+		ssao.gather_push_constant.screen_size[0] >>= 1;
+		ssao.gather_push_constant.screen_size[1] >>= 1;
+	}
+	ssao.gather_push_constant.z_far = p_projection.get_z_far();
+	ssao.gather_push_constant.z_near = p_projection.get_z_near();
+	ssao.gather_push_constant.orthogonal = p_projection.is_orthogonal();
+
+	ssao.gather_push_constant.proj_info[0] = -2.0f / (ssao.gather_push_constant.screen_size[0] * p_projection.matrix[0][0]);
+	ssao.gather_push_constant.proj_info[1] = -2.0f / (ssao.gather_push_constant.screen_size[1] * p_projection.matrix[1][1]);
+	ssao.gather_push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0];
+	ssao.gather_push_constant.proj_info[3] = (1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1];
+	//ssao.gather_push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0];
+	//ssao.gather_push_constant.proj_info[3] = -(1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1];
+
+	ssao.gather_push_constant.radius = p_radius;
+
+	ssao.gather_push_constant.proj_scale = float(p_projection.get_pixels_per_meter(ssao.gather_push_constant.screen_size[0]));
+	ssao.gather_push_constant.bias = p_bias;
+	ssao.gather_push_constant.intensity_div_r6 = p_intensity / pow(p_radius, 6.0f);
+
+	ssao.gather_push_constant.pixel_size[0] = 1.0 / p_depth_buffer_size.x;
+	ssao.gather_push_constant.pixel_size[1] = 1.0 / p_depth_buffer_size.y;
+
+	RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.gather_push_constant, sizeof(SSAOGatherPushConstant));
+
+	int x_groups = (ssao.gather_push_constant.screen_size[0] - 1) / 8 + 1;
+	int y_groups = (ssao.gather_push_constant.screen_size[1] - 1) / 8 + 1;
+
+	RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
+	RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+	/* THIRD PASS */
+	// Blur horizontal
+
+	ssao.blur_push_constant.edge_sharpness = p_edge_sharpness;
+	ssao.blur_push_constant.filter_scale = p_blur + 1;
+	ssao.blur_push_constant.screen_size[0] = ssao.gather_push_constant.screen_size[0];
+	ssao.blur_push_constant.screen_size[1] = ssao.gather_push_constant.screen_size[1];
+	ssao.blur_push_constant.z_far = p_projection.get_z_far();
+	ssao.blur_push_constant.z_near = p_projection.get_z_near();
+	ssao.blur_push_constant.orthogonal = p_projection.is_orthogonal();
+	ssao.blur_push_constant.axis[0] = 1;
+	ssao.blur_push_constant.axis[1] = 0;
+
+	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[p_half_size ? SSAO_BLUR_PASS_HALF : SSAO_BLUR_PASS]);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao1), 0);
+	if (p_half_size) {
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 1);
+	} else {
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 1);
+	}
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao2), 3);
+
+	RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant));
+
+	RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
+	RD::get_singleton()->compute_list_add_barrier(compute_list);
+
+	/* THIRD PASS */
+	// Blur vertical
+
+	ssao.blur_push_constant.axis[0] = 0;
+	ssao.blur_push_constant.axis[1] = 1;
+
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao2), 0);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_ao1), 3);
+
+	RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant));
+
+	RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
+
+	if (p_half_size) { //must upscale
+
+		/* FOURTH PASS */
+		// upscale if half size
+		//back to full size
+		ssao.blur_push_constant.screen_size[0] = p_depth_buffer_size.x;
+		ssao.blur_push_constant.screen_size[1] = p_depth_buffer_size.y;
+
+		RD::get_singleton()->compute_list_add_barrier(compute_list);
+		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssao.pipelines[SSAO_BLUR_UPSCALE]);
+
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_ao1), 0);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_upscale_buffer), 3);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_buffer), 1);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_depth_mipmaps_texture), 2);
+
+		RD::get_singleton()->compute_list_set_push_constant(compute_list, &ssao.blur_push_constant, sizeof(SSAOBlurPushConstant)); //not used but set anyway
+
+		x_groups = (p_depth_buffer_size.x - 1) / 8 + 1;
+		y_groups = (p_depth_buffer_size.y - 1) / 8 + 1;
+
+		RD::get_singleton()->compute_list_dispatch(compute_list, x_groups, y_groups, 1);
+	}
+
+	RD::get_singleton()->compute_list_end();
+}
+
 RasterizerEffectsRD::RasterizerEffectsRD() {
 
 	{
@@ -693,6 +850,61 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
 		}
 	}
 
+	{
+		// Initialize ssao
+		uint32_t pipeline = 0;
+		{
+			Vector<String> ssao_modes;
+			ssao_modes.push_back("\n#define MINIFY_START\n");
+			ssao_modes.push_back("\n");
+
+			ssao.minify_shader.initialize(ssao_modes);
+
+			ssao.minify_shader_version = ssao.minify_shader.version_create();
+
+			for (int i = 0; i <= SSAO_MINIFY_MIPMAP; i++) {
+				ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.minify_shader.version_get_shader(ssao.minify_shader_version, i));
+				pipeline++;
+			}
+		}
+		{
+			Vector<String> ssao_modes;
+			ssao_modes.push_back("\n#define SSAO_QUALITY_LOW\n");
+			ssao_modes.push_back("\n");
+			ssao_modes.push_back("\n#define SSAO_QUALITY_HIGH\n");
+			ssao_modes.push_back("\n#define SSAO_QUALITY_ULTRA\n");
+			ssao_modes.push_back("\n#define SSAO_QUALITY_LOW\n#define USE_HALF_SIZE\n");
+			ssao_modes.push_back("\n#define USE_HALF_SIZE\n");
+			ssao_modes.push_back("\n#define SSAO_QUALITY_HIGH\n#define USE_HALF_SIZE\n");
+			ssao_modes.push_back("\n#define SSAO_QUALITY_ULTRA\n#define USE_HALF_SIZE\n");
+
+			ssao.gather_shader.initialize(ssao_modes);
+
+			ssao.gather_shader_version = ssao.gather_shader.version_create();
+
+			for (int i = SSAO_GATHER_LOW; i <= SSAO_GATHER_ULTRA_HALF; i++) {
+				ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.gather_shader.version_get_shader(ssao.gather_shader_version, i - SSAO_GATHER_LOW));
+				pipeline++;
+			}
+		}
+		{
+			Vector<String> ssao_modes;
+			ssao_modes.push_back("\n#define MODE_FULL_SIZE\n");
+			ssao_modes.push_back("\n");
+			ssao_modes.push_back("\n#define MODE_UPSCALE\n");
+
+			ssao.blur_shader.initialize(ssao_modes);
+
+			ssao.blur_shader_version = ssao.blur_shader.version_create();
+
+			for (int i = SSAO_BLUR_PASS; i <= SSAO_BLUR_UPSCALE; i++) {
+				ssao.pipelines[pipeline] = RD::get_singleton()->compute_pipeline_create(ssao.blur_shader.version_get_shader(ssao.blur_shader_version, i - SSAO_BLUR_PASS));
+				pipeline++;
+			}
+		}
+
+		ERR_FAIL_COND(pipeline != SSAO_MAX);
+	}
 	RD::SamplerState sampler;
 	sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR;
 	sampler.min_filter = RD::SAMPLER_FILTER_LINEAR;

+ 78 - 2
servers/visual/rasterizer_rd/rasterizer_effects_rd.h

@@ -39,6 +39,9 @@
 #include "servers/visual/rasterizer_rd/shaders/cubemap_roughness.glsl.gen.h"
 #include "servers/visual/rasterizer_rd/shaders/luminance_reduce.glsl.gen.h"
 #include "servers/visual/rasterizer_rd/shaders/sky.glsl.gen.h"
+#include "servers/visual/rasterizer_rd/shaders/ssao.glsl.gen.h"
+#include "servers/visual/rasterizer_rd/shaders/ssao_blur.glsl.gen.h"
+#include "servers/visual/rasterizer_rd/shaders/ssao_minify.glsl.gen.h"
 #include "servers/visual/rasterizer_rd/shaders/tonemap.glsl.gen.h"
 
 #include "servers/visual_server.h"
@@ -71,7 +74,8 @@ class RasterizerEffectsRD {
 		BLUR_FLAG_USE_ORTHOGONAL_PROJECTION = (1 << 2),
 		BLUR_FLAG_DOF_NEAR_FIRST_TAP = (1 << 3),
 		BLUR_FLAG_GLOW_FIRST_PASS = (1 << 4),
-		BLUR_FLAG_FLIP_Y = (1 << 5)
+		BLUR_FLAG_FLIP_Y = (1 << 5),
+		BLUR_COPY_FORCE_LUMINANCE = (1 << 6)
 	};
 
 	struct BlurPushConstant {
@@ -265,6 +269,76 @@ class RasterizerEffectsRD {
 		RID pipelines[BOKEH_MAX];
 	} bokeh;
 
+	enum SSAOMode {
+		SSAO_MINIFY_FIRST,
+		SSAO_MINIFY_MIPMAP,
+		SSAO_GATHER_LOW,
+		SSAO_GATHER_MEDIUM,
+		SSAO_GATHER_HIGH,
+		SSAO_GATHER_ULTRA,
+		SSAO_GATHER_LOW_HALF,
+		SSAO_GATHER_MEDIUM_HALF,
+		SSAO_GATHER_HIGH_HALF,
+		SSAO_GATHER_ULTRA_HALF,
+		SSAO_BLUR_PASS,
+		SSAO_BLUR_PASS_HALF,
+		SSAO_BLUR_UPSCALE,
+		SSAO_MAX
+	};
+
+	struct SSAOMinifyPushConstant {
+		float pixel_size[2];
+		float z_far;
+		float z_near;
+		int32_t source_size[2];
+		uint32_t orthogonal;
+		uint32_t pad;
+	};
+
+	struct SSAOGatherPushConstant {
+		int32_t screen_size[2];
+		float z_far;
+		float z_near;
+
+		uint32_t orthogonal;
+		float intensity_div_r6;
+		float radius;
+		float bias;
+
+		float proj_info[4];
+		float pixel_size[2];
+		float proj_scale;
+		uint32_t pad;
+	};
+
+	struct SSAOBlurPushConstant {
+		float edge_sharpness;
+		int32_t filter_scale;
+		float z_far;
+		float z_near;
+		uint32_t orthogonal;
+		uint32_t pad[3];
+		int32_t axis[2];
+		int32_t screen_size[2];
+	};
+
+	struct SSAO {
+
+		SSAOMinifyPushConstant minify_push_constant;
+		SsaoMinifyShaderRD minify_shader;
+		RID minify_shader_version;
+
+		SSAOGatherPushConstant gather_push_constant;
+		SsaoShaderRD gather_shader;
+		RID gather_shader_version;
+
+		SSAOBlurPushConstant blur_push_constant;
+		SsaoBlurShaderRD blur_shader;
+		RID blur_shader_version;
+
+		RID pipelines[SSAO_MAX];
+	} ssao;
+
 	RID default_sampler;
 	RID default_mipmap_sampler;
 	RID index_buffer;
@@ -283,7 +357,7 @@ public:
 	//TODO must re-do most of the shaders in compute
 
 	void region_copy(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_region);
-	void copy_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, bool p_flip_y = false);
+	void copy_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2 &p_rect, bool p_flip_y = false, bool p_force_luminance = false);
 	void gaussian_blur(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, const Rect2 &p_region);
 	void gaussian_glow(RID p_source_rd_texture, RID p_framebuffer_half, RID p_rd_texture_half, RID p_dest_framebuffer, const Vector2 &p_pixel_size, float p_strength = 1.0, bool p_first_pass = false, float p_luminance_cap = 16.0, float p_exposure = 1.0, float p_bloom = 0.0, float p_hdr_bleed_treshold = 1.0, float p_hdr_bleed_scale = 1.0, RID p_auto_exposure = RID(), float p_auto_exposure_grey = 1.0);
 
@@ -331,6 +405,8 @@ public:
 
 	void tonemapper(RID p_source_color, RID p_dst_framebuffer, const TonemapSettings &p_settings);
 
+	void generate_ssao(RID p_depth_buffer, RID p_normal_buffer, const Size2i &p_depth_buffer_size, RID p_depth_mipmaps_texture, const Vector<RID> &depth_mipmaps, RID p_ao1, bool p_half_size, RID p_ao2, RID p_upscale_buffer, float p_intensity, float p_radius, float p_bias, const CameraMatrix &p_projection, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_edge_sharpness);
+
 	RasterizerEffectsRD();
 	~RasterizerEffectsRD();
 };

+ 298 - 101
servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.cpp

@@ -505,7 +505,7 @@ void RasterizerSceneHighEndRD::MaterialData::update_parameters(const Map<StringN
 		}
 	}
 
-	uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_singleton->shader.scene_shader.version_get_shader(shader_data->version, 0), 3);
+	uniform_set = RD::get_singleton()->uniform_set_create(uniforms, scene_singleton->shader.scene_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET);
 }
 RasterizerSceneHighEndRD::MaterialData::~MaterialData() {
 	if (uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(uniform_set)) {
@@ -525,19 +525,54 @@ RasterizerStorageRD::MaterialData *RasterizerSceneHighEndRD::_create_material_fu
 	return material_data;
 }
 
-RasterizerSceneHighEndRD::RenderBufferDataForward::~RenderBufferDataForward() {
+RasterizerSceneHighEndRD::RenderBufferDataHighEnd::~RenderBufferDataHighEnd() {
 	clear();
 }
 
-void RasterizerSceneHighEndRD::RenderBufferDataForward::clear() {
+void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::ensure_specular() {
 
-	if (color_fb.is_valid()) {
-		RD::get_singleton()->free(color_fb);
-		color_fb = RID();
+	if (!specular.is_valid()) {
+		RD::TextureFormat tf;
+		tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+		tf.width = width;
+		tf.height = height;
+		tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+
+		specular = RD::get_singleton()->texture_create(tf, RD::TextureView());
+
+		Vector<RID> fb;
+		fb.push_back(color);
+		fb.push_back(specular);
+		fb.push_back(depth);
+
+		color_specular_fb = RD::get_singleton()->framebuffer_create(fb);
+	}
+}
+
+void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::clear() {
+
+	if (specular.is_valid()) {
+		RD::get_singleton()->free(specular);
+		specular = RID();
+	}
+
+	color_specular_fb = RID();
+	color_fb = RID();
+
+	if (normal_buffer.is_valid()) {
+		RD::get_singleton()->free(normal_buffer);
+		normal_buffer = RID();
+		depth_normal_fb = RID();
+	}
+
+	if (roughness_buffer.is_valid()) {
+		RD::get_singleton()->free(roughness_buffer);
+		roughness_buffer = RID();
+		depth_normal_roughness_fb = RID();
 	}
 }
 
-void RasterizerSceneHighEndRD::RenderBufferDataForward::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, VS::ViewportMSAA p_msaa) {
+void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, VS::ViewportMSAA p_msaa) {
 	clear();
 
 	width = p_width;
@@ -559,16 +594,54 @@ void RasterizerSceneHighEndRD::RenderBufferDataForward::configure(RID p_color_bu
 
 		depth_fb = RD::get_singleton()->framebuffer_create(fb);
 	}
-	{
-		Vector<RID> fb;
-		fb.push_back(p_color_buffer);
+}
+
+void RasterizerSceneHighEndRD::_allocate_normal_texture(RenderBufferDataHighEnd *rb) {
+	if (rb->normal_buffer.is_valid()) {
+		return;
+	}
+
+	RD::TextureFormat tf;
+	tf.format = RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32;
+	tf.width = rb->width;
+	tf.height = rb->height;
+	tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
 
-		color_only_fb = RD::get_singleton()->framebuffer_create(fb);
+	rb->normal_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+	Vector<RID> fb;
+	fb.push_back(rb->depth);
+	fb.push_back(rb->normal_buffer);
+	rb->depth_normal_fb = RD::get_singleton()->framebuffer_create(fb);
+
+	_render_buffers_clear_uniform_set(rb);
+}
+
+void RasterizerSceneHighEndRD::_allocate_roughness_texture(RenderBufferDataHighEnd *rb) {
+
+	if (rb->roughness_buffer.is_valid()) {
+		return;
 	}
+
+	ERR_FAIL_COND(rb->normal_buffer.is_null());
+
+	RD::TextureFormat tf;
+	tf.format = RD::DATA_FORMAT_R8_UNORM;
+	tf.width = rb->width;
+	tf.height = rb->height;
+	tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT;
+
+	rb->roughness_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView());
+	Vector<RID> fb;
+	fb.push_back(rb->depth);
+	fb.push_back(rb->normal_buffer);
+	fb.push_back(rb->roughness_buffer);
+	rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb);
+
+	_render_buffers_clear_uniform_set(rb);
 }
 
 RasterizerSceneRD::RenderBufferData *RasterizerSceneHighEndRD::_create_render_buffer_data() {
-	return memnew(RenderBufferDataForward);
+	return memnew(RenderBufferDataHighEnd);
 }
 
 bool RasterizerSceneHighEndRD::free(RID p_rid) {
@@ -658,15 +731,25 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements,
 
 /// RENDERING ///
 
-void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi) {
+void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set) {
 
 	RD::DrawListID draw_list = p_draw_list;
 	RD::FramebufferFormatID framebuffer_format = p_framebuffer_Format;
 
 	//global scope bindings
-	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_base_uniform_set, 0);
-	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_pass_uniform_set, 1);
-	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_vec4_xform_uniform_set, 2);
+	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, render_base_uniform_set, SCENE_UNIFORM_SET);
+	if (p_radiance_uniform_set.is_valid()) {
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_radiance_uniform_set, RADIANCE_UNIFORM_SET);
+	} else {
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_radiance_uniform_set, RADIANCE_UNIFORM_SET);
+	}
+	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, view_dependant_uniform_set, VIEW_DEPENDANT_UNIFORM_SET);
+	if (p_render_buffers_uniform_set.is_valid()) {
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_render_buffers_uniform_set, RENDER_BUFFERS_UNIFORM_SET);
+	} else {
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_render_buffers_uniform_set, RENDER_BUFFERS_UNIFORM_SET);
+	}
+	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, default_vec4_xform_uniform_set, TRANSFORMS_UNIFORM_SET);
 
 	MaterialData *prev_material = nullptr;
 	//	ShaderData *prev_shader = nullptr;
@@ -707,7 +790,7 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
 			case VS::INSTANCE_MESH: {
 				primitive = storage->mesh_surface_get_primitive(e->instance->base, e->surface_index);
 				if (e->instance->skeleton.is_valid()) {
-					xforms_uniform_set = storage->skeleton_get_3d_uniform_set(e->instance->skeleton, default_shader_rd, 2);
+					xforms_uniform_set = storage->skeleton_get_3d_uniform_set(e->instance->skeleton, default_shader_rd, TRANSFORMS_UNIFORM_SET);
 				}
 			} break;
 			case VS::INSTANCE_MULTIMESH: {
@@ -715,7 +798,7 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
 				ERR_CONTINUE(!mesh.is_valid()); //should be a bug
 				primitive = storage->mesh_surface_get_primitive(mesh, e->surface_index);
 
-				xforms_uniform_set = storage->multimesh_get_3d_uniform_set(e->instance->base, default_shader_rd, 2);
+				xforms_uniform_set = storage->multimesh_get_3d_uniform_set(e->instance->base, default_shader_rd, TRANSFORMS_UNIFORM_SET);
 
 			} break;
 			case VS::INSTANCE_IMMEDIATE: {
@@ -821,14 +904,14 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l
 		}
 
 		if (xforms_uniform_set.is_valid() && prev_xforms_uniform_set != xforms_uniform_set) {
-			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, 2);
+			RD::get_singleton()->draw_list_bind_uniform_set(draw_list, xforms_uniform_set, TRANSFORMS_UNIFORM_SET);
 			prev_xforms_uniform_set = xforms_uniform_set;
 		}
 
 		if (material != prev_material) {
 			//update uniform set
 			if (material->uniform_set.is_valid()) {
-				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material->uniform_set, 3);
+				RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material->uniform_set, MATERIAL_UNIFORM_SET);
 			}
 
 			prev_material = material;
@@ -900,6 +983,7 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const Camer
 		scene_state.ubo.ambient_light_color_energy[3] = 1.0;
 		scene_state.ubo.use_ambient_cubemap = false;
 		scene_state.ubo.use_reflection_cubemap = false;
+		scene_state.ubo.ssao_enabled = false;
 
 	} else if (is_environment(p_environment)) {
 
@@ -947,6 +1031,16 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const Camer
 			scene_state.ubo.use_reflection_cubemap = false;
 		}
 
+		scene_state.ubo.ssao_enabled = environment_is_ssao_enabled(p_environment);
+		scene_state.ubo.ssao_ao_affect = environment_get_ssao_ao_affect(p_environment);
+		scene_state.ubo.ssao_light_affect = environment_get_ssao_light_affect(p_environment);
+
+		Color ao_color = environment_get_ao_color(p_environment);
+		scene_state.ubo.ao_color[0] = ao_color.r;
+		scene_state.ubo.ao_color[1] = ao_color.g;
+		scene_state.ubo.ao_color[2] = ao_color.b;
+		scene_state.ubo.ao_color[3] = ao_color.a;
+
 	} else {
 
 		if (p_reflection_probe.is_valid() && storage->reflection_probe_is_interior(reflection_probe_instance_get_probe(p_reflection_probe))) {
@@ -1557,9 +1651,9 @@ void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_lig
 
 void RasterizerSceneHighEndRD::_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, 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_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_bg_color) {
 
-	RenderBufferDataForward *render_buffer = NULL;
+	RenderBufferDataHighEnd *render_buffer = NULL;
 	if (p_render_buffer.is_valid()) {
-		render_buffer = (RenderBufferDataForward *)render_buffers_get_data(p_render_buffer);
+		render_buffer = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffer);
 	}
 
 	//first of all, make a new render pass
@@ -1621,12 +1715,40 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 	RID depth_framebuffer;
 	RID alpha_framebuffer;
 
+	PassMode depth_pass_mode = PASS_MODE_DEPTH;
+	Vector<Color> depth_pass_clear;
+
 	if (render_buffer) {
 		screen_pixel_size.width = 1.0 / render_buffer->width;
 		screen_pixel_size.height = 1.0 / render_buffer->height;
 
 		opaque_framebuffer = render_buffer->color_fb;
-		depth_framebuffer = render_buffer->depth_fb;
+		if (p_environment.is_valid() && environment_is_ssr_enabled(p_environment)) {
+			depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS;
+		} else if (p_environment.is_valid() && environment_is_ssao_enabled(p_environment)) {
+			depth_pass_mode = PASS_MODE_DEPTH_NORMAL;
+		}
+
+		switch (depth_pass_mode) {
+			case PASS_MODE_DEPTH: {
+				depth_framebuffer = render_buffer->depth_fb;
+			} break;
+			case PASS_MODE_DEPTH_NORMAL: {
+				_allocate_normal_texture(render_buffer);
+				depth_framebuffer = render_buffer->depth_normal_fb;
+				depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0));
+			} break;
+			case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: {
+				_allocate_normal_texture(render_buffer);
+				_allocate_roughness_texture(render_buffer);
+				depth_framebuffer = render_buffer->depth_normal_roughness_fb;
+				depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0));
+				depth_pass_clear.push_back(Color());
+			} break;
+			default: {
+			};
+		}
+
 		alpha_framebuffer = opaque_framebuffer;
 
 	} else if (p_reflection_probe.is_valid()) {
@@ -1660,7 +1782,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 	render_list.clear();
 	_fill_render_list(p_cull_result, p_cull_count, PASS_MODE_COLOR, render_buffer == nullptr);
 
-	RID radiance_cubemap;
+	RID radiance_uniform_set;
 	bool draw_sky = false;
 
 	Color clear_color;
@@ -1687,7 +1809,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 			case VS::ENV_BG_SKY: {
 				RID sky = environment_get_sky(p_environment);
 				if (sky.is_valid()) {
-					radiance_cubemap = sky_get_radiance_texture_rd(sky);
+					radiance_uniform_set = sky_get_radiance_uniform_set_rd(sky, radiance_uniform_set, RADIANCE_UNIFORM_SET);
 					draw_sky = true;
 				}
 			} break;
@@ -1708,7 +1830,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 		clear_color = p_default_bg_color;
 	}
 
-	_setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), radiance_cubemap, p_shadow_atlas, p_reflection_atlas);
+	_setup_view_dependant_uniform_set(p_shadow_atlas, p_reflection_atlas);
 
 	render_list.sort_by_key(false);
 
@@ -1719,13 +1841,26 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 	bool using_separate_specular = false;
 
 	bool depth_pre_pass = depth_framebuffer.is_valid();
+	RID render_buffers_uniform_set;
+
 	if (depth_pre_pass) { //depth pre pass
 		RENDER_TIMESTAMP("Render Depth Pre-Pass");
 
-		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, PASS_MODE_DEPTH, render_buffer == nullptr);
+		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, depth_pass_clear);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, depth_pass_mode, render_buffer == nullptr, radiance_uniform_set, RID());
 		RD::get_singleton()->draw_list_end();
 	}
+
+	if (p_render_buffer.is_valid() && p_environment.is_valid() && environment_is_ssao_enabled(p_environment)) {
+		_process_ssao(p_render_buffer, p_environment, render_buffer->normal_buffer, p_cam_projection);
+	}
+
+	if (p_render_buffer.is_valid()) {
+		//update the render buffers uniform set in case it changed
+		_update_render_buffers_uniform_set(p_render_buffer);
+		render_buffers_uniform_set = render_buffer->uniform_set;
+	}
+
 	RENDER_TIMESTAMP("Render Opaque Pass");
 
 	{
@@ -1734,7 +1869,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 		Vector<Color> c;
 		c.push_back(clear_color.to_linear());
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, will_continue ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(opaque_framebuffer), render_list.elements, render_list.element_count, false, PASS_MODE_COLOR, render_buffer == nullptr);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(opaque_framebuffer), render_list.elements, render_list.element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set);
 		RD::get_singleton()->draw_list_end();
 	}
 
@@ -1772,7 +1907,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor
 
 	{
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(alpha_framebuffer, can_continue ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(alpha_framebuffer), &render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, PASS_MODE_COLOR, render_buffer == nullptr);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(alpha_framebuffer), &render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set);
 		RD::get_singleton()->draw_list_end();
 	}
 
@@ -1867,7 +2002,7 @@ void RasterizerSceneHighEndRD::_render_shadow(RID p_framebuffer, InstanceBase **
 
 	_fill_render_list(p_cull_result, p_cull_count, pass_mode, true);
 
-	_setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), RID(), RID(), RID());
+	_setup_view_dependant_uniform_set(RID(), RID());
 
 	RENDER_TIMESTAMP("Render Shadow");
 
@@ -1878,7 +2013,7 @@ void RasterizerSceneHighEndRD::_render_shadow(RID p_framebuffer, InstanceBase **
 	{
 		//regular forward for now
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, p_use_dp_flip, pass_mode, true);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, p_use_dp_flip, pass_mode, true, RID(), RID());
 		RD::get_singleton()->draw_list_end();
 	}
 }
@@ -1901,7 +2036,7 @@ void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform
 	PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL;
 	_fill_render_list(p_cull_result, p_cull_count, pass_mode, true);
 
-	_setup_render_pass_uniform_set(RID(), RID(), RID(), RID(), RID(), RID(), RID());
+	_setup_view_dependant_uniform_set(RID(), RID());
 
 	RENDER_TIMESTAMP("Render Material");
 
@@ -1918,14 +2053,22 @@ void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform
 		clear.push_back(Color(0, 0, 0, 0));
 		clear.push_back(Color(0, 0, 0, 0));
 		RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region);
-		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true);
+		_render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), RID());
 		RD::get_singleton()->draw_list_end();
 	}
 }
 
+void RasterizerSceneHighEndRD::_base_uniforms_changed() {
+
+	if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
+		RD::get_singleton()->free(render_base_uniform_set);
+	}
+	render_base_uniform_set = RID();
+}
+
 void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 
-	if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set) || gi_probe_slots_are_dirty()) {
+	if (render_base_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
 
 		if (render_base_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) {
 			RD::get_singleton()->free(render_base_uniform_set);
@@ -2059,69 +2202,37 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() {
 			uniforms.push_back(u);
 		}
 
-		render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, 0);
+		{
+			RD::Uniform u;
+			u.binding = 12;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			if (directional_shadow_get_texture().is_valid()) {
+				u.ids.push_back(directional_shadow_get_texture());
+			} else {
+				u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE));
+			}
+			uniforms.push_back(u);
+		}
 
-		gi_probe_slots_make_not_dirty();
+		render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, SCENE_UNIFORM_SET);
 	}
 }
 
-void RasterizerSceneHighEndRD::_setup_render_pass_uniform_set(RID p_depth_buffer, RID p_color_buffer, RID p_normal_buffer, RID p_roughness_limit_buffer, RID p_radiance_cubemap, RID p_shadow_atlas, RID p_reflection_atlas) {
+void RasterizerSceneHighEndRD::_setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas) {
 
-	if (render_pass_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_pass_uniform_set)) {
-		RD::get_singleton()->free(render_pass_uniform_set);
+	if (view_dependant_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(view_dependant_uniform_set)) {
+		RD::get_singleton()->free(view_dependant_uniform_set);
 	}
 
 	//default render buffer and scene state uniform set
 
 	Vector<RD::Uniform> uniforms;
-	{
-		RD::Uniform u;
-		u.binding = 0;
-		u.type = RD::UNIFORM_TYPE_TEXTURE;
-		RID texture = p_depth_buffer.is_valid() ? p_depth_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE);
-		u.ids.push_back(texture);
-		uniforms.push_back(u);
-	}
-	{
-		RD::Uniform u;
-		u.binding = 1;
-		u.type = RD::UNIFORM_TYPE_TEXTURE;
-		RID texture = p_color_buffer.is_valid() ? p_color_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK);
-		u.ids.push_back(texture);
-		uniforms.push_back(u);
-	}
-	{
-		RD::Uniform u;
-		u.binding = 2;
-		u.type = RD::UNIFORM_TYPE_TEXTURE;
-		RID texture = p_normal_buffer.is_valid() ? p_normal_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL);
-		u.ids.push_back(texture);
-		uniforms.push_back(u);
-	}
-
-	{
-		RD::Uniform u;
-		u.binding = 3;
-		u.type = RD::UNIFORM_TYPE_TEXTURE;
-		RID texture = p_roughness_limit_buffer.is_valid() ? p_roughness_limit_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK);
-		u.ids.push_back(texture);
-		uniforms.push_back(u);
-	}
-
-	{
-		RD::Uniform u;
-		u.binding = 4;
-		u.type = RD::UNIFORM_TYPE_TEXTURE;
-		RID texture = p_radiance_cubemap.is_valid() ? p_radiance_cubemap : storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
-		u.ids.push_back(texture);
-		uniforms.push_back(u);
-	}
 
 	{
 
 		RID ref_texture = p_reflection_atlas.is_valid() ? reflection_atlas_get_texture(p_reflection_atlas) : RID();
 		RD::Uniform u;
-		u.binding = 5;
+		u.binding = 0;
 		u.type = RD::UNIFORM_TYPE_TEXTURE;
 		if (ref_texture.is_valid()) {
 			u.ids.push_back(ref_texture);
@@ -2133,7 +2244,7 @@ void RasterizerSceneHighEndRD::_setup_render_pass_uniform_set(RID p_depth_buffer
 
 	{
 		RD::Uniform u;
-		u.binding = 6;
+		u.binding = 1;
 		u.type = RD::UNIFORM_TYPE_TEXTURE;
 		RID texture;
 		if (p_shadow_atlas.is_valid()) {
@@ -2146,19 +2257,78 @@ void RasterizerSceneHighEndRD::_setup_render_pass_uniform_set(RID p_depth_buffer
 		uniforms.push_back(u);
 	}
 
-	{
-		RD::Uniform u;
-		u.binding = 7;
-		u.type = RD::UNIFORM_TYPE_TEXTURE;
-		if (directional_shadow_get_texture().is_valid()) {
-			u.ids.push_back(directional_shadow_get_texture());
-		} else {
-			u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE));
-		}
-		uniforms.push_back(u);
+	view_dependant_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, VIEW_DEPENDANT_UNIFORM_SET);
+}
+
+void RasterizerSceneHighEndRD::_render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb) {
+
+	if (!rb->uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(rb->uniform_set)) {
+		RD::get_singleton()->free(rb->uniform_set);
 	}
+	rb->uniform_set = RID();
+}
+
+void RasterizerSceneHighEndRD::_render_buffers_uniform_set_changed(RID p_render_buffers) {
+
+	RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
 
-	render_pass_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, 1);
+	_render_buffers_clear_uniform_set(rb);
+}
+
+void RasterizerSceneHighEndRD::_update_render_buffers_uniform_set(RID p_render_buffers) {
+
+	RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers);
+
+	if (rb->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->uniform_set)) {
+
+		Vector<RD::Uniform> uniforms;
+		{
+			RD::Uniform u;
+			u.binding = 0;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			RID texture = false && rb->depth.is_valid() ? rb->depth : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE);
+			u.ids.push_back(texture);
+			uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.binding = 1;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			RID bbt = render_buffers_get_back_buffer_texture(p_render_buffers);
+			RID texture = bbt.is_valid() ? bbt : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+			u.ids.push_back(texture);
+			uniforms.push_back(u);
+		}
+		{
+			RD::Uniform u;
+			u.binding = 2;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			RID texture = rb->normal_buffer.is_valid() ? rb->normal_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL);
+			u.ids.push_back(texture);
+			uniforms.push_back(u);
+		}
+
+		{
+			RD::Uniform u;
+			u.binding = 3;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			RID texture = rb->roughness_buffer.is_valid() ? rb->roughness_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+			u.ids.push_back(texture);
+			uniforms.push_back(u);
+		}
+
+		{
+			RD::Uniform u;
+			u.binding = 4;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			RID aot = render_buffers_get_ao_texture(p_render_buffers);
+			RID texture = aot.is_valid() ? aot : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK);
+			u.ids.push_back(texture);
+			uniforms.push_back(u);
+		}
+
+		rb->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET);
+	}
 }
 
 RasterizerSceneHighEndRD *RasterizerSceneHighEndRD::singleton = NULL;
@@ -2246,8 +2416,8 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag
 		Vector<String> shader_versions;
 		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n");
 		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n");
-		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define ENABLE_WRITE_NORMAL_BUFFER\n");
-		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define ENABLE_WRITE_NORMAL_ROUGHNESS_BUFFER\n");
+		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL\n");
+		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL\n#define MODE_RENDER_ROUGHNESS\n");
 		shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n");
 		shader_versions.push_back("");
 		shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n");
@@ -2388,7 +2558,7 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag
 
 		actions.sampler_array_name = "material_samplers";
 		actions.base_texture_binding_index = 1;
-		actions.texture_layout_set = 3;
+		actions.texture_layout_set = MATERIAL_UNIFORM_SET;
 		actions.base_uniform_string = "material.";
 		actions.base_varying_index = 10;
 
@@ -2445,7 +2615,7 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag
 		u.binding = 0;
 		uniforms.push_back(u);
 
-		default_vec4_xform_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, 2);
+		default_vec4_xform_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, TRANSFORMS_UNIFORM_SET);
 	}
 	{
 
@@ -2457,13 +2627,40 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag
 		shadow_sampler = RD::get_singleton()->sampler_create(sampler);
 	}
 
+	{
+		Vector<RD::Uniform> uniforms;
+
+		RD::Uniform u;
+		u.binding = 0;
+		u.type = RD::UNIFORM_TYPE_TEXTURE;
+		RID texture = storage->texture_rd_get_default(is_using_radiance_cubemap_array() ? RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK : RasterizerStorageRD::DEFAULT_RD_TEXTURE_CUBEMAP_BLACK);
+		u.ids.push_back(texture);
+		uniforms.push_back(u);
+
+		default_radiance_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RADIANCE_UNIFORM_SET);
+	}
+
+	{ //render buffers
+		Vector<RD::Uniform> uniforms;
+		for (int i = 0; i < 5; i++) {
+			RD::Uniform u;
+			u.binding = i;
+			u.type = RD::UNIFORM_TYPE_TEXTURE;
+			RID texture = storage->texture_rd_get_default(i == 0 ? RasterizerStorageRD::DEFAULT_RD_TEXTURE_WHITE : (i == 2 ? RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL : RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK));
+			u.ids.push_back(texture);
+			uniforms.push_back(u);
+		}
+
+		default_render_buffers_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET);
+	}
+
 	cluster_builder.setup(16, 8, 24);
 }
 
 RasterizerSceneHighEndRD::~RasterizerSceneHighEndRD() {
 	//clear base uniform set if still valid
-	if (render_pass_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(render_pass_uniform_set)) {
-		RD::get_singleton()->free(render_pass_uniform_set);
+	if (view_dependant_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(view_dependant_uniform_set)) {
+		RD::get_singleton()->free(view_dependant_uniform_set);
 	}
 
 	{

+ 40 - 6
servers/visual/rasterizer_rd/rasterizer_scene_high_end_rd.h

@@ -39,6 +39,15 @@
 
 class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 
+	enum {
+		SCENE_UNIFORM_SET = 0,
+		RADIANCE_UNIFORM_SET = 1,
+		VIEW_DEPENDANT_UNIFORM_SET = 2,
+		RENDER_BUFFERS_UNIFORM_SET = 3,
+		TRANSFORMS_UNIFORM_SET = 4,
+		MATERIAL_UNIFORM_SET = 5
+	};
+
 	/* Shader */
 
 	enum ShaderVersion {
@@ -186,28 +195,44 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 
 	/* Framebuffer */
 
-	struct RenderBufferDataForward : public RenderBufferData {
+	struct RenderBufferDataHighEnd : public RenderBufferData {
 		//for rendering, may be MSAAd
 		RID color;
 		RID depth;
+		RID specular;
+		RID normal_buffer;
+		RID roughness_buffer;
 		RID depth_fb;
+		RID depth_normal_fb;
+		RID depth_normal_roughness_fb;
 		RID color_fb;
-		RID color_only_fb;
+		RID color_specular_fb;
 		int width, height;
 
+		void ensure_specular();
 		void clear();
 		virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, VS::ViewportMSAA p_msaa);
 
-		~RenderBufferDataForward();
+		RID uniform_set;
+
+		~RenderBufferDataHighEnd();
 	};
 
 	virtual RenderBufferData *_create_render_buffer_data();
+	void _allocate_normal_texture(RenderBufferDataHighEnd *rb);
+	void _allocate_roughness_texture(RenderBufferDataHighEnd *rb);
 
 	RID shadow_sampler;
 	RID render_base_uniform_set;
-	RID render_pass_uniform_set;
+	RID view_dependant_uniform_set;
+
+	virtual void _base_uniforms_changed();
+	void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb);
+	virtual void _render_buffers_uniform_set_changed(RID p_render_buffers);
+
 	void _update_render_base_uniform_set();
-	void _setup_render_pass_uniform_set(RID p_depth_buffer, RID p_color_buffer, RID p_normal_buffer, RID p_roughness_limit_buffer, RID p_radiance_cubemap, RID p_shadow_atlas, RID p_reflection_atlas);
+	void _setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas);
+	void _update_render_buffers_uniform_set(RID p_render_buffers);
 
 	/* Scene State UBO */
 
@@ -318,6 +343,13 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 			float dual_paraboloid_side;
 			float z_far;
 			float z_near;
+
+			uint32_t ssao_enabled;
+			float ssao_light_affect;
+			float ssao_ao_affect;
+			uint32_t pad_ssao;
+
+			float ao_color[4];
 		};
 
 		UBO ubo;
@@ -503,6 +535,8 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	RID wireframe_material_shader;
 	RID wireframe_material;
 	RID default_shader_rd;
+	RID default_radiance_uniform_set;
+	RID default_render_buffers_uniform_set;
 
 	RID default_vec4_xform_buffer;
 	RID default_vec4_xform_uniform_set;
@@ -527,7 +561,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform);
 
 	void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth);
-	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi);
+	void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderList::Element **p_elements, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, bool p_no_gi, RID p_radiance_uniform_set, RID p_render_buffers_uniform_set);
 	_FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index);
 	_FORCE_INLINE_ void _add_geometry_with_material(InstanceBase *p_instance, uint32_t p_surface, MaterialData *p_material, RID p_material_rid, PassMode p_pass_mode, uint32_t p_geometry_index);
 

+ 189 - 14
servers/visual/rasterizer_rd/rasterizer_scene_rd.cpp

@@ -347,6 +347,30 @@ RID RasterizerSceneRD::sky_get_radiance_texture_rd(RID p_sky) const {
 	return sky->radiance;
 }
 
+RID RasterizerSceneRD::sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const {
+	Sky *sky = sky_owner.getornull(p_sky);
+	ERR_FAIL_COND_V(!sky, RID());
+
+	if (sky->uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(sky->uniform_set)) {
+
+		sky->uniform_set = RID();
+		if (sky->radiance.is_valid()) {
+			Vector<RD::Uniform> uniforms;
+			{
+				RD::Uniform u;
+				u.type = RD::UNIFORM_TYPE_TEXTURE;
+				u.binding = 0;
+				u.ids.push_back(sky->radiance);
+				uniforms.push_back(u);
+			}
+
+			sky->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, p_shader, p_set);
+		}
+	}
+
+	return sky->uniform_set;
+}
+
 RID RasterizerSceneRD::environment_create() {
 
 	return environment_owner.make_rid(Environent());
@@ -387,7 +411,7 @@ void RasterizerSceneRD::environment_set_canvas_max_layer(RID p_env, int p_max_la
 	ERR_FAIL_COND(!env);
 	env->canvas_max_layer = p_max_layer;
 }
-void RasterizerSceneRD::environment_set_ambient_light(RID p_env, const Color &p_color, VS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, VS::EnvironmentReflectionSource p_reflection_source) {
+void RasterizerSceneRD::environment_set_ambient_light(RID p_env, const Color &p_color, VS::EnvironmentAmbientSource p_ambient, float p_energy, float p_sky_contribution, VS::EnvironmentReflectionSource p_reflection_source, const Color &p_ao_color) {
 	Environent *env = environment_owner.getornull(p_env);
 	ERR_FAIL_COND(!env);
 	env->ambient_light = p_color;
@@ -395,6 +419,7 @@ void RasterizerSceneRD::environment_set_ambient_light(RID p_env, const Color &p_
 	env->ambient_light_energy = p_energy;
 	env->ambient_sky_contribution = p_sky_contribution;
 	env->reflection_source = p_reflection_source;
+	env->ao_color = p_ao_color;
 }
 
 VS::EnvironmentBG RasterizerSceneRD::environment_get_background(RID p_env) const {
@@ -458,6 +483,12 @@ VS::EnvironmentReflectionSource RasterizerSceneRD::environment_get_reflection_so
 	return env->reflection_source;
 }
 
+Color RasterizerSceneRD::environment_get_ao_color(RID p_env) const {
+	Environent *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, Color());
+	return env->ao_color;
+}
+
 void RasterizerSceneRD::environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale) {
 	Environent *env = environment_owner.getornull(p_env);
 	ERR_FAIL_COND(!env);
@@ -491,6 +522,50 @@ void RasterizerSceneRD::environment_set_glow(RID p_env, bool p_enable, int p_lev
 	env->glow_bicubic_upscale = p_bicubic_upscale;
 }
 
+void RasterizerSceneRD::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, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {
+
+	Environent *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND(!env);
+
+	env->ssao_enabled = p_enable;
+	env->ssao_radius = p_radius;
+	env->ssao_intensity = p_intensity;
+	env->ssao_bias = p_bias;
+	env->ssao_direct_light_affect = p_light_affect;
+	env->ssao_ao_channel_affect = p_ao_channel_affect;
+	env->ssao_blur = p_blur;
+}
+
+void RasterizerSceneRD::environment_set_ssao_quality(VS::EnvironmentSSAOQuality p_quality, bool p_half_size) {
+	ssao_quality = p_quality;
+	ssao_half_size = p_half_size;
+}
+
+bool RasterizerSceneRD::environment_is_ssao_enabled(RID p_env) const {
+
+	Environent *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, false);
+	return env->ssao_enabled;
+}
+
+float RasterizerSceneRD::environment_get_ssao_ao_affect(RID p_env) const {
+	Environent *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, false);
+	return env->ssao_ao_channel_affect;
+}
+float RasterizerSceneRD::environment_get_ssao_light_affect(RID p_env) const {
+	Environent *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, false);
+	return env->ssao_direct_light_affect;
+}
+
+bool RasterizerSceneRD::environment_is_ssr_enabled(RID p_env) const {
+
+	Environent *env = environment_owner.getornull(p_env);
+	ERR_FAIL_COND_V(!env, false);
+	return false;
+}
+
 bool RasterizerSceneRD::is_environment(RID p_env) const {
 	return environment_owner.owns(p_env);
 }
@@ -1065,6 +1140,8 @@ void RasterizerSceneRD::directional_shadow_atlas_set_size(int p_size) {
 		fb.push_back(directional_shadow.depth);
 		directional_shadow.fb = RD::get_singleton()->framebuffer_create(fb);
 	}
+
+	_base_uniforms_changed();
 }
 
 void RasterizerSceneRD::set_directional_shadow_count(int p_count) {
@@ -1725,8 +1802,9 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc
 		}
 
 		gi_probe->last_probe_data_version = data_version;
-		gi_probe_slots_dirty = true;
 		p_update_light_instances = true; //just in case
+
+		_base_uniforms_changed();
 	}
 
 	// UDPDATE TIME
@@ -2183,14 +2261,6 @@ const Vector<RID> &RasterizerSceneRD::gi_probe_get_slots() const {
 	return gi_probe_slots;
 }
 
-bool RasterizerSceneRD::gi_probe_slots_are_dirty() const {
-	return gi_probe_slots_dirty;
-}
-
-void RasterizerSceneRD::gi_probe_slots_make_not_dirty() {
-	gi_probe_slots_dirty = false;
-}
-
 RasterizerSceneRD::GIProbeQuality RasterizerSceneRD::gi_probe_get_quality() const {
 	return gi_probe_quality;
 }
@@ -2323,6 +2393,91 @@ void RasterizerSceneRD::_free_render_buffer_data(RenderBuffers *rb) {
 		RD::get_singleton()->free(rb->luminance.current);
 		rb->luminance.current = RID();
 	}
+
+	if (rb->ssao.ao[0].is_valid()) {
+		RD::get_singleton()->free(rb->ssao.depth);
+		RD::get_singleton()->free(rb->ssao.ao[0]);
+		if (rb->ssao.ao[1].is_valid()) {
+			RD::get_singleton()->free(rb->ssao.ao[1]);
+		}
+		if (rb->ssao.ao_full.is_valid()) {
+			RD::get_singleton()->free(rb->ssao.ao_full);
+		}
+
+		rb->ssao.depth = RID();
+		rb->ssao.ao[0] = RID();
+		rb->ssao.ao[1] = RID();
+		rb->ssao.ao_full = RID();
+		rb->ssao.depth_slices.clear();
+	}
+}
+
+void RasterizerSceneRD::_process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection) {
+
+	RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
+	ERR_FAIL_COND(!rb);
+
+	Environent *env = environment_owner.getornull(p_environment);
+	ERR_FAIL_COND(!env);
+
+	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]);
+		if (rb->ssao.ao[1].is_valid()) {
+			RD::get_singleton()->free(rb->ssao.ao[1]);
+		}
+		if (rb->ssao.ao_full.is_valid()) {
+			RD::get_singleton()->free(rb->ssao.ao_full);
+		}
+
+		rb->ssao.depth = RID();
+		rb->ssao.ao[0] = RID();
+		rb->ssao.ao[1] = RID();
+		rb->ssao.ao_full = RID();
+		rb->ssao.depth_slices.clear();
+	}
+
+	if (!rb->ssao.ao[0].is_valid()) {
+		//allocate depth slices
+
+		{
+			RD::TextureFormat tf;
+			tf.format = RD::DATA_FORMAT_R32_SFLOAT;
+			tf.width = rb->width / 2;
+			tf.height = rb->height / 2;
+			tf.mipmaps = Image::get_image_required_mipmaps(tf.width, tf.height, Image::FORMAT_RF) + 1;
+			tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+			rb->ssao.depth = RD::get_singleton()->texture_create(tf, RD::TextureView());
+			for (uint32_t i = 0; i < tf.mipmaps; i++) {
+				RID slice = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), rb->ssao.depth, 0, i);
+				rb->ssao.depth_slices.push_back(slice);
+			}
+		}
+
+		{
+			RD::TextureFormat tf;
+			tf.format = RD::DATA_FORMAT_R8_UNORM;
+			tf.width = ssao_half_size ? rb->width / 2 : rb->width;
+			tf.height = ssao_half_size ? rb->height / 2 : rb->height;
+			tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+			rb->ssao.ao[0] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+			rb->ssao.ao[1] = RD::get_singleton()->texture_create(tf, RD::TextureView());
+		}
+
+		if (ssao_half_size) {
+			//upsample texture
+			RD::TextureFormat tf;
+			tf.format = RD::DATA_FORMAT_R8_UNORM;
+			tf.width = rb->width;
+			tf.height = rb->height;
+			tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT;
+			rb->ssao.ao_full = RD::get_singleton()->texture_create(tf, RD::TextureView());
+		}
+
+		_render_buffers_uniform_set_changed(p_render_buffers);
+	}
+
+	storage->get_effects()->generate_ssao(rb->depth_texture, p_normal_buffer, Size2i(rb->width, rb->height), rb->ssao.depth, rb->ssao.depth_slices, rb->ssao.ao[0], rb->ssao.ao_full.is_valid(), rb->ssao.ao[1], rb->ssao.ao_full, env->ssao_intensity, env->ssao_radius, env->ssao_bias, p_projection, ssao_quality, env->ssao_blur, env->ssao_blur_edge_sharpness);
 }
 
 void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_buffers, RID p_environment, RID p_camera_effects, const CameraMatrix &p_projection) {
@@ -2340,6 +2495,7 @@ void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_bu
 
 		if (rb->blur[0].texture.is_null()) {
 			_allocate_blur_textures(rb);
+			_render_buffers_uniform_set_changed(p_render_buffers);
 		}
 
 		float bokeh_size = camfx->dof_blur_amount * 64.0;
@@ -2350,6 +2506,7 @@ void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_bu
 
 		if (rb->luminance.current.is_null()) {
 			_allocate_luminance_textures(rb);
+			_render_buffers_uniform_set_changed(p_render_buffers);
 		}
 
 		bool set_immediate = env->auto_exposure_version != rb->auto_exposure_version;
@@ -2372,6 +2529,7 @@ void RasterizerSceneRD::_render_buffers_post_process_and_tonemap(RID p_render_bu
 
 		if (rb->blur[0].texture.is_null()) {
 			_allocate_blur_textures(rb);
+			_render_buffers_uniform_set_changed(p_render_buffers);
 		}
 
 		for (int i = 0; i < VS::MAX_GLOW_LEVELS; i++) {
@@ -2456,7 +2614,7 @@ void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_s
 			RID shadow_atlas_texture = shadow_atlas_get_texture(p_shadow_atlas);
 			Size2 rtsize = storage->render_target_get_size(rb->render_target);
 
-			effects->copy_to_rect(shadow_atlas_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 2));
+			effects->copy_to_rect(shadow_atlas_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 2), false, true);
 		}
 	}
 
@@ -2465,7 +2623,7 @@ void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_s
 			RID shadow_atlas_texture = directional_shadow_get_texture();
 			Size2 rtsize = storage->render_target_get_size(rb->render_target);
 
-			effects->copy_to_rect(shadow_atlas_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 2));
+			effects->copy_to_rect(shadow_atlas_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 2), false, true);
 		}
 	}
 
@@ -2473,19 +2631,34 @@ void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_s
 		if (rb->luminance.current.is_valid()) {
 			Size2 rtsize = storage->render_target_get_size(rb->render_target);
 
-			effects->copy_to_rect(rb->luminance.current, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 8));
+			effects->copy_to_rect(rb->luminance.current, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize / 8), false, true);
 		}
 	}
+
+	if (debug_draw == VS::VIEWPORT_DEBUG_DRAW_SSAO && rb->ssao.ao[0].is_valid()) {
+		Size2 rtsize = storage->render_target_get_size(rb->render_target);
+		RID ao_buf = rb->ssao.ao_full.is_valid() ? rb->ssao.ao_full : rb->ssao.ao[0];
+		effects->copy_to_rect(ao_buf, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true);
+	}
 }
 
 RID RasterizerSceneRD::render_buffers_get_back_buffer_texture(RID p_render_buffers) {
 
 	RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
 	ERR_FAIL_COND_V(!rb, RID());
-	ERR_FAIL_COND_V(!rb->blur[0].texture.is_valid(), RID()); //should have been created for some reason?
+	if (!rb->blur[0].texture.is_valid()) {
+		return RID(); //not valid at the moment
+	}
 	return rb->blur[0].texture;
 }
 
+RID RasterizerSceneRD::render_buffers_get_ao_texture(RID p_render_buffers) {
+	RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
+	ERR_FAIL_COND_V(!rb, RID());
+
+	return rb->ssao.ao_full.is_valid() ? rb->ssao.ao_full : rb->ssao.ao[0];
+}
+
 void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, VS::ViewportMSAA p_msaa) {
 
 	RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers);
@@ -2516,6 +2689,7 @@ void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_ren
 	}
 
 	rb->data->configure(rb->texture, rb->depth_texture, p_width, p_height, p_msaa);
+	_render_buffers_uniform_set_changed(p_render_buffers);
 }
 
 int RasterizerSceneRD::get_roughness_layers() const {
@@ -2921,6 +3095,7 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) {
 
 	camera_effects_set_dof_blur_bokeh_shape(VS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/filters/depth_of_field_bokeh_shape"))));
 	camera_effects_set_dof_blur_quality(VS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/filters/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/quality/filters/depth_of_field_use_jitter"));
+	environment_set_ssao_quality(VS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size"));
 }
 
 RasterizerSceneRD::~RasterizerSceneRD() {

+ 38 - 5
servers/visual/rasterizer_rd/rasterizer_scene_rd.h

@@ -62,6 +62,11 @@ protected:
 
 	RenderBufferData *render_buffers_get_data(RID p_render_buffers);
 
+	virtual void _base_uniforms_changed() = 0;
+	virtual void _render_buffers_uniform_set_changed(RID p_render_buffers) = 0;
+
+	void _process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection);
+
 private:
 	VS::ViewportDebugDraw debug_draw = VS::VIEWPORT_DEBUG_DRAW_DISABLED;
 	double time_step = 0;
@@ -94,6 +99,7 @@ private:
 	/* SKY */
 	struct Sky {
 		RID radiance;
+		RID uniform_set;
 		int radiance_size = 256;
 		VS::SkyMode mode = VS::SKY_MODE_QUALITY;
 		RID panorama;
@@ -266,7 +272,6 @@ private:
 
 	bool gi_probe_use_anisotropy = false;
 	GIProbeQuality gi_probe_quality = GIPROBE_QUALITY_MEDIUM;
-	bool gi_probe_slots_dirty = true;
 
 	Vector<RID> gi_probe_slots;
 
@@ -455,6 +460,7 @@ private:
 		float ambient_light_energy = 1.0;
 		float ambient_sky_contribution = 1.0;
 		VS::EnvironmentReflectionSource reflection_source = VS::ENV_REFLECTION_SOURCE_BG;
+		Color ao_color;
 
 		/// Tonemap
 
@@ -481,8 +487,22 @@ private:
 		float glow_hdr_luminance_cap = 12.0;
 		float glow_hdr_bleed_scale = 2.0;
 		bool glow_bicubic_upscale = false;
+
+		/// SSAO
+
+		bool ssao_enabled = false;
+		float ssao_radius = 1;
+		float ssao_intensity = 1;
+		float ssao_bias = 0.01;
+		float ssao_direct_light_affect = 0.0;
+		float ssao_ao_channel_affect = 0.0;
+		float ssao_blur_edge_sharpness = 4.0;
+		VS::EnvironmentSSAOBlur ssao_blur = VS::ENV_SSAO_BLUR_3x3;
 	};
 
+	VS::EnvironmentSSAOQuality ssao_quality = VS::ENV_SSAO_QUALITY_MEDIUM;
+	bool ssao_half_size = false;
+
 	static uint64_t auto_exposure_counter;
 
 	mutable RID_Owner<Environent> environment_owner;
@@ -546,6 +566,13 @@ private:
 			Vector<RID> reduce;
 			RID current;
 		} luminance;
+
+		struct SSAO {
+			RID depth;
+			Vector<RID> depth_slices;
+			RID ao[2];
+			RID ao_full; //when using half-size
+		} ssao;
 	};
 
 	mutable RID_Owner<RenderBuffers> render_buffers_owner;
@@ -606,6 +633,7 @@ public:
 
 	RID sky_get_panorama_texture_rd(RID p_sky) const;
 	RID sky_get_radiance_texture_rd(RID p_sky) const;
+	RID sky_get_radiance_uniform_set_rd(RID p_sky, RID p_shader, int p_set) const;
 
 	/* ENVIRONMENT API */
 
@@ -618,7 +646,7 @@ public:
 	void environment_set_bg_color(RID p_env, const Color &p_color);
 	void environment_set_bg_energy(RID p_env, float p_energy);
 	void environment_set_canvas_max_layer(RID p_env, int p_max_layer);
-	void environment_set_ambient_light(RID p_env, const Color &p_color, VS::EnvironmentAmbientSource p_ambient = VS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, VS::EnvironmentReflectionSource p_reflection_source = VS::ENV_REFLECTION_SOURCE_BG);
+	void environment_set_ambient_light(RID p_env, const Color &p_color, VS::EnvironmentAmbientSource p_ambient = VS::ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, VS::EnvironmentReflectionSource p_reflection_source = VS::ENV_REFLECTION_SOURCE_BG, const Color &p_ao_color = Color());
 
 	VS::EnvironmentBG environment_get_background(RID p_env) const;
 	RID environment_get_sky(RID p_env) const;
@@ -632,6 +660,7 @@ public:
 	float environment_get_ambient_light_ambient_energy(RID p_env) const;
 	float environment_get_ambient_sky_contribution(RID p_env) const;
 	VS::EnvironmentReflectionSource environment_get_reflection_source(RID p_env) const;
+	Color environment_get_ao_color(RID p_env) const;
 
 	bool is_environment(RID p_env) const;
 
@@ -640,7 +669,12 @@ public:
 	void environment_set_fog(RID p_env, bool p_enable, float p_begin, float p_end, RID p_gradient_texture) {}
 
 	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, bool p_roughness) {}
-	void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, VS::EnvironmentSSAOQuality p_quality, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) {}
+	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, VS::EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness);
+	void environment_set_ssao_quality(VS::EnvironmentSSAOQuality p_quality, bool p_half_size);
+	bool environment_is_ssao_enabled(RID p_env) const;
+	float environment_get_ssao_ao_affect(RID p_env) const;
+	float environment_get_ssao_light_affect(RID p_env) const;
+	bool environment_is_ssr_enabled(RID p_env) const;
 
 	void environment_set_tonemap(RID p_env, VS::EnvironmentToneMapper p_tone_mapper, float p_exposure, float p_white, bool p_auto_exposure, float p_min_luminance, float p_max_luminance, float p_auto_exp_speed, float p_auto_exp_scale);
 	void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) {}
@@ -869,8 +903,6 @@ public:
 	}
 
 	const Vector<RID> &gi_probe_get_slots() const;
-	bool gi_probe_slots_are_dirty() const;
-	void gi_probe_slots_make_not_dirty();
 	_FORCE_INLINE_ bool gi_probe_is_anisotropic() const {
 		return gi_probe_use_anisotropy;
 	}
@@ -879,6 +911,7 @@ public:
 	RID render_buffers_create();
 	void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, VS::ViewportMSAA p_msaa);
 
+	RID render_buffers_get_ao_texture(RID p_render_buffers);
 	RID render_buffers_get_back_buffer_texture(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_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);

+ 1 - 1
servers/visual/rasterizer_rd/rasterizer_storage_rd.cpp

@@ -2052,7 +2052,7 @@ AABB RasterizerStorageRD::mesh_get_aabb(RID p_mesh, RID p_skeleton) {
 
 	AABB aabb;
 
-	for (int i = 0; i < mesh->surface_count; i++) {
+	for (uint32_t i = 0; i < mesh->surface_count; i++) {
 
 		AABB laabb;
 		if ((mesh->surfaces[i]->format & VS::ARRAY_FORMAT_BONES) && mesh->surfaces[i]->bone_aabbs.size()) {

+ 3 - 0
servers/visual/rasterizer_rd/shaders/SCsub

@@ -16,3 +16,6 @@ if 'RD_GLSL' in env['BUILDERS']:
     env.RD_GLSL('giprobe_sdf.glsl');
     env.RD_GLSL('luminance_reduce.glsl');
     env.RD_GLSL('bokeh_dof.glsl');
+    env.RD_GLSL('ssao.glsl');
+    env.RD_GLSL('ssao_minify.glsl');
+    env.RD_GLSL('ssao_blur.glsl');

+ 4 - 0
servers/visual/rasterizer_rd/shaders/blur.glsl

@@ -279,6 +279,9 @@ void main() {
 
 #ifdef MODE_SIMPLE_COPY
 	vec4 color = texture(source_color, uv_interp, 0.0);
+	if (bool(blur.flags & FLAG_COPY_FORCE_LUMINANCE)) {
+		color.rgb = vec3(max(max(color.r,color.g),color.b));
+	}
 	frag_color = color;
 #endif
 
@@ -286,5 +289,6 @@ void main() {
 	vec4 color = texture(source_color, uv_interp, 0.0);
 	float ssao = texture(source_ssao, uv_interp, 0.0).r;
 	frag_color = vec4(mix(color.rgb, color.rgb * mix(blur.ssao_color.rgb, vec3(1.0), ssao), color.a), 1.0);
+
 #endif
 }

+ 1 - 0
servers/visual/rasterizer_rd/shaders/blur_inc.glsl

@@ -4,6 +4,7 @@
 #define FLAG_DOF_NEAR_FIRST_TAP (1 << 3)
 #define FLAG_GLOW_FIRST_PASS (1 << 4)
 #define FLAG_FLIP_Y (1 << 5)
+#define FLAG_COPY_FORCE_LUMINANCE (1 << 6)
 
 layout(push_constant, binding = 1, std430) uniform Blur {
 	vec4 section;

+ 40 - 8
servers/visual/rasterizer_rd/shaders/scene_high_end.glsl

@@ -53,7 +53,7 @@ layout(location = 6) out vec3 binormal_interp;
 #endif
 
 #ifdef USE_MATERIAL_UNIFORMS
-layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
+layout(set = 5, binding = 0, std140) uniform MaterialUniforms{
 	/* clang-format off */
 MATERIAL_UNIFORMS
 	/* clang-format on */
@@ -316,7 +316,7 @@ layout(location = 8) in float dp_clip;
 #define projection_matrix scene_data.projection_matrix
 
 #ifdef USE_MATERIAL_UNIFORMS
-layout(set = 3, binding = 0, std140) uniform MaterialUniforms{
+layout(set = 5, binding = 0, std140) uniform MaterialUniforms{
 	/* clang-format off */
 MATERIAL_UNIFORMS
 	/* clang-format on */
@@ -341,6 +341,12 @@ layout(location = 4) out float depth_output_buffer;
 
 #endif
 
+#ifdef MODE_RENDER_NORMAL
+layout(location = 0) out vec4 normal_output_buffer;
+#ifdef MODE_RENDER_ROUGHNESS
+layout(location = 1) out float roughness_output_buffer;
+#endif //MODE_RENDER_ROUGHNESS
+#endif //MODE_RENDER_NORMAL
 #else // RENDER DEPTH
 
 #ifdef MODE_MULTIPLE_RENDER_TARGETS
@@ -1145,7 +1151,7 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
 
 		ao = 1.0 - min(1.0, ao);
 
-		light *= mix(1.0, ao, gi_probes.data[index].ambient_occlusion);
+		light = mix(scene_data.ao_color.rgb,light,mix(1.0, ao, gi_probes.data[index].ambient_occlusion));
 	}
 
 	out_diff += vec4(light * blend, blend);
@@ -1236,7 +1242,7 @@ void main() {
 
 	float normaldepth = 1.0;
 
-	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size;
+	vec2 screen_uv = gl_FragCoord.xy * scene_data.screen_pixel_size + scene_data.screen_pixel_size * 0.5; //account for center
 
 	float sss_strength = 0.0;
 
@@ -1633,18 +1639,44 @@ FRAGMENT_SHADER_CODE
 	emission_output_buffer.a = 0.0;
 #endif
 
+#ifdef MODE_RENDER_NORMAL
+	normal_output_buffer = vec4(normal * 0.5 + 0.5,0.0);
+#ifdef MODE_RENDER_ROUGHNESS
+	roughness_output_buffer = roughness;
+#endif //MODE_RENDER_ROUGHNESS
+#endif //MODE_RENDER_NORMAL
+
 //nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
 #else
 
 	specular_light *= scene_data.reflection_multiplier;
 	ambient_light *= albedo; //ambient must be multiplied by albedo at the end
 
+//ambient occlusion
 #if defined(AO_USED)
-	ambient_light *= ao;
+
+	if (scene_data.ssao_enabled && scene_data.ssao_ao_affect > 0.0) {
+		float ssao = texture(sampler2D(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]),screen_uv).r;
+		ao = mix(ao,min(ao,ssao),scene_data.ssao_ao_affect);
+		ao_light_affect = mix(ao_light_affect,max(ao_light_affect,scene_data.ssao_light_affect),scene_data.ssao_ao_affect);
+	}
+
+	ambient_light = mix(scene_data.ao_color.rgb,ambient_light,ao);
 	ao_light_affect = mix(1.0, ao, ao_light_affect);
-	specular_light *= ao_light_affect;
-	diffuse_light *= ao_light_affect;
-#endif
+	specular_light = mix(scene_data.ao_color.rgb,specular_light,ao_light_affect);
+	diffuse_light = mix(scene_data.ao_color.rgb,diffuse_light,ao_light_affect);
+
+#else
+
+	if (scene_data.ssao_enabled) {
+		float ao = texture(sampler2D(ao_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]),screen_uv).r;
+		ambient_light = mix(scene_data.ao_color.rgb,ambient_light,ao);
+		float ao_light_affect = mix(1.0, ao,scene_data.ssao_light_affect);
+		specular_light = mix(scene_data.ao_color.rgb,specular_light,ao_light_affect);
+		diffuse_light = mix(scene_data.ao_color.rgb,diffuse_light,ao_light_affect);
+	}
+
+#endif // AO_USED
 
 	// base color remapping
 	diffuse_light *= 1.0 - metallic; // TODO: avoid all diffuse and ambient light calculations when metallic == 1 up to this point

+ 28 - 13
servers/visual/rasterizer_rd/shaders/scene_high_end_inc.glsl

@@ -60,6 +60,13 @@ layout(set = 0, binding = 3, std140) uniform SceneData {
 	float z_far;
 	float z_near;
 
+	bool ssao_enabled;
+	float ssao_light_affect;
+	float ssao_ao_affect;
+	uint pad_ssao;
+
+	vec4 ao_color;
+
 #if 0
 	vec4 ambient_light_color;
 	vec4 bg_color;
@@ -211,33 +218,41 @@ layout(set = 0, binding = 11, std430) buffer ClusterData {
 	uint indices[];
 } cluster_data;
 
-/* Set 1, Scene data that changes per render pass */
+layout(set = 0, binding = 12) uniform texture2D directional_shadow_atlas;
+
+// decal atlas
 
-layout(set = 1, binding = 0) uniform texture2D depth_buffer;
-layout(set = 1, binding = 1) uniform texture2D color_buffer;
-layout(set = 1, binding = 2) uniform texture2D normal_buffer;
-layout(set = 1, binding = 3) uniform texture2D roughness_limit;
+/* Set 1, Radiance */
 
 #ifdef USE_RADIANCE_CUBEMAP_ARRAY
 
-layout(set = 1, binding = 4) uniform textureCubeArray radiance_cubemap;
+layout(set = 1, binding = 0) uniform textureCubeArray radiance_cubemap;
 
 #else
 
-layout(set = 1, binding = 4) uniform textureCube radiance_cubemap;
+layout(set = 1, binding = 0) uniform textureCube radiance_cubemap;
 
 #endif
 
-layout(set = 1, binding = 5) uniform textureCubeArray reflection_atlas;
 
-layout(set = 1, binding = 6) uniform texture2D shadow_atlas;
+/* Set 2, Reflection and Shadow Atlases (view dependant) */
+
+layout(set = 2, binding = 0) uniform textureCubeArray reflection_atlas;
+
+layout(set = 2, binding = 1) uniform texture2D shadow_atlas;
+
+/* Set 1, Render Buffers */
 
-layout(set = 1, binding = 7) uniform texture2D directional_shadow_atlas;
+layout(set = 3, binding = 0) uniform texture2D depth_buffer;
+layout(set = 3, binding = 1) uniform texture2D color_buffer;
+layout(set = 3, binding = 2) uniform texture2D normal_buffer;
+layout(set = 3, binding = 3) uniform texture2D roughness_buffer;
+layout(set = 3, binding = 4) uniform texture2D ao_buffer;
 
-/* Set 2 Skeleton & Instancing (Multimesh) */
+/* Set 4 Skeleton & Instancing (Multimesh) */
 
-layout(set = 2, binding = 0, std430) buffer Transforms {
+layout(set = 4, binding = 0, std430) buffer Transforms {
 	vec4 data[];
 } transforms;
 
-/* Set 3 User Material */
+/* Set 5 User Material */

+ 257 - 0
servers/visual/rasterizer_rd/shaders/ssao.glsl

@@ -0,0 +1,257 @@
+/* clang-format off */
+[compute]
+/* clang-format on */
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#define TWO_PI 6.283185307179586476925286766559
+
+#ifdef SSAO_QUALITY_HIGH
+#define NUM_SAMPLES (20)
+#endif
+
+#ifdef SSAO_QUALITY_ULTRA
+#define NUM_SAMPLES (48)
+#endif
+
+#ifdef SSAO_QUALITY_LOW
+#define NUM_SAMPLES (8)
+#endif
+
+#if !defined(SSAO_QUALITY_LOW) && !defined(SSAO_QUALITY_HIGH) && !defined(SSAO_QUALITY_ULTRA)
+#define NUM_SAMPLES (12)
+#endif
+
+// If using depth mip levels, the log of the maximum pixel offset before we need to switch to a lower
+// miplevel to maintain reasonable spatial locality in the cache
+// If this number is too small (< 3), too many taps will land in the same pixel, and we'll get bad variance that manifests as flashing.
+// If it is too high (> 5), we'll get bad performance because we're not using the MIP levels effectively
+#define LOG_MAX_OFFSET (3)
+
+// This must be less than or equal to the MAX_MIP_LEVEL defined in SSAO.cpp
+#define MAX_MIP_LEVEL (4)
+
+// This is the number of turns around the circle that the spiral pattern makes.  This should be prime to prevent
+// taps from lining up.  This particular choice was tuned for NUM_SAMPLES == 9
+
+const int ROTATIONS[] = int[](
+		1, 1, 2, 3, 2, 5, 2, 3, 2,
+		3, 3, 5, 5, 3, 4, 7, 5, 5, 7,
+		9, 8, 5, 5, 7, 7, 7, 8, 5, 8,
+		11, 12, 7, 10, 13, 8, 11, 8, 7, 14,
+		11, 11, 13, 12, 13, 19, 17, 13, 11, 18,
+		19, 11, 11, 14, 17, 21, 15, 16, 17, 18,
+		13, 17, 11, 17, 19, 18, 25, 18, 19, 19,
+		29, 21, 19, 27, 31, 29, 21, 18, 17, 29,
+		31, 31, 23, 18, 25, 26, 25, 23, 19, 34,
+		19, 27, 21, 25, 39, 29, 17, 21, 27);
+/* clang-format on */
+
+//#define NUM_SPIRAL_TURNS (7)
+const int NUM_SPIRAL_TURNS = ROTATIONS[NUM_SAMPLES - 1];
+
+layout(set = 0, binding = 0) uniform sampler2D source_depth_mipmaps;
+layout(r8, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
+
+#ifndef USE_HALF_SIZE
+layout(set = 2, binding = 0) uniform sampler2D source_depth;
+#endif
+
+layout(set = 3, binding = 0) uniform sampler2D source_normal;
+
+layout(push_constant, binding = 1, std430) uniform Params {
+	ivec2 screen_size;
+	float z_far;
+	float z_near;
+
+	bool orthogonal;
+	float intensity_div_r6;
+	float radius;
+	float bias;
+
+	vec4 proj_info;
+	vec2 pixel_size;
+	float proj_scale;
+	uint pad;
+
+} params;
+
+
+vec3 reconstructCSPosition(vec2 S, float z) {
+	if (params.orthogonal) {
+		return vec3((S.xy * params.proj_info.xy + params.proj_info.zw), z);
+	} else {
+		return vec3((S.xy * params.proj_info.xy + params.proj_info.zw) * z, z);
+	}
+}
+
+vec3 getPosition(ivec2 ssP) {
+	vec3 P;
+#ifdef USE_HALF_SIZE
+	P.z = texelFetch(source_depth_mipmaps, ssP, 0).r;
+	P.z = -P.z;
+#else
+	P.z = texelFetch(source_depth, ssP, 0).r;
+
+	P.z = P.z * 2.0 - 1.0;
+	if (params.orthogonal) {
+		P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+	} else {
+		P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near));
+	}
+	P.z = -P.z;
+#endif
+	// Offset to pixel center
+	P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
+	return P;
+}
+
+/** Returns a unit vector and a screen-space radius for the tap on a unit disk (the caller should scale by the actual disk radius) */
+vec2 tapLocation(int sampleNumber, float spinAngle, out float ssR) {
+	// Radius relative to ssR
+	float alpha = (float(sampleNumber) + 0.5) * (1.0 / float(NUM_SAMPLES));
+	float angle = alpha * (float(NUM_SPIRAL_TURNS) * 6.28) + spinAngle;
+
+	ssR = alpha;
+	return vec2(cos(angle), sin(angle));
+}
+
+/** Read the camera-space position of the point at screen-space pixel ssP + unitOffset * ssR.  Assumes length(unitOffset) == 1 */
+vec3 getOffsetPosition(ivec2 ssP, float ssR) {
+	// Derivation:
+	//  mipLevel = floor(log(ssR / MAX_OFFSET));
+
+	int mipLevel = clamp(int(floor(log2(ssR))) - LOG_MAX_OFFSET, 0, MAX_MIP_LEVEL);
+
+	vec3 P;
+
+	// We need to divide by 2^mipLevel to read the appropriately scaled coordinate from a MIP-map.
+	// Manually clamp to the texture size because texelFetch bypasses the texture unit
+	ivec2 mipP = clamp(ssP >> mipLevel, ivec2(0), (params.screen_size >> mipLevel) - ivec2(1));
+
+#ifdef USE_HALF_SIZE
+	P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel).r;
+	P.z = -P.z;
+#else
+	if (mipLevel < 1) {
+		//read from depth buffer
+		P.z = texelFetch(source_depth, mipP, 0).r;
+		P.z = P.z * 2.0 - 1.0;
+		if (params.orthogonal) {
+			P.z = ((P.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+		} else {
+			P.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - P.z * (params.z_far - params.z_near));
+		}
+		P.z = -P.z;
+
+	} else {
+		//read from mipmaps
+		P.z = texelFetch(source_depth_mipmaps, mipP, mipLevel - 1).r;
+		P.z = -P.z;
+	}
+#endif
+
+	// Offset to pixel center
+	P = reconstructCSPosition(vec2(ssP) + vec2(0.5), P.z);
+
+
+	return P;
+}
+
+/** Compute the occlusion due to sample with index \a i about the pixel at \a ssC that corresponds
+	to camera-space point \a C with unit normal \a n_C, using maximum screen-space sampling radius \a ssDiskRadius
+
+	Note that units of H() in the HPG12 paper are meters, not
+	unitless.  The whole falloff/sampling function is therefore
+	unitless.  In this implementation, we factor out (9 / radius).
+
+	Four versions of the falloff function are implemented below
+*/
+float sampleAO(in ivec2 ssC, in vec3 C, in vec3 n_C, in float ssDiskRadius, in float p_radius, in int tapIndex, in float randomPatternRotationAngle) {
+	// Offset on the unit disk, spun for this pixel
+	float ssR;
+	vec2 unitOffset = tapLocation(tapIndex, randomPatternRotationAngle, ssR);
+	ssR *= ssDiskRadius;
+
+	ivec2 ssP = ivec2(ssR * unitOffset) + ssC;
+
+	if (any(lessThan(ssP,ivec2(0))) || any(greaterThanEqual(ssP,params.screen_size))) {
+		return 0.0;
+	}
+
+	// The occluding point in camera space
+	vec3 Q = getOffsetPosition(ssP, ssR);
+
+	vec3 v = Q - C;
+
+	float vv = dot(v, v);
+	float vn = dot(v, n_C);
+
+	const float epsilon = 0.01;
+	float radius2 = p_radius * p_radius;
+
+	// A: From the HPG12 paper
+	// Note large epsilon to avoid overdarkening within cracks
+	//return float(vv < radius2) * max((vn - bias) / (epsilon + vv), 0.0) * radius2 * 0.6;
+
+	// B: Smoother transition to zero (lowers contrast, smoothing out corners). [Recommended]
+	float f = max(radius2 - vv, 0.0);
+	return f * f * f * max((vn - params.bias) / (epsilon + vv), 0.0);
+
+	// C: Medium contrast (which looks better at high radii), no division.  Note that the
+	// contribution still falls off with radius^2, but we've adjusted the rate in a way that is
+	// more computationally efficient and happens to be aesthetically pleasing.
+	// return 4.0 * max(1.0 - vv * invRadius2, 0.0) * max(vn - bias, 0.0);
+
+	// D: Low contrast, no division operation
+	// return 2.0 * float(vv < radius * radius) * max(vn - bias, 0.0);
+}
+
+void main() {
+	// Pixel being shaded
+	ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
+	if (any(greaterThan(ssC,params.screen_size))) { //too large, do nothing
+		return;
+	}
+
+	// World space point being shaded
+	vec3 C = getPosition(ssC);
+
+
+
+#ifdef USE_HALF_SIZE
+	vec3 n_C = texelFetch(source_normal,ssC<<1,0).xyz * 2.0 - 1.0;
+#else
+	vec3 n_C = texelFetch(source_normal,ssC,0).xyz * 2.0 - 1.0;
+#endif
+	n_C = normalize(n_C);
+	n_C.y = -n_C.y; //because this code reads flipped
+
+
+
+	// Hash function used in the HPG12 AlchemyAO paper
+	float randomPatternRotationAngle = mod(float((3 * ssC.x ^ ssC.y + ssC.x * ssC.y) * 10), TWO_PI);
+
+	// Reconstruct normals from positions. These will lead to 1-pixel black lines
+	// at depth discontinuities, however the blur will wipe those out so they are not visible
+	// in the final image.
+
+	// Choose the screen-space sample radius
+	// proportional to the projected area of the sphere
+
+	float ssDiskRadius = -params.proj_scale * params.radius;
+	if (!params.orthogonal) {
+		ssDiskRadius = -params.proj_scale * params.radius / C.z;
+	}
+	float sum = 0.0;
+	for (int i = 0; i < NUM_SAMPLES; ++i) {
+		sum += sampleAO(ssC, C, n_C, ssDiskRadius, params.radius, i, randomPatternRotationAngle);
+	}
+
+	float A = max(0.0, 1.0 - sum * params.intensity_div_r6 * (5.0 / float(NUM_SAMPLES)));
+
+	imageStore(dest_image,ssC,vec4(A));
+}

+ 163 - 0
servers/visual/rasterizer_rd/shaders/ssao_blur.glsl

@@ -0,0 +1,163 @@
+/* clang-format off */
+[compute]
+/* clang-format on */
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+
+layout(set = 0, binding = 0) uniform sampler2D source_ssao;
+layout(set = 1, binding = 0) uniform sampler2D source_depth;
+#ifdef MODE_UPSCALE
+layout(set = 2, binding = 0) uniform sampler2D source_depth_mipmaps;
+#endif
+
+layout(r8, set = 3, binding = 0) uniform restrict writeonly image2D dest_image;
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+// Tunable Parameters:
+
+layout(push_constant, binding = 1, std430) uniform Params {
+	float edge_sharpness; /** Increase to make depth edges crisper. Decrease to reduce flicker. */
+	int filter_scale;
+	float z_far;
+	float z_near;
+	bool orthogonal;
+	uint pad0;
+	uint pad1;
+	uint pad2;
+	ivec2 axis; /** (1, 0) or (0, 1) */
+	ivec2 screen_size;
+} params;
+
+
+
+/** Filter radius in pixels. This will be multiplied by SCALE. */
+#define R (4)
+
+//////////////////////////////////////////////////////////////////////////////////////////////
+
+// Gaussian coefficients
+const float gaussian[R + 1] =
+		//float[](0.356642, 0.239400, 0.072410, 0.009869);
+		//float[](0.398943, 0.241971, 0.053991, 0.004432, 0.000134);  // stddev = 1.0
+		float[](0.153170, 0.144893, 0.122649, 0.092902, 0.062970); // stddev = 2.0
+//float[](0.111220, 0.107798, 0.098151, 0.083953, 0.067458, 0.050920, 0.036108); // stddev = 3.0
+
+
+
+
+void main() {
+
+	// Pixel being shaded
+	ivec2 ssC = ivec2(gl_GlobalInvocationID.xy);
+	if (any(greaterThan(ssC,params.screen_size))) { //too large, do nothing
+		return;
+	}
+
+#ifdef MODE_UPSCALE
+
+	//closest one should be the same pixel, but check nearby just in case
+	float depth = texelFetch(source_depth, ssC, 0).r;
+
+	depth = depth * 2.0 - 1.0;
+	if (params.orthogonal) {
+		depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+	} else {
+		depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+	}
+
+	vec2 pixel_size = 1.0 / vec2(params.screen_size);
+	vec2 closest_uv = vec2(ssC) * pixel_size + pixel_size * 0.5;
+	vec2 from_uv = closest_uv;
+	vec2 ps2 = pixel_size;// * 2.0;
+
+	float closest_depth = abs(textureLod(source_depth_mipmaps,closest_uv,0.0).r-depth);
+
+	vec2 offsets[4]=vec2[](vec2(ps2.x,0),vec2(-ps2.x,0),vec2(0,ps2.y),vec2(0,-ps2.y));
+	for(int i=0;i<4;i++) {
+		vec2 neighbour = from_uv + offsets[i];
+		float neighbour_depth = abs(textureLod(source_depth_mipmaps,neighbour,0.0).r-depth);
+		if (neighbour_depth < closest_depth ) {
+			closest_uv = neighbour;
+			closest_depth = neighbour_depth;
+		}
+	}
+
+	float visibility = textureLod(source_ssao,closest_uv,0.0).r;
+	imageStore(dest_image,ssC,vec4(visibility));
+#else
+
+	float depth = texelFetch(source_depth, ssC, 0).r;
+
+#ifdef MODE_FULL_SIZE
+	depth = depth * 2.0 - 1.0;
+
+	if (params.orthogonal) {
+		depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+	} else {
+		depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+	}
+
+#endif
+	float depth_divide = 1.0 / params.z_far;
+
+	//depth *= depth_divide;
+
+	/*
+	if (depth > params.z_far * 0.999) {
+		discard; //skybox
+	}
+	*/
+
+	float sum = texelFetch(source_ssao, ssC, 0).r;
+
+	// Base weight for depth falloff.  Increase this for more blurriness,
+	// decrease it for better edge discrimination
+	float BASE = gaussian[0];
+	float totalWeight = BASE;
+	sum *= totalWeight;
+
+	ivec2 clamp_limit = params.screen_size - ivec2(1);
+
+	for (int r = -R; r <= R; ++r) {
+		// We already handled the zero case above.  This loop should be unrolled and the static branch optimized out,
+		// so the IF statement has no runtime cost
+		if (r != 0) {
+
+			ivec2 ppos = ssC + params.axis * (r * params.filter_scale);
+			float value = texelFetch(source_ssao, clamp(ppos, ivec2(0), clamp_limit), 0).r;
+			ivec2 rpos = clamp(ppos, ivec2(0), clamp_limit);
+
+
+			float temp_depth = texelFetch(source_depth, rpos, 0).r;
+#ifdef MODE_FULL_SIZE
+			temp_depth = temp_depth * 2.0 - 1.0;
+			if (params.orthogonal) {
+				temp_depth = ((temp_depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+			} else {
+				temp_depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - temp_depth * (params.z_far - params.z_near));
+			}
+			//temp_depth *= depth_divide;
+#endif
+			// spatial domain: offset gaussian tap
+			float weight = 0.3 + gaussian[abs(r)];
+			//weight *= max(0.0, dot(temp_normal, normal));
+
+			// range domain (the "bilateral" weight). As depth difference increases, decrease weight.
+			weight *= max(0.0, 1.0 - params.edge_sharpness * abs(temp_depth - depth));
+
+			sum += value * weight;
+			totalWeight += weight;
+		}
+	}
+
+	const float epsilon = 0.0001;
+	float visibility = sum / (totalWeight + epsilon);
+
+	imageStore(dest_image,ssC,vec4(visibility));
+#endif
+}

+ 49 - 0
servers/visual/rasterizer_rd/shaders/ssao_minify.glsl

@@ -0,0 +1,49 @@
+/* clang-format off */
+[compute]
+/* clang-format on */
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+layout(push_constant, binding = 1, std430) uniform Params {
+	vec2 pixel_size;
+	float z_far;
+	float z_near;
+	ivec2 source_size;
+	bool orthogonal;
+	uint pad;
+} params;
+
+
+#ifdef MINIFY_START
+layout(set = 0, binding = 0) uniform sampler2D source_texture;
+#else
+layout(r32f, set = 0, binding = 0) uniform restrict readonly image2D source_image;
+#endif
+layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_image;
+
+void main() {
+
+	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+
+	if (any(greaterThan(pos,params.source_size>>1))) { //too large, do nothing
+		return;
+	}
+
+#ifdef MINIFY_START
+	float depth = texelFetch(source_texture,pos<<1,0).r * 2.0 - 1.0;
+	if (params.orthogonal) {
+		depth = ((depth + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+	} else {
+		depth = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - depth * (params.z_far - params.z_near));
+	}
+#else
+	float depth = imageLoad(source_image,pos<<1).r;
+#endif
+
+	imageStore(dest_image,pos,vec4(depth));
+
+}

+ 3 - 2
servers/visual/visual_server_raster.h

@@ -518,14 +518,15 @@ public:
 	BIND2(environment_set_bg_color, RID, const Color &)
 	BIND2(environment_set_bg_energy, RID, float)
 	BIND2(environment_set_canvas_max_layer, RID, int)
-	BIND6(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource)
+	BIND7(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource, const Color &)
 
 // FIXME: Disabled during Vulkan refactoring, should be ported.
 #if 0
 	BIND2(environment_set_camera_feed_id, RID, int)
 #endif
 	BIND7(environment_set_ssr, RID, bool, int, float, float, float, bool)
-	BIND13(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
+	BIND9(environment_set_ssao, RID, bool, float, float, float, float, float, EnvironmentSSAOBlur, float)
+	BIND2(environment_set_ssao_quality, EnvironmentSSAOQuality, bool)
 
 	BIND12(environment_set_glow, RID, bool, int, float, float, float, float, EnvironmentGlowBlendMode, float, float, float, bool)
 

+ 3 - 0
servers/visual/visual_server_viewport.cpp

@@ -450,6 +450,9 @@ void VisualServerViewport::viewport_set_size(RID p_viewport, int p_width, int p_
 	Viewport *viewport = viewport_owner.getornull(p_viewport);
 	ERR_FAIL_COND(!viewport);
 
+	//	if (viewport->size.width == p_width && viewport->size.height == p_height) {
+	//		return; //nothing to do
+	//	}
 	viewport->size = Size2(p_width, p_height);
 	VSG::storage->render_target_set_size(viewport->render_target, p_width, p_height);
 	if (viewport->render_buffers.is_valid()) {

+ 4 - 2
servers/visual/visual_server_wrap_mt.h

@@ -432,14 +432,16 @@ public:
 	FUNC2(environment_set_bg_color, RID, const Color &)
 	FUNC2(environment_set_bg_energy, RID, float)
 	FUNC2(environment_set_canvas_max_layer, RID, int)
-	FUNC6(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource)
+	FUNC7(environment_set_ambient_light, RID, const Color &, EnvironmentAmbientSource, float, float, EnvironmentReflectionSource, const Color &)
 
 // FIXME: Disabled during Vulkan refactoring, should be ported.
 #if 0
 	FUNC2(environment_set_camera_feed_id, RID, int)
 #endif
 	FUNC7(environment_set_ssr, RID, bool, int, float, float, float, bool)
-	FUNC13(environment_set_ssao, RID, bool, float, float, float, float, float, float, float, const Color &, EnvironmentSSAOQuality, EnvironmentSSAOBlur, float)
+	FUNC9(environment_set_ssao, RID, bool, float, float, float, float, float, EnvironmentSSAOBlur, float)
+
+	FUNC2(environment_set_ssao_quality, EnvironmentSSAOQuality, bool)
 
 	FUNC12(environment_set_glow, RID, bool, int, float, float, float, float, EnvironmentGlowBlendMode, float, float, float, bool)
 

+ 5 - 1
servers/visual_server.cpp

@@ -1825,7 +1825,7 @@ void VisualServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("environment_set_tonemap", "env", "tone_mapper", "exposure", "white", "auto_exposure", "min_luminance", "max_luminance", "auto_exp_speed", "auto_exp_grey"), &VisualServer::environment_set_tonemap);
 	ClassDB::bind_method(D_METHOD("environment_set_adjustment", "env", "enable", "brightness", "contrast", "saturation", "ramp"), &VisualServer::environment_set_adjustment);
 	ClassDB::bind_method(D_METHOD("environment_set_ssr", "env", "enable", "max_steps", "fade_in", "fade_out", "depth_tolerance", "roughness"), &VisualServer::environment_set_ssr);
-	ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "radius2", "intensity2", "bias", "light_affect", "ao_channel_affect", "color", "quality", "blur", "bilateral_sharpness"), &VisualServer::environment_set_ssao);
+	ClassDB::bind_method(D_METHOD("environment_set_ssao", "env", "enable", "radius", "intensity", "radius2", "intensity2", "bias", "light_affect", "ao_channel_affect", "color", "blur", "bilateral_sharpness"), &VisualServer::environment_set_ssao);
 	ClassDB::bind_method(D_METHOD("environment_set_fog", "env", "enable", "color", "sun_color", "sun_amount"), &VisualServer::environment_set_fog);
 
 	ClassDB::bind_method(D_METHOD("environment_set_fog_depth", "env", "enable", "depth_begin", "depth_end", "depth_curve", "transmit", "transmit_curve"), &VisualServer::environment_set_fog_depth);
@@ -2322,6 +2322,10 @@ VisualServer::VisualServer() {
 	GLOBAL_DEF("rendering/quality/filters/depth_of_field_bokeh_quality", 2);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/filters/depth_of_field_bokeh_quality", PropertyInfo(Variant::INT, "rendering/quality/filters/depth_of_field_bokeh_quality", PROPERTY_HINT_ENUM, "Very Low (Fast),Low,Medium,High (Slow)"));
 	GLOBAL_DEF("rendering/quality/filters/depth_of_field_use_jitter", false);
+
+	GLOBAL_DEF("rendering/quality/ssao/quality", 1);
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/ssao/quality", PropertyInfo(Variant::INT, "rendering/quality/ssao/quality", PROPERTY_HINT_ENUM, "Low (Fast),Medium,High (Slow),Ultra (Very Slow)"));
+	GLOBAL_DEF("rendering/quality/ssao/half_size", false);
 }
 
 VisualServer::~VisualServer() {

+ 12 - 8
servers/visual_server.h

@@ -664,6 +664,7 @@ public:
 		VIEWPORT_DEBUG_DRAW_SHADOW_ATLAS,
 		VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
 		VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE,
+		VIEWPORT_DEBUG_DRAW_SSAO,
 
 	};
 
@@ -718,7 +719,7 @@ public:
 	virtual void environment_set_bg_color(RID p_env, const Color &p_color) = 0;
 	virtual void environment_set_bg_energy(RID p_env, float p_energy) = 0;
 	virtual void environment_set_canvas_max_layer(RID p_env, int p_max_layer) = 0;
-	virtual void environment_set_ambient_light(RID p_env, const Color &p_color, EnvironmentAmbientSource p_ambient = ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, EnvironmentReflectionSource p_reflection_source = ENV_REFLECTION_SOURCE_BG) = 0;
+	virtual void environment_set_ambient_light(RID p_env, const Color &p_color, EnvironmentAmbientSource p_ambient = ENV_AMBIENT_SOURCE_BG, float p_energy = 1.0, float p_sky_contribution = 0.0, EnvironmentReflectionSource p_reflection_source = ENV_REFLECTION_SOURCE_BG, const Color &p_ao_color = Color()) = 0;
 // FIXME: Disabled during Vulkan refactoring, should be ported.
 #if 0
 	virtual void environment_set_camera_feed_id(RID p_env, int p_camera_feed_id) = 0;
@@ -745,12 +746,6 @@ public:
 
 	virtual void environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_in, float p_fade_out, float p_depth_tolerance, bool p_roughness) = 0;
 
-	enum EnvironmentSSAOQuality {
-		ENV_SSAO_QUALITY_LOW,
-		ENV_SSAO_QUALITY_MEDIUM,
-		ENV_SSAO_QUALITY_HIGH,
-	};
-
 	enum EnvironmentSSAOBlur {
 		ENV_SSAO_BLUR_DISABLED,
 		ENV_SSAO_BLUR_1x1,
@@ -758,7 +753,16 @@ public:
 		ENV_SSAO_BLUR_3x3,
 	};
 
-	virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity, float p_radius2, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, const Color &p_color, EnvironmentSSAOQuality p_quality, EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
+	virtual void environment_set_ssao(RID p_env, bool p_enable, float p_radius, float p_intensity2, float p_bias, float p_light_affect, float p_ao_channel_affect, EnvironmentSSAOBlur p_blur, float p_bilateral_sharpness) = 0;
+
+	enum EnvironmentSSAOQuality {
+		ENV_SSAO_QUALITY_LOW,
+		ENV_SSAO_QUALITY_MEDIUM,
+		ENV_SSAO_QUALITY_HIGH,
+		ENV_SSAO_QUALITY_ULTRA,
+	};
+
+	virtual void environment_set_ssao_quality(EnvironmentSSAOQuality p_quality, bool p_half_size) = 0;
 
 	virtual void environment_set_fog(RID p_env, bool p_enable, const Color &p_color, const Color &p_sun_color, float p_sun_amount) = 0;
 	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;