Browse Source

Addition of SDFGI for open world global illumination
Move GI to a deferred pass

Juan Linietsky 5 years ago
parent
commit
201d606b3d
53 changed files with 7823 additions and 779 deletions
  1. 9 9
      .travis.yml
  2. 8 0
      core/local_vector.h
  3. 1 1
      core/math/basis.cpp
  4. 2 0
      core/math/geometry_3d.cpp
  5. 10 0
      core/math/geometry_3d.h
  6. 1 0
      core/variant_op.cpp
  7. 203 10
      drivers/vulkan/rendering_device_vulkan.cpp
  8. 25 7
      drivers/vulkan/rendering_device_vulkan.h
  9. 7 1
      editor/editor_node.cpp
  10. 17 4
      editor/plugins/node_3d_editor_plugin.cpp
  11. 3 1
      editor/plugins/node_3d_editor_plugin.h
  12. 3 3
      scene/3d/baked_lightmap.cpp
  13. 5 5
      scene/3d/light_3d.cpp
  14. 2 2
      scene/3d/light_3d.h
  15. 36 32
      scene/3d/reflection_probe.cpp
  16. 17 7
      scene/3d/reflection_probe.h
  17. 3 1
      scene/main/viewport.cpp
  18. 4 2
      scene/main/viewport.h
  19. 192 0
      scene/resources/environment.cpp
  20. 67 0
      scene/resources/environment.h
  21. 30 8
      servers/rendering/rasterizer.h
  22. 48 5
      servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp
  23. 27 3
      servers/rendering/rasterizer_rd/rasterizer_effects_rd.h
  24. 1 0
      servers/rendering/rasterizer_rd/rasterizer_rd.h
  25. 428 264
      servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp
  26. 49 42
      servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h
  27. 1563 28
      servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp
  28. 469 33
      servers/rendering/rasterizer_rd/rasterizer_scene_rd.h
  29. 51 22
      servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp
  30. 16 12
      servers/rendering/rasterizer_rd/rasterizer_storage_rd.h
  31. 7 0
      servers/rendering/rasterizer_rd/shaders/SCsub
  32. 18 2
      servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl
  33. 663 0
      servers/rendering/rasterizer_rd/shaders/gi.glsl
  34. 110 0
      servers/rendering/rasterizer_rd/shaders/resolve.glsl
  35. 404 187
      servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl
  36. 104 29
      servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl
  37. 4 6
      servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl
  38. 275 0
      servers/rendering/rasterizer_rd/shaders/sdfgi_debug.glsl
  39. 231 0
      servers/rendering/rasterizer_rd/shaders/sdfgi_debug_probes.glsl
  40. 472 0
      servers/rendering/rasterizer_rd/shaders/sdfgi_direct_light.glsl
  41. 182 0
      servers/rendering/rasterizer_rd/shaders/sdfgi_fields.glsl
  42. 605 0
      servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl
  43. 1155 0
      servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl
  44. 16 4
      servers/rendering/rendering_device.cpp
  45. 15 4
      servers/rendering/rendering_device.h
  46. 4 0
      servers/rendering/rendering_server_raster.cpp
  47. 13 5
      servers/rendering/rendering_server_raster.h
  48. 124 17
      servers/rendering/rendering_server_scene.cpp
  49. 16 2
      servers/rendering/rendering_server_scene.h
  50. 16 5
      servers/rendering/rendering_server_wrap_mt.h
  51. 1 0
      servers/rendering/shader_types.cpp
  52. 18 10
      servers/rendering_server.cpp
  53. 73 6
      servers/rendering_server.h

+ 9 - 9
.travis.yml

@@ -100,15 +100,15 @@ matrix:
           packages:
             - *linux_deps
 
-    - name: Javascript export template (release, emscripten latest)
-      stage: build
-      env: PLATFORM=javascript TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-emcc-latest EXTRA_ARGS="use_closure_compiler=yes"
-      os: linux
-      compiler: clang
-      addons:
-        apt:
-          packages:
-            - *linux_deps
+#    - name: Javascript export template (release, emscripten latest)
+#      stage: build
+#      env: PLATFORM=javascript TOOLS=no TARGET=release CACHE_NAME=${PLATFORM}-emcc-latest EXTRA_ARGS="use_closure_compiler=yes"
+#      os: linux
+#      compiler: clang
+#      addons:
+#        apt:
+#          packages:
+#            - *linux_deps
 
 before_install:
   - eval "${MATRIX_EVAL}"

+ 8 - 0
core/local_vector.h

@@ -45,6 +45,14 @@ private:
 	T *data = nullptr;
 
 public:
+	T *ptr() {
+		return data;
+	}
+
+	const T *ptr() const {
+		return data;
+	}
+
 	_FORCE_INLINE_ void push_back(T p_elem) {
 		if (unlikely(count == capacity)) {
 			if (capacity == 0) {

+ 1 - 1
core/math/basis.cpp

@@ -773,7 +773,7 @@ Basis::operator String() const {
 				mtx += ", ";
 			}
 
-			mtx += rtos(elements[i][j]);
+			mtx += rtos(elements[j][i]); //matrix is stored transposed for performance, so print it transposed
 		}
 	}
 

+ 2 - 0
core/math/geometry_3d.cpp

@@ -993,6 +993,8 @@ Vector<uint32_t> Geometry3D::generate_edf(const Vector<bool> &p_voxels, const Ve
 		}
 	}
 
+	memdelete_arr(work_memory);
+
 	return ret;
 }
 

+ 10 - 0
core/math/geometry_3d.h

@@ -945,6 +945,16 @@ public:
 		return Color(va6 * v6, vb6 * v6, vc6 * v6, vd6 * v6);
 #undef STP
 	}
+
+	_FORCE_INLINE_ static Vector3 octahedron_map_decode(const Vector2 &p_uv) {
+		// https://twitter.com/Stubbesaurus/status/937994790553227264
+		Vector2 f = p_uv * 2.0 - Vector2(1.0, 1.0);
+		Vector3 n = Vector3(f.x, f.y, 1.0f - Math::abs(f.x) - Math::abs(f.y));
+		float t = CLAMP(-n.z, 0.0, 1.0);
+		n.x += n.x >= 0 ? -t : t;
+		n.y += n.y >= 0 ? -t : t;
+		return n.normalized();
+	}
 };
 
 #endif // GEOMETRY_3D_H

+ 1 - 0
core/variant_op.cpp

@@ -1786,6 +1786,7 @@ Variant Variant::get_named(const StringName &p_index, bool *r_valid) const {
 	if (r_valid) {
 		*r_valid = true;
 	}
+
 	switch (type) {
 		case VECTOR2: {
 			const Vector2 *v = reinterpret_cast<const Vector2 *>(_data._mem);

+ 203 - 10
drivers/vulkan/rendering_device_vulkan.cpp

@@ -38,7 +38,7 @@
 
 #include "thirdparty/spirv-reflect/spirv_reflect.h"
 
-//#define FORCE_FULL_BARRIER
+#define FORCE_FULL_BARRIER
 
 void RenderingDeviceVulkan::_add_dependency(RID p_id, RID p_depends_on) {
 	if (!dependency_map.has(p_depends_on)) {
@@ -3203,6 +3203,63 @@ RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_c
 	return id;
 }
 
+RenderingDevice::FramebufferFormatID RenderingDeviceVulkan::framebuffer_format_create_empty(const Size2i &p_size) {
+	ERR_FAIL_COND_V(p_size.width <= 0 || p_size.height <= 0, INVALID_FORMAT_ID);
+
+	FramebufferFormatKey key;
+	key.empty_size = p_size;
+
+	const Map<FramebufferFormatKey, FramebufferFormatID>::Element *E = framebuffer_format_cache.find(key);
+	if (E) {
+		//exists, return
+		return E->get();
+	}
+
+	VkSubpassDescription subpass;
+	subpass.flags = 0;
+	subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+	subpass.inputAttachmentCount = 0; //unsupported for now
+	subpass.pInputAttachments = nullptr;
+	subpass.colorAttachmentCount = 0;
+	subpass.pColorAttachments = nullptr;
+	subpass.pDepthStencilAttachment = nullptr;
+	subpass.pResolveAttachments = nullptr;
+	subpass.preserveAttachmentCount = 0;
+	subpass.pPreserveAttachments = nullptr;
+
+	VkRenderPassCreateInfo render_pass_create_info;
+	render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+	render_pass_create_info.pNext = nullptr;
+	render_pass_create_info.flags = 0;
+	render_pass_create_info.attachmentCount = 0;
+	render_pass_create_info.pAttachments = nullptr;
+	render_pass_create_info.subpassCount = 1;
+	render_pass_create_info.pSubpasses = &subpass;
+	render_pass_create_info.dependencyCount = 0;
+	render_pass_create_info.pDependencies = nullptr;
+
+	VkRenderPass render_pass;
+	VkResult res = vkCreateRenderPass(device, &render_pass_create_info, nullptr, &render_pass);
+
+	ERR_FAIL_COND_V_MSG(res, VK_NULL_HANDLE, "vkCreateRenderPass for empty fb failed with error " + itos(res) + ".");
+
+	if (render_pass == VK_NULL_HANDLE) { //was likely invalid
+		return INVALID_ID;
+	}
+
+	FramebufferFormatID id = FramebufferFormatID(framebuffer_format_cache.size()) | (FramebufferFormatID(ID_TYPE_FRAMEBUFFER_FORMAT) << FramebufferFormatID(ID_BASE_SHIFT));
+
+	E = framebuffer_format_cache.insert(key, id);
+
+	FramebufferFormat fb_format;
+	fb_format.E = E;
+	fb_format.color_attachments = 0;
+	fb_format.render_pass = render_pass;
+	fb_format.samples = TEXTURE_SAMPLES_1;
+	framebuffer_formats[id] = fb_format;
+	return id;
+}
+
 RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_texture_samples(FramebufferFormatID p_format) {
 	Map<FramebufferFormatID, FramebufferFormat>::Element *E = framebuffer_formats.find(p_format);
 	ERR_FAIL_COND_V(!E, TEXTURE_SAMPLES_1);
@@ -3214,6 +3271,16 @@ RenderingDevice::TextureSamples RenderingDeviceVulkan::framebuffer_format_get_te
 /**** RENDER TARGET ****/
 /***********************/
 
+RID RenderingDeviceVulkan::framebuffer_create_empty(const Size2i &p_size, FramebufferFormatID p_format_check) {
+	_THREAD_SAFE_METHOD_
+	Framebuffer framebuffer;
+	framebuffer.format_id = framebuffer_format_create_empty(p_size);
+	ERR_FAIL_COND_V(p_format_check != INVALID_FORMAT_ID && framebuffer.format_id != p_format_check, RID());
+	framebuffer.size = p_size;
+
+	return framebuffer_owner.make_rid(framebuffer);
+}
+
 RID RenderingDeviceVulkan::framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check) {
 	_THREAD_SAFE_METHOD_
 
@@ -4205,13 +4272,18 @@ RID RenderingDeviceVulkan::uniform_buffer_create(uint32_t p_size_bytes, const Ve
 	return uniform_buffer_owner.make_rid(buffer);
 }
 
-RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data) {
+RID RenderingDeviceVulkan::storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data, uint32_t p_usage) {
 	_THREAD_SAFE_METHOD_
 
 	ERR_FAIL_COND_V(p_data.size() && (uint32_t)p_data.size() != p_size_bytes, RID());
 
 	Buffer buffer;
-	Error err = _buffer_allocate(&buffer, p_size_bytes, VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY);
+	buffer.usage = p_usage;
+	uint32_t flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
+	if (p_usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT) {
+		flags |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT;
+	}
+	Error err = _buffer_allocate(&buffer, p_size_bytes, flags, VMA_MEMORY_USAGE_GPU_ONLY);
 	ERR_FAIL_COND_V(err != OK, RID());
 
 	if (p_data.size()) {
@@ -4420,7 +4492,7 @@ RID RenderingDeviceVulkan::uniform_set_create(const Vector<Uniform> &p_uniforms,
 			}
 		}
 		ERR_FAIL_COND_V_MSG(uniform_idx == -1, RID(),
-				"All the shader bindings for the given set must be covered by the uniforms provided.");
+				"All the shader bindings for the given set must be covered by the uniforms provided. Binding (" + itos(set_uniform.binding) + ") was not provided.");
 
 		const Uniform &uniform = uniforms[uniform_idx];
 
@@ -5475,7 +5547,7 @@ Error RenderingDeviceVulkan::_draw_list_setup_framebuffer(Framebuffer *p_framebu
 	return OK;
 }
 
-Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents) {
+Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures) {
 	VkRenderPassBeginInfo render_pass_begin;
 	render_pass_begin.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
 	render_pass_begin.pNext = nullptr;
@@ -5520,6 +5592,37 @@ Error RenderingDeviceVulkan::_draw_list_render_pass_begin(Framebuffer *framebuff
 	render_pass_begin.clearValueCount = clear_values.size();
 	render_pass_begin.pClearValues = clear_values.ptr();
 
+	for (int i = 0; i < p_storage_textures.size(); i++) {
+		Texture *texture = texture_owner.getornull(p_storage_textures[i]);
+		ERR_CONTINUE_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), "Supplied storage texture " + itos(i) + " for draw list ist not set to be used for storage.");
+
+		if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) {
+			//must change layout to general
+			VkImageMemoryBarrier image_memory_barrier;
+			image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+			image_memory_barrier.pNext = nullptr;
+			image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+			image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+			image_memory_barrier.oldLayout = texture->layout;
+			image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL;
+
+			image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+			image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+			image_memory_barrier.image = texture->image;
+			image_memory_barrier.subresourceRange.aspectMask = texture->read_aspect_mask;
+			image_memory_barrier.subresourceRange.baseMipLevel = texture->base_mipmap;
+			image_memory_barrier.subresourceRange.levelCount = texture->mipmaps;
+			image_memory_barrier.subresourceRange.baseArrayLayer = texture->base_layer;
+			image_memory_barrier.subresourceRange.layerCount = texture->layers;
+
+			vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
+
+			texture->layout = VK_IMAGE_LAYOUT_GENERAL;
+
+			draw_list_storage_textures.push_back(p_storage_textures[i]);
+		}
+	}
+
 	vkCmdBeginRenderPass(command_buffer, &render_pass_begin, subpass_contents);
 
 	//mark textures as bound
@@ -5576,7 +5679,7 @@ void RenderingDeviceVulkan::_draw_list_insert_clear_region(DrawList *draw_list,
 	vkCmdClearAttachments(draw_list->command_buffer, clear_attachments.size(), clear_attachments.ptr(), 1, &cr);
 }
 
-RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) {
+RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) {
 	_THREAD_SAFE_METHOD_
 
 	ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time.");
@@ -5626,7 +5729,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu
 	ERR_FAIL_COND_V(err != OK, INVALID_ID);
 
 	VkCommandBuffer command_buffer = frames[frame].draw_command_buffer;
-	err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE);
+	err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, command_buffer, VK_SUBPASS_CONTENTS_INLINE, p_storage_textures);
 
 	if (err != OK) {
 		return INVALID_ID;
@@ -5666,7 +5769,7 @@ RenderingDevice::DrawListID RenderingDeviceVulkan::draw_list_begin(RID p_framebu
 	return ID_TYPE_DRAW_LIST;
 }
 
-Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) {
+Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) {
 	_THREAD_SAFE_METHOD_
 
 	ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION);
@@ -5748,7 +5851,7 @@ Error RenderingDeviceVulkan::draw_list_begin_split(RID p_framebuffer, uint32_t p
 	ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE);
 
 	VkCommandBuffer frame_command_buffer = frames[frame].draw_command_buffer;
-	err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS);
+	err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, vkframebuffer, render_pass, frame_command_buffer, VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS, p_storage_textures);
 
 	if (err != OK) {
 		return ERR_CANT_CREATE;
@@ -6229,6 +6332,33 @@ void RenderingDeviceVulkan::draw_list_end() {
 
 	draw_list_bound_textures.clear();
 
+	for (int i = 0; i < draw_list_storage_textures.size(); i++) {
+		Texture *texture = texture_owner.getornull(draw_list_storage_textures[i]);
+
+		VkImageMemoryBarrier image_memory_barrier;
+		image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+		image_memory_barrier.pNext = nullptr;
+		image_memory_barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
+		image_memory_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
+		image_memory_barrier.oldLayout = texture->layout;
+		image_memory_barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+
+		image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+		image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
+		image_memory_barrier.image = texture->image;
+		image_memory_barrier.subresourceRange.aspectMask = texture->read_aspect_mask;
+		image_memory_barrier.subresourceRange.baseMipLevel = texture->base_mipmap;
+		image_memory_barrier.subresourceRange.levelCount = texture->mipmaps;
+		image_memory_barrier.subresourceRange.baseArrayLayer = texture->base_layer;
+		image_memory_barrier.subresourceRange.layerCount = texture->layers;
+
+		vkCmdPipelineBarrier(frames[frame].draw_command_buffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
+
+		texture->layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+	}
+
+	draw_list_storage_textures.clear();
+
 	// To ensure proper synchronization, we must make sure rendering is done before:
 	//  * Some buffer is copied
 	//  * Another render pass happens (since we may be done
@@ -6495,6 +6625,62 @@ void RenderingDeviceVulkan::compute_list_dispatch(ComputeListID p_list, uint32_t
 	vkCmdDispatch(cl->command_buffer, p_x_groups, p_y_groups, p_z_groups);
 }
 
+void RenderingDeviceVulkan::compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset) {
+	ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST);
+	ERR_FAIL_COND(!compute_list);
+
+	ComputeList *cl = compute_list;
+	Buffer *buffer = storage_buffer_owner.getornull(p_buffer);
+	ERR_FAIL_COND(!buffer);
+
+	ERR_FAIL_COND_MSG(!(buffer->usage & STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT), "Buffer provided was not created to do indirect dispatch.");
+
+	ERR_FAIL_COND_MSG(p_offset + 12 > buffer->size, "Offset provided (+12) is past the end of buffer.");
+
+#ifdef DEBUG_ENABLED
+	ERR_FAIL_COND_MSG(!cl->validation.active, "Submitted Compute Lists can no longer be modified.");
+#endif
+
+#ifdef DEBUG_ENABLED
+
+	ERR_FAIL_COND_MSG(!cl->validation.pipeline_active, "No compute pipeline was set before attempting to draw.");
+
+	if (cl->validation.pipeline_push_constant_size > 0) {
+		//using push constants, check that they were supplied
+		ERR_FAIL_COND_MSG(!cl->validation.pipeline_push_constant_supplied,
+				"The shader in this pipeline requires a push constant to be set before drawing, but it's not present.");
+	}
+
+#endif
+
+	//Bind descriptor sets
+
+	for (uint32_t i = 0; i < cl->state.set_count; i++) {
+		if (cl->state.sets[i].pipeline_expected_format == 0) {
+			continue; //nothing expected by this pipeline
+		}
+#ifdef DEBUG_ENABLED
+		if (cl->state.sets[i].pipeline_expected_format != cl->state.sets[i].uniform_set_format) {
+			if (cl->state.sets[i].uniform_set_format == 0) {
+				ERR_FAIL_MSG("Uniforms were never supplied for set (" + itos(i) + ") at the time of drawing, which are required by the pipeline");
+			} else if (uniform_set_owner.owns(cl->state.sets[i].uniform_set)) {
+				UniformSet *us = uniform_set_owner.getornull(cl->state.sets[i].uniform_set);
+				ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + "):\n" + _shader_uniform_debug(us->shader_id, us->shader_set) + "\nare not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader));
+			} else {
+				ERR_FAIL_MSG("Uniforms supplied for set (" + itos(i) + ", which was was just freed) are not the same format as required by the pipeline shader. Pipeline shader requires the following bindings:\n" + _shader_uniform_debug(cl->state.pipeline_shader));
+			}
+		}
+#endif
+		if (!cl->state.sets[i].bound) {
+			//All good, see if this requires re-binding
+			vkCmdBindDescriptorSets(cl->command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, cl->state.pipeline_layout, i, 1, &cl->state.sets[i].descriptor_set, 0, nullptr);
+			cl->state.sets[i].bound = true;
+		}
+	}
+
+	vkCmdDispatchIndirect(cl->command_buffer, buffer->buffer, p_offset);
+}
+
 void RenderingDeviceVulkan::compute_list_add_barrier(ComputeListID p_list) {
 #ifdef FORCE_FULL_BARRIER
 	_full_barrier(true);
@@ -6534,10 +6720,17 @@ void RenderingDeviceVulkan::compute_list_end() {
 #ifdef FORCE_FULL_BARRIER
 	_full_barrier(true);
 #else
-	_memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT, true);
+	_memory_barrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_INPUT_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT, VK_ACCESS_SHADER_WRITE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT | VK_ACCESS_INDEX_READ_BIT | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INDIRECT_COMMAND_READ_BIT, true);
 #endif
 }
 
+void RenderingDeviceVulkan::full_barrier() {
+#ifndef DEBUG_ENABLED
+	ERR_PRINT("Full barrier is debug-only, should not be used in production");
+#endif
+	_full_barrier(true);
+}
+
 #if 0
 void RenderingDeviceVulkan::draw_list_render_secondary_to_framebuffer(ID p_framebuffer, ID *p_draw_lists, uint32_t p_draw_list_count, InitialAction p_initial_action, FinalAction p_final_action, const Vector<Variant> &p_clear_colors) {
 

+ 25 - 7
drivers/vulkan/rendering_device_vulkan.h

@@ -204,10 +204,17 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	Error _insert_staging_block();
 
 	struct Buffer {
-		uint32_t size = 0;
-		VkBuffer buffer = VK_NULL_HANDLE;
-		VmaAllocation allocation = nullptr;
+		uint32_t size;
+		uint32_t usage;
+		VkBuffer buffer;
+		VmaAllocation allocation;
 		VkDescriptorBufferInfo buffer_info; //used for binding
+		Buffer() {
+			size = 0;
+			usage = 0;
+			buffer = VK_NULL_HANDLE;
+			allocation = nullptr;
+		}
 	};
 
 	Error _buffer_allocate(Buffer *p_buffer, uint32_t p_size, uint32_t p_usage, VmaMemoryUsage p_mapping);
@@ -229,8 +236,13 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	// used for the render pipelines.
 
 	struct FramebufferFormatKey {
+		Size2i empty_size;
 		Vector<AttachmentFormat> attachments;
 		bool operator<(const FramebufferFormatKey &p_key) const {
+			if (empty_size != p_key.empty_size) {
+				return empty_size < p_key.empty_size;
+			}
+
 			int as = attachments.size();
 			int bs = p_key.attachments.size();
 			if (as != bs) {
@@ -773,12 +785,13 @@ class RenderingDeviceVulkan : public RenderingDevice {
 	uint32_t draw_list_count;
 	bool draw_list_split;
 	Vector<RID> draw_list_bound_textures;
+	Vector<RID> draw_list_storage_textures;
 	bool draw_list_unbind_color_textures;
 	bool draw_list_unbind_depth_textures;
 
 	void _draw_list_insert_clear_region(DrawList *draw_list, Framebuffer *framebuffer, Point2i viewport_offset, Point2i viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil);
 	Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, VkFramebuffer *r_framebuffer, VkRenderPass *r_render_pass);
-	Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents);
+	Error _draw_list_render_pass_begin(Framebuffer *framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i viewport_offset, Point2i viewport_size, VkFramebuffer vkframebuffer, VkRenderPass render_pass, VkCommandBuffer command_buffer, VkSubpassContents subpass_contents, const Vector<RID> &p_storage_textures);
 	_FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id);
 
 	/**********************/
@@ -923,9 +936,11 @@ public:
 	/*********************/
 
 	virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format);
+	virtual FramebufferFormatID framebuffer_format_create_empty(const Size2i &p_size);
 	virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format);
 
 	virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID);
+	virtual RID framebuffer_create_empty(const Size2i &p_size, FramebufferFormatID p_format_check = INVALID_ID);
 
 	virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer);
 
@@ -961,7 +976,7 @@ public:
 	/*****************/
 
 	virtual RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>());
-	virtual RID storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>());
+	virtual RID storage_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>(), uint32_t p_usage = 0);
 	virtual RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>());
 
 	virtual RID uniform_set_create(const Vector<Uniform> &p_uniforms, RID p_shader, uint32_t p_shader_set);
@@ -998,8 +1013,8 @@ public:
 
 	virtual DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color());
 
-	virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
-	virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
+	virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>());
+	virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>());
 
 	virtual void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline);
 	virtual void draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index);
@@ -1026,8 +1041,11 @@ public:
 	virtual void compute_list_add_barrier(ComputeListID p_list);
 
 	virtual void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups);
+	virtual void compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset);
 	virtual void compute_list_end();
 
+	virtual void full_barrier();
+
 	/**************/
 	/**** FREE ****/
 	/**************/

+ 7 - 1
editor/editor_node.cpp

@@ -360,7 +360,7 @@ void EditorNode::_notification(int p_what) {
 				bool dof_jitter = GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_use_jitter");
 				RS::get_singleton()->camera_effects_set_dof_blur_quality(dof_quality, dof_jitter);
 				RS::get_singleton()->environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size"));
-				RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_curve"));
+				RS::get_singleton()->screen_space_roughness_limiter_set_active(GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_enable"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_amount"), GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_limit"));
 				bool glow_bicubic = int(GLOBAL_GET("rendering/quality/glow/upscale_mode")) > 0;
 				RS::get_singleton()->environment_glow_set_use_bicubic_upscale(glow_bicubic);
 				RS::EnvironmentSSRRoughnessQuality ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/quality/screen_space_reflection/roughness_quality")));
@@ -376,6 +376,12 @@ void EditorNode::_notification(int p_what) {
 				RS::get_singleton()->directional_shadow_quality_set(directional_shadow_quality);
 				float probe_update_speed = GLOBAL_GET("rendering/lightmapper/probe_capture_update_speed");
 				RS::get_singleton()->lightmap_set_probe_capture_update_speed(probe_update_speed);
+				RS::EnvironmentSDFGIFramesToConverge frames_to_converge = RS::EnvironmentSDFGIFramesToConverge(int(GLOBAL_GET("rendering/sdfgi/frames_to_converge")));
+				RS::get_singleton()->environment_set_sdfgi_frames_to_converge(frames_to_converge);
+				RS::EnvironmentSDFGIRayCount ray_count = RS::EnvironmentSDFGIRayCount(int(GLOBAL_GET("rendering/sdfgi/probe_ray_count")));
+				RS::get_singleton()->environment_set_sdfgi_ray_count(ray_count);
+				RS::GIProbeQuality gi_probe_quality = RS::GIProbeQuality(int(GLOBAL_GET("rendering/quality/gi_probes/quality")));
+				RS::get_singleton()->gi_probe_set_quality(gi_probe_quality);
 			}
 
 			ResourceImporterTexture::get_singleton()->update_imports();

+ 17 - 4
editor/plugins/node_3d_editor_plugin.cpp

@@ -487,6 +487,10 @@ ObjectID Node3DEditorViewport::_select_ray(const Point2 &p_pos, bool p_append, b
 	Vector3 pos = _get_ray_pos(p_pos);
 	Vector2 shrinked_pos = p_pos / subviewport_container->get_stretch_shrink();
 
+	if (viewport->get_debug_draw() == Viewport::DEBUG_DRAW_SDFGI_PROBES) {
+		RS::get_singleton()->sdfgi_set_debug_probe_select(pos, ray);
+	}
+
 	Vector<ObjectID> instances = RenderingServer::get_singleton()->instances_cull_ray(pos, ray, get_tree()->get_root()->get_world_3d()->get_scenario());
 	Set<Ref<EditorNode3DGizmo>> found_gizmos;
 
@@ -2984,7 +2988,9 @@ void Node3DEditorViewport::_menu_option(int p_option) {
 		case VIEW_DISPLAY_DEBUG_SSAO:
 		case VIEW_DISPLAY_DEBUG_PSSM_SPLITS:
 		case VIEW_DISPLAY_DEBUG_DECAL_ATLAS:
-		case VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER: {
+		case VIEW_DISPLAY_DEBUG_SDFGI:
+		case VIEW_DISPLAY_DEBUG_SDFGI_PROBES:
+		case VIEW_DISPLAY_DEBUG_GI_BUFFER: {
 			static const int display_options[] = {
 				VIEW_DISPLAY_NORMAL,
 				VIEW_DISPLAY_WIREFRAME,
@@ -3000,9 +3006,11 @@ void Node3DEditorViewport::_menu_option(int p_option) {
 				VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION,
 				VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE,
 				VIEW_DISPLAY_DEBUG_SSAO,
-				VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER,
+				VIEW_DISPLAY_DEBUG_GI_BUFFER,
 				VIEW_DISPLAY_DEBUG_PSSM_SPLITS,
 				VIEW_DISPLAY_DEBUG_DECAL_ATLAS,
+				VIEW_DISPLAY_DEBUG_SDFGI,
+				VIEW_DISPLAY_DEBUG_SDFGI_PROBES,
 				VIEW_MAX
 			};
 			static const Viewport::DebugDraw debug_draw_modes[] = {
@@ -3020,9 +3028,11 @@ void Node3DEditorViewport::_menu_option(int p_option) {
 				Viewport::DEBUG_DRAW_GI_PROBE_EMISSION,
 				Viewport::DEBUG_DRAW_SCENE_LUMINANCE,
 				Viewport::DEBUG_DRAW_SSAO,
-				Viewport::DEBUG_DRAW_ROUGHNESS_LIMITER,
+				Viewport::DEBUG_DRAW_GI_BUFFER,
 				Viewport::DEBUG_DRAW_PSSM_SPLITS,
 				Viewport::DEBUG_DRAW_DECAL_ATLAS,
+				Viewport::DEBUG_DRAW_SDFGI,
+				Viewport::DEBUG_DRAW_SDFGI_PROBES,
 			};
 
 			int idx = 0;
@@ -3898,11 +3908,14 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, Edito
 	display_submenu->add_radio_check_item(TTR("GIProbe Albedo"), VIEW_DISPLAY_DEBUG_GIPROBE_ALBEDO);
 	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("SDFGI Cascades"), VIEW_DISPLAY_DEBUG_SDFGI);
+	display_submenu->add_radio_check_item(TTR("SDFGI Probes"), VIEW_DISPLAY_DEBUG_SDFGI_PROBES);
+	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->add_separator();
-	display_submenu->add_radio_check_item(TTR("Roughness Limiter"), VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER);
+	display_submenu->add_radio_check_item(TTR("GI Buffer"), VIEW_DISPLAY_DEBUG_GI_BUFFER);
 	display_submenu->set_name("display_advanced");
 	view_menu->get_popup()->add_submenu_item(TTR("Display Advanced..."), "display_advanced", VIEW_DISPLAY_ADVANCED);
 	view_menu->get_popup()->add_separator();

+ 3 - 1
editor/plugins/node_3d_editor_plugin.h

@@ -214,9 +214,11 @@ class Node3DEditorViewport : public Control {
 		VIEW_DISPLAY_DEBUG_GIPROBE_EMISSION,
 		VIEW_DISPLAY_DEBUG_SCENE_LUMINANCE,
 		VIEW_DISPLAY_DEBUG_SSAO,
-		VIEW_DISPLAY_DEBUG_ROUGHNESS_LIMITER,
 		VIEW_DISPLAY_DEBUG_PSSM_SPLITS,
 		VIEW_DISPLAY_DEBUG_DECAL_ATLAS,
+		VIEW_DISPLAY_DEBUG_SDFGI,
+		VIEW_DISPLAY_DEBUG_SDFGI_PROBES,
+		VIEW_DISPLAY_DEBUG_GI_BUFFER,
 		VIEW_LOCK_ROTATION,
 		VIEW_CINEMATIC_PREVIEW,
 		VIEW_AUTO_ORTHOGONAL,

+ 3 - 3
scene/3d/baked_lightmap.cpp

@@ -894,13 +894,13 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d
 
 			if (Object::cast_to<DirectionalLight3D>(light)) {
 				DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light);
-				lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_ALL, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE));
+				lightmapper->add_directional_light(light->get_bake_mode() == Light3D::BAKE_STATIC, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_SIZE));
 			} else if (Object::cast_to<OmniLight3D>(light)) {
 				OmniLight3D *l = Object::cast_to<OmniLight3D>(light);
-				lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_ALL, xf.origin, l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
+				lightmapper->add_omni_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
 			} else if (Object::cast_to<SpotLight3D>(light)) {
 				SpotLight3D *l = Object::cast_to<SpotLight3D>(light);
-				lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_ALL, xf.origin, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
+				lightmapper->add_spot_light(light->get_bake_mode() == Light3D::BAKE_STATIC, xf.origin, -xf.basis.get_axis(Vector3::AXIS_Z).normalized(), l->get_color(), l->get_param(Light3D::PARAM_ENERGY), l->get_param(Light3D::PARAM_RANGE), l->get_param(Light3D::PARAM_ATTENUATION), l->get_param(Light3D::PARAM_SPOT_ANGLE), l->get_param(Light3D::PARAM_SPOT_ATTENUATION), l->get_param(Light3D::PARAM_SIZE));
 			}
 		}
 		for (int i = 0; i < probes_found.size(); i++) {

+ 5 - 5
scene/3d/light_3d.cpp

@@ -144,7 +144,7 @@ Vector<Face3> Light3D::get_faces(uint32_t p_usage_flags) const {
 
 void Light3D::set_bake_mode(BakeMode p_mode) {
 	bake_mode = p_mode;
-	RS::get_singleton()->light_set_use_gi(light, p_mode != BAKE_DISABLED);
+	RS::get_singleton()->light_set_bake_mode(light, RS::LightBakeMode(p_mode));
 }
 
 Light3D::BakeMode Light3D::get_bake_mode() const {
@@ -261,7 +261,7 @@ void Light3D::_bind_methods() {
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE);
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative");
 	ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR);
-	ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disable,Indirect,All"), "set_bake_mode", "get_bake_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "light_bake_mode", PROPERTY_HINT_ENUM, "Disabled,Dynamic,Static"), "set_bake_mode", "get_bake_mode");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "light_cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
 	ADD_GROUP("Shadow", "shadow_");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "shadow_enabled"), "set_shadow", "has_shadow");
@@ -296,8 +296,8 @@ void Light3D::_bind_methods() {
 	BIND_ENUM_CONSTANT(PARAM_MAX);
 
 	BIND_ENUM_CONSTANT(BAKE_DISABLED);
-	BIND_ENUM_CONSTANT(BAKE_INDIRECT);
-	BIND_ENUM_CONSTANT(BAKE_ALL);
+	BIND_ENUM_CONSTANT(BAKE_DYNAMIC);
+	BIND_ENUM_CONSTANT(BAKE_STATIC);
 }
 
 Light3D::Light3D(RenderingServer::LightType p_type) {
@@ -319,7 +319,7 @@ Light3D::Light3D(RenderingServer::LightType p_type) {
 	RS::get_singleton()->instance_set_base(get_instance(), light);
 
 	reverse_cull = false;
-	bake_mode = BAKE_INDIRECT;
+	bake_mode = BAKE_DYNAMIC;
 
 	editor_only = false;
 	set_color(Color(1, 1, 1, 1));

+ 2 - 2
scene/3d/light_3d.h

@@ -64,8 +64,8 @@ public:
 
 	enum BakeMode {
 		BAKE_DISABLED,
-		BAKE_INDIRECT,
-		BAKE_ALL
+		BAKE_DYNAMIC,
+		BAKE_STATIC
 	};
 
 private:

+ 36 - 32
scene/3d/reflection_probe.cpp

@@ -39,31 +39,32 @@ float ReflectionProbe::get_intensity() const {
 	return intensity;
 }
 
-void ReflectionProbe::set_interior_ambient(Color p_ambient) {
-	interior_ambient = p_ambient;
-	RS::get_singleton()->reflection_probe_set_interior_ambient(probe, p_ambient);
+void ReflectionProbe::set_ambient_mode(AmbientMode p_mode) {
+	ambient_mode = p_mode;
+	RS::get_singleton()->reflection_probe_set_ambient_mode(probe, RS::ReflectionProbeAmbientMode(p_mode));
+	_change_notify();
 }
 
-void ReflectionProbe::set_interior_ambient_energy(float p_energy) {
-	interior_ambient_energy = p_energy;
-	RS::get_singleton()->reflection_probe_set_interior_ambient_energy(probe, p_energy);
+ReflectionProbe::AmbientMode ReflectionProbe::get_ambient_mode() const {
+	return ambient_mode;
 }
 
-float ReflectionProbe::get_interior_ambient_energy() const {
-	return interior_ambient_energy;
+void ReflectionProbe::set_ambient_color(Color p_ambient) {
+	ambient_color = p_ambient;
+	RS::get_singleton()->reflection_probe_set_ambient_color(probe, p_ambient);
 }
 
-Color ReflectionProbe::get_interior_ambient() const {
-	return interior_ambient;
+void ReflectionProbe::set_ambient_color_energy(float p_energy) {
+	ambient_color_energy = p_energy;
+	RS::get_singleton()->reflection_probe_set_ambient_energy(probe, p_energy);
 }
 
-void ReflectionProbe::set_interior_ambient_probe_contribution(float p_contribution) {
-	interior_ambient_probe_contribution = p_contribution;
-	RS::get_singleton()->reflection_probe_set_interior_ambient_probe_contribution(probe, p_contribution);
+float ReflectionProbe::get_ambient_color_energy() const {
+	return ambient_color_energy;
 }
 
-float ReflectionProbe::get_interior_ambient_probe_contribution() const {
-	return interior_ambient_probe_contribution;
+Color ReflectionProbe::get_ambient_color() const {
+	return ambient_color;
 }
 
 void ReflectionProbe::set_max_distance(float p_distance) {
@@ -130,7 +131,6 @@ bool ReflectionProbe::is_box_projection_enabled() const {
 void ReflectionProbe::set_as_interior(bool p_enable) {
 	interior = p_enable;
 	RS::get_singleton()->reflection_probe_set_as_interior(probe, interior);
-	_change_notify();
 }
 
 bool ReflectionProbe::is_set_as_interior() const {
@@ -176,8 +176,8 @@ Vector<Face3> ReflectionProbe::get_faces(uint32_t p_usage_flags) const {
 }
 
 void ReflectionProbe::_validate_property(PropertyInfo &property) const {
-	if (property.name == "interior/ambient_color" || property.name == "interior/ambient_energy" || property.name == "interior/ambient_contrib") {
-		if (!interior) {
+	if (property.name == "interior/ambient_color" || property.name == "interior/ambient_color_energy") {
+		if (ambient_mode != AMBIENT_COLOR) {
 			property.usage = PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL;
 		}
 	}
@@ -187,14 +187,14 @@ void ReflectionProbe::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_intensity", "intensity"), &ReflectionProbe::set_intensity);
 	ClassDB::bind_method(D_METHOD("get_intensity"), &ReflectionProbe::get_intensity);
 
-	ClassDB::bind_method(D_METHOD("set_interior_ambient", "ambient"), &ReflectionProbe::set_interior_ambient);
-	ClassDB::bind_method(D_METHOD("get_interior_ambient"), &ReflectionProbe::get_interior_ambient);
+	ClassDB::bind_method(D_METHOD("set_ambient_mode", "ambient"), &ReflectionProbe::set_ambient_mode);
+	ClassDB::bind_method(D_METHOD("get_ambient_mode"), &ReflectionProbe::get_ambient_mode);
 
-	ClassDB::bind_method(D_METHOD("set_interior_ambient_energy", "ambient_energy"), &ReflectionProbe::set_interior_ambient_energy);
-	ClassDB::bind_method(D_METHOD("get_interior_ambient_energy"), &ReflectionProbe::get_interior_ambient_energy);
+	ClassDB::bind_method(D_METHOD("set_ambient_color", "ambient"), &ReflectionProbe::set_ambient_color);
+	ClassDB::bind_method(D_METHOD("get_ambient_color"), &ReflectionProbe::get_ambient_color);
 
-	ClassDB::bind_method(D_METHOD("set_interior_ambient_probe_contribution", "ambient_probe_contribution"), &ReflectionProbe::set_interior_ambient_probe_contribution);
-	ClassDB::bind_method(D_METHOD("get_interior_ambient_probe_contribution"), &ReflectionProbe::get_interior_ambient_probe_contribution);
+	ClassDB::bind_method(D_METHOD("set_ambient_color_energy", "ambient_energy"), &ReflectionProbe::set_ambient_color_energy);
+	ClassDB::bind_method(D_METHOD("get_ambient_color_energy"), &ReflectionProbe::get_ambient_color_energy);
 
 	ClassDB::bind_method(D_METHOD("set_max_distance", "max_distance"), &ReflectionProbe::set_max_distance);
 	ClassDB::bind_method(D_METHOD("get_max_distance"), &ReflectionProbe::get_max_distance);
@@ -226,24 +226,28 @@ void ReflectionProbe::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents");
 	ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_as_interior", "is_set_as_interior");
 	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled");
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask");
 
-	ADD_GROUP("Interior", "interior_");
-	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior_enable"), "set_as_interior", "is_set_as_interior");
-	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "interior_ambient_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_interior_ambient", "get_interior_ambient");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interior_ambient_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_interior_ambient_energy", "get_interior_ambient_energy");
-	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interior_ambient_contrib", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_interior_ambient_probe_contribution", "get_interior_ambient_probe_contribution");
+	ADD_GROUP("Ambient", "ambient_");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,ConstantColor"), "set_ambient_mode", "get_ambient_mode");
+	ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ambient_color", "get_ambient_color");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_color_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_color_energy", "get_ambient_color_energy");
 
 	BIND_ENUM_CONSTANT(UPDATE_ONCE);
 	BIND_ENUM_CONSTANT(UPDATE_ALWAYS);
+
+	BIND_ENUM_CONSTANT(AMBIENT_DISABLED);
+	BIND_ENUM_CONSTANT(AMBIENT_ENVIRONMENT);
+	BIND_ENUM_CONSTANT(AMBIENT_COLOR);
 }
 
 ReflectionProbe::ReflectionProbe() {
 	intensity = 1.0;
-	interior_ambient = Color(0, 0, 0);
-	interior_ambient_probe_contribution = 0;
-	interior_ambient_energy = 1.0;
+	ambient_mode = AMBIENT_ENVIRONMENT;
+	ambient_color = Color(0, 0, 0);
+	ambient_color_energy = 1.0;
 	max_distance = 0;
 	extents = Vector3(1, 1, 1);
 	origin_offset = Vector3(0, 0, 0);

+ 17 - 7
scene/3d/reflection_probe.h

@@ -45,6 +45,12 @@ public:
 		UPDATE_ALWAYS,
 	};
 
+	enum AmbientMode {
+		AMBIENT_DISABLED,
+		AMBIENT_ENVIRONMENT,
+		AMBIENT_COLOR
+	};
+
 private:
 	RID probe;
 	float intensity;
@@ -54,9 +60,9 @@ private:
 	bool box_projection;
 	bool enable_shadows;
 	bool interior;
-	Color interior_ambient;
-	float interior_ambient_energy;
-	float interior_ambient_probe_contribution;
+	AmbientMode ambient_mode;
+	Color ambient_color;
+	float ambient_color_energy;
 
 	uint32_t cull_mask;
 	UpdateMode update_mode;
@@ -69,11 +75,14 @@ public:
 	void set_intensity(float p_intensity);
 	float get_intensity() const;
 
-	void set_interior_ambient(Color p_ambient);
-	Color get_interior_ambient() const;
+	void set_ambient_mode(AmbientMode p_mode);
+	AmbientMode get_ambient_mode() const;
+
+	void set_ambient_color(Color p_ambient);
+	Color get_ambient_color() const;
 
-	void set_interior_ambient_energy(float p_energy);
-	float get_interior_ambient_energy() const;
+	void set_ambient_color_energy(float p_energy);
+	float get_ambient_color_energy() const;
 
 	void set_interior_ambient_probe_contribution(float p_contribution);
 	float get_interior_ambient_probe_contribution() const;
@@ -109,6 +118,7 @@ public:
 	~ReflectionProbe();
 };
 
+VARIANT_ENUM_CAST(ReflectionProbe::AmbientMode);
 VARIANT_ENUM_CAST(ReflectionProbe::UpdateMode);
 
 #endif // REFLECTIONPROBE_H

+ 3 - 1
scene/main/viewport.cpp

@@ -3404,9 +3404,11 @@ void Viewport::_bind_methods() {
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS);
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_SCENE_LUMINANCE);
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_SSAO);
-	BIND_ENUM_CONSTANT(DEBUG_DRAW_ROUGHNESS_LIMITER);
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_PSSM_SPLITS);
 	BIND_ENUM_CONSTANT(DEBUG_DRAW_DECAL_ATLAS);
+	BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI);
+	BIND_ENUM_CONSTANT(DEBUG_DRAW_SDFGI_PROBES);
+	BIND_ENUM_CONSTANT(DEBUG_DRAW_GI_BUFFER);
 
 	BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_NEAREST);
 	BIND_ENUM_CONSTANT(DEFAULT_CANVAS_ITEM_TEXTURE_FILTER_LINEAR);

+ 4 - 2
scene/main/viewport.h

@@ -137,9 +137,11 @@ public:
 		DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
 		DEBUG_DRAW_SCENE_LUMINANCE,
 		DEBUG_DRAW_SSAO,
-		DEBUG_DRAW_ROUGHNESS_LIMITER,
 		DEBUG_DRAW_PSSM_SPLITS,
-		DEBUG_DRAW_DECAL_ATLAS
+		DEBUG_DRAW_DECAL_ATLAS,
+		DEBUG_DRAW_SDFGI,
+		DEBUG_DRAW_SDFGI_PROBES,
+		DEBUG_DRAW_GI_BUFFER,
 	};
 
 	enum DefaultCanvasItemTextureFilter {

+ 192 - 0
scene/resources/environment.cpp

@@ -745,6 +745,135 @@ bool Environment::_set(const StringName &p_name, const Variant &p_value) {
 }
 #endif
 
+void Environment::set_sdfgi_enabled(bool p_enabled) {
+	sdfgi_enabled = p_enabled;
+	_update_sdfgi();
+}
+
+bool Environment::is_sdfgi_enabled() const {
+	return sdfgi_enabled;
+}
+
+void Environment::set_sdfgi_cascades(SDFGICascades p_cascades) {
+	sdfgi_cascades = p_cascades;
+	_update_sdfgi();
+}
+Environment::SDFGICascades Environment::get_sdfgi_cascades() const {
+	return sdfgi_cascades;
+}
+
+void Environment::set_sdfgi_min_cell_size(float p_size) {
+	sdfgi_min_cell_size = p_size;
+	_change_notify("sdfgi_max_distance");
+	_change_notify("sdfgi_cascade0_distance");
+	_update_sdfgi();
+}
+float Environment::get_sdfgi_min_cell_size() const {
+	return sdfgi_min_cell_size;
+}
+
+void Environment::set_sdfgi_max_distance(float p_size) {
+	p_size /= 64.0;
+	int cc[3] = { 4, 6, 8 };
+	int cascades = cc[sdfgi_cascades];
+	for (int i = 0; i < cascades; i++) {
+		p_size *= 0.5; //halve for each cascade
+	}
+	sdfgi_min_cell_size = p_size;
+	_change_notify("sdfgi_min_cell_size");
+	_change_notify("sdfgi_cascade0_distance");
+	_update_sdfgi();
+}
+float Environment::get_sdfgi_max_distance() const {
+	float md = sdfgi_min_cell_size;
+	md *= 64.0;
+	int cc[3] = { 4, 6, 8 };
+	int cascades = cc[sdfgi_cascades];
+	for (int i = 0; i < cascades; i++) {
+		md *= 2.0;
+	}
+	return md;
+}
+
+void Environment::set_sdfgi_cascade0_distance(float p_size) {
+	sdfgi_min_cell_size = p_size / 64.0;
+	_change_notify("sdfgi_min_cell_size");
+	_change_notify("sdfgi_max_distance");
+	_update_sdfgi();
+}
+float Environment::get_sdfgi_cascade0_distance() const {
+	return sdfgi_min_cell_size * 64.0;
+}
+
+void Environment::set_sdfgi_use_occlusion(bool p_enable) {
+	sdfgi_use_occlusion = p_enable;
+	_update_sdfgi();
+}
+
+bool Environment::is_sdfgi_using_occlusion() const {
+	return sdfgi_use_occlusion;
+}
+
+void Environment::set_sdfgi_use_multi_bounce(bool p_enable) {
+	sdfgi_use_multibounce = p_enable;
+	_update_sdfgi();
+}
+bool Environment::is_sdfgi_using_multi_bounce() const {
+	return sdfgi_use_multibounce;
+}
+
+void Environment::set_sdfgi_use_enhance_ssr(bool p_enable) {
+	sdfgi_enhance_ssr = p_enable;
+	_update_sdfgi();
+}
+bool Environment::is_sdfgi_using_enhance_ssr() const {
+	return sdfgi_enhance_ssr;
+}
+
+void Environment::set_sdfgi_read_sky_light(bool p_enable) {
+	sdfgi_read_sky_light = p_enable;
+	_update_sdfgi();
+}
+
+bool Environment::is_sdfgi_reading_sky_light() const {
+	return sdfgi_read_sky_light;
+}
+
+void Environment::set_sdfgi_energy(float p_energy) {
+	sdfgi_energy = p_energy;
+	_update_sdfgi();
+}
+float Environment::get_sdfgi_energy() const {
+	return sdfgi_energy;
+}
+
+void Environment::set_sdfgi_normal_bias(float p_bias) {
+	sdfgi_normal_bias = p_bias;
+	_update_sdfgi();
+}
+float Environment::get_sdfgi_normal_bias() const {
+	return sdfgi_normal_bias;
+}
+
+void Environment::set_sdfgi_probe_bias(float p_bias) {
+	sdfgi_probe_bias = p_bias;
+	_update_sdfgi();
+}
+float Environment::get_sdfgi_probe_bias() const {
+	return sdfgi_probe_bias;
+}
+
+void Environment::set_sdfgi_y_scale(SDFGIYScale p_y_scale) {
+	sdfgi_y_scale = p_y_scale;
+	_update_sdfgi();
+}
+Environment::SDFGIYScale Environment::get_sdfgi_y_scale() const {
+	return sdfgi_y_scale;
+}
+void Environment::_update_sdfgi() {
+	RS::get_singleton()->environment_set_sdfgi(environment, sdfgi_enabled, RS::EnvironmentSDFGICascades(sdfgi_cascades), sdfgi_min_cell_size, RS::EnvironmentSDFGIYScale(sdfgi_y_scale), sdfgi_use_occlusion, sdfgi_use_multibounce, sdfgi_read_sky_light, sdfgi_enhance_ssr, sdfgi_energy, sdfgi_normal_bias, sdfgi_probe_bias);
+}
+
 void Environment::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_background", "mode"), &Environment::set_background);
 	ClassDB::bind_method(D_METHOD("set_sky", "sky"), &Environment::set_sky);
@@ -944,6 +1073,56 @@ void Environment::_bind_methods() {
 	ADD_PROPERTY(PropertyInfo(Variant::INT, "ssao_blur", PROPERTY_HINT_ENUM, "Disabled,1x1,2x2,3x3"), "set_ssao_blur", "get_ssao_blur");
 	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ssao_edge_sharpness", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_ssao_edge_sharpness", "get_ssao_edge_sharpness");
 
+	ClassDB::bind_method(D_METHOD("set_sdfgi_enabled", "enabled"), &Environment::set_sdfgi_enabled);
+	ClassDB::bind_method(D_METHOD("is_sdfgi_enabled"), &Environment::is_sdfgi_enabled);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_cascades", "amount"), &Environment::set_sdfgi_cascades);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_cascades"), &Environment::get_sdfgi_cascades);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_min_cell_size", "strength"), &Environment::set_sdfgi_min_cell_size);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_min_cell_size"), &Environment::get_sdfgi_min_cell_size);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_max_distance", "strength"), &Environment::set_sdfgi_max_distance);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_max_distance"), &Environment::get_sdfgi_max_distance);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_cascade0_distance", "strength"), &Environment::set_sdfgi_cascade0_distance);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_cascade0_distance"), &Environment::get_sdfgi_cascade0_distance);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_use_occlusion", "enable"), &Environment::set_sdfgi_use_occlusion);
+	ClassDB::bind_method(D_METHOD("is_sdfgi_using_occlusion"), &Environment::is_sdfgi_using_occlusion);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_use_multi_bounce", "enable"), &Environment::set_sdfgi_use_multi_bounce);
+	ClassDB::bind_method(D_METHOD("is_sdfgi_using_multi_bounce"), &Environment::is_sdfgi_using_multi_bounce);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_read_sky_light", "enable"), &Environment::set_sdfgi_read_sky_light);
+	ClassDB::bind_method(D_METHOD("is_sdfgi_reading_sky_light"), &Environment::is_sdfgi_reading_sky_light);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_energy", "amount"), &Environment::set_sdfgi_energy);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_energy"), &Environment::get_sdfgi_energy);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_normal_bias", "bias"), &Environment::set_sdfgi_normal_bias);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_normal_bias"), &Environment::get_sdfgi_normal_bias);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_probe_bias", "bias"), &Environment::set_sdfgi_probe_bias);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_probe_bias"), &Environment::get_sdfgi_probe_bias);
+
+	ClassDB::bind_method(D_METHOD("set_sdfgi_y_scale", "scale"), &Environment::set_sdfgi_y_scale);
+	ClassDB::bind_method(D_METHOD("get_sdfgi_y_scale"), &Environment::get_sdfgi_y_scale);
+
+	ADD_GROUP("SDFGI", "sdfgi_");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_enabled"), "set_sdfgi_enabled", "is_sdfgi_enabled");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_multi_bounce"), "set_sdfgi_use_multi_bounce", "is_sdfgi_using_multi_bounce");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_use_occlusion"), "set_sdfgi_use_occlusion", "is_sdfgi_using_occlusion");
+	ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sdfgi_read_sky_light"), "set_sdfgi_read_sky_light", "is_sdfgi_reading_sky_light");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_cascades", PROPERTY_HINT_ENUM, "4 Cascades,6 Cascades,8 Cascades"), "set_sdfgi_cascades", "get_sdfgi_cascades");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_min_cell_size", PROPERTY_HINT_RANGE, "0.01,64,0.01"), "set_sdfgi_min_cell_size", "get_sdfgi_min_cell_size");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_cascade0_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_cascade0_distance", "get_sdfgi_cascade0_distance");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_max_distance", PROPERTY_HINT_RANGE, "0.1,16384,0.1,or_greater"), "set_sdfgi_max_distance", "get_sdfgi_max_distance");
+	ADD_PROPERTY(PropertyInfo(Variant::INT, "sdfgi_y_scale", PROPERTY_HINT_ENUM, "Disable,75%,50%"), "set_sdfgi_y_scale", "get_sdfgi_y_scale");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_energy"), "set_sdfgi_energy", "get_sdfgi_energy");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_normal_bias"), "set_sdfgi_normal_bias", "get_sdfgi_normal_bias");
+	ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "sdfgi_probe_bias"), "set_sdfgi_probe_bias", "get_sdfgi_probe_bias");
+
 	ClassDB::bind_method(D_METHOD("set_glow_enabled", "enabled"), &Environment::set_glow_enabled);
 	ClassDB::bind_method(D_METHOD("is_glow_enabled"), &Environment::is_glow_enabled);
 
@@ -1128,6 +1307,19 @@ Environment::Environment() {
 
 	set_fog_color(Color(0.5, 0.6, 0.7));
 	set_fog_sun_color(Color(1.0, 0.9, 0.7));
+
+	sdfgi_enabled = false;
+	sdfgi_cascades = SDFGI_CASCADES_6;
+	sdfgi_min_cell_size = 0.2;
+	sdfgi_use_occlusion = false;
+	sdfgi_use_multibounce = false;
+	sdfgi_read_sky_light = false;
+	sdfgi_enhance_ssr = false;
+	sdfgi_energy = 1.0;
+	sdfgi_normal_bias = 1.1;
+	sdfgi_probe_bias = 1.1;
+	sdfgi_y_scale = SDFGI_Y_SCALE_DISABLED;
+	_update_sdfgi();
 }
 
 Environment::~Environment() {

+ 67 - 0
scene/resources/environment.h

@@ -86,6 +86,18 @@ public:
 		SSAO_BLUR_3x3
 	};
 
+	enum SDFGICascades {
+		SDFGI_CASCADES_4,
+		SDFGI_CASCADES_6,
+		SDFGI_CASCADES_8,
+	};
+
+	enum SDFGIYScale {
+		SDFGI_Y_SCALE_DISABLED,
+		SDFGI_Y_SCALE_75_PERCENT,
+		SDFGI_Y_SCALE_50_PERCENT,
+	};
+
 private:
 	RID environment;
 
@@ -163,6 +175,20 @@ private:
 	float fog_height_max;
 	float fog_height_curve;
 
+	bool sdfgi_enabled;
+	SDFGICascades sdfgi_cascades;
+	float sdfgi_min_cell_size;
+	bool sdfgi_use_occlusion;
+	bool sdfgi_use_multibounce;
+	bool sdfgi_read_sky_light;
+	bool sdfgi_enhance_ssr;
+	float sdfgi_energy;
+	float sdfgi_normal_bias;
+	float sdfgi_probe_bias;
+	SDFGIYScale sdfgi_y_scale;
+
+	void _update_sdfgi();
+
 protected:
 	static void _bind_methods();
 	virtual void _validate_property(PropertyInfo &property) const;
@@ -354,6 +380,45 @@ public:
 	void set_fog_height_curve(float p_distance);
 	float get_fog_height_curve() const;
 
+	void set_sdfgi_enabled(bool p_enabled);
+	bool is_sdfgi_enabled() const;
+
+	void set_sdfgi_cascades(SDFGICascades p_cascades);
+	SDFGICascades get_sdfgi_cascades() const;
+
+	void set_sdfgi_min_cell_size(float p_size);
+	float get_sdfgi_min_cell_size() const;
+
+	void set_sdfgi_cascade0_distance(float p_size);
+	float get_sdfgi_cascade0_distance() const;
+
+	void set_sdfgi_max_distance(float p_size);
+	float get_sdfgi_max_distance() const;
+
+	void set_sdfgi_use_occlusion(bool p_enable);
+	bool is_sdfgi_using_occlusion() const;
+
+	void set_sdfgi_use_multi_bounce(bool p_enable);
+	bool is_sdfgi_using_multi_bounce() const;
+
+	void set_sdfgi_use_enhance_ssr(bool p_enable);
+	bool is_sdfgi_using_enhance_ssr() const;
+
+	void set_sdfgi_read_sky_light(bool p_enable);
+	bool is_sdfgi_reading_sky_light() const;
+
+	void set_sdfgi_energy(float p_energy);
+	float get_sdfgi_energy() const;
+
+	void set_sdfgi_normal_bias(float p_bias);
+	float get_sdfgi_normal_bias() const;
+
+	void set_sdfgi_probe_bias(float p_bias);
+	float get_sdfgi_probe_bias() const;
+
+	void set_sdfgi_y_scale(SDFGIYScale p_y_scale);
+	SDFGIYScale get_sdfgi_y_scale() const;
+
 	virtual RID get_rid() const;
 
 	Environment();
@@ -366,6 +431,8 @@ VARIANT_ENUM_CAST(Environment::ReflectionSource)
 VARIANT_ENUM_CAST(Environment::ToneMapper)
 VARIANT_ENUM_CAST(Environment::GlowBlendMode)
 VARIANT_ENUM_CAST(Environment::SSAOBlur)
+VARIANT_ENUM_CAST(Environment::SDFGICascades)
+VARIANT_ENUM_CAST(Environment::SDFGIYScale)
 
 class CameraEffects : public Resource {
 	GDCLASS(CameraEffects, Resource);

+ 30 - 8
servers/rendering/rasterizer.h

@@ -50,6 +50,16 @@ public:
 	virtual int get_directional_light_shadow_size(RID p_light_intance) = 0;
 	virtual void set_directional_shadow_count(int p_count) = 0;
 
+	/* SDFGI UPDATE */
+
+	struct InstanceBase;
+
+	virtual void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) = 0;
+	virtual int sdfgi_get_pending_region_count(RID p_render_buffers) const = 0;
+	virtual AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const = 0;
+	virtual uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const = 0;
+	virtual void sdfgi_update_probes(RID p_render_buffers, RID p_environment, const RID *p_directional_light_instances, uint32_t p_directional_light_count, const RID *p_positional_light_instances, uint32_t p_positional_light_count) = 0;
+
 	/* SKY API */
 
 	virtual RID sky_create() = 0;
@@ -86,6 +96,11 @@ public:
 
 	virtual void environment_set_ssao_quality(RS::EnvironmentSSAOQuality p_quality, bool p_half_size) = 0;
 
+	virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, bool p_use_multibounce, bool p_read_sky, bool p_enhance_ssr, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
+
+	virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) = 0;
+	virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) = 0;
+
 	virtual void environment_set_tonemap(RID p_env, RS::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;
 
 	virtual void environment_set_adjustment(RID p_env, bool p_enable, float p_brightness, float p_contrast, float p_saturation, RID p_ramp) = 0;
@@ -111,8 +126,6 @@ public:
 	virtual void shadows_quality_set(RS::ShadowQuality p_quality) = 0;
 	virtual void directional_shadow_quality_set(RS::ShadowQuality p_quality) = 0;
 
-	struct InstanceBase;
-
 	struct InstanceDependency {
 		void instance_notify_changed(bool p_aabb, bool p_dependencies);
 		void instance_notify_deleted(RID p_deleted);
@@ -248,6 +261,7 @@ public:
 
 	virtual RID light_instance_create(RID p_light) = 0;
 	virtual void light_instance_set_transform(RID p_light_instance, const Transform &p_transform) = 0;
+	virtual void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) = 0;
 	virtual void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2()) = 0;
 	virtual void light_instance_mark_visible(RID p_light_instance) = 0;
 	virtual bool light_instances_can_render_shadow_cube() const {
@@ -273,10 +287,14 @@ public:
 	virtual bool gi_probe_needs_update(RID p_probe) const = 0;
 	virtual void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects) = 0;
 
+	virtual void gi_probe_set_quality(RS::GIProbeQuality) = 0;
+
 	virtual void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass) = 0;
 
 	virtual void render_shadow(RID p_light, RID p_shadow_atlas, int p_pass, InstanceBase **p_cull_result, int p_cull_count) = 0;
 	virtual void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
+	virtual void render_sdfgi(RID p_render_buffers, int p_region, InstanceBase **p_cull_result, int p_cull_count) = 0;
+	virtual void render_sdfgi_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const RID **p_positional_light_cull_result, const uint32_t *p_positional_light_cull_count) = 0;
 
 	virtual void set_scene_pass(uint64_t p_pass) = 0;
 	virtual void set_time(double p_time, double p_step) = 0;
@@ -285,7 +303,7 @@ public:
 	virtual RID render_buffers_create() = 0;
 	virtual void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa) = 0;
 
-	virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) = 0;
+	virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0;
 	virtual bool screen_space_roughness_limiter_is_active() const = 0;
 
 	virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality) = 0;
@@ -295,6 +313,8 @@ public:
 
 	virtual bool free(RID p_rid) = 0;
 
+	virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0;
+
 	virtual void update() = 0;
 	virtual ~RasterizerScene() {}
 };
@@ -484,7 +504,8 @@ public:
 	virtual void light_set_negative(RID p_light, bool p_enable) = 0;
 	virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) = 0;
 	virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) = 0;
-	virtual void light_set_use_gi(RID p_light, bool p_enable) = 0;
+	virtual void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) = 0;
+	virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) = 0;
 
 	virtual void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode) = 0;
 
@@ -503,7 +524,8 @@ public:
 	virtual AABB light_get_aabb(RID p_light) const = 0;
 	virtual float light_get_param(RID p_light, RS::LightParam p_param) = 0;
 	virtual Color light_get_color(RID p_light) = 0;
-	virtual bool light_get_use_gi(RID p_light) = 0;
+	virtual RS::LightBakeMode light_get_bake_mode(RID p_light) = 0;
+	virtual uint32_t light_get_max_sdfgi_cascade(RID p_light) = 0;
 	virtual uint64_t light_get_version(RID p_light) const = 0;
 
 	/* PROBE API */
@@ -513,9 +535,9 @@ public:
 	virtual void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode) = 0;
 	virtual void reflection_probe_set_resolution(RID p_probe, int p_resolution) = 0;
 	virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0;
-	virtual void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) = 0;
-	virtual void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) = 0;
-	virtual void reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) = 0;
+	virtual void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) = 0;
+	virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) = 0;
+	virtual void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) = 0;
 	virtual void reflection_probe_set_max_distance(RID p_probe, float p_distance) = 0;
 	virtual void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) = 0;
 	virtual void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) = 0;

+ 48 - 5
servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp

@@ -218,7 +218,7 @@ void RasterizerEffectsRD::copy_to_atlas_fb(RID p_source_rd_texture, RID p_dest_f
 	RD::get_singleton()->draw_list_draw(draw_list, true);
 }
 
-void RasterizerEffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero) {
+void RasterizerEffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y, bool p_force_luminance, bool p_alpha_to_zero, bool p_srgb, RID p_secondary) {
 	zeromem(&copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
 
 	if (p_flip_y) {
@@ -230,10 +230,16 @@ void RasterizerEffectsRD::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_fr
 	if (p_alpha_to_zero) {
 		copy_to_fb.push_constant.alpha_to_zero = true;
 	}
+	if (p_srgb) {
+		copy_to_fb.push_constant.srgb = true;
+	}
 
 	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, copy_to_fb.pipelines[COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
+	RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[p_secondary.is_valid() ? COPY_TO_FB_COPY2 : COPY_TO_FB_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer)));
 	RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_source_rd_texture), 0);
+	if (p_secondary.is_valid()) {
+		RD::get_singleton()->draw_list_bind_uniform_set(draw_list, _get_uniform_set_from_texture(p_secondary), 1);
+	}
 	RD::get_singleton()->draw_list_bind_index_array(draw_list, index_array);
 	RD::get_singleton()->draw_list_set_push_constant(draw_list, &copy_to_fb.push_constant, sizeof(CopyToFbPushConstant));
 	RD::get_singleton()->draw_list_draw(draw_list, true);
@@ -434,7 +440,7 @@ void RasterizerEffectsRD::gaussian_glow(RID p_source_rd_texture, RID p_texture,
 	RD::get_singleton()->compute_list_end();
 }
 
-void RasterizerEffectsRD::screen_space_reflection(RID p_diffuse, RID p_normal, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_roughness, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera) {
+void RasterizerEffectsRD::screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RenderingServer::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera) {
 	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
 
 	int32_t x_groups = (p_screen_size.width - 1) / 8 + 1;
@@ -451,7 +457,7 @@ void RasterizerEffectsRD::screen_space_reflection(RID p_diffuse, RID p_normal, R
 		RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, ssr_scale.pipeline);
 
 		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_diffuse), 0);
-		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture_pair(p_depth, p_normal), 1);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture_pair(p_depth, p_normal_roughness), 1);
 		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_output_blur), 2);
 		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_scale_depth, p_scale_normal), 3);
 
@@ -491,7 +497,7 @@ void RasterizerEffectsRD::screen_space_reflection(RID p_diffuse, RID p_normal, R
 
 		if (p_roughness_quality != RS::ENV_SSR_ROUGNESS_QUALITY_DISABLED) {
 			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_output, p_blur_radius), 1);
-			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture_pair(p_metallic, p_roughness), 3);
+			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture_pair(p_metallic, p_normal_roughness), 3);
 		} else {
 			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_output), 1);
 			RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_metallic), 3);
@@ -1201,6 +1207,28 @@ void RasterizerEffectsRD::render_sky(RD::DrawListID p_list, float p_time, RID p_
 	RD::get_singleton()->draw_list_draw(draw_list, true);
 }
 
+void RasterizerEffectsRD::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_giprobe, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_giprobe, Vector2i p_screen_size, int p_samples) {
+	ResolvePushConstant push_constant;
+	push_constant.screen_size[0] = p_screen_size.x;
+	push_constant.screen_size[1] = p_screen_size.y;
+	push_constant.samples = p_samples;
+
+	RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin();
+	RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, resolve.pipelines[p_source_giprobe.is_valid() ? RESOLVE_MODE_GI_GIPROBE : RESOLVE_MODE_GI]);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture_pair(p_source_depth, p_source_normal_roughness), 0);
+	RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_image_pair(p_dest_depth, p_dest_normal_roughness), 1);
+	if (p_source_giprobe.is_valid()) {
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_compute_uniform_set_from_texture(p_source_giprobe), 2);
+		RD::get_singleton()->compute_list_bind_uniform_set(compute_list, _get_uniform_set_from_image(p_dest_giprobe), 3);
+	}
+
+	RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(ResolvePushConstant));
+
+	RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1, 8, 8, 1);
+
+	RD::get_singleton()->compute_list_end();
+}
+
 RasterizerEffectsRD::RasterizerEffectsRD() {
 	{ // Initialize copy
 		Vector<String> copy_modes;
@@ -1228,6 +1256,7 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
 		Vector<String> copy_modes;
 		copy_modes.push_back("\n");
 		copy_modes.push_back("\n#define MODE_PANORAMA_TO_DP\n");
+		copy_modes.push_back("\n#define MODE_TWO_SOURCES\n");
 
 		copy_to_fb.shader.initialize(copy_modes);
 
@@ -1517,6 +1546,20 @@ RasterizerEffectsRD::RasterizerEffectsRD() {
 		}
 	}
 
+	{
+		Vector<String> resolve_modes;
+		resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n");
+		resolve_modes.push_back("\n#define MODE_RESOLVE_GI\n#define GIPROBE_RESOLVE\n");
+
+		resolve.shader.initialize(resolve_modes);
+
+		resolve.shader_version = resolve.shader.version_create();
+
+		for (int i = 0; i < RESOLVE_MODE_MAX; i++) {
+			resolve.pipelines[i] = RD::get_singleton()->compute_pipeline_create(resolve.shader.version_get_shader(resolve.shader_version, i));
+		}
+	}
+
 	RD::SamplerState sampler;
 	sampler.mag_filter = RD::SAMPLER_FILTER_LINEAR;
 	sampler.min_filter = RD::SAMPLER_FILTER_LINEAR;

+ 27 - 3
servers/rendering/rasterizer_rd/rasterizer_effects_rd.h

@@ -41,6 +41,7 @@
 #include "servers/rendering/rasterizer_rd/shaders/cubemap_filter.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/cubemap_roughness.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/luminance_reduce.glsl.gen.h"
+#include "servers/rendering/rasterizer_rd/shaders/resolve.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/roughness_limiter.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/screen_space_reflection_filter.glsl.gen.h"
@@ -114,6 +115,7 @@ class RasterizerEffectsRD {
 	enum CopyToFBMode {
 		COPY_TO_FB_COPY,
 		COPY_TO_FB_COPY_PANORAMA_TO_DP,
+		COPY_TO_FB_COPY2,
 		COPY_TO_FB_MAX,
 
 	};
@@ -126,7 +128,8 @@ class RasterizerEffectsRD {
 
 		uint32_t force_luminance;
 		uint32_t alpha_to_zero;
-		uint32_t pad[2];
+		uint32_t srgb;
+		uint32_t pad;
 	};
 
 	struct CopyToFb {
@@ -512,6 +515,25 @@ class RasterizerEffectsRD {
 		RID pipelines[3]; //3 quality levels
 	} sss;
 
+	struct ResolvePushConstant {
+		int32_t screen_size[2];
+		int32_t samples;
+		uint32_t pad;
+	};
+
+	enum ResolveMode {
+		RESOLVE_MODE_GI,
+		RESOLVE_MODE_GI_GIPROBE,
+		RESOLVE_MODE_MAX
+	};
+
+	struct Resolve {
+		ResolvePushConstant push_constant;
+		ResolveShaderRD shader;
+		RID shader_version;
+		RID pipelines[RESOLVE_MODE_MAX]; //3 quality levels
+	} resolve;
+
 	RID default_sampler;
 	RID default_mipmap_sampler;
 	RID index_buffer;
@@ -544,7 +566,7 @@ class RasterizerEffectsRD {
 	RID _get_compute_uniform_set_from_image_pair(RID p_texture, RID p_texture2);
 
 public:
-	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false);
+	void copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_alpha_to_zero = false, bool p_srgb = false, RID p_secondary = RID());
 	void copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, const Rect2i &p_rect, bool p_flip_y = false, bool p_force_luminance = false, bool p_all_source = false, bool p_8_bit_dst = false);
 	void copy_cubemap_to_panorama(RID p_source_cube, RID p_dest_panorama, const Size2i &p_panorama_size, float p_lod, bool p_is_array);
 	void copy_depth_to_rect(RID p_source_rd_texture, RID p_dest_framebuffer, const Rect2i &p_rect, bool p_flip_y = false);
@@ -605,10 +627,12 @@ public:
 	void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array);
 	void render_sky(RD::DrawListID p_list, float p_time, RID p_fb, RID p_samplers, RID p_lights, RenderPipelineVertexFormatCacheRD *p_pipeline, RID p_uniform_set, RID p_texture_set, const CameraMatrix &p_camera, const Basis &p_orientation, float p_multiplier, const Vector3 &p_position);
 
-	void screen_space_reflection(RID p_diffuse, RID p_normal, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_roughness, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera);
+	void screen_space_reflection(RID p_diffuse, RID p_normal_roughness, RS::EnvironmentSSRRoughnessQuality p_roughness_quality, RID p_blur_radius, RID p_blur_radius2, RID p_metallic, const Color &p_metallic_mask, RID p_depth, RID p_scale_depth, RID p_scale_normal, RID p_output, RID p_output_blur, const Size2i &p_screen_size, int p_max_steps, float p_fade_in, float p_fade_out, float p_tolerance, const CameraMatrix &p_camera);
 	void merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_base, RID p_reflection);
 	void sub_surface_scattering(RID p_diffuse, RID p_diffuse2, RID p_depth, const CameraMatrix &p_camera, const Size2i &p_screen_size, float p_scale, float p_depth_scale, RS::SubSurfaceScatteringQuality p_quality);
 
+	void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_giprobe, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_giprobe, Vector2i p_screen_size, int p_samples);
+
 	RasterizerEffectsRD();
 	~RasterizerEffectsRD();
 };

+ 1 - 0
servers/rendering/rasterizer_rd/rasterizer_rd.h

@@ -74,6 +74,7 @@ public:
 
 	_ALWAYS_INLINE_ uint64_t get_frame_number() const { return frame; }
 	_ALWAYS_INLINE_ float get_frame_delta_time() const { return delta; }
+	_ALWAYS_INLINE_ double get_total_time() const { return time; }
 
 	static Error is_viable() {
 		return OK;

File diff suppressed because it is too large
+ 428 - 264
servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp


+ 49 - 42
servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h

@@ -47,18 +47,23 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		MATERIAL_UNIFORM_SET = 5
 	};
 
+	enum {
+		SDFGI_MAX_CASCADES = 8,
+		MAX_GI_PROBES = 8
+	};
+
 	/* Scene Shader */
 
 	enum ShaderVersion {
 		SHADER_VERSION_DEPTH_PASS,
 		SHADER_VERSION_DEPTH_PASS_DP,
-		SHADER_VERSION_DEPTH_PASS_WITH_NORMAL,
 		SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS,
+		SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_GIPROBE,
 		SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL,
+		SHADER_VERSION_DEPTH_PASS_WITH_SDF,
 		SHADER_VERSION_COLOR_PASS,
+		SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI,
 		SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR,
-		SHADER_VERSION_VCT_COLOR_PASS,
-		SHADER_VERSION_VCT_COLOR_PASS_WITH_SEPARATE_SPECULAR,
 		SHADER_VERSION_LIGHTMAP_COLOR_PASS,
 		SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR,
 		SHADER_VERSION_MAX
@@ -203,8 +208,11 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		RID color;
 		RID depth;
 		RID specular;
-		RID normal_buffer;
-		RID roughness_buffer;
+		RID normal_roughness_buffer;
+		RID giprobe_buffer;
+
+		RID ambient_buffer;
+		RID reflection_buffer;
 
 		RS::ViewportMSAA msaa;
 		RD::TextureSamples texture_samples;
@@ -212,18 +220,22 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		RID color_msaa;
 		RID depth_msaa;
 		RID specular_msaa;
-		RID normal_buffer_msaa;
+		RID normal_roughness_buffer_msaa;
 		RID roughness_buffer_msaa;
+		RID giprobe_buffer_msaa;
 
 		RID depth_fb;
-		RID depth_normal_fb;
 		RID depth_normal_roughness_fb;
+		RID depth_normal_roughness_giprobe_fb;
 		RID color_fb;
 		RID color_specular_fb;
 		RID specular_only_fb;
 		int width, height;
 
+		RID render_sdfgi_uniform_set;
 		void ensure_specular();
+		void ensure_gi();
+		void ensure_giprobe();
 		void clear();
 		virtual void configure(RID p_color_buffer, RID p_depth_buffer, int p_width, int p_height, RS::ViewportMSAA p_msaa);
 
@@ -233,8 +245,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	};
 
 	virtual RenderBufferData *_create_render_buffer_data();
-	void _allocate_normal_texture(RenderBufferDataHighEnd *rb);
-	void _allocate_roughness_texture(RenderBufferDataHighEnd *rb);
+	void _allocate_normal_roughness_texture(RenderBufferDataHighEnd *rb);
 
 	RID shadow_sampler;
 	RID render_base_uniform_set;
@@ -245,11 +256,12 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	virtual void _base_uniforms_changed();
 	void _render_buffers_clear_uniform_set(RenderBufferDataHighEnd *rb);
 	virtual void _render_buffers_uniform_set_changed(RID p_render_buffers);
-	virtual RID _render_buffers_get_roughness_texture(RID p_render_buffers);
 	virtual RID _render_buffers_get_normal_texture(RID p_render_buffers);
+	virtual RID _render_buffers_get_ambient_texture(RID p_render_buffers);
+	virtual RID _render_buffers_get_reflection_texture(RID p_render_buffers);
 
 	void _update_render_base_uniform_set();
-	void _setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas);
+	void _setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count);
 	void _update_render_buffers_uniform_set(RID p_render_buffers);
 
 	/* Scene State UBO */
@@ -260,7 +272,8 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		float box_offset[3];
 		uint32_t mask;
 		float params[4]; // intensity, 0, interior , boxproject
-		float ambient[4]; // ambient color, energy
+		float ambient[3]; // ambient color,
+		uint32_t ambient_mode;
 		float local_matrix[16]; // up to here for spot and omni, rest is for directional
 	};
 
@@ -315,22 +328,6 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		float uv_scale4[2];
 	};
 
-	struct GIProbeData {
-		float xform[16];
-		float bounds[3];
-		float dynamic_range;
-
-		float bias;
-		float normal_bias;
-		uint32_t blend_ambient;
-		uint32_t texture_slot;
-
-		float anisotropy_strength;
-		float ao;
-		float ao_size;
-		uint32_t pad[1];
-	};
-
 	struct LightmapData {
 		float normal_xform[12];
 	};
@@ -358,6 +355,8 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	};
 
 	enum {
+		INSTANCE_DATA_FLAG_USE_GI_BUFFERS = 1 << 6,
+		INSTANCE_DATA_FLAG_USE_SDFGI = 1 << 7,
 		INSTANCE_DATA_FLAG_USE_LIGHTMAP_CAPTURE = 1 << 8,
 		INSTANCE_DATA_FLAG_USE_LIGHTMAP = 1 << 9,
 		INSTANCE_DATA_FLAG_USE_SH_LIGHTMAP = 1 << 10,
@@ -430,10 +429,19 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 			float ssao_ao_affect;
 			uint32_t roughness_limiter_enabled;
 
+			float roughness_limiter_amount;
+			float roughness_limiter_limit;
+			uint32_t roughness_limiter_pad[2];
+
 			float ao_color[4];
 
+			float sdf_to_bounds[16];
+
+			int32_t sdf_offset[3];
 			uint32_t material_uv2_mode;
-			uint32_t pad_material[3];
+
+			int32_t sdf_size[3];
+			uint32_t gi_upscale_for_msaa;
 		};
 
 		UBO ubo;
@@ -445,11 +453,6 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		RID reflection_buffer;
 		uint32_t max_reflection_probes_per_instance;
 
-		GIProbeData *gi_probes;
-		uint32_t max_gi_probes;
-		RID gi_probe_buffer;
-		uint32_t max_gi_probe_probes_per_instance;
-
 		LightmapData *lightmaps;
 		uint32_t max_lightmaps;
 		RID lightmap_buffer;
@@ -498,7 +501,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 					uint64_t material_index : 15;
 					uint64_t shader_index : 12;
 					uint64_t uses_instancing : 1;
-					uint64_t uses_vct : 1;
+					uint64_t uses_forward_gi : 1;
 					uint64_t uses_lightmap : 1;
 					uint64_t depth_layer : 4;
 					uint64_t priority : 8;
@@ -625,6 +628,7 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 	RID wireframe_material_shader;
 	RID wireframe_material;
 	RID default_shader_rd;
+	RID default_shader_sdfgi_rd;
 	RID default_radiance_uniform_set;
 	RID default_render_buffers_uniform_set;
 
@@ -640,30 +644,33 @@ class RasterizerSceneHighEndRD : public RasterizerSceneRD {
 		PASS_MODE_SHADOW,
 		PASS_MODE_SHADOW_DP,
 		PASS_MODE_DEPTH,
-		PASS_MODE_DEPTH_NORMAL,
 		PASS_MODE_DEPTH_NORMAL_ROUGHNESS,
+		PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE,
 		PASS_MODE_DEPTH_MATERIAL,
+		PASS_MODE_SDF,
 	};
 
-	void _setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false);
+	void _setup_environment(RID p_environment, RID p_render_buffers, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers = false, bool p_pancake_shadows = false);
 	void _setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows);
 	void _setup_decals(const RID *p_decal_instances, int p_decal_count, const Transform &p_camera_inverse_xform);
 	void _setup_reflections(RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, const Transform &p_camera_inverse_transform, RID p_environment);
-	void _setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform);
 	void _setup_lightmaps(InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, const Transform &p_cam_transform);
 
-	void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth);
+	void _fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth, bool p_has_sdfgi = false, bool p_has_opaque_gi = false);
 	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, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2());
-	_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);
+	_FORCE_INLINE_ void _add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index, bool p_using_sdfgi = false);
+	_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, bool p_using_sdfgi = false);
+
+	void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_using_sdfgi = false);
 
-	void _fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi);
+	Map<Size2i, RID> sdfgi_framebuffer_size_cache;
 
 protected:
 	virtual void _render_scene(RID p_render_buffer, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_camera_effects, RID p_shadow_atlas, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, const Color &p_default_bg_color);
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake);
 	virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
 	virtual void _render_uv2(InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
+	virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, InstanceBase **p_cull_result, int p_cull_count, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture);
 
 public:
 	virtual void set_time(double p_time, double p_step);

File diff suppressed because it is too large
+ 1563 - 28
servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp


+ 469 - 33
servers/rendering/rasterizer_rd/rasterizer_scene_rd.h

@@ -31,22 +31,22 @@
 #ifndef RASTERIZER_SCENE_RD_H
 #define RASTERIZER_SCENE_RD_H
 
+#include "core/local_vector.h"
 #include "core/rid_owner.h"
 #include "servers/rendering/rasterizer.h"
 #include "servers/rendering/rasterizer_rd/rasterizer_storage_rd.h"
+#include "servers/rendering/rasterizer_rd/shaders/gi.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/giprobe.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/giprobe_debug.glsl.gen.h"
+#include "servers/rendering/rasterizer_rd/shaders/sdfgi_debug.glsl.gen.h"
+#include "servers/rendering/rasterizer_rd/shaders/sdfgi_debug_probes.glsl.gen.h"
+#include "servers/rendering/rasterizer_rd/shaders/sdfgi_direct_light.glsl.gen.h"
+#include "servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl.gen.h"
+#include "servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl.gen.h"
 #include "servers/rendering/rasterizer_rd/shaders/sky.glsl.gen.h"
 #include "servers/rendering/rendering_device.h"
 
 class RasterizerSceneRD : public RasterizerScene {
-public:
-	enum GIProbeQuality {
-		GIPROBE_QUALITY_ULTRA_LOW,
-		GIPROBE_QUALITY_MEDIUM,
-		GIPROBE_QUALITY_HIGH,
-	};
-
 protected:
 	double time;
 
@@ -81,23 +81,27 @@ protected:
 	virtual void _render_shadow(RID p_framebuffer, InstanceBase **p_cull_result, int p_cull_count, const CameraMatrix &p_projection, const Transform &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool use_dp_flip, bool p_use_pancake) = 0;
 	virtual void _render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
 	virtual void _render_uv2(InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region) = 0;
+	virtual void _render_sdfgi(RID p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, InstanceBase **p_cull_result, int p_cull_count, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture) = 0;
 
 	virtual void _debug_giprobe(RID p_gi_probe, RenderingDevice::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform, bool p_lighting, bool p_emission, float p_alpha);
+	void _debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform);
 
 	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;
-	virtual RID _render_buffers_get_roughness_texture(RID p_render_buffers) = 0;
 	virtual RID _render_buffers_get_normal_texture(RID p_render_buffers) = 0;
+	virtual RID _render_buffers_get_ambient_texture(RID p_render_buffers) = 0;
+	virtual RID _render_buffers_get_reflection_texture(RID p_render_buffers) = 0;
 
 	void _process_ssao(RID p_render_buffers, RID p_environment, RID p_normal_buffer, const CameraMatrix &p_projection);
-	void _process_ssr(RID p_render_buffers, RID p_dest_framebuffer, RID p_normal_buffer, RID p_roughness_buffer, RID p_specular_buffer, RID p_metallic, const Color &p_metallic_mask, RID p_environment, const CameraMatrix &p_projection, bool p_use_additive);
+	void _process_ssr(RID p_render_buffers, RID p_dest_framebuffer, RID p_normal_buffer, RID p_specular_buffer, RID p_metallic, const Color &p_metallic_mask, RID p_environment, const CameraMatrix &p_projection, bool p_use_additive);
 	void _process_sss(RID p_render_buffers, const CameraMatrix &p_camera);
 
 	void _setup_sky(RID p_environment, const Vector3 &p_position, const Size2i p_screen_size);
 	void _update_sky(RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform);
 	void _draw_sky(bool p_can_continue_color, bool p_can_continue_depth, RID p_fb, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform);
+	void _process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_ambient_buffer, RID p_reflection_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count);
 
 private:
 	RS::ViewportDebugDraw debug_draw = RS::VIEWPORT_DEBUG_DRAW_DISABLED;
@@ -267,6 +271,8 @@ private:
 		SkyMaterialData *prev_material;
 		Vector3 prev_position;
 		float prev_time;
+
+		RID sdfgi_integrate_sky_uniform_set;
 	};
 
 	Sky *dirty_sky_list = nullptr;
@@ -388,13 +394,10 @@ private:
 	struct GIProbeInstance {
 		RID probe;
 		RID texture;
-		RID anisotropy[2]; //only if anisotropy is used
-		RID anisotropy_r16[2]; //only if anisotropy is used
 		RID write_buffer;
 
 		struct Mipmap {
 			RID texture;
-			RID anisotropy[2]; //only if anisotropy is used
 			RID uniform_set;
 			RID second_bounce_uniform_set;
 			RID write_uniform_set;
@@ -423,7 +426,7 @@ private:
 		uint32_t last_probe_version = 0;
 		uint32_t last_probe_data_version = 0;
 
-		uint64_t last_pass = 0;
+		//uint64_t last_pass = 0;
 		uint32_t render_index = 0;
 
 		bool has_dynamic_object_data = false;
@@ -435,11 +438,6 @@ private:
 	uint32_t gi_probe_max_lights;
 	RID gi_probe_lights_uniform;
 
-	bool gi_probe_use_anisotropy = false;
-	GIProbeQuality gi_probe_quality = GIPROBE_QUALITY_MEDIUM;
-
-	Vector<RID> gi_probe_slots;
-
 	enum {
 		GI_PROBE_SHADER_VERSION_COMPUTE_LIGHT,
 		GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE,
@@ -458,6 +456,8 @@ private:
 
 	mutable RID_Owner<GIProbeInstance> gi_probe_instance_owner;
 
+	RS::GIProbeQuality gi_probe_quality = RS::GI_PROBE_QUALITY_HIGH;
+
 	enum {
 		GI_PROBE_DEBUG_COLOR,
 		GI_PROBE_DEBUG_LIGHT,
@@ -591,6 +591,7 @@ private:
 
 		ShadowTransform shadow_transform[4];
 
+		AABB aabb;
 		RID self;
 		RID light;
 		Transform transform;
@@ -680,6 +681,19 @@ private:
 		float ssr_fade_in = 0.15;
 		float ssr_fade_out = 2.0;
 		float ssr_depth_tolerance = 0.2;
+
+		/// SDFGI
+		bool sdfgi_enabled = false;
+		RS::EnvironmentSDFGICascades sdfgi_cascades;
+		float sdfgi_min_cell_size = 0.2;
+		bool sdfgi_use_occlusion = false;
+		bool sdfgi_use_multibounce = false;
+		bool sdfgi_read_sky_light = false;
+		bool sdfgi_enhance_ssr = false;
+		float sdfgi_energy = 1.0;
+		float sdfgi_normal_bias = 1.1;
+		float sdfgi_probe_bias = 1.1;
+		RS::EnvironmentSDFGIYScale sdfgi_y_scale = RS::ENV_SDFGI_Y_SCALE_DISABLED;
 	};
 
 	RS::EnvironmentSSAOQuality ssao_quality = RS::ENV_SSAO_QUALITY_MEDIUM;
@@ -719,7 +733,13 @@ private:
 
 	/* RENDER BUFFERS */
 
+	struct SDFGI;
+
 	struct RenderBuffers {
+		enum {
+			MAX_GIPROBES = 8
+		};
+
 		RenderBufferData *data = nullptr;
 		int width = 0, height = 0;
 		RS::ViewportMSAA msaa = RS::VIEWPORT_MSAA_DISABLED;
@@ -732,6 +752,9 @@ private:
 		RID texture; //main texture for rendering to, must be filled after done rendering
 		RID depth_texture; //main depth texture
 
+		RID gi_uniform_set;
+		SDFGI *sdfgi = nullptr;
+
 		//built-in textures used for ping pong image processing and blurring
 		struct Blur {
 			RID texture;
@@ -764,10 +787,389 @@ private:
 			RID depth_scaled;
 			RID blur_radius[2];
 		} ssr;
+
+		RID giprobe_textures[MAX_GIPROBES];
+		RID giprobe_buffer;
 	};
 
+	RID default_giprobe_buffer;
+
+	/* SDFGI */
+
+	struct SDFGI {
+		enum {
+			MAX_CASCADES = 8,
+			CASCADE_SIZE = 128,
+			PROBE_DIVISOR = 16,
+			ANISOTROPY_SIZE = 6,
+			MAX_DYNAMIC_LIGHTS = 128,
+			MAX_STATIC_LIGHTS = 1024,
+			LIGHTPROBE_OCT_SIZE = 6,
+			SH_SIZE = 16
+		};
+
+		struct Cascade {
+			struct UBO {
+				float offset[3];
+				float to_cell;
+				int32_t probe_offset[3];
+				uint32_t pad;
+			};
+
+			//cascade blocks are full-size for volume (128^3), half size for albedo/emission
+			RID sdf_tex;
+			RID light_tex;
+			RID light_aniso_0_tex;
+			RID light_aniso_1_tex;
+
+			RID light_data;
+			RID light_aniso_0_data;
+			RID light_aniso_1_data;
+
+			struct SolidCell { // this struct is unused, but remains as reference for size
+				uint32_t position;
+				uint32_t albedo;
+				uint32_t static_light;
+				uint32_t static_light_aniso;
+			};
+
+			RID solid_cell_dispatch_buffer; //buffer for indirect compute dispatch
+			RID solid_cell_buffer;
+
+			RID lightprobe_history_tex;
+			RID lightprobe_average_tex;
+
+			float cell_size;
+			Vector3i position;
+
+			static const Vector3i DIRTY_ALL;
+			Vector3i dirty_regions; //(0,0,0 is not dirty, negative is refresh from the end, DIRTY_ALL is refresh all.
+
+			RID sdf_store_uniform_set;
+			RID sdf_direct_light_uniform_set;
+			RID scroll_uniform_set;
+			RID scroll_occlusion_uniform_set;
+			RID integrate_uniform_set;
+			RID lights_buffer;
+		};
+
+		//used for rendering (voxelization)
+		RID render_albedo;
+		RID render_emission;
+		RID render_emission_aniso;
+		RID render_occlusion[8];
+		RID render_geom_facing;
+
+		RID render_sdf[2];
+		RID render_sdf_half[2];
+
+		//used for ping pong processing in cascades
+		RID sdf_initialize_uniform_set;
+		RID sdf_initialize_half_uniform_set;
+		RID jump_flood_uniform_set[2];
+		RID jump_flood_half_uniform_set[2];
+		RID sdf_upscale_uniform_set;
+		int upscale_jfa_uniform_set_index;
+		RID occlusion_uniform_set;
+
+		uint32_t cascade_size = 128;
+
+		LocalVector<Cascade> cascades;
+
+		RID lightprobe_texture;
+		RID lightprobe_data;
+		RID occlusion_texture;
+		RID occlusion_data;
+
+		RID lightprobe_history_scroll; //used for scrolling lightprobes
+		RID lightprobe_average_scroll; //used for scrolling lightprobes
+
+		uint32_t history_size = 0;
+		float solid_cell_ratio = 0;
+		uint32_t solid_cell_count = 0;
+
+		RS::EnvironmentSDFGICascades cascade_mode;
+		float min_cell_size = 0;
+		uint32_t probe_axis_count = 0; //amount of probes per axis, this is an odd number because it encloses endpoints
+
+		RID debug_uniform_set;
+		RID debug_probes_uniform_set;
+		RID cascades_ubo;
+
+		bool uses_occlusion = false;
+		bool uses_multibounce = false;
+		bool reads_sky = false;
+		float energy = 1.0;
+		float normal_bias = 1.1;
+		float probe_bias = 1.1;
+		RS::EnvironmentSDFGIYScale y_scale_mode = RS::ENV_SDFGI_Y_SCALE_DISABLED;
+
+		float y_mult = 1.0;
+
+		uint32_t render_pass = 0;
+	};
+
+	RS::EnvironmentSDFGIRayCount sdfgi_ray_count = RS::ENV_SDFGI_RAY_COUNT_16;
+	RS::EnvironmentSDFGIFramesToConverge sdfgi_frames_to_converge = RS::ENV_SDFGI_CONVERGE_IN_10_FRAMES;
+	float sdfgi_solid_cell_ratio = 0.25;
+	Vector3 sdfgi_debug_probe_pos;
+	Vector3 sdfgi_debug_probe_dir;
+	bool sdfgi_debug_probe_enabled = false;
+	Vector3i sdfgi_debug_probe_index;
+
+	struct SDGIShader {
+		enum SDFGIPreprocessShaderVersion {
+			PRE_PROCESS_SCROLL,
+			PRE_PROCESS_SCROLL_OCCLUSION,
+			PRE_PROCESS_JUMP_FLOOD_INITIALIZE,
+			PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF,
+			PRE_PROCESS_JUMP_FLOOD,
+			PRE_PROCESS_JUMP_FLOOD_OPTIMIZED,
+			PRE_PROCESS_JUMP_FLOOD_UPSCALE,
+			PRE_PROCESS_OCCLUSION,
+			PRE_PROCESS_STORE,
+			PRE_PROCESS_MAX
+		};
+
+		struct PreprocessPushConstant {
+			int32_t scroll[3];
+			int32_t grid_size;
+
+			int32_t probe_offset[3];
+			int32_t step_size;
+
+			int32_t half_size;
+			uint32_t occlusion_index;
+			int32_t cascade;
+			uint32_t pad;
+		};
+
+		SdfgiPreprocessShaderRD preprocess;
+		RID preprocess_shader;
+		RID preprocess_pipeline[PRE_PROCESS_MAX];
+
+		struct DebugPushConstant {
+			float grid_size[3];
+			uint32_t max_cascades;
+
+			int32_t screen_size[2];
+			uint32_t use_occlusion;
+			float y_mult;
+
+			float cam_extent[3];
+			uint32_t probe_axis_size;
+
+			float cam_transform[16];
+		};
+
+		SdfgiDebugShaderRD debug;
+		RID debug_shader;
+		RID debug_shader_version;
+		RID debug_pipeline;
+
+		enum ProbeDebugMode {
+			PROBE_DEBUG_PROBES,
+			PROBE_DEBUG_VISIBILITY,
+			PROBE_DEBUG_MAX
+		};
+
+		struct DebugProbesPushConstant {
+			float projection[16];
+
+			uint32_t band_power;
+			uint32_t sections_in_band;
+			uint32_t band_mask;
+			float section_arc;
+
+			float grid_size[3];
+			uint32_t cascade;
+
+			uint32_t pad;
+			float y_mult;
+			int32_t probe_debug_index;
+			int32_t probe_axis_size;
+		};
+
+		SdfgiDebugProbesShaderRD debug_probes;
+		RID debug_probes_shader;
+		RID debug_probes_shader_version;
+
+		RenderPipelineVertexFormatCacheRD debug_probes_pipeline[PROBE_DEBUG_MAX];
+
+		struct Light {
+			float color[3];
+			float energy;
+
+			float direction[3];
+			uint32_t has_shadow;
+
+			float position[3];
+			float attenuation;
+
+			uint32_t type;
+			float spot_angle;
+			float spot_attenuation;
+			float radius;
+
+			float shadow_color[4];
+		};
+
+		struct DirectLightPushConstant {
+			float grid_size[3];
+			uint32_t max_cascades;
+
+			uint32_t cascade;
+			uint32_t light_count;
+			uint32_t process_offset;
+			uint32_t process_increment;
+
+			int32_t probe_axis_size;
+			uint32_t multibounce;
+			float y_mult;
+			uint32_t pad;
+		};
+
+		enum {
+			DIRECT_LIGHT_MODE_STATIC,
+			DIRECT_LIGHT_MODE_DYNAMIC,
+			DIRECT_LIGHT_MODE_MAX
+		};
+		SdfgiDirectLightShaderRD direct_light;
+		RID direct_light_shader;
+		RID direct_light_pipeline[DIRECT_LIGHT_MODE_MAX];
+
+		enum {
+			INTEGRATE_MODE_PROCESS,
+			INTEGRATE_MODE_STORE,
+			INTEGRATE_MODE_SCROLL,
+			INTEGRATE_MODE_SCROLL_STORE,
+			INTEGRATE_MODE_MAX
+		};
+		struct IntegratePushConstant {
+			enum {
+				SKY_MODE_DISABLED,
+				SKY_MODE_COLOR,
+				SKY_MODE_SKY,
+			};
+
+			float grid_size[3];
+			uint32_t max_cascades;
+
+			uint32_t probe_axis_size;
+			uint32_t cascade;
+			uint32_t history_index;
+			uint32_t history_size;
+
+			uint32_t ray_count;
+			float ray_bias;
+			int32_t image_size[2];
+
+			int32_t world_offset[3];
+			uint32_t sky_mode;
+
+			int32_t scroll[3];
+			float sky_energy;
+
+			float sky_color[3];
+			float y_mult;
+		};
+
+		SdfgiIntegrateShaderRD integrate;
+		RID integrate_shader;
+		RID integrate_pipeline[INTEGRATE_MODE_MAX];
+
+		RID integrate_default_sky_uniform_set;
+
+	} sdfgi_shader;
+
+	void _sdfgi_erase(RenderBuffers *rb);
+	int _sdfgi_get_pending_region_data(RID p_render_buffers, int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const;
+	void _sdfgi_update_cascades(RID p_render_buffers);
+
+	/* GI */
+
+	struct GI {
+		struct SDFGIData {
+			float grid_size[3];
+			uint32_t max_cascades;
+
+			uint32_t use_occlusion;
+			int32_t probe_axis_size;
+			float probe_to_uvw;
+			float normal_bias;
+
+			float lightprobe_tex_pixel_size[3];
+			float energy;
+
+			float lightprobe_uv_offset[3];
+			float y_mult;
+
+			float occlusion_clamp[3];
+			uint32_t pad3;
+
+			float occlusion_renormalize[3];
+			uint32_t pad4;
+
+			float cascade_probe_size[3];
+			uint32_t pad5;
+
+			struct ProbeCascadeData {
+				float position[3]; //offset of (0,0,0) in world coordinates
+				float to_probe; // 1/bounds * grid_size
+				int32_t probe_world_offset[3];
+				float to_cell; // 1/bounds * grid_size
+			};
+
+			ProbeCascadeData cascades[SDFGI::MAX_CASCADES];
+		};
+
+		struct GIProbeData {
+			float xform[16];
+			float bounds[3];
+			float dynamic_range;
+
+			float bias;
+			float normal_bias;
+			uint32_t blend_ambient;
+			uint32_t texture_slot;
+
+			float anisotropy_strength;
+			float ao;
+			float ao_size;
+			uint32_t pad[1];
+		};
+
+		struct PushConstant {
+			int32_t screen_size[2];
+			float z_near;
+			float z_far;
+
+			float proj_info[4];
+
+			uint32_t max_giprobes;
+			uint32_t high_quality_vct;
+			uint32_t use_sdfgi;
+			uint32_t orthogonal;
+
+			float ao_color[3];
+			uint32_t pad;
+
+			float cam_rotation[12];
+		};
+
+		RID sdfgi_ubo;
+		enum {
+			MODE_MAX = 1
+		};
+
+		GiShaderRD shader;
+		RID shader_version;
+		RID pipelines[MODE_MAX];
+	} gi;
+
 	bool screen_space_roughness_limiter = false;
-	float screen_space_roughness_limiter_curve = 1.0;
+	float screen_space_roughness_limiter_amount = 0.25;
+	float screen_space_roughness_limiter_limit = 0.18;
 
 	mutable RID_Owner<RenderBuffers> render_buffers_owner;
 
@@ -777,10 +1179,16 @@ private:
 
 	void _render_buffers_debug_draw(RID p_render_buffers, RID p_shadow_atlas);
 	void _render_buffers_post_process_and_tonemap(RID p_render_buffers, RID p_environment, RID p_camera_effects, const CameraMatrix &p_projection);
+	void _sdfgi_debug_draw(RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform);
 
 	uint64_t scene_pass = 0;
 	uint64_t shadow_atlas_realloc_tolerance_msec = 500;
 
+	struct SDFGICosineNeighbour {
+		uint32_t neighbour;
+		float weight;
+	};
+
 public:
 	/* SHADOW ATLAS API */
 
@@ -818,6 +1226,15 @@ public:
 		return Size2i(directional_shadow.size, directional_shadow.size);
 	}
 
+	/* SDFGI UPDATE */
+
+	int sdfgi_get_lightprobe_octahedron_size() const { return SDFGI::LIGHTPROBE_OCT_SIZE; }
+	virtual void sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position);
+	virtual int sdfgi_get_pending_region_count(RID p_render_buffers) const;
+	virtual AABB sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const;
+	virtual uint32_t sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const;
+	virtual void sdfgi_update_probes(RID p_render_buffers, RID p_environment, const RID *p_directional_light_instances, uint32_t p_directional_light_count, const RID *p_positional_light_instances, uint32_t p_positional_light_count);
+	RID sdfgi_get_ubo() const { return gi.sdfgi_ubo; }
 	/* SKY API */
 
 	RID sky_create();
@@ -871,6 +1288,11 @@ public:
 	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;
+	bool environment_is_sdfgi_enabled(RID p_env) const;
+
+	virtual void environment_set_sdfgi(RID p_env, bool p_enable, RS::EnvironmentSDFGICascades p_cascades, float p_min_cell_size, RS::EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, bool p_use_multibounce, bool p_read_sky, bool p_enhance_ssr, float p_energy, float p_normal_bias, float p_probe_bias);
+	virtual void environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count);
+	virtual void environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames);
 
 	void environment_set_ssr_roughness_quality(RS::EnvironmentSSRRoughnessQuality p_quality);
 	RS::EnvironmentSSRRoughnessQuality environment_get_ssr_roughness_quality() const;
@@ -894,6 +1316,7 @@ public:
 
 	RID light_instance_create(RID p_light);
 	void light_instance_set_transform(RID p_light_instance, const Transform &p_transform);
+	void light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb);
 	void light_instance_set_shadow_transform(RID p_light_instance, const CameraMatrix &p_projection, const Transform &p_transform, float p_far, float p_split, int p_pass, float p_shadow_texel_size, float p_bias_scale = 1.0, float p_range_begin = 0, const Vector2 &p_uv_scale = Vector2());
 	void light_instance_mark_visible(RID p_light_instance);
 
@@ -1107,6 +1530,8 @@ public:
 	bool gi_probe_needs_update(RID p_probe) const;
 	void gi_probe_update(RID p_probe, bool p_update_light_instances, const Vector<RID> &p_light_instances, int p_dynamic_object_count, InstanceBase **p_dynamic_objects);
 
+	void gi_probe_set_quality(RS::GIProbeQuality p_quality) { gi_probe_quality = p_quality; }
+
 	_FORCE_INLINE_ uint32_t gi_probe_instance_get_slot(RID p_probe) {
 		GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
 		return gi_probe->slot;
@@ -1124,10 +1549,6 @@ public:
 		GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
 		return gi_probe->texture;
 	}
-	_FORCE_INLINE_ RID gi_probe_instance_get_aniso_texture(RID p_probe, int p_index) {
-		GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_probe);
-		return gi_probe->anisotropy[p_index];
-	}
 
 	_FORCE_INLINE_ void gi_probe_instance_set_render_index(RID p_instance, uint32_t p_render_index) {
 		GIProbeInstance *gi_probe = gi_probe_instance_owner.getornull(p_instance);
@@ -1141,7 +1562,7 @@ public:
 
 		return gi_probe->render_index;
 	}
-
+	/*
 	_FORCE_INLINE_ void gi_probe_instance_set_render_pass(RID p_instance, uint32_t p_render_pass) {
 		GIProbeInstance *g_probe = gi_probe_instance_owner.getornull(p_instance);
 		ERR_FAIL_COND(!g_probe);
@@ -1154,18 +1575,27 @@ public:
 
 		return g_probe->last_pass;
 	}
-
-	const Vector<RID> &gi_probe_get_slots() const;
-	_FORCE_INLINE_ bool gi_probe_is_anisotropic() const {
-		return gi_probe_use_anisotropy;
-	}
-	GIProbeQuality gi_probe_get_quality() const;
-
+*/
 	RID render_buffers_create();
 	void render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RS::ViewportScreenSpaceAA p_screen_space_aa);
 
 	RID render_buffers_get_ao_texture(RID p_render_buffers);
 	RID render_buffers_get_back_buffer_texture(RID p_render_buffers);
+	RID render_buffers_get_gi_probe_buffer(RID p_render_buffers);
+	RID render_buffers_get_default_gi_probe_buffer();
+
+	uint32_t render_buffers_get_sdfgi_cascade_count(RID p_render_buffers) const;
+	bool render_buffers_is_sdfgi_enabled(RID p_render_buffers) const;
+	RID render_buffers_get_sdfgi_irradiance_probes(RID p_render_buffers) const;
+	Vector3 render_buffers_get_sdfgi_cascade_offset(RID p_render_buffers, uint32_t p_cascade) const;
+	Vector3i render_buffers_get_sdfgi_cascade_probe_offset(RID p_render_buffers, uint32_t p_cascade) const;
+	float render_buffers_get_sdfgi_cascade_probe_size(RID p_render_buffers, uint32_t p_cascade) const;
+	float render_buffers_get_sdfgi_normal_bias(RID p_render_buffers) const;
+	uint32_t render_buffers_get_sdfgi_cascade_probe_count(RID p_render_buffers) const;
+	uint32_t render_buffers_get_sdfgi_cascade_size(RID p_render_buffers) const;
+	bool render_buffers_is_sdfgi_using_occlusion(RID p_render_buffers) const;
+	float render_buffers_get_sdfgi_energy(RID p_render_buffers) const;
+	RID render_buffers_get_sdfgi_occlusion_texture(RID p_render_buffers) const;
 
 	void render_scene(RID p_render_buffers, const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID *p_light_cull_result, int p_light_cull_count, RID *p_reflection_probe_cull_result, int p_reflection_probe_cull_count, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count, RID *p_decal_cull_result, int p_decal_cull_count, InstanceBase **p_lightmap_cull_result, int p_lightmap_cull_count, RID p_environment, RID p_shadow_atlas, RID p_camera_effects, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
 
@@ -1173,6 +1603,9 @@ public:
 
 	void render_material(const Transform &p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_ortogonal, InstanceBase **p_cull_result, int p_cull_count, RID p_framebuffer, const Rect2i &p_region);
 
+	void render_sdfgi(RID p_render_buffers, int p_region, InstanceBase **p_cull_result, int p_cull_count);
+	void render_sdfgi_static_lights(RID p_render_buffers, uint32_t p_cascade_count, const uint32_t *p_cascade_indices, const RID **p_positional_light_cull_result, const uint32_t *p_positional_light_cull_count);
+
 	virtual void set_scene_pass(uint64_t p_pass) {
 		scene_pass = p_pass;
 	}
@@ -1180,9 +1613,10 @@ public:
 		return scene_pass;
 	}
 
-	virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve);
+	virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit);
 	virtual bool screen_space_roughness_limiter_is_active() const;
-	virtual float screen_space_roughness_limiter_get_curve() const;
+	virtual float screen_space_roughness_limiter_get_amount() const;
+	virtual float screen_space_roughness_limiter_get_limit() const;
 
 	virtual void sub_surface_scattering_set_quality(RS::SubSurfaceScatteringQuality p_quality);
 	RS::SubSurfaceScatteringQuality sub_surface_scattering_get_quality() const;
@@ -1221,6 +1655,8 @@ public:
 
 	virtual void set_time(double p_time, double p_step);
 
+	void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir);
+
 	RasterizerSceneRD(RasterizerStorageRD *p_storage);
 	~RasterizerSceneRD();
 };

+ 51 - 22
servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp

@@ -3387,11 +3387,21 @@ void RasterizerStorageRD::light_set_reverse_cull_face_mode(RID p_light, bool p_e
 	light->instance_dependency.instance_notify_changed(true, false);
 }
 
-void RasterizerStorageRD::light_set_use_gi(RID p_light, bool p_enabled) {
+void RasterizerStorageRD::light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode) {
 	Light *light = light_owner.getornull(p_light);
 	ERR_FAIL_COND(!light);
 
-	light->use_gi = p_enabled;
+	light->bake_mode = p_bake_mode;
+
+	light->version++;
+	light->instance_dependency.instance_notify_changed(true, false);
+}
+
+void RasterizerStorageRD::light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) {
+	Light *light = light_owner.getornull(p_light);
+	ERR_FAIL_COND(!light);
+
+	light->max_sdfgi_cascade = p_cascade;
 
 	light->version++;
 	light->instance_dependency.instance_notify_changed(true, false);
@@ -3460,11 +3470,18 @@ RS::LightDirectionalShadowDepthRangeMode RasterizerStorageRD::light_directional_
 	return light->directional_range_mode;
 }
 
-bool RasterizerStorageRD::light_get_use_gi(RID p_light) {
-	Light *light = light_owner.getornull(p_light);
-	ERR_FAIL_COND_V(!light, false);
+uint32_t RasterizerStorageRD::light_get_max_sdfgi_cascade(RID p_light) {
+	const Light *light = light_owner.getornull(p_light);
+	ERR_FAIL_COND_V(!light, 0);
+
+	return light->max_sdfgi_cascade;
+}
+
+RS::LightBakeMode RasterizerStorageRD::light_get_bake_mode(RID p_light) {
+	const Light *light = light_owner.getornull(p_light);
+	ERR_FAIL_COND_V(!light, RS::LIGHT_BAKE_DISABLED);
 
-	return light->use_gi;
+	return light->bake_mode;
 }
 
 uint64_t RasterizerStorageRD::light_get_version(RID p_light) const {
@@ -3517,25 +3534,25 @@ void RasterizerStorageRD::reflection_probe_set_intensity(RID p_probe, float p_in
 	reflection_probe->intensity = p_intensity;
 }
 
-void RasterizerStorageRD::reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient) {
+void RasterizerStorageRD::reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode) {
 	ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
 	ERR_FAIL_COND(!reflection_probe);
 
-	reflection_probe->interior_ambient = p_ambient;
+	reflection_probe->ambient_mode = p_mode;
 }
 
-void RasterizerStorageRD::reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) {
+void RasterizerStorageRD::reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) {
 	ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
 	ERR_FAIL_COND(!reflection_probe);
 
-	reflection_probe->interior_ambient_energy = p_energy;
+	reflection_probe->ambient_color = p_color;
 }
 
-void RasterizerStorageRD::reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) {
+void RasterizerStorageRD::reflection_probe_set_ambient_energy(RID p_probe, float p_energy) {
 	ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
 	ERR_FAIL_COND(!reflection_probe);
 
-	reflection_probe->interior_ambient_probe_contrib = p_contrib;
+	reflection_probe->ambient_color_energy = p_energy;
 }
 
 void RasterizerStorageRD::reflection_probe_set_max_distance(RID p_probe, float p_distance) {
@@ -3683,25 +3700,23 @@ bool RasterizerStorageRD::reflection_probe_is_box_projection(RID p_probe) const
 	return reflection_probe->box_projection;
 }
 
-Color RasterizerStorageRD::reflection_probe_get_interior_ambient(RID p_probe) const {
+RS::ReflectionProbeAmbientMode RasterizerStorageRD::reflection_probe_get_ambient_mode(RID p_probe) const {
 	const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
-	ERR_FAIL_COND_V(!reflection_probe, Color());
-
-	return reflection_probe->interior_ambient;
+	ERR_FAIL_COND_V(!reflection_probe, RS::REFLECTION_PROBE_AMBIENT_DISABLED);
+	return reflection_probe->ambient_mode;
 }
 
-float RasterizerStorageRD::reflection_probe_get_interior_ambient_energy(RID p_probe) const {
+Color RasterizerStorageRD::reflection_probe_get_ambient_color(RID p_probe) const {
 	const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
-	ERR_FAIL_COND_V(!reflection_probe, 0);
+	ERR_FAIL_COND_V(!reflection_probe, Color());
 
-	return reflection_probe->interior_ambient_energy;
+	return reflection_probe->ambient_color;
 }
-
-float RasterizerStorageRD::reflection_probe_get_interior_ambient_probe_contribution(RID p_probe) const {
+float RasterizerStorageRD::reflection_probe_get_ambient_color_energy(RID p_probe) const {
 	const ReflectionProbe *reflection_probe = reflection_probe_owner.getornull(p_probe);
 	ERR_FAIL_COND_V(!reflection_probe, 0);
 
-	return reflection_probe->interior_ambient_probe_contrib;
+	return reflection_probe->ambient_color_energy;
 }
 
 RID RasterizerStorageRD::decal_create() {
@@ -5878,6 +5893,20 @@ RasterizerStorageRD::RasterizerStorageRD() {
 		}
 
 		default_rd_textures[DEFAULT_RD_TEXTURE_MULTIMESH_BUFFER] = RD::get_singleton()->texture_buffer_create(16, RD::DATA_FORMAT_R8G8B8A8_UNORM, pv);
+
+		for (int i = 0; i < 16; i++) {
+			pv.set(i * 4 + 0, 0);
+			pv.set(i * 4 + 1, 0);
+			pv.set(i * 4 + 2, 0);
+			pv.set(i * 4 + 3, 0);
+		}
+
+		{
+			tformat.format = RD::DATA_FORMAT_R8G8B8A8_UINT;
+			Vector<Vector<uint8_t>> vpv;
+			vpv.push_back(pv);
+			default_rd_textures[DEFAULT_RD_TEXTURE_2D_UINT] = RD::get_singleton()->texture_create(tformat, RD::TextureView(), vpv);
+		}
 	}
 
 	{ //create default cubemap

+ 16 - 12
servers/rendering/rasterizer_rd/rasterizer_storage_rd.h

@@ -92,6 +92,7 @@ public:
 		DEFAULT_RD_TEXTURE_CUBEMAP_ARRAY_BLACK,
 		DEFAULT_RD_TEXTURE_3D_WHITE,
 		DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE,
+		DEFAULT_RD_TEXTURE_2D_UINT,
 		DEFAULT_RD_TEXTURE_MAX
 	};
 
@@ -420,7 +421,8 @@ private:
 		bool shadow = false;
 		bool negative = false;
 		bool reverse_cull = false;
-		bool use_gi = true;
+		RS::LightBakeMode bake_mode = RS::LIGHT_BAKE_DYNAMIC;
+		uint32_t max_sdfgi_cascade = 2;
 		uint32_t cull_mask = 0xFFFFFFFF;
 		RS::LightOmniShadowMode omni_shadow_mode = RS::LIGHT_OMNI_SHADOW_DUAL_PARABOLOID;
 		RS::LightDirectionalShadowMode directional_shadow_mode = RS::LIGHT_DIRECTIONAL_SHADOW_ORTHOGONAL;
@@ -439,9 +441,9 @@ private:
 		RS::ReflectionProbeUpdateMode update_mode = RS::REFLECTION_PROBE_UPDATE_ONCE;
 		int resolution = 256;
 		float intensity = 1.0;
-		Color interior_ambient;
-		float interior_ambient_energy = 1.0;
-		float interior_ambient_probe_contrib = 0.0;
+		RS::ReflectionProbeAmbientMode ambient_mode = RS::REFLECTION_PROBE_AMBIENT_ENVIRONMENT;
+		Color ambient_color;
+		float ambient_color_energy = 1.0;
 		float max_distance = 0;
 		Vector3 extents = Vector3(1, 1, 1);
 		Vector3 origin_offset;
@@ -1041,7 +1043,8 @@ public:
 	void light_set_negative(RID p_light, bool p_enable);
 	void light_set_cull_mask(RID p_light, uint32_t p_mask);
 	void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled);
-	void light_set_use_gi(RID p_light, bool p_enabled);
+	void light_set_bake_mode(RID p_light, RS::LightBakeMode p_bake_mode);
+	void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade);
 
 	void light_omni_set_shadow_mode(RID p_light, RS::LightOmniShadowMode p_mode);
 
@@ -1118,7 +1121,8 @@ public:
 		return light->param[RS::LIGHT_PARAM_TRANSMITTANCE_BIAS];
 	}
 
-	bool light_get_use_gi(RID p_light);
+	RS::LightBakeMode light_get_bake_mode(RID p_light);
+	uint32_t light_get_max_sdfgi_cascade(RID p_light);
 	uint64_t light_get_version(RID p_light) const;
 
 	/* PROBE API */
@@ -1127,9 +1131,9 @@ public:
 
 	void reflection_probe_set_update_mode(RID p_probe, RS::ReflectionProbeUpdateMode p_mode);
 	void reflection_probe_set_intensity(RID p_probe, float p_intensity);
-	void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_ambient);
-	void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy);
-	void reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib);
+	void reflection_probe_set_ambient_mode(RID p_probe, RS::ReflectionProbeAmbientMode p_mode);
+	void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color);
+	void reflection_probe_set_ambient_energy(RID p_probe, float p_energy);
 	void reflection_probe_set_max_distance(RID p_probe, float p_distance);
 	void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents);
 	void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset);
@@ -1151,9 +1155,9 @@ public:
 	float reflection_probe_get_intensity(RID p_probe) const;
 	bool reflection_probe_is_interior(RID p_probe) const;
 	bool reflection_probe_is_box_projection(RID p_probe) const;
-	Color reflection_probe_get_interior_ambient(RID p_probe) const;
-	float reflection_probe_get_interior_ambient_energy(RID p_probe) const;
-	float reflection_probe_get_interior_ambient_probe_contribution(RID p_probe) const;
+	RS::ReflectionProbeAmbientMode reflection_probe_get_ambient_mode(RID p_probe) const;
+	Color reflection_probe_get_ambient_color(RID p_probe) const;
+	float reflection_probe_get_ambient_color_energy(RID p_probe) const;
 
 	void base_update_dependency(RID p_base, RasterizerScene::InstanceBase *p_instance);
 	void skeleton_update_dependency(RID p_skeleton, RasterizerScene::InstanceBase *p_instance);

+ 7 - 0
servers/rendering/rasterizer_rd/shaders/SCsub

@@ -28,3 +28,10 @@ if "RD_GLSL" in env["BUILDERS"]:
     env.RD_GLSL("screen_space_reflection_scale.glsl")
     env.RD_GLSL("subsurface_scattering.glsl")
     env.RD_GLSL("specular_merge.glsl")
+    env.RD_GLSL("gi.glsl")
+    env.RD_GLSL("resolve.glsl")
+    env.RD_GLSL("sdfgi_preprocess.glsl")
+    env.RD_GLSL("sdfgi_integrate.glsl")
+    env.RD_GLSL("sdfgi_direct_light.glsl")
+    env.RD_GLSL("sdfgi_debug.glsl")
+    env.RD_GLSL("sdfgi_debug_probes.glsl")

+ 18 - 2
servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl

@@ -47,16 +47,26 @@ layout(push_constant, binding = 1, std430) uniform Params {
 
 	bool force_luminance;
 	bool alpha_to_zero;
-	uint pad[2];
+	bool srgb;
+	uint pad;
 }
 params;
 
 layout(location = 0) in vec2 uv_interp;
 
 layout(set = 0, binding = 0) uniform sampler2D source_color;
-
+#ifdef MODE_TWO_SOURCES
+layout(set = 1, binding = 0) uniform sampler2D source_color2;
+#endif
 layout(location = 0) out vec4 frag_color;
 
+vec3 linear_to_srgb(vec3 color) {
+	//if going to srgb, clamp from 0 to 1.
+	color = clamp(color, vec3(0.0), vec3(1.0));
+	const vec3 a = vec3(0.055f);
+	return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
 void main() {
 	vec2 uv = uv_interp;
 
@@ -89,11 +99,17 @@ void main() {
 	}
 #endif
 	vec4 color = textureLod(source_color, uv, 0.0);
+#ifdef MODE_TWO_SOURCES
+	color += textureLod(source_color2, uv, 0.0);
+#endif
 	if (params.force_luminance) {
 		color.rgb = vec3(max(max(color.r, color.g), color.b));
 	}
 	if (params.alpha_to_zero) {
 		color.rgb *= color.a;
 	}
+	if (params.srgb) {
+		color.rgb = linear_to_srgb(color.rgb);
+	}
 	frag_color = color;
 }

+ 663 - 0
servers/rendering/rasterizer_rd/shaders/gi.glsl

@@ -0,0 +1,663 @@
+#[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#define M_PI 3.141592
+
+#define SDFGI_MAX_CASCADES 8
+
+//set 0 for SDFGI and render buffers
+
+layout(set = 0, binding = 1) uniform texture3D sdf_cascades[SDFGI_MAX_CASCADES];
+layout(set = 0, binding = 2) uniform texture3D light_cascades[SDFGI_MAX_CASCADES];
+layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[SDFGI_MAX_CASCADES];
+layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[SDFGI_MAX_CASCADES];
+layout(set = 0, binding = 5) uniform texture3D occlusion_texture;
+
+layout(set = 0, binding = 6) uniform sampler linear_sampler;
+layout(set = 0, binding = 7) uniform sampler linear_sampler_with_mipmaps;
+
+struct ProbeCascadeData {
+	vec3 position;
+	float to_probe;
+	ivec3 probe_world_offset;
+	float to_cell; // 1/bounds * grid_size
+};
+
+layout(rgba16f, set = 0, binding = 9) uniform restrict writeonly image2D ambient_buffer;
+layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D reflection_buffer;
+
+layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture;
+
+layout(set = 0, binding = 12) uniform texture2D depth_buffer;
+layout(set = 0, binding = 13) uniform texture2D normal_roughness_buffer;
+layout(set = 0, binding = 14) uniform utexture2D giprobe_buffer;
+
+layout(set = 0, binding = 15, std140) uniform SDFGI {
+	vec3 grid_size;
+	uint max_cascades;
+
+	bool use_occlusion;
+	int probe_axis_size;
+	float probe_to_uvw;
+	float normal_bias;
+
+	vec3 lightprobe_tex_pixel_size;
+	float energy;
+
+	vec3 lightprobe_uv_offset;
+	float y_mult;
+
+	vec3 occlusion_clamp;
+	uint pad3;
+
+	vec3 occlusion_renormalize;
+	uint pad4;
+
+	vec3 cascade_probe_size;
+	uint pad5;
+
+	ProbeCascadeData cascades[SDFGI_MAX_CASCADES];
+}
+sdfgi;
+
+#define MAX_GI_PROBES 8
+
+struct GIProbeData {
+	mat4 xform;
+	vec3 bounds;
+	float dynamic_range;
+
+	float bias;
+	float normal_bias;
+	bool blend_ambient;
+	uint texture_slot;
+
+	float anisotropy_strength;
+	float ambient_occlusion;
+	float ambient_occlusion_size;
+	uint pad2;
+};
+
+layout(set = 0, binding = 16, std140) uniform GIProbes {
+	GIProbeData data[MAX_GI_PROBES];
+}
+gi_probes;
+
+layout(set = 0, binding = 17) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	ivec2 screen_size;
+	float z_near;
+	float z_far;
+
+	vec4 proj_info;
+
+	uint max_giprobes;
+	bool high_quality_vct;
+	bool use_sdfgi;
+	bool orthogonal;
+
+	vec3 ao_color;
+	uint pad;
+
+	mat3x4 cam_rotation;
+}
+params;
+
+vec2 octahedron_wrap(vec2 v) {
+	vec2 signVal;
+	signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
+	signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
+	return (1.0 - abs(v.yx)) * signVal;
+}
+
+vec2 octahedron_encode(vec3 n) {
+	// https://twitter.com/Stubbesaurus/status/937994790553227264
+	n /= (abs(n.x) + abs(n.y) + abs(n.z));
+	n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
+	n.xy = n.xy * 0.5 + 0.5;
+	return n.xy;
+}
+
+vec4 blend_color(vec4 src, vec4 dst) {
+	vec4 res;
+	float sa = 1.0 - src.a;
+	res.a = dst.a * sa + src.a;
+	if (res.a == 0.0) {
+		res.rgb = vec3(0);
+	} else {
+		res.rgb = (dst.rgb * dst.a * sa + src.rgb * src.a) / res.a;
+	}
+	return res;
+}
+
+vec3 reconstruct_position(ivec2 screen_pos) {
+	vec3 pos;
+	pos.z = texelFetch(sampler2D(depth_buffer, linear_sampler), screen_pos, 0).r;
+
+	pos.z = pos.z * 2.0 - 1.0;
+	if (params.orthogonal) {
+		pos.z = ((pos.z + (params.z_far + params.z_near) / (params.z_far - params.z_near)) * (params.z_far - params.z_near)) / 2.0;
+	} else {
+		pos.z = 2.0 * params.z_near * params.z_far / (params.z_far + params.z_near - pos.z * (params.z_far - params.z_near));
+	}
+	pos.z = -pos.z;
+
+	pos.xy = vec2(screen_pos) * params.proj_info.xy + params.proj_info.zw;
+	if (!params.orthogonal) {
+		pos.xy *= pos.z;
+	}
+
+	return pos;
+}
+
+void sdfgi_probe_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, float roughness, out vec3 diffuse_light, out vec3 specular_light) {
+	cascade_pos += cam_normal * sdfgi.normal_bias;
+
+	vec3 base_pos = floor(cascade_pos);
+	//cascade_pos += mix(vec3(0.0),vec3(0.01),lessThan(abs(cascade_pos-base_pos),vec3(0.01))) * cam_normal;
+	ivec3 probe_base_pos = ivec3(base_pos);
+
+	vec4 diffuse_accum = vec4(0.0);
+	vec3 specular_accum;
+
+	ivec3 tex_pos = ivec3(probe_base_pos.xy, int(cascade));
+	tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size;
+	tex_pos.xy = tex_pos.xy * (SDFGI_OCT_SIZE + 2) + ivec2(1);
+
+	vec3 diffuse_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
+
+	vec3 specular_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_specular_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
+
+	specular_accum = vec3(0.0);
+
+	vec4 light_accum = vec4(0.0);
+	float weight_accum = 0.0;
+
+	for (uint j = 0; j < 8; j++) {
+		ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
+		ivec3 probe_posi = probe_base_pos;
+		probe_posi += offset;
+
+		// Compute weight
+
+		vec3 probe_pos = vec3(probe_posi);
+		vec3 probe_to_pos = cascade_pos - probe_pos;
+		vec3 probe_dir = normalize(-probe_to_pos);
+
+		vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
+		float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(cam_normal, probe_dir));
+
+		// Compute lightprobe occlusion
+
+		if (sdfgi.use_occlusion) {
+			ivec3 occ_indexv = abs((sdfgi.cascades[cascade].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4);
+			vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3)));
+
+			vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw;
+			occ_pos.z += float(cascade);
+			if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures
+				occ_pos.x += 1.0;
+			}
+
+			occ_pos *= sdfgi.occlusion_renormalize;
+			float occlusion = dot(textureLod(sampler3D(occlusion_texture, linear_sampler), occ_pos, 0.0), occ_mask);
+
+			weight *= max(occlusion, 0.01);
+		}
+
+		// Compute lightprobe texture position
+
+		vec3 diffuse;
+		vec3 pos_uvw = diffuse_posf;
+		pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
+		pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
+		diffuse = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb;
+
+		diffuse_accum += vec4(diffuse * weight, weight);
+
+		{
+			vec3 specular = vec3(0.0);
+			vec3 pos_uvw = specular_posf;
+			pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
+			pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
+			if (roughness < 0.99) {
+				specular = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb;
+			}
+			if (roughness > 0.2) {
+				specular = mix(specular, textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0).rgb, (roughness - 0.2) * 1.25);
+			}
+
+			specular_accum += specular * weight;
+		}
+	}
+
+	if (diffuse_accum.a > 0.0) {
+		diffuse_accum.rgb /= diffuse_accum.a;
+	}
+
+	diffuse_light = diffuse_accum.rgb;
+
+	if (diffuse_accum.a > 0.0) {
+		specular_accum /= diffuse_accum.a;
+	}
+
+	specular_light = specular_accum;
+}
+
+void sdfgi_process(vec3 vertex, vec3 normal, vec3 reflection, float roughness, out vec4 ambient_light, out vec4 reflection_light) {
+	//make vertex orientation the world one, but still align to camera
+	vertex.y *= sdfgi.y_mult;
+	normal.y *= sdfgi.y_mult;
+	reflection.y *= sdfgi.y_mult;
+
+	//renormalize
+	normal = normalize(normal);
+	reflection = normalize(reflection);
+
+	vec3 cam_pos = vertex;
+	vec3 cam_normal = normal;
+
+	vec4 light_accum = vec4(0.0);
+	float weight_accum = 0.0;
+
+	vec4 light_blend_accum = vec4(0.0);
+	float weight_blend_accum = 0.0;
+
+	float blend = -1.0;
+
+	// helper constants, compute once
+
+	uint cascade = 0xFFFFFFFF;
+	vec3 cascade_pos;
+	vec3 cascade_normal;
+
+	for (uint i = 0; i < sdfgi.max_cascades; i++) {
+		cascade_pos = (cam_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe;
+
+		if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) {
+			continue; //skip cascade
+		}
+
+		cascade = i;
+		break;
+	}
+
+	if (cascade < SDFGI_MAX_CASCADES) {
+		ambient_light = vec4(0, 0, 0, 1);
+		reflection_light = vec4(0, 0, 0, 1);
+
+		float blend;
+		vec3 diffuse, specular;
+		sdfgi_probe_process(cascade, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse, specular);
+
+		{
+			//process blend
+			float blend_from = (float(sdfgi.probe_axis_size - 1) / 2.0) - 2.5;
+			float blend_to = blend_from + 2.0;
+
+			vec3 inner_pos = cam_pos * sdfgi.cascades[cascade].to_probe;
+
+			float len = length(inner_pos);
+
+			inner_pos = abs(normalize(inner_pos));
+			len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
+
+			if (len >= blend_from) {
+				blend = smoothstep(blend_from, blend_to, len);
+			} else {
+				blend = 0.0;
+			}
+		}
+
+		if (blend > 0.0) {
+			//blend
+			if (cascade == sdfgi.max_cascades - 1) {
+				ambient_light.a = 1.0 - blend;
+				reflection_light.a = 1.0 - blend;
+
+			} else {
+				vec3 diffuse2, specular2;
+				cascade_pos = (cam_pos - sdfgi.cascades[cascade + 1].position) * sdfgi.cascades[cascade + 1].to_probe;
+				sdfgi_probe_process(cascade + 1, cascade_pos, cam_pos, cam_normal, reflection, roughness, diffuse2, specular2);
+				diffuse = mix(diffuse, diffuse2, blend);
+				specular = mix(specular, specular2, blend);
+			}
+		}
+
+		ambient_light.rgb = diffuse;
+#if 1
+		if (roughness < 0.2) {
+			vec3 pos_to_uvw = 1.0 / sdfgi.grid_size;
+			vec4 light_accum = vec4(0.0);
+
+			float blend_size = (sdfgi.grid_size.x / float(sdfgi.probe_axis_size - 1)) * 0.5;
+
+			float radius_sizes[SDFGI_MAX_CASCADES];
+			cascade = 0xFFFF;
+
+			float base_distance = length(cam_pos);
+			for (uint i = 0; i < sdfgi.max_cascades; i++) {
+				radius_sizes[i] = (1.0 / sdfgi.cascades[i].to_cell) * (sdfgi.grid_size.x * 0.5 - blend_size);
+				if (cascade == 0xFFFF && base_distance < radius_sizes[i]) {
+					cascade = i;
+				}
+			}
+
+			cascade = min(cascade, sdfgi.max_cascades - 1);
+
+			float max_distance = radius_sizes[sdfgi.max_cascades - 1];
+			vec3 ray_pos = cam_pos;
+			vec3 ray_dir = reflection;
+
+			{
+				float prev_radius = cascade > 0 ? radius_sizes[cascade - 1] : 0.0;
+				float base_blend = (base_distance - prev_radius) / (radius_sizes[cascade] - prev_radius);
+				float bias = (1.0 + base_blend) * 1.1;
+				vec3 abs_ray_dir = abs(ray_dir);
+				//ray_pos += ray_dir * (bias / sdfgi.cascades[cascade].to_cell); //bias to avoid self occlusion
+				ray_pos += (ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) + cam_normal * 1.4) * bias / sdfgi.cascades[cascade].to_cell;
+			}
+
+			float softness = 0.2 + min(1.0, roughness * 5.0) * 4.0; //approximation to roughness so it does not seem like a hard fade
+			while (length(ray_pos) < max_distance) {
+				for (uint i = 0; i < sdfgi.max_cascades; i++) {
+					if (i >= cascade && length(ray_pos) < radius_sizes[i]) {
+						cascade = max(i, cascade); //never go down
+
+						vec3 pos = ray_pos - sdfgi.cascades[i].position;
+						pos *= sdfgi.cascades[i].to_cell * pos_to_uvw;
+
+						float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), pos).r * 255.0 - 1.1;
+
+						vec4 hit_light = vec4(0.0);
+						if (distance < softness) {
+							hit_light.rgb = texture(sampler3D(light_cascades[i], linear_sampler), pos).rgb;
+							hit_light.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
+							hit_light.a = clamp(1.0 - (distance / softness), 0.0, 1.0);
+							hit_light.rgb *= hit_light.a;
+						}
+
+						distance /= sdfgi.cascades[i].to_cell;
+
+						if (i < (sdfgi.max_cascades - 1)) {
+							pos = ray_pos - sdfgi.cascades[i + 1].position;
+							pos *= sdfgi.cascades[i + 1].to_cell * pos_to_uvw;
+
+							float distance2 = texture(sampler3D(sdf_cascades[i + 1], linear_sampler), pos).r * 255.0 - 1.1;
+
+							vec4 hit_light2 = vec4(0.0);
+							if (distance2 < softness) {
+								hit_light2.rgb = texture(sampler3D(light_cascades[i + 1], linear_sampler), pos).rgb;
+								hit_light2.rgb *= 0.5; //approximation given value read is actually meant for anisotropy
+								hit_light2.a = clamp(1.0 - (distance2 / softness), 0.0, 1.0);
+								hit_light2.rgb *= hit_light2.a;
+							}
+
+							float prev_radius = i == 0 ? 0.0 : radius_sizes[i - 1];
+							float blend = clamp((length(ray_pos) - prev_radius) / (radius_sizes[i] - prev_radius), 0.0, 1.0);
+
+							distance2 /= sdfgi.cascades[i + 1].to_cell;
+
+							hit_light = mix(hit_light, hit_light2, blend);
+							distance = mix(distance, distance2, blend);
+						}
+
+						light_accum += hit_light;
+						ray_pos += ray_dir * distance;
+						break;
+					}
+				}
+
+				if (light_accum.a > 0.99) {
+					break;
+				}
+			}
+
+			vec3 light = light_accum.rgb / max(light_accum.a, 0.00001);
+			float alpha = min(1.0, light_accum.a);
+
+			float b = min(1.0, roughness * 5.0);
+
+			float sa = 1.0 - b;
+
+			reflection_light.a = alpha * sa + b;
+			if (reflection_light.a == 0) {
+				specular = vec3(0.0);
+			} else {
+				specular = (light * alpha * sa + specular * b) / reflection_light.a;
+			}
+		}
+
+#endif
+
+		reflection_light.rgb = specular;
+
+		ambient_light.rgb *= sdfgi.energy;
+		reflection_light.rgb *= sdfgi.energy;
+	} else {
+		ambient_light = vec4(0);
+		reflection_light = vec4(0);
+	}
+}
+
+//standard voxel cone trace
+vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
+	float dist = p_bias;
+	vec4 color = vec4(0.0);
+
+	while (dist < max_distance && color.a < 0.95) {
+		float diameter = max(1.0, 2.0 * tan_half_angle * dist);
+		vec3 uvw_pos = (pos + dist * direction) * cell_size;
+		float half_diameter = diameter * 0.5;
+		//check if outside, then break
+		if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) {
+			break;
+		}
+		vec4 scolor = textureLod(sampler3D(probe, linear_sampler_with_mipmaps), uvw_pos, log2(diameter));
+		float a = (1.0 - color.a);
+		color += a * scolor;
+		dist += half_diameter;
+	}
+
+	return color;
+}
+
+vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float max_distance, float p_bias) {
+	float dist = p_bias;
+	vec4 color = vec4(0.0);
+	float radius = max(0.5, dist);
+	float lod_level = log2(radius * 2.0);
+
+	while (dist < max_distance && color.a < 0.95) {
+		vec3 uvw_pos = (pos + dist * direction) * cell_size;
+
+		//check if outside, then break
+		if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) {
+			break;
+		}
+		vec4 scolor = textureLod(sampler3D(probe, linear_sampler_with_mipmaps), uvw_pos, lod_level);
+		lod_level += 1.0;
+
+		float a = (1.0 - color.a);
+		scolor *= a;
+		color += scolor;
+		dist += radius;
+		radius = max(0.5, dist);
+	}
+	return color;
+}
+
+void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 normal_xform, float roughness, inout vec4 out_spec, inout vec4 out_diff, inout float out_blend) {
+	position = (gi_probes.data[index].xform * vec4(position, 1.0)).xyz;
+	ref_vec = normalize((gi_probes.data[index].xform * vec4(ref_vec, 0.0)).xyz);
+	normal = normalize((gi_probes.data[index].xform * vec4(normal, 0.0)).xyz);
+
+	position += normal * gi_probes.data[index].normal_bias;
+
+	//this causes corrupted pixels, i have no idea why..
+	if (any(bvec2(any(lessThan(position, vec3(0.0))), any(greaterThan(position, gi_probes.data[index].bounds))))) {
+		return;
+	}
+
+	mat3 dir_xform = mat3(gi_probes.data[index].xform) * normal_xform;
+
+	vec3 blendv = abs(position / gi_probes.data[index].bounds * 2.0 - 1.0);
+	float blend = clamp(1.0 - max(blendv.x, max(blendv.y, blendv.z)), 0.0, 1.0);
+	//float blend=1.0;
+
+	float max_distance = length(gi_probes.data[index].bounds);
+	vec3 cell_size = 1.0 / gi_probes.data[index].bounds;
+
+	//irradiance
+
+	vec4 light = vec4(0.0);
+
+	if (params.high_quality_vct) {
+		const uint cone_dir_count = 6;
+		vec3 cone_dirs[cone_dir_count] = vec3[](
+				vec3(0.0, 0.0, 1.0),
+				vec3(0.866025, 0.0, 0.5),
+				vec3(0.267617, 0.823639, 0.5),
+				vec3(-0.700629, 0.509037, 0.5),
+				vec3(-0.700629, -0.509037, 0.5),
+				vec3(0.267617, -0.823639, 0.5));
+
+		float cone_weights[cone_dir_count] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15);
+		float cone_angle_tan = 0.577;
+
+		for (uint i = 0; i < cone_dir_count; i++) {
+			vec3 dir = normalize(dir_xform * cone_dirs[i]);
+			light += cone_weights[i] * voxel_cone_trace(gi_probe_textures[index], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
+		}
+	} else {
+		const uint cone_dir_count = 4;
+		vec3 cone_dirs[cone_dir_count] = vec3[](
+				vec3(0.707107, 0.0, 0.707107),
+				vec3(0.0, 0.707107, 0.707107),
+				vec3(-0.707107, 0.0, 0.707107),
+				vec3(0.0, -0.707107, 0.707107));
+
+		float cone_weights[cone_dir_count] = float[](0.25, 0.25, 0.25, 0.25);
+		for (int i = 0; i < cone_dir_count; i++) {
+			vec3 dir = normalize(dir_xform * cone_dirs[i]);
+			light += cone_weights[i] * voxel_cone_trace_45_degrees(gi_probe_textures[index], cell_size, position, dir, max_distance, gi_probes.data[index].bias);
+		}
+	}
+
+	if (gi_probes.data[index].ambient_occlusion > 0.001) {
+		float size = 1.0 + gi_probes.data[index].ambient_occlusion_size * 7.0;
+
+		float taps, blend;
+		blend = modf(size, taps);
+		float ao = 0.0;
+		for (float i = 1.0; i <= taps; i++) {
+			vec3 ofs = (position + normal * (i * 0.5 + 1.0)) * cell_size;
+			ao += textureLod(sampler3D(gi_probe_textures[index], linear_sampler_with_mipmaps), ofs, i - 1.0).a * i;
+		}
+
+		if (blend > 0.001) {
+			vec3 ofs = (position + normal * ((taps + 1.0) * 0.5 + 1.0)) * cell_size;
+			ao += textureLod(sampler3D(gi_probe_textures[index], linear_sampler_with_mipmaps), ofs, taps).a * (taps + 1.0) * blend;
+		}
+
+		ao = 1.0 - min(1.0, ao);
+
+		light.rgb = mix(params.ao_color, light.rgb, mix(1.0, ao, gi_probes.data[index].ambient_occlusion));
+	}
+
+	light.rgb *= gi_probes.data[index].dynamic_range;
+	if (!gi_probes.data[index].blend_ambient) {
+		light.a = 1.0;
+	}
+
+	out_diff += light * blend;
+
+	//radiance
+	vec4 irr_light = voxel_cone_trace(gi_probe_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias);
+	irr_light.rgb *= gi_probes.data[index].dynamic_range;
+	if (!gi_probes.data[index].blend_ambient) {
+		irr_light.a = 1.0;
+	}
+
+	out_spec += irr_light * blend;
+
+	out_blend += blend;
+}
+
+vec4 fetch_normal_and_roughness(ivec2 pos) {
+	vec4 normal_roughness = texelFetch(sampler2D(normal_roughness_buffer, linear_sampler), pos, 0);
+
+	normal_roughness.xyz = normalize(normal_roughness.xyz * 2.0 - 1.0);
+	return normal_roughness;
+}
+
+void main() {
+	// Pixel being shaded
+	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+	if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
+		return;
+	}
+
+	vec3 vertex = reconstruct_position(pos);
+	vertex.y = -vertex.y;
+
+	vec4 normal_roughness = fetch_normal_and_roughness(pos);
+	vec3 normal = normal_roughness.xyz;
+
+	vec4 ambient_light = vec4(0.0), reflection_light = vec4(0.0);
+
+	if (normal.length() > 0.5) {
+		//valid normal, can do GI
+		float roughness = normal_roughness.w;
+
+		vertex = mat3(params.cam_rotation) * vertex;
+		normal = normalize(mat3(params.cam_rotation) * normal);
+
+		vec3 reflection = normalize(reflect(normalize(vertex), normal));
+
+		if (params.use_sdfgi) {
+			sdfgi_process(vertex, normal, reflection, roughness, ambient_light, reflection_light);
+		}
+
+		if (params.max_giprobes > 0) {
+			uvec2 giprobe_tex = texelFetch(usampler2D(giprobe_buffer, linear_sampler), pos, 0).rg;
+			roughness *= roughness;
+			//find arbitrary tangent and bitangent, then build a matrix
+			vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
+			vec3 tangent = normalize(cross(v0, normal));
+			vec3 bitangent = normalize(cross(tangent, normal));
+			mat3 normal_mat = mat3(tangent, bitangent, normal);
+
+			vec4 amb_accum = vec4(0.0);
+			vec4 spec_accum = vec4(0.0);
+			float blend_accum = 0.0;
+
+			for (uint i = 0; i < params.max_giprobes; i++) {
+				if (any(equal(uvec2(i), giprobe_tex))) {
+					gi_probe_compute(i, vertex, normal, reflection, normal_mat, roughness, spec_accum, amb_accum, blend_accum);
+				}
+			}
+			if (blend_accum > 0.0) {
+				amb_accum /= blend_accum;
+				spec_accum /= blend_accum;
+			}
+
+			if (params.use_sdfgi) {
+				reflection_light = blend_color(spec_accum, reflection_light);
+				ambient_light = blend_color(amb_accum, ambient_light);
+			} else {
+				reflection_light = spec_accum;
+				ambient_light = amb_accum;
+			}
+		}
+	}
+
+	imageStore(ambient_buffer, pos, ambient_light);
+	imageStore(reflection_buffer, pos, reflection_light);
+}

+ 110 - 0
servers/rendering/rasterizer_rd/shaders/resolve.glsl

@@ -0,0 +1,110 @@
+#[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#ifdef MODE_RESOLVE_GI
+layout(set = 0, binding = 0) uniform sampler2DMS source_depth;
+layout(set = 0, binding = 1) uniform sampler2DMS source_normal_roughness;
+
+layout(r32f, set = 1, binding = 0) uniform restrict writeonly image2D dest_depth;
+layout(rgba8, set = 1, binding = 1) uniform restrict writeonly image2D dest_normal_roughness;
+
+#ifdef GIPROBE_RESOLVE
+layout(set = 2, binding = 0) uniform usampler2DMS source_giprobe;
+layout(rg8ui, set = 3, binding = 0) uniform restrict writeonly uimage2D dest_giprobe;
+#endif
+
+#endif
+
+layout(push_constant, binding = 16, std430) uniform Params {
+	ivec2 screen_size;
+	int sample_count;
+	uint pad;
+}
+params;
+
+void main() {
+	// Pixel being shaded
+	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+	if (any(greaterThanEqual(pos, params.screen_size))) { //too large, do nothing
+		return;
+	}
+
+#ifdef MODE_RESOLVE_GI
+
+	float best_depth = 1e20;
+	vec4 best_normal_roughness = vec4(0.0);
+#ifdef GIPROBE_RESOLVE
+	uvec2 best_giprobe;
+#endif
+
+#if 0
+
+	for(int i=0;i<params.sample_count;i++) {
+		float depth = texelFetch(source_depth,pos,i).r;
+		if (depth < best_depth) { //use the depth closest to camera
+			best_depth = depth;
+			best_normal_roughness = texelFetch(source_normal_roughness,pos,i);
+
+#ifdef GIPROBE_RESOLVE
+			best_giprobe = texelFetch(source_giprobe,pos,i).rg;
+#endif
+		}
+	}
+
+#else
+
+	float depths[16];
+	int depth_indices[16];
+	int depth_amount[16];
+	int depth_count = 0;
+
+	for (int i = 0; i < params.sample_count; i++) {
+		float depth = texelFetch(source_depth, pos, i).r;
+		int depth_index = -1;
+		for (int j = 0; j < depth_count; j++) {
+			if (abs(depths[j] - depth) < 0.000001) {
+				depth_index = j;
+				break;
+			}
+		}
+
+		if (depth_index == -1) {
+			depths[depth_count] = depth;
+			depth_indices[depth_count] = i;
+			depth_amount[depth_count] = 1;
+			depth_count += 1;
+		} else {
+			depth_amount[depth_index] += 1;
+		}
+	}
+
+	int depth_least = 0xFFFF;
+	int best_index = 0;
+	for (int j = 0; j < depth_count; j++) {
+		if (depth_amount[j] < depth_least) {
+			best_index = depth_indices[j];
+			depth_least = depth_amount[j];
+		}
+	}
+
+	best_depth = texelFetch(source_depth, pos, best_index).r;
+	best_normal_roughness = texelFetch(source_normal_roughness, pos, best_index);
+#ifdef GIPROBE_RESOLVE
+	best_giprobe = texelFetch(source_giprobe, pos, best_index).rg;
+#endif
+
+#endif
+
+	imageStore(dest_depth, pos, vec4(best_depth));
+	imageStore(dest_normal_roughness, pos, vec4(best_normal_roughness));
+#ifdef GIPROBE_RESOLVE
+	imageStore(dest_giprobe, pos, uvec4(best_giprobe, 0, 0));
+#endif
+
+#endif
+}

+ 404 - 187
servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl

@@ -258,7 +258,6 @@ VERTEX_SHADER_CODE
 		}
 	}
 #endif
-
 #ifdef MODE_RENDER_MATERIAL
 	if (scene_data.material_uv2_mode) {
 		gl_Position.xy = (uv2_attrib.xy + draw_call.bake_uv2_offset) * 2.0 - 1.0;
@@ -341,11 +340,13 @@ 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
+#ifdef MODE_RENDER_NORMAL_ROUGHNESS
+layout(location = 0) out vec4 normal_roughness_output_buffer;
+
+#ifdef MODE_RENDER_GIPROBE
+layout(location = 1) out uvec2 giprobe_buffer;
+#endif
+
 #endif //MODE_RENDER_NORMAL
 #else // RENDER DEPTH
 
@@ -1321,37 +1322,39 @@ void reflection_process(uint ref_index, vec3 vertex, vec3 normal, float roughnes
 		reflection_accum += reflection;
 	}
 
-#if !defined(USE_LIGHTMAP) && !defined(USE_VOXEL_CONE_TRACING)
-	if (reflections.data[ref_index].ambient.a > 0.0) { //compute ambient using skybox
+	switch (reflections.data[ref_index].ambient_mode) {
+		case REFLECTION_AMBIENT_DISABLED: {
+			//do nothing
+		} break;
+		case REFLECTION_AMBIENT_ENVIRONMENT: {
+			//do nothing
+			vec3 local_amb_vec = (reflections.data[ref_index].local_matrix * vec4(normal, 0.0)).xyz;
 
-		vec3 local_amb_vec = (reflections.data[ref_index].local_matrix * vec4(normal, 0.0)).xyz;
+			vec4 ambient_out;
 
-		vec4 ambient_out;
-
-		ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb;
-
-		ambient_out.a = blend;
-		ambient_out.rgb = mix(reflections.data[ref_index].ambient.rgb, ambient_out.rgb, reflections.data[ref_index].ambient.a);
-		if (reflections.data[ref_index].params.z < 0.5) {
-			ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
-		}
+			ambient_out.rgb = textureLod(samplerCubeArray(reflection_atlas, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), vec4(local_amb_vec, reflections.data[ref_index].index), MAX_ROUGHNESS_LOD).rgb;
+			ambient_out.a = blend;
+			if (reflections.data[ref_index].params.z < 0.5) { //interior
+				ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
+			}
 
-		ambient_out.rgb *= ambient_out.a;
-		ambient_accum += ambient_out;
-	} else {
-		vec4 ambient_out;
-		ambient_out.a = blend;
-		ambient_out.rgb = reflections.data[ref_index].ambient.rgb;
-		if (reflections.data[ref_index].params.z < 0.5) {
-			ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
-		}
-		ambient_out.rgb *= ambient_out.a;
-		ambient_accum += ambient_out;
+			ambient_out.rgb *= ambient_out.a;
+			ambient_accum += ambient_out;
+		} break;
+		case REFLECTION_AMBIENT_COLOR: {
+			vec4 ambient_out;
+			ambient_out.a = blend;
+			ambient_out.rgb = reflections.data[ref_index].ambient;
+			if (reflections.data[ref_index].params.z < 0.5) {
+				ambient_out.rgb = mix(ambient_light, ambient_out.rgb, blend);
+			}
+			ambient_out.rgb *= ambient_out.a;
+			ambient_accum += ambient_out;
+		} break;
 	}
-#endif //USE_LIGHTMAP or VCT
 }
 
-#ifdef USE_VOXEL_CONE_TRACING
+#ifdef USE_FORWARD_GI
 
 //standard voxel cone trace
 vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
@@ -1375,42 +1378,6 @@ vec4 voxel_cone_trace(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction,
 	return color;
 }
 
-#ifndef GI_PROBE_HIGH_QUALITY
-//faster version for 45 degrees
-
-#ifdef GI_PROBE_USE_ANISOTROPY
-
-vec4 voxel_cone_trace_anisotropic_45_degrees(texture3D probe, texture3D aniso_pos, texture3D aniso_neg, vec3 normal, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
-	float dist = p_bias;
-	vec4 color = vec4(0.0);
-	float radius = max(0.5, tan_half_angle * dist);
-	float lod_level = log2(radius * 2.0);
-
-	while (dist < max_distance && color.a < 0.95) {
-		vec3 uvw_pos = (pos + dist * direction) * cell_size;
-		//check if outside, then break
-		if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + radius * cell_size)))) {
-			break;
-		}
-
-		vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level);
-		vec3 aniso_neg = textureLod(sampler3D(aniso_neg, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level).rgb;
-		vec3 aniso_pos = textureLod(sampler3D(aniso_pos, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, lod_level).rgb;
-
-		scolor.rgb *= dot(max(vec3(0.0), (normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-normal * aniso_neg)), vec3(1.0));
-		lod_level += 1.0;
-
-		float a = (1.0 - color.a);
-		scolor *= a;
-		color += scolor;
-		dist += radius;
-		radius = max(0.5, tan_half_angle * dist);
-	}
-
-	return color;
-}
-#else
-
 vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
 	float dist = p_bias;
 	vec4 color = vec4(0.0);
@@ -1437,41 +1404,6 @@ vec4 voxel_cone_trace_45_degrees(texture3D probe, vec3 cell_size, vec3 pos, vec3
 	return color;
 }
 
-#endif
-
-#elif defined(GI_PROBE_USE_ANISOTROPY)
-
-//standard voxel cone trace
-vec4 voxel_cone_trace_anisotropic(texture3D probe, texture3D aniso_pos, texture3D aniso_neg, vec3 normal, vec3 cell_size, vec3 pos, vec3 direction, float tan_half_angle, float max_distance, float p_bias) {
-	float dist = p_bias;
-	vec4 color = vec4(0.0);
-
-	while (dist < max_distance && color.a < 0.95) {
-		float diameter = max(1.0, 2.0 * tan_half_angle * dist);
-		vec3 uvw_pos = (pos + dist * direction) * cell_size;
-		float half_diameter = diameter * 0.5;
-		//check if outside, then break
-		if (any(greaterThan(abs(uvw_pos - 0.5), vec3(0.5f + half_diameter * cell_size)))) {
-			break;
-		}
-		float log2_diameter = log2(diameter);
-		vec4 scolor = textureLod(sampler3D(probe, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter);
-		vec3 aniso_neg = textureLod(sampler3D(aniso_neg, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter).rgb;
-		vec3 aniso_pos = textureLod(sampler3D(aniso_pos, material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), uvw_pos, log2_diameter).rgb;
-
-		scolor.rgb *= dot(max(vec3(0.0), (normal * aniso_pos)), vec3(1.0)) + dot(max(vec3(0.0), (-normal * aniso_neg)), vec3(1.0));
-
-		float a = (1.0 - color.a);
-		scolor *= a;
-		color += scolor;
-		dist += half_diameter;
-	}
-
-	return color;
-}
-
-#endif
-
 void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3 normal_xform, float roughness, vec3 ambient, vec3 environment, inout vec4 out_spec, inout vec4 out_diff) {
 	position = (gi_probes.data[index].xform * vec4(position, 1.0)).xyz;
 	ref_vec = normalize((gi_probes.data[index].xform * vec4(ref_vec, 0.0)).xyz);
@@ -1493,31 +1425,6 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
 
 	//radiance
 
-#ifdef GI_PROBE_HIGH_QUALITY
-
-#define MAX_CONE_DIRS 6
-	vec3 cone_dirs[MAX_CONE_DIRS] = vec3[](
-			vec3(0.0, 0.0, 1.0),
-			vec3(0.866025, 0.0, 0.5),
-			vec3(0.267617, 0.823639, 0.5),
-			vec3(-0.700629, 0.509037, 0.5),
-			vec3(-0.700629, -0.509037, 0.5),
-			vec3(0.267617, -0.823639, 0.5));
-
-	float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.15, 0.15, 0.15, 0.15, 0.15);
-	float cone_angle_tan = 0.577;
-
-#elif defined(GI_PROBE_LOW_QUALITY)
-
-#define MAX_CONE_DIRS 1
-
-	vec3 cone_dirs[MAX_CONE_DIRS] = vec3[](
-			vec3(0.0, 0.0, 1.0));
-
-	float cone_weights[MAX_CONE_DIRS] = float[](1.0);
-	float cone_angle_tan = 4; //~76 degrees
-#else // MEDIUM QUALITY
-
 #define MAX_CONE_DIRS 4
 
 	vec3 cone_dirs[MAX_CONE_DIRS] = vec3[](
@@ -1529,31 +1436,13 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
 	float cone_weights[MAX_CONE_DIRS] = float[](0.25, 0.25, 0.25, 0.25);
 	float cone_angle_tan = 0.98269;
 
-#endif
 	vec3 light = vec3(0.0);
 
 	for (int i = 0; i < MAX_CONE_DIRS; i++) {
 		vec3 dir = normalize((gi_probes.data[index].xform * vec4(normal_xform * cone_dirs[i], 0.0)).xyz);
 
-#if defined(GI_PROBE_HIGH_QUALITY) || defined(GI_PROBE_LOW_QUALITY)
-
-#ifdef GI_PROBE_USE_ANISOTROPY
-		vec4 cone_light = voxel_cone_trace_anisotropic(gi_probe_textures[gi_probes.data[index].texture_slot], gi_probe_textures[gi_probes.data[index].texture_slot + 1], gi_probe_textures[gi_probes.data[index].texture_slot + 2], normalize(mix(dir, normal, gi_probes.data[index].anisotropy_strength)), cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
-#else
-
-		vec4 cone_light = voxel_cone_trace(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
-
-#endif // GI_PROBE_USE_ANISOTROPY
-
-#else
-
-#ifdef GI_PROBE_USE_ANISOTROPY
-		vec4 cone_light = voxel_cone_trace_anisotropic_45_degrees(gi_probe_textures[gi_probes.data[index].texture_slot], gi_probe_textures[gi_probes.data[index].texture_slot + 1], gi_probe_textures[gi_probes.data[index].texture_slot + 2], normalize(mix(dir, normal, gi_probes.data[index].anisotropy_strength)), cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
-#else
-		vec4 cone_light = voxel_cone_trace_45_degrees(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
-#endif // GI_PROBE_USE_ANISOTROPY
+		vec4 cone_light = voxel_cone_trace_45_degrees(gi_probe_textures[index], cell_size, position, dir, cone_angle_tan, max_distance, gi_probes.data[index].bias);
 
-#endif
 		if (gi_probes.data[index].blend_ambient) {
 			cone_light.rgb = mix(ambient, cone_light.rgb, min(1.0, cone_light.a / 0.95));
 		}
@@ -1562,33 +1451,10 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
 	}
 
 	light *= gi_probes.data[index].dynamic_range;
-
-	if (gi_probes.data[index].ambient_occlusion > 0.001) {
-		float size = 1.0 + gi_probes.data[index].ambient_occlusion_size * 7.0;
-
-		float taps, blend;
-		blend = modf(size, taps);
-		float ao = 0.0;
-		for (float i = 1.0; i <= taps; i++) {
-			vec3 ofs = (position + normal * (i * 0.5 + 1.0)) * cell_size;
-			ao += textureLod(sampler3D(gi_probe_textures[gi_probes.data[index].texture_slot], material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ofs, i - 1.0).a * i;
-		}
-
-		if (blend > 0.001) {
-			vec3 ofs = (position + normal * ((taps + 1.0) * 0.5 + 1.0)) * cell_size;
-			ao += textureLod(sampler3D(gi_probe_textures[gi_probes.data[index].texture_slot], material_samplers[SAMPLER_LINEAR_WITH_MIPMAPS_CLAMP]), ofs, taps).a * (taps + 1.0) * blend;
-		}
-
-		ao = 1.0 - min(1.0, ao);
-
-		light = mix(scene_data.ao_color.rgb, light, mix(1.0, ao, gi_probes.data[index].ambient_occlusion));
-	}
-
 	out_diff += vec4(light * blend, blend);
 
 	//irradiance
-#ifndef GI_PROBE_LOW_QUALITY
-	vec4 irr_light = voxel_cone_trace(gi_probe_textures[gi_probes.data[index].texture_slot], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias);
+	vec4 irr_light = voxel_cone_trace(gi_probe_textures[index], cell_size, position, ref_vec, tan(roughness * 0.5 * M_PI * 0.99), max_distance, gi_probes.data[index].bias);
 	if (gi_probes.data[index].blend_ambient) {
 		irr_light.rgb = mix(environment, irr_light.rgb, min(1.0, irr_light.a / 0.95));
 	}
@@ -1596,10 +1462,142 @@ void gi_probe_compute(uint index, vec3 position, vec3 normal, vec3 ref_vec, mat3
 	//irr_light=vec3(0.0);
 
 	out_spec += vec4(irr_light.rgb * blend, blend);
-#endif
 }
 
-#endif //USE_VOXEL_CONE_TRACING
+#endif //USE_FORWARD_GI
+
+vec2 octahedron_wrap(vec2 v) {
+	vec2 signVal;
+	signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
+	signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
+	return (1.0 - abs(v.yx)) * signVal;
+}
+
+vec2 octahedron_encode(vec3 n) {
+	// https://twitter.com/Stubbesaurus/status/937994790553227264
+	n /= (abs(n.x) + abs(n.y) + abs(n.z));
+	n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
+	n.xy = n.xy * 0.5 + 0.5;
+	return n.xy;
+}
+
+void sdfgi_process(uint cascade, vec3 cascade_pos, vec3 cam_pos, vec3 cam_normal, vec3 cam_specular_normal, bool use_specular, float roughness, out vec3 diffuse_light, out vec3 specular_light, out float blend) {
+	cascade_pos += cam_normal * sdfgi.normal_bias;
+
+	vec3 base_pos = floor(cascade_pos);
+	//cascade_pos += mix(vec3(0.0),vec3(0.01),lessThan(abs(cascade_pos-base_pos),vec3(0.01))) * cam_normal;
+	ivec3 probe_base_pos = ivec3(base_pos);
+
+	vec4 diffuse_accum = vec4(0.0);
+	vec3 specular_accum;
+
+	ivec3 tex_pos = ivec3(probe_base_pos.xy, int(cascade));
+	tex_pos.x += probe_base_pos.z * sdfgi.probe_axis_size;
+	tex_pos.xy = tex_pos.xy * (SDFGI_OCT_SIZE + 2) + ivec2(1);
+
+	vec3 diffuse_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
+
+	vec3 specular_posf;
+
+	if (use_specular) {
+		specular_accum = vec3(0.0);
+		specular_posf = (vec3(tex_pos) + vec3(octahedron_encode(cam_specular_normal) * float(SDFGI_OCT_SIZE), 0.0)) * sdfgi.lightprobe_tex_pixel_size;
+	}
+
+	vec4 light_accum = vec4(0.0);
+	float weight_accum = 0.0;
+
+	for (uint j = 0; j < 8; j++) {
+		ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
+		ivec3 probe_posi = probe_base_pos;
+		probe_posi += offset;
+
+		// Compute weight
+
+		vec3 probe_pos = vec3(probe_posi);
+		vec3 probe_to_pos = cascade_pos - probe_pos;
+		vec3 probe_dir = normalize(-probe_to_pos);
+
+		vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
+		float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(cam_normal, probe_dir));
+
+		// Compute lightprobe occlusion
+
+		if (sdfgi.use_occlusion) {
+			ivec3 occ_indexv = abs((sdfgi.cascades[cascade].probe_world_offset + probe_posi) & ivec3(1, 1, 1)) * ivec3(1, 2, 4);
+			vec4 occ_mask = mix(vec4(0.0), vec4(1.0), equal(ivec4(occ_indexv.x | occ_indexv.y), ivec4(0, 1, 2, 3)));
+
+			vec3 occ_pos = clamp(cascade_pos, probe_pos - sdfgi.occlusion_clamp, probe_pos + sdfgi.occlusion_clamp) * sdfgi.probe_to_uvw;
+			occ_pos.z += float(cascade);
+			if (occ_indexv.z != 0) { //z bit is on, means index is >=4, so make it switch to the other half of textures
+				occ_pos.x += 1.0;
+			}
+
+			occ_pos *= sdfgi.occlusion_renormalize;
+			float occlusion = dot(textureLod(sampler3D(sdfgi_occlusion_cascades, material_samplers[SAMPLER_LINEAR_CLAMP]), occ_pos, 0.0), occ_mask);
+
+			weight *= max(occlusion, 0.01);
+		}
+
+		// Compute lightprobe texture position
+
+		vec3 diffuse;
+		vec3 pos_uvw = diffuse_posf;
+		pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
+		pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
+		diffuse = textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb;
+
+		diffuse_accum += vec4(diffuse * weight, weight);
+
+		if (use_specular) {
+			vec3 specular = vec3(0.0);
+			vec3 pos_uvw = specular_posf;
+			pos_uvw.xy += vec2(offset.xy) * sdfgi.lightprobe_uv_offset.xy;
+			pos_uvw.x += float(offset.z) * sdfgi.lightprobe_uv_offset.z;
+			if (roughness < 0.99) {
+				specular = textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw + vec3(0, 0, float(sdfgi.max_cascades)), 0.0).rgb;
+			}
+			if (roughness > 0.5) {
+				specular = mix(specular, textureLod(sampler2DArray(sdfgi_lightprobe_texture, material_samplers[SAMPLER_LINEAR_CLAMP]), pos_uvw, 0.0).rgb, (roughness - 0.5) * 2.0);
+			}
+
+			specular_accum += specular * weight;
+		}
+	}
+
+	if (diffuse_accum.a > 0.0) {
+		diffuse_accum.rgb /= diffuse_accum.a;
+	}
+
+	diffuse_light = diffuse_accum.rgb;
+
+	if (use_specular) {
+		if (diffuse_accum.a > 0.0) {
+			specular_accum /= diffuse_accum.a;
+		}
+
+		specular_light = specular_accum;
+	}
+
+	{
+		//process blend
+		float blend_from = (float(sdfgi.probe_axis_size - 1) / 2.0) - 2.5;
+		float blend_to = blend_from + 2.0;
+
+		vec3 inner_pos = cam_pos * sdfgi.cascades[cascade].to_probe;
+
+		float len = length(inner_pos);
+
+		inner_pos = abs(normalize(inner_pos));
+		len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
+
+		if (len >= blend_from) {
+			blend = smoothstep(blend_from, blend_to, len);
+		} else {
+			blend = 0.0;
+		}
+	}
+}
 
 #endif //!defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
@@ -1812,6 +1810,15 @@ FRAGMENT_SHADER_CODE
 #endif //not render depth
 	/////////////////////// LIGHTING //////////////////////////////
 
+	if (scene_data.roughness_limiter_enabled) {
+		//http://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf
+		float roughness2 = roughness * roughness;
+		vec3 dndu = dFdx(normal), dndv = dFdx(normal);
+		float variance = scene_data.roughness_limiter_amount * (dot(dndu, dndu) + dot(dndv, dndv));
+		float kernelRoughness2 = min(2.0 * variance, scene_data.roughness_limiter_limit); //limit effect
+		float filteredRoughness2 = min(1.0, roughness2 + kernelRoughness2);
+		roughness = sqrt(filteredRoughness2);
+	}
 	//apply energy conservation
 
 	vec3 specular_light = vec3(0.0, 0.0, 0.0);
@@ -1820,11 +1827,6 @@ FRAGMENT_SHADER_CODE
 
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
 
-	if (scene_data.roughness_limiter_enabled) {
-		float limit = texelFetch(sampler2D(roughness_buffer, material_samplers[SAMPLER_NEAREST_CLAMP]), ivec2(gl_FragCoord.xy), 0).r;
-		roughness = max(roughness, limit);
-	}
-
 	if (scene_data.use_reflection_cubemap) {
 		vec3 ref_vec = reflect(-view, normal);
 		ref_vec = scene_data.radiance_inverse_xform * ref_vec;
@@ -1871,7 +1873,6 @@ FRAGMENT_SHADER_CODE
 #endif
 
 #if !defined(MODE_RENDER_DEPTH) && !defined(MODE_UNSHADED)
-	//gi probes
 
 #ifdef USE_LIGHTMAP
 
@@ -1928,10 +1929,80 @@ FRAGMENT_SHADER_CODE
 			ambient_light += textureLod(sampler2DArray(lightmap_textures[ofs], material_samplers[SAMPLER_LINEAR_CLAMP]), uvw, 0.0).rgb;
 		}
 	}
-#endif
-	//lightmap capture
+#elif defined(USE_FORWARD_GI)
+
+	if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_SDFGI)) { //has lightmap capture
+
+		//make vertex orientation the world one, but still align to camera
+		vec3 cam_pos = mat3(scene_data.camera_matrix) * vertex;
+		vec3 cam_normal = mat3(scene_data.camera_matrix) * normal;
+		vec3 cam_reflection = mat3(scene_data.camera_matrix) * reflect(-view, normal);
+
+		//apply y-mult
+		cam_pos.y *= sdfgi.y_mult;
+		cam_normal.y *= sdfgi.y_mult;
+		cam_normal = normalize(cam_normal);
+		cam_reflection.y *= sdfgi.y_mult;
+		cam_normal = normalize(cam_normal);
+		cam_reflection = normalize(cam_reflection);
+
+		vec4 light_accum = vec4(0.0);
+		float weight_accum = 0.0;
+
+		vec4 light_blend_accum = vec4(0.0);
+		float weight_blend_accum = 0.0;
+
+		float blend = -1.0;
+
+		// helper constants, compute once
+
+		uint cascade = 0xFFFFFFFF;
+		vec3 cascade_pos;
+		vec3 cascade_normal;
+
+		for (uint i = 0; i < sdfgi.max_cascades; i++) {
+			cascade_pos = (cam_pos - sdfgi.cascades[i].position) * sdfgi.cascades[i].to_probe;
+
+			if (any(lessThan(cascade_pos, vec3(0.0))) || any(greaterThanEqual(cascade_pos, sdfgi.cascade_probe_size))) {
+				continue; //skip cascade
+			}
+
+			cascade = i;
+			break;
+		}
+
+		if (cascade < SDFGI_MAX_CASCADES) {
+			bool use_specular = true;
+			float blend;
+			vec3 diffuse, specular;
+			sdfgi_process(cascade, cascade_pos, cam_pos, cam_normal, cam_reflection, use_specular, roughness, diffuse, specular, blend);
+
+			if (blend > 0.0) {
+				//blend
+				if (cascade == sdfgi.max_cascades - 1) {
+					diffuse = mix(diffuse, ambient_light, blend);
+					if (use_specular) {
+						specular = mix(specular, specular_light, blend);
+					}
+				} else {
+					vec3 diffuse2, specular2;
+					float blend2;
+					cascade_pos = (cam_pos - sdfgi.cascades[cascade + 1].position) * sdfgi.cascades[cascade + 1].to_probe;
+					sdfgi_process(cascade + 1, cascade_pos, cam_pos, cam_normal, cam_reflection, use_specular, roughness, diffuse2, specular2, blend2);
+					diffuse = mix(diffuse, diffuse2, blend);
+					if (use_specular) {
+						specular = mix(specular, specular2, blend);
+					}
+				}
+			}
+
+			ambient_light = diffuse;
+			if (use_specular) {
+				specular_light = specular;
+			}
+		}
+	}
 
-#ifdef USE_VOXEL_CONE_TRACING
 	if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GIPROBE)) { // process giprobes
 
 		uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
@@ -1963,6 +2034,56 @@ FRAGMENT_SHADER_CODE
 		specular_light = spec_accum.rgb;
 		ambient_light = amb_accum.rgb;
 	}
+#else
+	if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GI_BUFFERS)) { //use GI buffers
+
+		ivec2 coord;
+
+		if (scene_data.gi_upscale_for_msaa) {
+			/*
+			//find the closest depth to upscale from, based on neighbours
+			ivec2 base_coord = ivec2(gl_FragCoord.xy);
+			float z_dist = gl_FragCoord.z;
+			ivec2 closest_coord = base_coord;
+			float closest_z_dist = abs(texelFetch(sampler2D(depth_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord,0).r-z_dist);
+
+			for(int i=0;i<4;i++) {
+				const ivec2 neighbours[4]=ivec2[](ivec2(-1,0),ivec2(1,0),ivec2(0,-1),ivec2(0,1));
+				ivec2 neighbour_coord = base_coord + neighbours[i];
+				float neighbour_z_dist = abs(texelFetch(sampler2D(depth_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord,0).r-z_dist);
+				if (neighbour_z_dist < closest_z_dist) {
+					closest_z_dist = neighbour_z_dist;
+					closest_coord = neighbour_coord;
+				}
+			}
+
+*/
+			ivec2 base_coord = ivec2(gl_FragCoord.xy);
+			ivec2 closest_coord = base_coord;
+			float closest_ang = dot(normal, texelFetch(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), base_coord, 0).xyz * 2.0 - 1.0);
+
+			for (int i = 0; i < 4; i++) {
+				const ivec2 neighbours[4] = ivec2[](ivec2(-1, 0), ivec2(1, 0), ivec2(0, -1), ivec2(0, 1));
+				ivec2 neighbour_coord = base_coord + neighbours[i];
+				float neighbour_ang = dot(normal, texelFetch(sampler2D(normal_roughness_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), neighbour_coord, 0).xyz * 2.0 - 1.0);
+				if (neighbour_ang > closest_ang) {
+					closest_ang = neighbour_ang;
+					closest_coord = neighbour_coord;
+				}
+			}
+
+			coord = closest_coord;
+
+		} else {
+			coord = ivec2(gl_FragCoord.xy);
+		}
+
+		vec4 buffer_ambient = texelFetch(sampler2D(ambient_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0);
+		vec4 buffer_reflection = texelFetch(sampler2D(reflection_buffer, material_samplers[SAMPLER_LINEAR_CLAMP]), coord, 0);
+
+		ambient_light = mix(ambient_light, buffer_ambient.rgb, buffer_ambient.a);
+		specular_light = mix(specular_light, buffer_reflection.rgb, buffer_reflection.a);
+	}
 #endif
 
 	{ // process reflections
@@ -2376,6 +2497,93 @@ FRAGMENT_SHADER_CODE
 
 #ifdef MODE_RENDER_DEPTH
 
+#ifdef MODE_RENDER_SDF
+
+	{
+		vec3 local_pos = (scene_data.sdf_to_bounds * vec4(vertex, 1.0)).xyz;
+		ivec3 grid_pos = scene_data.sdf_offset + ivec3(local_pos * vec3(scene_data.sdf_size));
+
+		uint albedo16 = 0x1; //solid flag
+		albedo16 |= clamp(uint(albedo.r * 31.0), 0, 31) << 11;
+		albedo16 |= clamp(uint(albedo.g * 31.0), 0, 31) << 6;
+		albedo16 |= clamp(uint(albedo.b * 31.0), 0, 31) << 1;
+
+		imageStore(albedo_volume_grid, grid_pos, uvec4(albedo16));
+
+		uint facing_bits = 0;
+		const vec3 aniso_dir[6] = vec3[](
+				vec3(1, 0, 0),
+				vec3(0, 1, 0),
+				vec3(0, 0, 1),
+				vec3(-1, 0, 0),
+				vec3(0, -1, 0),
+				vec3(0, 0, -1));
+
+		vec3 cam_normal = mat3(scene_data.camera_matrix) * normal;
+
+		for (uint i = 0; i < 6; i++) {
+			if (dot(cam_normal, aniso_dir[i]) > 0.001) {
+				facing_bits |= (1 << i);
+			}
+		}
+
+		imageAtomicOr(geom_facing_grid, grid_pos, facing_bits); //store facing bits
+
+		if (length(emission) > 0.001) {
+			float lumas[6];
+			vec3 light_total = vec3(0);
+
+			for (int i = 0; i < 6; i++) {
+				float strength = max(0.0, dot(cam_normal, aniso_dir[i]));
+				vec3 light = emission * strength;
+				light_total += light;
+				lumas[i] = max(light.r, max(light.g, light.b));
+			}
+
+			float luma_total = max(light_total.r, max(light_total.g, light_total.b));
+
+			uint light_aniso = 0;
+
+			for (int i = 0; i < 6; i++) {
+				light_aniso |= min(31, uint((lumas[i] / luma_total) * 31.0)) << (i * 5);
+			}
+
+			//compress to RGBE9995 to save space
+
+			const float pow2to9 = 512.0f;
+			const float B = 15.0f;
+			const float N = 9.0f;
+			const float LN2 = 0.6931471805599453094172321215;
+
+			float cRed = clamp(light_total.r, 0.0, 65408.0);
+			float cGreen = clamp(light_total.g, 0.0, 65408.0);
+			float cBlue = clamp(light_total.b, 0.0, 65408.0);
+
+			float cMax = max(cRed, max(cGreen, cBlue));
+
+			float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B;
+
+			float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f);
+
+			float exps = expp + 1.0f;
+
+			if (0.0 <= sMax && sMax < pow2to9) {
+				exps = expp;
+			}
+
+			float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
+			float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
+			float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
+			//store as 8985 to have 2 extra neighbour bits
+			uint light_rgbe = ((uint(sRed) & 0x1FF) >> 1) | ((uint(sGreen) & 0x1FF) << 8) | (((uint(sBlue) & 0x1FF) >> 1) << 17) | ((uint(exps) & 0x1F) << 25);
+
+			imageStore(emission_grid, grid_pos, uvec4(light_rgbe));
+			imageStore(emission_aniso_grid, grid_pos, uvec4(light_aniso));
+		}
+	}
+
+#endif
+
 #ifdef MODE_RENDER_MATERIAL
 
 	albedo_output_buffer.rgb = albedo;
@@ -2398,11 +2606,21 @@ 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
+#ifdef MODE_RENDER_NORMAL_ROUGHNESS
+	normal_roughness_output_buffer = vec4(normal * 0.5 + 0.5, roughness);
+
+#ifdef MODE_RENDER_GIPROBE
+	if (bool(instances.data[instance_index].flags & INSTANCE_FLAGS_USE_GIPROBE)) { // process giprobes
+		uint index1 = instances.data[instance_index].gi_offset & 0xFFFF;
+		uint index2 = instances.data[instance_index].gi_offset >> 16;
+		giprobe_buffer.x = index1 & 0xFF;
+		giprobe_buffer.y = index2 & 0xFF;
+	} else {
+		giprobe_buffer.x = 0xFF;
+		giprobe_buffer.y = 0xFF;
+	}
+#endif
+
 #endif //MODE_RENDER_NORMAL
 
 //nothing happens, so a tree-ssa optimizer will result in no fragment shader :)
@@ -2455,7 +2673,6 @@ FRAGMENT_SHADER_CODE
 #endif
 	diffuse_buffer = vec4(emission + diffuse_light + ambient_light, sss_strength);
 	specular_buffer = vec4(specular_light, metallic);
-
 #endif
 
 #else //MODE_MULTIPLE_RENDER_TARGETS

+ 104 - 29
servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl

@@ -1,6 +1,8 @@
 #define M_PI 3.14159265359
 #define ROUGHNESS_MAX_LOD 5
 
+#define MAX_GI_PROBES 8
+
 layout(push_constant, binding = 0, std430) uniform DrawCall {
 	uint instance_index;
 	uint pad; //16 bits minimum size
@@ -27,6 +29,8 @@ layout(set = 0, binding = 1) uniform sampler material_samplers[12];
 
 layout(set = 0, binding = 2) uniform sampler shadow_sampler;
 
+#define SDFGI_MAX_CASCADES 8
+
 layout(set = 0, binding = 3, std140) uniform SceneData {
 	mat4 projection_matrix;
 	mat4 inv_projection_matrix;
@@ -76,11 +80,19 @@ layout(set = 0, binding = 3, std140) uniform SceneData {
 	float ssao_ao_affect;
 	bool roughness_limiter_enabled;
 
+	float roughness_limiter_amount;
+	float roughness_limiter_limit;
+	uvec2 roughness_limiter_pad;
+
 	vec4 ao_color;
+
+	mat4 sdf_to_bounds;
+
+	ivec3 sdf_offset;
 	bool material_uv2_mode;
-	uint pad_material0;
-	uint pad_material1;
-	uint pad_material2;
+
+	ivec3 sdf_size;
+	bool gi_upscale_for_msaa;
 
 #if 0
 	vec4 ambient_light_color;
@@ -120,6 +132,8 @@ layout(set = 0, binding = 3, std140) uniform SceneData {
 
 scene_data;
 
+#define INSTANCE_FLAGS_USE_GI_BUFFERS (1 << 6)
+#define INSTANCE_FLAGS_USE_SDFGI (1 << 7)
 #define INSTANCE_FLAGS_USE_LIGHTMAP_CAPTURE (1 << 8)
 #define INSTANCE_FLAGS_USE_LIGHTMAP (1 << 9)
 #define INSTANCE_FLAGS_USE_SH_LIGHTMAP (1 << 10)
@@ -175,13 +189,18 @@ layout(set = 0, binding = 5, std430) restrict readonly buffer Lights {
 }
 lights;
 
+#define REFLECTION_AMBIENT_DISABLED 0
+#define REFLECTION_AMBIENT_ENVIRONMENT 1
+#define REFLECTION_AMBIENT_COLOR 2
+
 struct ReflectionData {
 	vec3 box_extents;
 	float index;
 	vec3 box_offset;
 	uint mask;
 	vec4 params; // intensity, 0, interior , boxproject
-	vec4 ambient; // ambient color, energy
+	vec3 ambient; // ambient color
+	uint ambient_mode;
 	mat4 local_matrix; // up to here for spot and omni, rest is for directional
 	// notes: for ambientblend, use distance to edge to blend between already existing global environment
 };
@@ -229,29 +248,6 @@ layout(set = 0, binding = 7, std140) uniform DirectionalLights {
 }
 directional_lights;
 
-struct GIProbeData {
-	mat4 xform;
-	vec3 bounds;
-	float dynamic_range;
-
-	float bias;
-	float normal_bias;
-	bool blend_ambient;
-	uint texture_slot;
-
-	float anisotropy_strength;
-	float ambient_occlusion;
-	float ambient_occlusion_size;
-	uint pad2;
-};
-
-layout(set = 0, binding = 8, std140) uniform GIProbes {
-	GIProbeData data[MAX_GI_PROBES];
-}
-gi_probes;
-
-layout(set = 0, binding = 9) uniform texture3D gi_probe_textures[MAX_GI_PROBE_TEXTURES];
-
 #define LIGHTMAP_FLAG_USE_DIRECTION 1
 #define LIGHTMAP_FLAG_USE_SPECULAR_DIRECTION 2
 
@@ -319,6 +315,41 @@ layout(set = 0, binding = 19, std430) restrict readonly buffer GlobalVariableDat
 }
 global_variables;
 
+struct SDFGIProbeCascadeData {
+	vec3 position;
+	float to_probe;
+	ivec3 probe_world_offset;
+	float to_cell; // 1/bounds * grid_size
+};
+
+layout(set = 0, binding = 20, std140) uniform SDFGI {
+	vec3 grid_size;
+	uint max_cascades;
+
+	bool use_occlusion;
+	int probe_axis_size;
+	float probe_to_uvw;
+	float normal_bias;
+
+	vec3 lightprobe_tex_pixel_size;
+	float energy;
+
+	vec3 lightprobe_uv_offset;
+	float y_mult;
+
+	vec3 occlusion_clamp;
+	uint pad3;
+
+	vec3 occlusion_renormalize;
+	uint pad4;
+
+	vec3 cascade_probe_size;
+	uint pad5;
+
+	SDFGIProbeCascadeData cascades[SDFGI_MAX_CASCADES];
+}
+sdfgi;
+
 // decal atlas
 
 /* Set 1, Radiance */
@@ -339,13 +370,57 @@ layout(set = 2, binding = 0) uniform textureCubeArray reflection_atlas;
 
 layout(set = 2, binding = 1) uniform texture2D shadow_atlas;
 
+layout(set = 2, binding = 2) uniform texture3D gi_probe_textures[MAX_GI_PROBES];
+
 /* Set 3, Render Buffers */
 
+#ifdef MODE_RENDER_SDF
+
+layout(r16ui, set = 3, binding = 0) uniform restrict writeonly uimage3D albedo_volume_grid;
+layout(r32ui, set = 3, binding = 1) uniform restrict writeonly uimage3D emission_grid;
+layout(r32ui, set = 3, binding = 2) uniform restrict writeonly uimage3D emission_aniso_grid;
+layout(r32ui, set = 3, binding = 3) uniform restrict uimage3D geom_facing_grid;
+
+//still need to be present for shaders that use it, so remap them to something
+#define depth_buffer shadow_atlas
+#define color_buffer shadow_atlas
+#define normal_roughness_buffer shadow_atlas
+
+#else
+
 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 = 2) uniform texture2D normal_roughness_buffer;
 layout(set = 3, binding = 4) uniform texture2D ao_buffer;
+layout(set = 3, binding = 5) uniform texture2D ambient_buffer;
+layout(set = 3, binding = 6) uniform texture2D reflection_buffer;
+
+layout(set = 3, binding = 7) uniform texture2DArray sdfgi_lightprobe_texture;
+
+layout(set = 3, binding = 8) uniform texture3D sdfgi_occlusion_cascades;
+
+struct GIProbeData {
+	mat4 xform;
+	vec3 bounds;
+	float dynamic_range;
+
+	float bias;
+	float normal_bias;
+	bool blend_ambient;
+	uint texture_slot;
+
+	float anisotropy_strength;
+	float ambient_occlusion;
+	float ambient_occlusion_size;
+	uint pad2;
+};
+
+layout(set = 3, binding = 9, std140) uniform GIProbes {
+	GIProbeData data[MAX_GI_PROBES];
+}
+gi_probes;
+
+#endif
 
 /* Set 4 Skeleton & Instancing (Multimesh) */
 

+ 4 - 6
servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl

@@ -12,11 +12,8 @@ layout(rgba16f, set = 1, binding = 0) uniform restrict writeonly image2D ssr_ima
 #ifdef MODE_ROUGH
 layout(r8, set = 1, binding = 1) uniform restrict writeonly image2D blur_radius_image;
 #endif
-layout(rgba8, set = 2, binding = 0) uniform restrict readonly image2D source_normal;
+layout(rgba8, set = 2, binding = 0) uniform restrict readonly image2D source_normal_roughness;
 layout(set = 3, binding = 0) uniform sampler2D source_metallic;
-#ifdef MODE_ROUGH
-layout(set = 3, binding = 1) uniform sampler2D source_roughness;
-#endif
 
 layout(push_constant, binding = 2, std430) uniform Params {
 	vec4 proj_info;
@@ -75,7 +72,8 @@ void main() {
 	// World space point being shaded
 	vec3 vertex = reconstructCSPosition(uv * vec2(params.screen_size), base_depth);
 
-	vec3 normal = imageLoad(source_normal, ssC).xyz * 2.0 - 1.0;
+	vec4 normal_roughness = imageLoad(source_normal_roughness, ssC);
+	vec3 normal = normal_roughness.xyz * 2.0 - 1.0;
 	normal = normalize(normal);
 	normal.y = -normal.y; //because this code reads flipped
 
@@ -208,7 +206,7 @@ void main() {
 
 		// if roughness is enabled, do screen space cone tracing
 		float blur_radius = 0.0;
-		float roughness = texelFetch(source_roughness, ssC << 1, 0).r;
+		float roughness = normal_roughness.w;
 
 		if (roughness > 0.001) {
 			float cone_angle = min(roughness, 0.999) * M_PI * 0.5;

+ 275 - 0
servers/rendering/rasterizer_rd/shaders/sdfgi_debug.glsl

@@ -0,0 +1,275 @@
+#[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#define MAX_CASCADES 8
+
+layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
+layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES];
+layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES];
+layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES];
+layout(set = 0, binding = 5) uniform texture3D occlusion_texture;
+
+layout(set = 0, binding = 8) uniform sampler linear_sampler;
+
+struct CascadeData {
+	vec3 offset; //offset of (0,0,0) in world coordinates
+	float to_cell; // 1/bounds * grid_size
+	ivec3 probe_world_offset;
+	uint pad;
+};
+
+layout(set = 0, binding = 9, std140) uniform Cascades {
+	CascadeData data[MAX_CASCADES];
+}
+cascades;
+
+layout(rgba16f, set = 0, binding = 10) uniform restrict writeonly image2D screen_buffer;
+
+layout(set = 0, binding = 11) uniform texture2DArray lightprobe_texture;
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	vec3 grid_size;
+	uint max_cascades;
+
+	ivec2 screen_size;
+	bool use_occlusion;
+	float y_mult;
+
+	vec3 cam_extent;
+	int probe_axis_size;
+
+	mat4 cam_transform;
+}
+params;
+
+vec3 linear_to_srgb(vec3 color) {
+	//if going to srgb, clamp from 0 to 1.
+	color = clamp(color, vec3(0.0), vec3(1.0));
+	const vec3 a = vec3(0.055f);
+	return mix((vec3(1.0f) + a) * pow(color.rgb, vec3(1.0f / 2.4f)) - a, 12.92f * color.rgb, lessThan(color.rgb, vec3(0.0031308f)));
+}
+
+vec2 octahedron_wrap(vec2 v) {
+	vec2 signVal;
+	signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
+	signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
+	return (1.0 - abs(v.yx)) * signVal;
+}
+
+vec2 octahedron_encode(vec3 n) {
+	// https://twitter.com/Stubbesaurus/status/937994790553227264
+	n /= (abs(n.x) + abs(n.y) + abs(n.z));
+	n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
+	n.xy = n.xy * 0.5 + 0.5;
+	return n.xy;
+}
+
+void main() {
+	// Pixel being shaded
+	ivec2 screen_pos = ivec2(gl_GlobalInvocationID.xy);
+	if (any(greaterThanEqual(screen_pos, params.screen_size))) { //too large, do nothing
+		return;
+	}
+
+	vec3 ray_pos;
+	vec3 ray_dir;
+	{
+		ray_pos = params.cam_transform[3].xyz;
+
+		ray_dir.xy = params.cam_extent.xy * ((vec2(screen_pos) / vec2(params.screen_size)) * 2.0 - 1.0);
+		ray_dir.z = params.cam_extent.z;
+
+		ray_dir = normalize(mat3(params.cam_transform) * ray_dir);
+	}
+
+	ray_pos.y *= params.y_mult;
+	ray_dir.y *= params.y_mult;
+	ray_dir = normalize(ray_dir);
+
+	vec3 pos_to_uvw = 1.0 / params.grid_size;
+
+	vec3 light = vec3(0.0);
+	float blend = 0.0;
+
+#if 1
+	vec3 inv_dir = 1.0 / ray_dir;
+
+	float rough = 0.5;
+	bool hit = false;
+
+	for (uint i = 0; i < params.max_cascades; i++) {
+		//convert to local bounds
+		vec3 pos = ray_pos - cascades.data[i].offset;
+		pos *= cascades.data[i].to_cell;
+
+		// Should never happen for debug, since we start mostly at the bounds center,
+		// but add anyway.
+		//if (any(lessThan(pos,vec3(0.0))) || any(greaterThanEqual(pos,params.grid_size))) {
+		//	continue; //already past bounds for this cascade, goto next
+		//}
+
+		//find maximum advance distance (until reaching bounds)
+		vec3 t0 = -pos * inv_dir;
+		vec3 t1 = (params.grid_size - pos) * inv_dir;
+		vec3 tmax = max(t0, t1);
+		float max_advance = min(tmax.x, min(tmax.y, tmax.z));
+
+		float advance = 0.0;
+		vec3 uvw;
+		hit = false;
+
+		while (advance < max_advance) {
+			//read how much to advance from SDF
+			uvw = (pos + ray_dir * advance) * pos_to_uvw;
+
+			float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), uvw).r * 255.0 - 1.7;
+
+			if (distance < 0.001) {
+				//consider hit
+				hit = true;
+				break;
+			}
+
+			advance += distance;
+		}
+
+		if (!hit) {
+			pos += ray_dir * min(advance, max_advance);
+			pos /= cascades.data[i].to_cell;
+			pos += cascades.data[i].offset;
+			ray_pos = pos;
+			continue;
+		}
+
+		//compute albedo, emission and normal at hit point
+
+		const float EPSILON = 0.001;
+		vec3 hit_normal = normalize(vec3(
+				texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
+				texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
+				texture(sampler3D(sdf_cascades[i], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[i], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
+
+		vec3 hit_light = texture(sampler3D(light_cascades[i], linear_sampler), uvw).rgb;
+		vec4 aniso0 = texture(sampler3D(aniso0_cascades[i], linear_sampler), uvw);
+		vec3 hit_aniso0 = aniso0.rgb;
+		vec3 hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[i], linear_sampler), uvw).rg);
+
+		hit_light *= (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
+
+		if (blend > 0.0) {
+			light = mix(light, hit_light, blend);
+			blend = 0.0;
+		} else {
+			light = hit_light;
+
+			//process blend
+			float blend_from = (float(params.probe_axis_size - 1) / 2.0) - 2.5;
+			float blend_to = blend_from + 2.0;
+
+			vec3 cam_pos = params.cam_transform[3].xyz - cascades.data[i].offset;
+			cam_pos *= cascades.data[i].to_cell;
+
+			pos += ray_dir * min(advance, max_advance);
+			vec3 inner_pos = pos - cam_pos;
+
+			inner_pos = inner_pos * float(params.probe_axis_size - 1) / params.grid_size.x;
+
+			float len = length(inner_pos);
+
+			inner_pos = abs(normalize(inner_pos));
+			len *= max(inner_pos.x, max(inner_pos.y, inner_pos.z));
+
+			if (len >= blend_from) {
+				blend = smoothstep(blend_from, blend_to, len);
+
+				pos /= cascades.data[i].to_cell;
+				pos += cascades.data[i].offset;
+				ray_pos = pos;
+				hit = false; //continue trace for blend
+
+				continue;
+			}
+		}
+
+		break;
+	}
+
+	light = mix(light, vec3(0.0), blend);
+
+#else
+
+	vec3 inv_dir = 1.0 / ray_dir;
+
+	bool hit = false;
+	vec4 light_accum = vec4(0.0);
+
+	float blend_size = (params.grid_size.x / float(params.probe_axis_size - 1)) * 0.5;
+
+	float radius_sizes[MAX_CASCADES];
+	for (uint i = 0; i < params.max_cascades; i++) {
+		radius_sizes[i] = (1.0 / cascades.data[i].to_cell) * (params.grid_size.x * 0.5 - blend_size);
+	}
+
+	float max_distance = radius_sizes[params.max_cascades - 1];
+	float advance = 0;
+	while (advance < max_distance) {
+		for (uint i = 0; i < params.max_cascades; i++) {
+			if (advance < radius_sizes[i]) {
+				vec3 pos = (ray_pos + ray_dir * advance) - cascades.data[i].offset;
+				pos *= cascades.data[i].to_cell * pos_to_uvw;
+
+				float distance = texture(sampler3D(sdf_cascades[i], linear_sampler), pos).r * 255.0 - 1.0;
+
+				vec4 hit_light = vec4(0.0);
+				if (distance < 1.0) {
+					hit_light.a = max(0.0, 1.0 - distance);
+					hit_light.rgb = texture(sampler3D(light_cascades[i], linear_sampler), pos).rgb;
+					hit_light.rgb *= hit_light.a;
+				}
+
+				distance /= cascades.data[i].to_cell;
+
+				if (i < (params.max_cascades - 1)) {
+					pos = (ray_pos + ray_dir * advance) - cascades.data[i + 1].offset;
+					pos *= cascades.data[i + 1].to_cell * pos_to_uvw;
+
+					float distance2 = texture(sampler3D(sdf_cascades[i + 1], linear_sampler), pos).r * 255.0 - 1.0;
+
+					vec4 hit_light2 = vec4(0.0);
+					if (distance2 < 1.0) {
+						hit_light2.a = max(0.0, 1.0 - distance2);
+						hit_light2.rgb = texture(sampler3D(light_cascades[i + 1], linear_sampler), pos).rgb;
+						hit_light2.rgb *= hit_light2.a;
+					}
+
+					float prev_radius = i == 0 ? 0.0 : radius_sizes[i - 1];
+					float blend = (advance - prev_radius) / (radius_sizes[i] - prev_radius);
+
+					distance2 /= cascades.data[i + 1].to_cell;
+
+					hit_light = mix(hit_light, hit_light2, blend);
+					distance = mix(distance, distance2, blend);
+				}
+
+				light_accum += hit_light;
+				advance += distance;
+				break;
+			}
+		}
+
+		if (light_accum.a > 0.98) {
+			break;
+		}
+	}
+
+	light = light_accum.rgb / light_accum.a;
+
+#endif
+
+	imageStore(screen_buffer, screen_pos, vec4(linear_to_srgb(light), 1.0));
+}

+ 231 - 0
servers/rendering/rasterizer_rd/shaders/sdfgi_debug_probes.glsl

@@ -0,0 +1,231 @@
+#[vertex]
+
+#version 450
+
+VERSION_DEFINES
+
+#define MAX_CASCADES 8
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	mat4 projection;
+
+	uint band_power;
+	uint sections_in_band;
+	uint band_mask;
+	float section_arc;
+
+	vec3 grid_size;
+	uint cascade;
+
+	uint pad;
+	float y_mult;
+	uint probe_debug_index;
+	int probe_axis_size;
+}
+params;
+
+// http://in4k.untergrund.net/html_articles/hugi_27_-_coding_corner_polaris_sphere_tessellation_101.htm
+
+vec3 get_sphere_vertex(uint p_vertex_id) {
+	float x_angle = float(p_vertex_id & 1u) + (p_vertex_id >> params.band_power);
+
+	float y_angle =
+			float((p_vertex_id & params.band_mask) >> 1) + ((p_vertex_id >> params.band_power) * params.sections_in_band);
+
+	x_angle *= params.section_arc * 0.5f; // remember - 180AA x rot not 360
+	y_angle *= -params.section_arc;
+
+	vec3 point = vec3(sin(x_angle) * sin(y_angle), cos(x_angle), sin(x_angle) * cos(y_angle));
+
+	return point;
+}
+
+#ifdef MODE_PROBES
+
+layout(location = 0) out vec3 normal_interp;
+layout(location = 1) out flat uint probe_index;
+
+#endif
+
+#ifdef MODE_VISIBILITY
+
+layout(location = 0) out float visibility;
+
+#endif
+
+struct CascadeData {
+	vec3 offset; //offset of (0,0,0) in world coordinates
+	float to_cell; // 1/bounds * grid_size
+	ivec3 probe_world_offset;
+	uint pad;
+};
+
+layout(set = 0, binding = 1, std140) uniform Cascades {
+	CascadeData data[MAX_CASCADES];
+}
+cascades;
+
+layout(set = 0, binding = 4) uniform texture3D occlusion_texture;
+layout(set = 0, binding = 3) uniform sampler linear_sampler;
+
+void main() {
+#ifdef MODE_PROBES
+	probe_index = gl_InstanceIndex;
+
+	normal_interp = get_sphere_vertex(gl_VertexIndex);
+
+	vec3 vertex = normal_interp * 0.2;
+
+	float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
+
+	ivec3 probe_cell;
+	probe_cell.x = int(probe_index % params.probe_axis_size);
+	probe_cell.y = int(probe_index / (params.probe_axis_size * params.probe_axis_size));
+	probe_cell.z = int((probe_index / params.probe_axis_size) % params.probe_axis_size);
+
+	vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0);
+
+	gl_Position = params.projection * vec4(vertex, 1.0);
+#endif
+
+#ifdef MODE_VISIBILITY
+
+	int probe_index = int(params.probe_debug_index);
+
+	vec3 vertex = get_sphere_vertex(gl_VertexIndex) * 0.01;
+
+	float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
+
+	ivec3 probe_cell;
+	probe_cell.x = int(probe_index % params.probe_axis_size);
+	probe_cell.y = int((probe_index % (params.probe_axis_size * params.probe_axis_size)) / params.probe_axis_size);
+	probe_cell.z = int(probe_index / (params.probe_axis_size * params.probe_axis_size));
+
+	vertex += (cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size) / vec3(1.0, params.y_mult, 1.0);
+
+	int probe_voxels = int(params.grid_size.x) / int(params.probe_axis_size - 1);
+	int occluder_index = int(gl_InstanceIndex);
+
+	int diameter = probe_voxels * 2;
+	ivec3 occluder_pos;
+	occluder_pos.x = int(occluder_index % diameter);
+	occluder_pos.y = int(occluder_index / (diameter * diameter));
+	occluder_pos.z = int((occluder_index / diameter) % diameter);
+
+	float cell_size = 1.0 / cascades.data[params.cascade].to_cell;
+
+	ivec3 occluder_offset = occluder_pos - ivec3(diameter / 2);
+	vertex += ((vec3(occluder_offset) + vec3(0.5)) * cell_size) / vec3(1.0, params.y_mult, 1.0);
+
+	ivec3 global_cell = probe_cell + cascades.data[params.cascade].probe_world_offset;
+	uint occlusion_layer = 0;
+	if ((global_cell.x & 1) != 0) {
+		occlusion_layer |= 1;
+	}
+	if ((global_cell.y & 1) != 0) {
+		occlusion_layer |= 2;
+	}
+	if ((global_cell.z & 1) != 0) {
+		occlusion_layer |= 4;
+	}
+	ivec3 tex_pos = probe_cell * probe_voxels + occluder_offset;
+
+	const vec4 layer_axis[4] = vec4[](
+			vec4(1, 0, 0, 0),
+			vec4(0, 1, 0, 0),
+			vec4(0, 0, 1, 0),
+			vec4(0, 0, 0, 1));
+
+	tex_pos.z += int(params.cascade) * int(params.grid_size);
+	if (occlusion_layer >= 4) {
+		tex_pos.x += int(params.grid_size.x);
+		occlusion_layer &= 3;
+	}
+
+	visibility = dot(texelFetch(sampler3D(occlusion_texture, linear_sampler), tex_pos, 0), layer_axis[occlusion_layer]);
+
+	gl_Position = params.projection * vec4(vertex, 1.0);
+
+#endif
+}
+
+#[fragment]
+
+#version 450
+
+VERSION_DEFINES
+
+layout(location = 0) out vec4 frag_color;
+
+layout(set = 0, binding = 2) uniform texture2DArray lightprobe_texture;
+layout(set = 0, binding = 3) uniform sampler linear_sampler;
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	mat4 projection;
+
+	uint band_power;
+	uint sections_in_band;
+	uint band_mask;
+	float section_arc;
+
+	vec3 grid_size;
+	uint cascade;
+
+	uint pad;
+	float y_mult;
+	uint probe_debug_index;
+	int probe_axis_size;
+}
+params;
+
+#ifdef MODE_PROBES
+
+layout(location = 0) in vec3 normal_interp;
+layout(location = 1) in flat uint probe_index;
+
+#endif
+
+#ifdef MODE_VISIBILITY
+layout(location = 0) in float visibility;
+#endif
+
+vec2 octahedron_wrap(vec2 v) {
+	vec2 signVal;
+	signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
+	signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
+	return (1.0 - abs(v.yx)) * signVal;
+}
+
+vec2 octahedron_encode(vec3 n) {
+	// https://twitter.com/Stubbesaurus/status/937994790553227264
+	n /= (abs(n.x) + abs(n.y) + abs(n.z));
+	n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
+	n.xy = n.xy * 0.5 + 0.5;
+	return n.xy;
+}
+
+void main() {
+#ifdef MODE_PROBES
+
+	ivec3 tex_pos;
+	tex_pos.x = int(probe_index) % params.probe_axis_size; //x
+	tex_pos.y = int(probe_index) / (params.probe_axis_size * params.probe_axis_size);
+	tex_pos.x += params.probe_axis_size * ((int(probe_index) / params.probe_axis_size) % params.probe_axis_size); //z
+	tex_pos.z = int(params.cascade);
+
+	vec3 tex_pos_ofs = vec3(octahedron_encode(normal_interp) * float(OCT_SIZE), 0.0);
+	vec3 tex_posf = vec3(vec2(tex_pos.xy * (OCT_SIZE + 2) + ivec2(1)), float(tex_pos.z)) + tex_pos_ofs;
+
+	tex_posf.xy /= vec2(ivec2(params.probe_axis_size * params.probe_axis_size * (OCT_SIZE + 2), params.probe_axis_size * (OCT_SIZE + 2)));
+
+	vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), tex_posf, 0.0);
+
+	frag_color = indirect_light;
+
+#endif
+
+#ifdef MODE_VISIBILITY
+
+	frag_color = vec4(vec3(1, visibility, visibility), 1.0);
+#endif
+}

+ 472 - 0
servers/rendering/rasterizer_rd/shaders/sdfgi_direct_light.glsl

@@ -0,0 +1,472 @@
+#[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
+
+#define MAX_CASCADES 8
+
+layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
+layout(set = 0, binding = 2) uniform sampler linear_sampler;
+
+layout(set = 0, binding = 3, std430) restrict readonly buffer DispatchData {
+	uint x;
+	uint y;
+	uint z;
+	uint total_count;
+}
+dispatch_data;
+
+struct ProcessVoxel {
+	uint position; //xyz 7 bit packed, extra 11 bits for neigbours
+	uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neibhbours
+	uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbous
+	uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbours
+	//total neighbours: 26
+};
+
+#ifdef MODE_PROCESS_STATIC
+layout(set = 0, binding = 4, std430) restrict buffer ProcessVoxels {
+#else
+layout(set = 0, binding = 4, std430) restrict buffer readonly ProcessVoxels {
+#endif
+	ProcessVoxel data[];
+}
+process_voxels;
+
+layout(r32ui, set = 0, binding = 5) uniform restrict uimage3D dst_light;
+layout(rgba8, set = 0, binding = 6) uniform restrict image3D dst_aniso0;
+layout(rg8, set = 0, binding = 7) uniform restrict image3D dst_aniso1;
+
+struct CascadeData {
+	vec3 offset; //offset of (0,0,0) in world coordinates
+	float to_cell; // 1/bounds * grid_size
+	ivec3 probe_world_offset;
+	uint pad;
+};
+
+layout(set = 0, binding = 8, std140) uniform Cascades {
+	CascadeData data[MAX_CASCADES];
+}
+cascades;
+
+#define LIGHT_TYPE_DIRECTIONAL 0
+#define LIGHT_TYPE_OMNI 1
+#define LIGHT_TYPE_SPOT 2
+
+struct Light {
+	vec3 color;
+	float energy;
+
+	vec3 direction;
+	bool has_shadow;
+
+	vec3 position;
+	float attenuation;
+
+	uint type;
+	float spot_angle;
+	float spot_attenuation;
+	float radius;
+
+	vec4 shadow_color;
+};
+
+layout(set = 0, binding = 9, std140) buffer restrict readonly Lights {
+	Light data[];
+}
+lights;
+
+layout(set = 0, binding = 10) uniform texture2DArray lightprobe_texture;
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	vec3 grid_size;
+	uint max_cascades;
+
+	uint cascade;
+	uint light_count;
+	uint process_offset;
+	uint process_increment;
+
+	int probe_axis_size;
+	bool multibounce;
+	float y_mult;
+	uint pad;
+}
+params;
+
+vec2 octahedron_wrap(vec2 v) {
+	vec2 signVal;
+	signVal.x = v.x >= 0.0 ? 1.0 : -1.0;
+	signVal.y = v.y >= 0.0 ? 1.0 : -1.0;
+	return (1.0 - abs(v.yx)) * signVal;
+}
+
+vec2 octahedron_encode(vec3 n) {
+	// https://twitter.com/Stubbesaurus/status/937994790553227264
+	n /= (abs(n.x) + abs(n.y) + abs(n.z));
+	n.xy = n.z >= 0.0 ? n.xy : octahedron_wrap(n.xy);
+	n.xy = n.xy * 0.5 + 0.5;
+	return n.xy;
+}
+
+void main() {
+	uint voxel_index = uint(gl_GlobalInvocationID.x);
+
+	//used for skipping voxels every N frames
+	voxel_index = params.process_offset + voxel_index * params.process_increment;
+
+	if (voxel_index >= dispatch_data.total_count) {
+		return;
+	}
+
+	uint voxel_position = process_voxels.data[voxel_index].position;
+
+	//keep for storing to texture
+	ivec3 positioni = ivec3((uvec3(voxel_position, voxel_position, voxel_position) >> uvec3(0, 7, 14)) & uvec3(0x7F));
+
+	vec3 position = vec3(positioni) + vec3(0.5);
+	position /= cascades.data[params.cascade].to_cell;
+	position += cascades.data[params.cascade].offset;
+
+	uint voxel_albedo = process_voxels.data[voxel_index].albedo;
+
+	vec3 albedo = vec3(uvec3(voxel_albedo >> 10, voxel_albedo >> 5, voxel_albedo) & uvec3(0x1F)) / float(0x1F);
+	vec3 light_accum[6];
+
+	uint valid_aniso = (voxel_albedo >> 15) & 0x3F;
+
+	{
+		uint rgbe = process_voxels.data[voxel_index].light;
+
+		//read rgbe8985
+		float r = float((rgbe & 0xff) << 1);
+		float g = float((rgbe >> 8) & 0x1ff);
+		float b = float(((rgbe >> 17) & 0xff) << 1);
+		float e = float((rgbe >> 25) & 0x1F);
+		float m = pow(2.0, e - 15.0 - 9.0);
+
+		vec3 l = vec3(r, g, b) * m;
+
+		uint aniso = process_voxels.data[voxel_index].light_aniso;
+		for (uint i = 0; i < 6; i++) {
+			float strength = ((aniso >> (i * 5)) & 0x1F) / float(0x1F);
+			light_accum[i] = l * strength;
+		}
+	}
+
+	const vec3 aniso_dir[6] = vec3[](
+			vec3(1, 0, 0),
+			vec3(0, 1, 0),
+			vec3(0, 0, 1),
+			vec3(-1, 0, 0),
+			vec3(0, -1, 0),
+			vec3(0, 0, -1));
+
+	// Raytrace light
+
+	vec3 pos_to_uvw = 1.0 / params.grid_size;
+	vec3 uvw_ofs = pos_to_uvw * 0.5;
+
+	for (uint i = 0; i < params.light_count; i++) {
+		float attenuation = 1.0;
+		vec3 direction;
+		float light_distance = 1e20;
+
+		switch (lights.data[i].type) {
+			case LIGHT_TYPE_DIRECTIONAL: {
+				direction = -lights.data[i].direction;
+			} break;
+			case LIGHT_TYPE_OMNI: {
+				vec3 rel_vec = lights.data[i].position - position;
+				direction = normalize(rel_vec);
+				light_distance = length(rel_vec);
+				rel_vec.y /= params.y_mult;
+				attenuation = pow(clamp(1.0 - length(rel_vec) / lights.data[i].radius, 0.0, 1.0), lights.data[i].attenuation);
+			} break;
+			case LIGHT_TYPE_SPOT: {
+				vec3 rel_vec = lights.data[i].position - position;
+				direction = normalize(rel_vec);
+				light_distance = length(rel_vec);
+				rel_vec.y /= params.y_mult;
+				attenuation = pow(clamp(1.0 - length(rel_vec) / lights.data[i].radius, 0.0, 1.0), lights.data[i].attenuation);
+
+				float angle = acos(dot(normalize(rel_vec), -lights.data[i].direction));
+				if (angle > lights.data[i].spot_angle) {
+					attenuation = 0.0;
+				} else {
+					float d = clamp(angle / lights.data[i].spot_angle, 0, 1);
+					attenuation *= pow(1.0 - d, lights.data[i].spot_attenuation);
+				}
+			} break;
+		}
+
+		if (attenuation < 0.001) {
+			continue;
+		}
+
+		bool hit = false;
+
+		vec3 ray_pos = position;
+		vec3 ray_dir = direction;
+		vec3 inv_dir = 1.0 / ray_dir;
+
+		//this is how to properly bias outgoing rays
+		float cell_size = 1.0 / cascades.data[params.cascade].to_cell;
+		ray_pos += sign(direction) * cell_size * 0.48; // go almost to the box edge but remain inside
+		ray_pos += ray_dir * 0.4 * cell_size; //apply a small bias from there
+
+		for (uint j = params.cascade; j < params.max_cascades; j++) {
+			//convert to local bounds
+			vec3 pos = ray_pos - cascades.data[j].offset;
+			pos *= cascades.data[j].to_cell;
+			float local_distance = light_distance * cascades.data[j].to_cell;
+
+			if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) {
+				continue; //already past bounds for this cascade, goto next
+			}
+
+			//find maximum advance distance (until reaching bounds)
+			vec3 t0 = -pos * inv_dir;
+			vec3 t1 = (params.grid_size - pos) * inv_dir;
+			vec3 tmax = max(t0, t1);
+			float max_advance = min(tmax.x, min(tmax.y, tmax.z));
+
+			max_advance = min(local_distance, max_advance);
+
+			float advance = 0.0;
+			float occlusion = 1.0;
+
+			while (advance < max_advance) {
+				//read how much to advance from SDF
+				vec3 uvw = (pos + ray_dir * advance) * pos_to_uvw;
+
+				float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0;
+				if (distance < 0.001) {
+					//consider hit
+					hit = true;
+					break;
+				}
+
+				occlusion = min(occlusion, distance);
+
+				advance += distance;
+			}
+
+			if (hit) {
+				attenuation *= occlusion;
+				break;
+			}
+
+			if (advance >= local_distance) {
+				break; //past light distance, abandon search
+			}
+			//change ray origin to collision with bounds
+			pos += ray_dir * max_advance;
+			pos /= cascades.data[j].to_cell;
+			pos += cascades.data[j].offset;
+			light_distance -= max_advance / cascades.data[j].to_cell;
+			ray_pos = pos;
+		}
+
+		if (!hit) {
+			vec3 light = albedo * lights.data[i].color.rgb * lights.data[i].energy * attenuation;
+
+			for (int j = 0; j < 6; j++) {
+				if (bool(valid_aniso & (1 << j))) {
+					light_accum[j] += max(0.0, dot(aniso_dir[j], direction)) * light;
+				}
+			}
+		}
+	}
+
+	// Add indirect light
+
+	if (params.multibounce) {
+		vec3 pos = (vec3(positioni) + vec3(0.5)) * float(params.probe_axis_size - 1) / params.grid_size;
+		ivec3 probe_base_pos = ivec3(pos);
+
+		vec4 probe_accum[6] = vec4[](vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0), vec4(0.0));
+		float weight_accum[6] = float[](0, 0, 0, 0, 0, 0);
+
+		ivec3 tex_pos = ivec3(probe_base_pos.xy, int(params.cascade));
+		tex_pos.x += probe_base_pos.z * int(params.probe_axis_size);
+
+		tex_pos.xy = tex_pos.xy * (OCT_SIZE + 2) + ivec2(1);
+
+		vec3 base_tex_posf = vec3(tex_pos);
+		vec2 tex_pixel_size = 1.0 / vec2(ivec2((OCT_SIZE + 2) * params.probe_axis_size * params.probe_axis_size, (OCT_SIZE + 2) * params.probe_axis_size));
+		vec3 probe_uv_offset = (ivec3(OCT_SIZE + 2, OCT_SIZE + 2, (OCT_SIZE + 2) * params.probe_axis_size)) * tex_pixel_size.xyx;
+
+		for (uint j = 0; j < 8; j++) {
+			ivec3 offset = (ivec3(j) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1);
+			ivec3 probe_posi = probe_base_pos;
+			probe_posi += offset;
+
+			// Compute weight
+
+			vec3 probe_pos = vec3(probe_posi);
+			vec3 probe_to_pos = pos - probe_pos;
+			vec3 probe_dir = normalize(-probe_to_pos);
+
+			// Compute lightprobe texture position
+
+			vec3 trilinear = vec3(1.0) - abs(probe_to_pos);
+
+			for (uint k = 0; k < 6; k++) {
+				if (bool(valid_aniso & (1 << k))) {
+					vec3 n = aniso_dir[k];
+					float weight = trilinear.x * trilinear.y * trilinear.z * max(0.005, dot(n, probe_dir));
+
+					vec3 tex_posf = base_tex_posf + vec3(octahedron_encode(n) * float(OCT_SIZE), 0.0);
+					tex_posf.xy *= tex_pixel_size;
+
+					vec3 pos_uvw = tex_posf;
+					pos_uvw.xy += vec2(offset.xy) * probe_uv_offset.xy;
+					pos_uvw.x += float(offset.z) * probe_uv_offset.z;
+					vec4 indirect_light = textureLod(sampler2DArray(lightprobe_texture, linear_sampler), pos_uvw, 0.0);
+
+					probe_accum[k] += indirect_light * weight;
+					weight_accum[k] += weight;
+				}
+			}
+		}
+
+		for (uint k = 0; k < 6; k++) {
+			if (weight_accum[k] > 0.0) {
+				light_accum[k] += probe_accum[k].rgb * albedo / weight_accum[k];
+			}
+		}
+	}
+
+	// Store the light in the light texture
+
+	float lumas[6];
+	vec3 light_total = vec3(0);
+
+	for (int i = 0; i < 6; i++) {
+		light_total += light_accum[i];
+		lumas[i] = max(light_accum[i].r, max(light_accum[i].g, light_accum[i].b));
+	}
+
+	float luma_total = max(light_total.r, max(light_total.g, light_total.b));
+
+	uint light_total_rgbe;
+
+	{
+		//compress to RGBE9995 to save space
+
+		const float pow2to9 = 512.0f;
+		const float B = 15.0f;
+		const float N = 9.0f;
+		const float LN2 = 0.6931471805599453094172321215;
+
+		float cRed = clamp(light_total.r, 0.0, 65408.0);
+		float cGreen = clamp(light_total.g, 0.0, 65408.0);
+		float cBlue = clamp(light_total.b, 0.0, 65408.0);
+
+		float cMax = max(cRed, max(cGreen, cBlue));
+
+		float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B;
+
+		float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f);
+
+		float exps = expp + 1.0f;
+
+		if (0.0 <= sMax && sMax < pow2to9) {
+			exps = expp;
+		}
+
+		float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
+		float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
+		float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
+#ifdef MODE_PROCESS_STATIC
+		//since its self-save, use RGBE8985
+		light_total_rgbe = ((uint(sRed) & 0x1FF) >> 1) | ((uint(sGreen) & 0x1FF) << 8) | (((uint(sBlue) & 0x1FF) >> 1) << 17) | ((uint(exps) & 0x1F) << 25);
+
+#else
+		light_total_rgbe = (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27);
+#endif
+	}
+
+#ifdef MODE_PROCESS_DYNAMIC
+
+	vec4 aniso0;
+	aniso0.r = lumas[0] / luma_total;
+	aniso0.g = lumas[1] / luma_total;
+	aniso0.b = lumas[2] / luma_total;
+	aniso0.a = lumas[3] / luma_total;
+
+	vec2 aniso1;
+	aniso1.r = lumas[4] / luma_total;
+	aniso1.g = lumas[5] / luma_total;
+
+	//save to 3D textures
+	imageStore(dst_aniso0, positioni, aniso0);
+	imageStore(dst_aniso1, positioni, vec4(aniso1, 0.0, 0.0));
+	imageStore(dst_light, positioni, uvec4(light_total_rgbe));
+
+	//also fill neighbours, so light interpolation during the indirect pass works
+
+	//recover the neighbour list from the leftover bits
+	uint neighbours = (voxel_albedo >> 21) | ((voxel_position >> 21) << 11) | ((process_voxels.data[voxel_index].light >> 30) << 22) | ((process_voxels.data[voxel_index].light_aniso >> 30) << 24);
+
+	const uint max_neighbours = 26;
+	const ivec3 neighbour_positions[max_neighbours] = ivec3[](
+			ivec3(-1, -1, -1),
+			ivec3(-1, -1, 0),
+			ivec3(-1, -1, 1),
+			ivec3(-1, 0, -1),
+			ivec3(-1, 0, 0),
+			ivec3(-1, 0, 1),
+			ivec3(-1, 1, -1),
+			ivec3(-1, 1, 0),
+			ivec3(-1, 1, 1),
+			ivec3(0, -1, -1),
+			ivec3(0, -1, 0),
+			ivec3(0, -1, 1),
+			ivec3(0, 0, -1),
+			ivec3(0, 0, 1),
+			ivec3(0, 1, -1),
+			ivec3(0, 1, 0),
+			ivec3(0, 1, 1),
+			ivec3(1, -1, -1),
+			ivec3(1, -1, 0),
+			ivec3(1, -1, 1),
+			ivec3(1, 0, -1),
+			ivec3(1, 0, 0),
+			ivec3(1, 0, 1),
+			ivec3(1, 1, -1),
+			ivec3(1, 1, 0),
+			ivec3(1, 1, 1));
+
+	for (uint i = 0; i < max_neighbours; i++) {
+		if (bool(neighbours & (1 << i))) {
+			ivec3 neighbour_pos = positioni + neighbour_positions[i];
+			imageStore(dst_light, neighbour_pos, uvec4(light_total_rgbe));
+			imageStore(dst_aniso0, neighbour_pos, aniso0);
+			imageStore(dst_aniso1, neighbour_pos, vec4(aniso1, 0.0, 0.0));
+		}
+	}
+
+#endif
+
+#ifdef MODE_PROCESS_STATIC
+
+	//save back the anisotropic
+
+	uint light = process_voxels.data[voxel_index].light & (3 << 30);
+	light |= light_total_rgbe;
+	process_voxels.data[voxel_index].light = light; //replace
+
+	uint light_aniso = process_voxels.data[voxel_index].light_aniso & (3 << 30);
+	for (int i = 0; i < 6; i++) {
+		light_aniso |= min(31, uint((lumas[i] / luma_total) * 31.0)) << (i * 5);
+	}
+
+	process_voxels.data[voxel_index].light_aniso = light_aniso;
+
+#endif
+}

+ 182 - 0
servers/rendering/rasterizer_rd/shaders/sdfgi_fields.glsl

@@ -0,0 +1,182 @@
+/* clang-format off */
+[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = OCT_RES, local_size_y = OCT_RES, local_size_z = 1) in;
+
+/* clang-format on */
+
+#define MAX_CASCADES 8
+
+layout(rgba16f, set = 0, binding = 1) uniform restrict image2DArray irradiance_texture;
+layout(rg16f, set = 0, binding = 2) uniform restrict image2DArray depth_texture;
+
+ayout(rgba32ui, set = 0, binding = 3) uniform restrict uimage2DArray irradiance_history_texture;
+layout(rg32ui, set = 0, binding = 4) uniform restrict uimage2DArray depth_history_texture;
+
+struct CascadeData {
+	vec3 offset; //offset of (0,0,0) in world coordinates
+	float to_cell; // 1/bounds * grid_size
+};
+
+layout(set = 0, binding = 5, std140) uniform Cascades {
+	CascadeData data[MAX_CASCADES];
+}
+cascades;
+
+#define DEPTH_HISTORY_BITS 24
+#define IRRADIANCE_HISTORY_BITS 16
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	vec3 grid_size;
+	uint max_cascades;
+
+	uint probe_axis_size;
+	uint cascade;
+	uint history_size;
+	uint pad0;
+
+	ivec3 scroll; //scroll in probes
+	uint pad1;
+}
+params;
+
+void main() {
+	ivec2 local = ivec2(gl_LocalInvocationID.xy);
+	ivec2 probe = ivec2(gl_WorkGroupID.xy);
+
+	ivec3 probe_cell;
+	probe_cell.x = probe.x % int(params.probe_axis_size);
+	probe_cell.y = probe.y;
+	probe_cell.z = probe.x / int(params.probe_axis_size);
+
+#ifdef MODE_SCROLL_BEGIN
+
+	ivec3 read_cell = probe_cell - params.scroll;
+
+	uint src_layer = (params.history_size + 1) * params.cascade;
+	uint dst_layer = (params.history_size + 1) * params.max_cascades;
+
+	for (uint i = 0; i <= params.history_size; i++) {
+		ivec3 write_pos = ivec3(probe * OCT_RES + local, int(i));
+
+		if (any(lessThan(read_pos, ivec3(0))) || any(greaterThanEqual(read_pos, ivec3(params.probe_axis_size)))) {
+			// nowhere to read from for scrolling, try finding the value from upper probes
+
+#ifdef MODE_IRRADIANCE
+			imageStore(irradiance_history_texture, write_pos, uvec4(0));
+#endif
+#ifdef MODE_DEPTH
+			imageStore(depth_history_texture, write_pos, uvec4(0));
+#endif
+		} else {
+			ivec3 read_pos;
+			read_pos.xy = read_cell.xy;
+			read_pos.x += read_cell.z * params.probe_axis_size;
+			read_pos.xy = read_pos.xy * OCT_RES + local;
+			read_pos.z = int(i);
+
+#ifdef MODE_IRRADIANCE
+			uvec4 value = imageLoad(irradiance_history_texture, read_pos);
+			imageStore(irradiance_history_texture, write_pos, value);
+#endif
+#ifdef MODE_DEPTH
+			uvec2 value = imageLoad(depth_history_texture, read_pos);
+			imageStore(depth_history_texture, write_pos, value);
+#endif
+		}
+	}
+
+#endif // MODE_SCROLL_BEGIN
+
+#ifdef MODE_SCROLL_END
+
+	uint src_layer = (params.history_size + 1) * params.max_cascades;
+	uint dst_layer = (params.history_size + 1) * params.cascade;
+
+	for (uint i = 0; i <= params.history_size; i++) {
+		ivec3 pos = ivec3(probe * OCT_RES + local, int(i));
+
+#ifdef MODE_IRRADIANCE
+		uvec4 value = imageLoad(irradiance_history_texture, read_pos);
+		imageStore(irradiance_history_texture, write_pos, value);
+#endif
+#ifdef MODE_DEPTH
+		uvec2 value = imageLoad(depth_history_texture, read_pos);
+		imageStore(depth_history_texture, write_pos, value);
+#endif
+	}
+
+#endif //MODE_SCROLL_END
+
+#ifdef MODE_STORE
+
+	uint src_layer = (params.history_size + 1) * params.cascade + params.history_size;
+	ivec3 read_pos = ivec3(probe * OCT_RES + local, int(src_layer));
+
+	ivec3 write_pos = ivec3(probe * (OCT_RES + 2) + ivec2(1), int(params.cascade));
+
+	ivec3 copy_to[4] = ivec3[](write_pos, ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2));
+
+#ifdef MODE_IRRADIANCE
+	uvec4 average = imageLoad(irradiance_history_texture, read_pos);
+	vec4 light_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
+
+#endif
+#ifdef MODE_DEPTH
+	uvec2 value = imageLoad(depth_history_texture, read_pos);
+	vec2 depth_accum = vec4(average / params.history_size) / float(1 << IRRADIANCE_HISTORY_BITS);
+
+	float probe_cell_size = float(params.grid_size / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
+	float max_depth = length(params.grid_size / cascades.data[params.max_cascades - 1].to_cell);
+	max_depth /= probe_cell_size;
+
+	depth_value = (vec2(average / params.history_size) / float(1 << DEPTH_HISTORY_BITS)) * vec2(max_depth, max_depth * max_depth);
+
+#endif
+
+	/* Fill the border if required */
+
+	if (local == ivec2(0, 0)) {
+		copy_to[1] = texture_pos + ivec3(OCT_RES - 1, -1, 0);
+		copy_to[2] = texture_pos + ivec3(-1, OCT_RES - 1, 0);
+		copy_to[3] = texture_pos + ivec3(OCT_RES, OCT_RES, 0);
+	} else if (local == ivec2(OCT_RES - 1, 0)) {
+		copy_to[1] = texture_pos + ivec3(0, -1, 0);
+		copy_to[2] = texture_pos + ivec3(OCT_RES, OCT_RES - 1, 0);
+		copy_to[3] = texture_pos + ivec3(-1, OCT_RES, 0);
+	} else if (local == ivec2(0, OCT_RES - 1)) {
+		copy_to[1] = texture_pos + ivec3(-1, 0, 0);
+		copy_to[2] = texture_pos + ivec3(OCT_RES - 1, OCT_RES, 0);
+		copy_to[3] = texture_pos + ivec3(OCT_RES, -1, 0);
+	} else if (local == ivec2(OCT_RES - 1, OCT_RES - 1)) {
+		copy_to[1] = texture_pos + ivec3(0, OCT_RES, 0);
+		copy_to[2] = texture_pos + ivec3(OCT_RES, 0, 0);
+		copy_to[3] = texture_pos + ivec3(-1, -1, 0);
+	} else if (local.y == 0) {
+		copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y - 1, 0);
+	} else if (local.x == 0) {
+		copy_to[1] = texture_pos + ivec3(local.x - 1, OCT_RES - local.y - 1, 0);
+	} else if (local.y == OCT_RES - 1) {
+		copy_to[1] = texture_pos + ivec3(OCT_RES - local.x - 1, local.y + 1, 0);
+	} else if (local.x == OCT_RES - 1) {
+		copy_to[1] = texture_pos + ivec3(local.x + 1, OCT_RES - local.y - 1, 0);
+	}
+
+	for (int i = 0; i < 4; i++) {
+		if (copy_to[i] == ivec3(-2, -2, -2)) {
+			continue;
+		}
+#ifdef MODE_IRRADIANCE
+		imageStore(irradiance_texture, copy_to[i], light_accum);
+#endif
+#ifdef MODE_DEPTH
+		imageStore(depth_texture, copy_to[i], vec4(depth_value, 0.0, 0.0));
+#endif
+	}
+
+#endif // MODE_STORE
+}

+ 605 - 0
servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl

@@ -0,0 +1,605 @@
+#[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
+
+#define MAX_CASCADES 8
+
+layout(set = 0, binding = 1) uniform texture3D sdf_cascades[MAX_CASCADES];
+layout(set = 0, binding = 2) uniform texture3D light_cascades[MAX_CASCADES];
+layout(set = 0, binding = 3) uniform texture3D aniso0_cascades[MAX_CASCADES];
+layout(set = 0, binding = 4) uniform texture3D aniso1_cascades[MAX_CASCADES];
+
+layout(set = 0, binding = 6) uniform sampler linear_sampler;
+
+struct CascadeData {
+	vec3 offset; //offset of (0,0,0) in world coordinates
+	float to_cell; // 1/bounds * grid_size
+	ivec3 probe_world_offset;
+	uint pad;
+};
+
+layout(set = 0, binding = 7, std140) uniform Cascades {
+	CascadeData data[MAX_CASCADES];
+}
+cascades;
+
+layout(r32ui, set = 0, binding = 8) uniform restrict uimage2DArray lightprobe_texture_data;
+layout(rgba16i, set = 0, binding = 9) uniform restrict iimage2DArray lightprobe_history_texture;
+layout(rgba32i, set = 0, binding = 10) uniform restrict iimage2D lightprobe_average_texture;
+
+//used for scrolling
+layout(rgba16i, set = 0, binding = 11) uniform restrict iimage2DArray lightprobe_history_scroll_texture;
+layout(rgba32i, set = 0, binding = 12) uniform restrict iimage2D lightprobe_average_scroll_texture;
+
+layout(rgba32i, set = 0, binding = 13) uniform restrict iimage2D lightprobe_average_parent_texture;
+
+layout(set = 1, binding = 0) uniform textureCube sky_irradiance;
+
+layout(set = 1, binding = 1) uniform sampler linear_sampler_mipmaps;
+
+#define HISTORY_BITS 10
+
+#define SKY_MODE_DISABLED 0
+#define SKY_MODE_COLOR 1
+#define SKY_MODE_SKY 2
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	vec3 grid_size;
+	uint max_cascades;
+
+	uint probe_axis_size;
+	uint cascade;
+	uint history_index;
+	uint history_size;
+
+	uint ray_count;
+	float ray_bias;
+	ivec2 image_size;
+
+	ivec3 world_offset;
+	uint sky_mode;
+
+	ivec3 scroll;
+	float sky_energy;
+
+	vec3 sky_color;
+	float y_mult;
+}
+params;
+
+const float PI = 3.14159265f;
+const float GOLDEN_ANGLE = PI * (3.0 - sqrt(5.0));
+
+vec3 vogel_hemisphere(uint p_index, uint p_count, float p_offset) {
+	float r = sqrt(float(p_index) + 0.5f) / sqrt(float(p_count));
+	float theta = float(p_index) * GOLDEN_ANGLE + p_offset;
+	float y = cos(r * PI * 0.5);
+	float l = sin(r * PI * 0.5);
+	return vec3(l * cos(theta), l * sin(theta), y * (float(p_index & 1) * 2.0 - 1.0));
+}
+
+uvec3 hash3(uvec3 x) {
+	x = ((x >> 16) ^ x) * 0x45d9f3b;
+	x = ((x >> 16) ^ x) * 0x45d9f3b;
+	x = (x >> 16) ^ x;
+	return x;
+}
+
+float hashf3(vec3 co) {
+	return fract(sin(dot(co, vec3(12.9898, 78.233, 137.13451))) * 43758.5453);
+}
+
+vec3 octahedron_encode(vec2 f) {
+	// https://twitter.com/Stubbesaurus/status/937994790553227264
+	f = f * 2.0 - 1.0;
+	vec3 n = vec3(f.x, f.y, 1.0f - abs(f.x) - abs(f.y));
+	float t = clamp(-n.z, 0.0, 1.0);
+	n.x += n.x >= 0 ? -t : t;
+	n.y += n.y >= 0 ? -t : t;
+	return normalize(n);
+}
+
+uint rgbe_encode(vec3 color) {
+	const float pow2to9 = 512.0f;
+	const float B = 15.0f;
+	const float N = 9.0f;
+	const float LN2 = 0.6931471805599453094172321215;
+
+	float cRed = clamp(color.r, 0.0, 65408.0);
+	float cGreen = clamp(color.g, 0.0, 65408.0);
+	float cBlue = clamp(color.b, 0.0, 65408.0);
+
+	float cMax = max(cRed, max(cGreen, cBlue));
+
+	float expp = max(-B - 1.0f, floor(log(cMax) / LN2)) + 1.0f + B;
+
+	float sMax = floor((cMax / pow(2.0f, expp - B - N)) + 0.5f);
+
+	float exps = expp + 1.0f;
+
+	if (0.0 <= sMax && sMax < pow2to9) {
+		exps = expp;
+	}
+
+	float sRed = floor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
+	float sGreen = floor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
+	float sBlue = floor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
+	return (uint(sRed) & 0x1FF) | ((uint(sGreen) & 0x1FF) << 9) | ((uint(sBlue) & 0x1FF) << 18) | ((uint(exps) & 0x1F) << 27);
+}
+
+void main() {
+	ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+	if (any(greaterThanEqual(pos, params.image_size))) { //too large, do nothing
+		return;
+	}
+
+#ifdef MODE_PROCESS
+
+	float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
+
+	ivec3 probe_cell;
+	probe_cell.x = pos.x % int(params.probe_axis_size);
+	probe_cell.y = pos.y;
+	probe_cell.z = pos.x / int(params.probe_axis_size);
+
+	vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
+	vec3 pos_to_uvw = 1.0 / params.grid_size;
+
+	vec4 probe_sh_accum[SH_SIZE] = vec4[](
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0)
+#if (SH_SIZE == 16)
+					,
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0),
+			vec4(0.0)
+#endif
+	);
+
+	// quickly ensure each probe has a different "offset" for the vogel function, based on integer world position
+	uvec3 h3 = hash3(uvec3(params.world_offset + probe_cell));
+	float offset = hashf3(vec3(h3 & uvec3(0xFFFFF)));
+
+	//for a more homogeneous hemisphere, alternate based on history frames
+	uint ray_offset = params.history_index;
+	uint ray_mult = params.history_size;
+	uint ray_total = ray_mult * params.ray_count;
+
+	for (uint i = 0; i < params.ray_count; i++) {
+		vec3 ray_dir = vogel_hemisphere(ray_offset + i * ray_mult, ray_total, offset);
+		ray_dir.y *= params.y_mult;
+		ray_dir = normalize(ray_dir);
+
+		//needs to be visible
+		vec3 ray_pos = probe_pos;
+		vec3 inv_dir = 1.0 / ray_dir;
+
+		bool hit = false;
+		vec3 hit_normal;
+		vec3 hit_light;
+		vec3 hit_aniso0;
+		vec3 hit_aniso1;
+
+		float bias = params.ray_bias;
+		vec3 abs_ray_dir = abs(ray_dir);
+		ray_pos += ray_dir * 1.0 / max(abs_ray_dir.x, max(abs_ray_dir.y, abs_ray_dir.z)) * bias / cascades.data[params.cascade].to_cell;
+
+		for (uint j = params.cascade; j < params.max_cascades; j++) {
+			//convert to local bounds
+			vec3 pos = ray_pos - cascades.data[j].offset;
+			pos *= cascades.data[j].to_cell;
+
+			if (any(lessThan(pos, vec3(0.0))) || any(greaterThanEqual(pos, params.grid_size))) {
+				continue; //already past bounds for this cascade, goto next
+			}
+
+			//find maximum advance distance (until reaching bounds)
+			vec3 t0 = -pos * inv_dir;
+			vec3 t1 = (params.grid_size - pos) * inv_dir;
+			vec3 tmax = max(t0, t1);
+			float max_advance = min(tmax.x, min(tmax.y, tmax.z));
+
+			float advance = 0.0;
+
+			vec3 uvw;
+
+			while (advance < max_advance) {
+				//read how much to advance from SDF
+				uvw = (pos + ray_dir * advance) * pos_to_uvw;
+
+				float distance = texture(sampler3D(sdf_cascades[j], linear_sampler), uvw).r * 255.0 - 1.0;
+				if (distance < 0.001) {
+					//consider hit
+					hit = true;
+					break;
+				}
+
+				advance += distance;
+			}
+
+			if (hit) {
+				const float EPSILON = 0.001;
+				hit_normal = normalize(vec3(
+						texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(EPSILON, 0.0, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(EPSILON, 0.0, 0.0)).r,
+						texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, EPSILON, 0.0)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, EPSILON, 0.0)).r,
+						texture(sampler3D(sdf_cascades[j], linear_sampler), uvw + vec3(0.0, 0.0, EPSILON)).r - texture(sampler3D(sdf_cascades[j], linear_sampler), uvw - vec3(0.0, 0.0, EPSILON)).r));
+
+				hit_light = texture(sampler3D(light_cascades[j], linear_sampler), uvw).rgb;
+				vec4 aniso0 = texture(sampler3D(aniso0_cascades[j], linear_sampler), uvw);
+				hit_aniso0 = aniso0.rgb;
+				hit_aniso1 = vec3(aniso0.a, texture(sampler3D(aniso1_cascades[j], linear_sampler), uvw).rg);
+
+				break;
+			}
+
+			//change ray origin to collision with bounds
+			pos += ray_dir * max_advance;
+			pos /= cascades.data[j].to_cell;
+			pos += cascades.data[j].offset;
+			ray_pos = pos;
+		}
+
+		vec4 light;
+		if (hit) {
+			//one liner magic
+			light.rgb = hit_light * (dot(max(vec3(0.0), (hit_normal * hit_aniso0)), vec3(1.0)) + dot(max(vec3(0.0), (-hit_normal * hit_aniso1)), vec3(1.0)));
+			light.a = 1.0;
+		} else if (params.sky_mode == SKY_MODE_SKY) {
+			light.rgb = textureLod(samplerCube(sky_irradiance, linear_sampler_mipmaps), ray_dir, 2.0).rgb; //use second mipmap because we dont usually throw a lot of rays, so this compensates
+			light.rgb *= params.sky_energy;
+			light.a = 0.0;
+
+		} else if (params.sky_mode == SKY_MODE_COLOR) {
+			light.rgb = params.sky_color;
+			light.rgb *= params.sky_energy;
+			light.a = 0.0;
+		} else {
+			light = vec4(0, 0, 0, 0);
+		}
+
+		vec3 ray_dir2 = ray_dir * ray_dir;
+		float c[SH_SIZE] = float[](
+
+				0.282095, //l0
+				0.488603 * ray_dir.y, //l1n1
+				0.488603 * ray_dir.z, //l1n0
+				0.488603 * ray_dir.x, //l1p1
+				1.092548 * ray_dir.x * ray_dir.y, //l2n2
+				1.092548 * ray_dir.y * ray_dir.z, //l2n1
+				0.315392 * (3.0 * ray_dir2.z - 1.0), //l20
+				1.092548 * ray_dir.x * ray_dir.z, //l2p1
+				0.546274 * (ray_dir2.x - ray_dir2.y) //l2p2
+#if (SH_SIZE == 16)
+				,
+				0.590043 * ray_dir.y * (3.0f * ray_dir2.x - ray_dir2.y),
+				2.890611 * ray_dir.y * ray_dir.x * ray_dir.z,
+				0.646360 * ray_dir.y * (-1.0f + 5.0f * ray_dir2.z),
+				0.373176 * (5.0f * ray_dir2.z * ray_dir.z - 3.0f * ray_dir.z),
+				0.457045 * ray_dir.x * (-1.0f + 5.0f * ray_dir2.z),
+				1.445305 * (ray_dir2.x - ray_dir2.y) * ray_dir.z,
+				0.590043 * ray_dir.x * (ray_dir2.x - 3.0f * ray_dir2.y)
+
+#endif
+		);
+
+		for (uint j = 0; j < SH_SIZE; j++) {
+			probe_sh_accum[j] += light * c[j];
+		}
+	}
+
+	for (uint i = 0; i < SH_SIZE; i++) {
+		// store in history texture
+		ivec3 prev_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(params.history_index));
+		ivec2 average_pos = prev_pos.xy;
+
+		vec4 value = probe_sh_accum[i] * 4.0 / float(params.ray_count);
+
+		ivec4 ivalue = clamp(ivec4(value * float(1 << HISTORY_BITS)), -32768, 32767); //clamp to 16 bits, so higher values don't break average
+
+		ivec4 prev_value = imageLoad(lightprobe_history_texture, prev_pos);
+		ivec4 average = imageLoad(lightprobe_average_texture, average_pos);
+
+		average -= prev_value;
+		average += ivalue;
+
+		imageStore(lightprobe_history_texture, prev_pos, ivalue);
+		imageStore(lightprobe_average_texture, average_pos, average);
+	}
+#endif // MODE PROCESS
+
+#ifdef MODE_STORE
+
+	// converting to octahedral in this step is requiered because
+	// octahedral is much faster to read from the screen than spherical harmonics,
+	// despite the very slight quality loss
+
+	ivec2 sh_pos = (pos / OCT_SIZE) * ivec2(1, SH_SIZE);
+	ivec2 oct_pos = (pos / OCT_SIZE) * (OCT_SIZE + 2) + ivec2(1);
+	ivec2 local_pos = pos % OCT_SIZE;
+
+	//fill the spherical harmonic
+	vec4 sh[SH_SIZE];
+
+	for (uint i = 0; i < SH_SIZE; i++) {
+		// store in history texture
+		ivec2 average_pos = sh_pos + ivec2(0, i);
+		ivec4 average = imageLoad(lightprobe_average_texture, average_pos);
+
+		sh[i] = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
+	}
+
+	//compute the octahedral normal for this texel
+	vec3 normal = octahedron_encode(vec2(local_pos) / float(OCT_SIZE));
+	/*
+	// read the spherical harmonic
+	const float c1 = 0.429043;
+	const float c2 = 0.511664;
+	const float c3 = 0.743125;
+	const float c4 = 0.886227;
+	const float c5 = 0.247708;
+	vec4 light = (c1 * sh[8] * (normal.x * normal.x - normal.y * normal.y) +
+					  c3 * sh[6] * normal.z * normal.z +
+					  c4 * sh[0] -
+					  c5 * sh[6] +
+					  2.0 * c1 * sh[4] * normal.x * normal.y +
+					  2.0 * c1 * sh[7] * normal.x * normal.z +
+					  2.0 * c1 * sh[5] * normal.y * normal.z +
+					  2.0 * c2 * sh[3] * normal.x +
+					  2.0 * c2 * sh[1] * normal.y +
+					  2.0 * c2 * sh[2] * normal.z);
+*/
+	vec3 normal2 = normal * normal;
+	float c[SH_SIZE] = float[](
+
+			0.282095, //l0
+			0.488603 * normal.y, //l1n1
+			0.488603 * normal.z, //l1n0
+			0.488603 * normal.x, //l1p1
+			1.092548 * normal.x * normal.y, //l2n2
+			1.092548 * normal.y * normal.z, //l2n1
+			0.315392 * (3.0 * normal2.z - 1.0), //l20
+			1.092548 * normal.x * normal.z, //l2p1
+			0.546274 * (normal2.x - normal2.y) //l2p2
+#if (SH_SIZE == 16)
+			,
+			0.590043 * normal.y * (3.0f * normal2.x - normal2.y),
+			2.890611 * normal.y * normal.x * normal.z,
+			0.646360 * normal.y * (-1.0f + 5.0f * normal2.z),
+			0.373176 * (5.0f * normal2.z * normal.z - 3.0f * normal.z),
+			0.457045 * normal.x * (-1.0f + 5.0f * normal2.z),
+			1.445305 * (normal2.x - normal2.y) * normal.z,
+			0.590043 * normal.x * (normal2.x - 3.0f * normal2.y)
+
+#endif
+	);
+
+	const float l_mult[SH_SIZE] = float[](
+			1.0,
+			2.0 / 3.0,
+			2.0 / 3.0,
+			2.0 / 3.0,
+			1.0 / 4.0,
+			1.0 / 4.0,
+			1.0 / 4.0,
+			1.0 / 4.0,
+			1.0 / 4.0
+#if (SH_SIZE == 16)
+			, // l4 does not contribute to irradiance
+			0.0,
+			0.0,
+			0.0,
+			0.0,
+			0.0,
+			0.0,
+			0.0
+#endif
+	);
+
+	vec3 irradiance = vec3(0.0);
+	vec3 radiance = vec3(0.0);
+
+	for (uint i = 0; i < SH_SIZE; i++) {
+		vec3 m = sh[i].rgb * c[i] * 4.0;
+		irradiance += m * l_mult[i];
+		radiance += m;
+	}
+
+	//encode RGBE9995 for the final texture
+
+	uint irradiance_rgbe = rgbe_encode(irradiance);
+	uint radiance_rgbe = rgbe_encode(radiance);
+
+	//store in octahedral map
+
+	ivec3 texture_pos = ivec3(oct_pos, int(params.cascade));
+	ivec3 copy_to[4] = ivec3[](ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2), ivec3(-2, -2, -2));
+	copy_to[0] = texture_pos + ivec3(local_pos, 0);
+
+	if (local_pos == ivec2(0, 0)) {
+		copy_to[1] = texture_pos + ivec3(OCT_SIZE - 1, -1, 0);
+		copy_to[2] = texture_pos + ivec3(-1, OCT_SIZE - 1, 0);
+		copy_to[3] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE, 0);
+	} else if (local_pos == ivec2(OCT_SIZE - 1, 0)) {
+		copy_to[1] = texture_pos + ivec3(0, -1, 0);
+		copy_to[2] = texture_pos + ivec3(OCT_SIZE, OCT_SIZE - 1, 0);
+		copy_to[3] = texture_pos + ivec3(-1, OCT_SIZE, 0);
+	} else if (local_pos == ivec2(0, OCT_SIZE - 1)) {
+		copy_to[1] = texture_pos + ivec3(-1, 0, 0);
+		copy_to[2] = texture_pos + ivec3(OCT_SIZE - 1, OCT_SIZE, 0);
+		copy_to[3] = texture_pos + ivec3(OCT_SIZE, -1, 0);
+	} else if (local_pos == ivec2(OCT_SIZE - 1, OCT_SIZE - 1)) {
+		copy_to[1] = texture_pos + ivec3(0, OCT_SIZE, 0);
+		copy_to[2] = texture_pos + ivec3(OCT_SIZE, 0, 0);
+		copy_to[3] = texture_pos + ivec3(-1, -1, 0);
+	} else if (local_pos.y == 0) {
+		copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y - 1, 0);
+	} else if (local_pos.x == 0) {
+		copy_to[1] = texture_pos + ivec3(local_pos.x - 1, OCT_SIZE - local_pos.y - 1, 0);
+	} else if (local_pos.y == OCT_SIZE - 1) {
+		copy_to[1] = texture_pos + ivec3(OCT_SIZE - local_pos.x - 1, local_pos.y + 1, 0);
+	} else if (local_pos.x == OCT_SIZE - 1) {
+		copy_to[1] = texture_pos + ivec3(local_pos.x + 1, OCT_SIZE - local_pos.y - 1, 0);
+	}
+
+	for (int i = 0; i < 4; i++) {
+		if (copy_to[i] == ivec3(-2, -2, -2)) {
+			continue;
+		}
+		imageStore(lightprobe_texture_data, copy_to[i], uvec4(irradiance_rgbe));
+		imageStore(lightprobe_texture_data, copy_to[i] + ivec3(0, 0, int(params.max_cascades)), uvec4(radiance_rgbe));
+	}
+
+#endif
+
+#ifdef MODE_SCROLL
+
+	ivec3 probe_cell;
+	probe_cell.x = pos.x % int(params.probe_axis_size);
+	probe_cell.y = pos.y;
+	probe_cell.z = pos.x / int(params.probe_axis_size);
+
+	ivec3 read_probe = probe_cell - params.scroll;
+
+	if (all(greaterThanEqual(read_probe, ivec3(0))) && all(lessThan(read_probe, ivec3(params.probe_axis_size)))) {
+		// can scroll
+		ivec2 tex_pos;
+		tex_pos = read_probe.xy;
+		tex_pos.x += read_probe.z * int(params.probe_axis_size);
+
+		//scroll
+		for (uint j = 0; j < params.history_size; j++) {
+			for (int i = 0; i < SH_SIZE; i++) {
+				// copy from history texture
+				ivec3 src_pos = ivec3(tex_pos.x, tex_pos.y * SH_SIZE + i, int(j));
+				ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
+				ivec4 value = imageLoad(lightprobe_history_texture, src_pos);
+				imageStore(lightprobe_history_scroll_texture, dst_pos, value);
+			}
+		}
+
+		for (int i = 0; i < SH_SIZE; i++) {
+			// copy from average texture
+			ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + i);
+			ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i);
+			ivec4 value = imageLoad(lightprobe_average_texture, src_pos);
+			imageStore(lightprobe_average_scroll_texture, dst_pos, value);
+		}
+	} else if (params.cascade < params.max_cascades - 1) {
+		//cant scroll, must look for position in parent cascade
+
+		//to global coords
+		float probe_cell_size = float(params.grid_size.x / float(params.probe_axis_size - 1)) / cascades.data[params.cascade].to_cell;
+		vec3 probe_pos = cascades.data[params.cascade].offset + vec3(probe_cell) * probe_cell_size;
+
+		//to parent local coords
+		probe_pos -= cascades.data[params.cascade + 1].offset;
+		probe_pos *= cascades.data[params.cascade + 1].to_cell;
+		probe_pos = probe_pos * float(params.probe_axis_size - 1) / float(params.grid_size.x);
+
+		ivec3 probe_posi = ivec3(probe_pos);
+		//add up all light, no need to use occlusion here, since occlusion will do its work afterwards
+
+		vec4 average_light[SH_SIZE] = vec4[](vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0)
+#if (SH_SIZE == 16)
+																															 ,
+				vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0), vec4(0)
+#endif
+		);
+		float total_weight = 0.0;
+
+		for (int i = 0; i < 8; i++) {
+			ivec3 offset = probe_posi + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1));
+
+			vec3 trilinear = vec3(1.0) - abs(probe_pos - vec3(offset));
+			float weight = trilinear.x * trilinear.y * trilinear.z;
+
+			ivec2 tex_pos;
+			tex_pos = offset.xy;
+			tex_pos.x += offset.z * int(params.probe_axis_size);
+
+			for (int j = 0; j < SH_SIZE; j++) {
+				// copy from history texture
+				ivec2 src_pos = ivec2(tex_pos.x, tex_pos.y * SH_SIZE + j);
+				ivec4 average = imageLoad(lightprobe_average_parent_texture, src_pos);
+				vec4 value = (vec4(average) / float(params.history_size)) / float(1 << HISTORY_BITS);
+				average_light[j] += value * weight;
+			}
+
+			total_weight += weight;
+		}
+
+		if (total_weight > 0.0) {
+			total_weight = 1.0 / total_weight;
+		}
+		//store the averaged values everywhere
+
+		for (int i = 0; i < SH_SIZE; i++) {
+			ivec4 ivalue = clamp(ivec4(average_light[i] * total_weight * float(1 << HISTORY_BITS)), ivec4(-32768), ivec4(32767)); //clamp to 16 bits, so higher values don't break average
+			// copy from history texture
+			ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, 0);
+			for (uint j = 0; j < params.history_size; j++) {
+				dst_pos.z = int(j);
+				imageStore(lightprobe_history_scroll_texture, dst_pos, ivalue);
+			}
+
+			ivalue *= int(params.history_size); //average needs to have all history added up
+			imageStore(lightprobe_average_scroll_texture, dst_pos.xy, ivalue);
+		}
+
+	} else {
+		// clear and let it re-raytrace, only for the last cascade, which happens very un-often
+		//scroll
+		for (uint j = 0; j < params.history_size; j++) {
+			for (int i = 0; i < SH_SIZE; i++) {
+				// copy from history texture
+				ivec3 dst_pos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
+				imageStore(lightprobe_history_scroll_texture, dst_pos, ivec4(0));
+			}
+		}
+
+		for (int i = 0; i < SH_SIZE; i++) {
+			// copy from average texture
+			ivec2 dst_pos = ivec2(pos.x, pos.y * SH_SIZE + i);
+			imageStore(lightprobe_average_scroll_texture, dst_pos, ivec4(0));
+		}
+	}
+
+#endif
+
+#ifdef MODE_SCROLL_STORE
+
+	//do not update probe texture, as these will be updated later
+
+	for (uint j = 0; j < params.history_size; j++) {
+		for (int i = 0; i < SH_SIZE; i++) {
+			// copy from history texture
+			ivec3 spos = ivec3(pos.x, pos.y * SH_SIZE + i, int(j));
+			ivec4 value = imageLoad(lightprobe_history_scroll_texture, spos);
+			imageStore(lightprobe_history_texture, spos, value);
+		}
+	}
+
+	for (int i = 0; i < SH_SIZE; i++) {
+		// copy from average texture
+		ivec2 spos = ivec2(pos.x, pos.y * SH_SIZE + i);
+		ivec4 average = imageLoad(lightprobe_average_scroll_texture, spos);
+		imageStore(lightprobe_average_texture, spos, average);
+	}
+
+#endif
+}

+ 1155 - 0
servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl

@@ -0,0 +1,1155 @@
+#[compute]
+
+#version 450
+
+VERSION_DEFINES
+
+#ifdef MODE_JUMPFLOOD_OPTIMIZED
+#define GROUP_SIZE 8
+
+layout(local_size_x = GROUP_SIZE, local_size_y = GROUP_SIZE, local_size_z = GROUP_SIZE) in;
+
+#elif defined(MODE_OCCLUSION) || defined(MODE_SCROLL)
+//buffer layout
+layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
+
+#else
+//grid layout
+layout(local_size_x = 4, local_size_y = 4, local_size_z = 4) in;
+
+#endif
+
+#if defined(MODE_INITIALIZE_JUMP_FLOOD) || defined(MODE_INITIALIZE_JUMP_FLOOD_HALF)
+layout(r16ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_color;
+layout(rgba8ui, set = 0, binding = 2) uniform restrict writeonly uimage3D dst_positions;
+#endif
+
+#ifdef MODE_UPSCALE_JUMP_FLOOD
+layout(r16ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_color;
+layout(rgba8ui, set = 0, binding = 2) uniform restrict readonly uimage3D src_positions_half;
+layout(rgba8ui, set = 0, binding = 3) uniform restrict writeonly uimage3D dst_positions;
+#endif
+
+#if defined(MODE_JUMPFLOOD) || defined(MODE_JUMPFLOOD_OPTIMIZED)
+layout(rgba8ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_positions;
+layout(rgba8ui, set = 0, binding = 2) uniform restrict writeonly uimage3D dst_positions;
+#endif
+
+#ifdef MODE_JUMPFLOOD_OPTIMIZED
+
+shared uvec4 group_positions[(GROUP_SIZE + 2) * (GROUP_SIZE + 2) * (GROUP_SIZE + 2)]; //4x4x4 with margins
+
+void group_store(ivec3 p_pos, uvec4 p_value) {
+	uint offset = uint(p_pos.z * (GROUP_SIZE + 2) * (GROUP_SIZE + 2) + p_pos.y * (GROUP_SIZE + 2) + p_pos.x);
+	group_positions[offset] = p_value;
+}
+
+uvec4 group_load(ivec3 p_pos) {
+	uint offset = uint(p_pos.z * (GROUP_SIZE + 2) * (GROUP_SIZE + 2) + p_pos.y * (GROUP_SIZE + 2) + p_pos.x);
+	return group_positions[offset];
+}
+
+#endif
+
+#ifdef MODE_OCCLUSION
+
+layout(r16ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_color;
+layout(r8, set = 0, binding = 2) uniform restrict image3D dst_occlusion[8];
+layout(r32ui, set = 0, binding = 3) uniform restrict readonly uimage3D src_facing;
+
+const uvec2 group_size_offset[11] = uvec2[](uvec2(1, 0), uvec2(3, 1), uvec2(6, 4), uvec2(10, 10), uvec2(15, 20), uvec2(21, 35), uvec2(28, 56), uvec2(36, 84), uvec2(42, 120), uvec2(46, 162), uvec2(48, 208));
+const uint group_pos[256] = uint[](0,
+		65536, 256, 1,
+		131072, 65792, 512, 65537, 257, 2,
+		196608, 131328, 66048, 768, 131073, 65793, 513, 65538, 258, 3,
+		262144, 196864, 131584, 66304, 1024, 196609, 131329, 66049, 769, 131074, 65794, 514, 65539, 259, 4,
+		327680, 262400, 197120, 131840, 66560, 1280, 262145, 196865, 131585, 66305, 1025, 196610, 131330, 66050, 770, 131075, 65795, 515, 65540, 260, 5,
+		393216, 327936, 262656, 197376, 132096, 66816, 1536, 327681, 262401, 197121, 131841, 66561, 1281, 262146, 196866, 131586, 66306, 1026, 196611, 131331, 66051, 771, 131076, 65796, 516, 65541, 261, 6,
+		458752, 393472, 328192, 262912, 197632, 132352, 67072, 1792, 393217, 327937, 262657, 197377, 132097, 66817, 1537, 327682, 262402, 197122, 131842, 66562, 1282, 262147, 196867, 131587, 66307, 1027, 196612, 131332, 66052, 772, 131077, 65797, 517, 65542, 262, 7,
+		459008, 393728, 328448, 263168, 197888, 132608, 67328, 458753, 393473, 328193, 262913, 197633, 132353, 67073, 1793, 393218, 327938, 262658, 197378, 132098, 66818, 1538, 327683, 262403, 197123, 131843, 66563, 1283, 262148, 196868, 131588, 66308, 1028, 196613, 131333, 66053, 773, 131078, 65798, 518, 65543, 263,
+		459264, 393984, 328704, 263424, 198144, 132864, 459009, 393729, 328449, 263169, 197889, 132609, 67329, 458754, 393474, 328194, 262914, 197634, 132354, 67074, 1794, 393219, 327939, 262659, 197379, 132099, 66819, 1539, 327684, 262404, 197124, 131844, 66564, 1284, 262149, 196869, 131589, 66309, 1029, 196614, 131334, 66054, 774, 131079, 65799, 519,
+		459520, 394240, 328960, 263680, 198400, 459265, 393985, 328705, 263425, 198145, 132865, 459010, 393730, 328450, 263170, 197890, 132610, 67330, 458755, 393475, 328195, 262915, 197635, 132355, 67075, 1795, 393220, 327940, 262660, 197380, 132100, 66820, 1540, 327685, 262405, 197125, 131845, 66565, 1285, 262150, 196870, 131590, 66310, 1030, 196615, 131335, 66055, 775);
+
+shared uint occlusion_facing[((OCCLUSION_SIZE * 2) * (OCCLUSION_SIZE * 2) * (OCCLUSION_SIZE * 2)) / 4];
+
+uint get_facing(ivec3 p_pos) {
+	uint ofs = uint(p_pos.z * OCCLUSION_SIZE * 2 * OCCLUSION_SIZE * 2 + p_pos.y * OCCLUSION_SIZE * 2 + p_pos.x);
+	uint v = occlusion_facing[ofs / 4];
+	return (v >> ((ofs % 4) * 8)) & 0xFF;
+}
+
+#endif
+
+#ifdef MODE_STORE
+
+layout(rgba8ui, set = 0, binding = 1) uniform restrict readonly uimage3D src_positions;
+layout(r16ui, set = 0, binding = 2) uniform restrict readonly uimage3D src_albedo;
+layout(r8, set = 0, binding = 3) uniform restrict readonly image3D src_occlusion[8];
+layout(r32ui, set = 0, binding = 4) uniform restrict readonly uimage3D src_light;
+layout(r32ui, set = 0, binding = 5) uniform restrict readonly uimage3D src_light_aniso;
+layout(r32ui, set = 0, binding = 6) uniform restrict readonly uimage3D src_facing;
+
+layout(r8, set = 0, binding = 7) uniform restrict writeonly image3D dst_sdf;
+layout(r16ui, set = 0, binding = 8) uniform restrict writeonly uimage3D dst_occlusion;
+
+layout(set = 0, binding = 10, std430) restrict buffer DispatchData {
+	uint x;
+	uint y;
+	uint z;
+	uint total_count;
+}
+dispatch_data;
+
+struct ProcessVoxel {
+	uint position; //xyz 7 bit packed, extra 11 bits for neigbours
+	uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neibhbours
+	uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbous
+	uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbours
+	//total neighbours: 26
+};
+
+layout(set = 0, binding = 11, std430) restrict buffer writeonly ProcessVoxels {
+	ProcessVoxel data[];
+}
+dst_process_voxels;
+
+shared ProcessVoxel store_positions[4 * 4 * 4];
+shared uint store_position_count;
+shared uint store_from_index;
+#endif
+
+#ifdef MODE_SCROLL
+
+layout(r16ui, set = 0, binding = 1) uniform restrict writeonly uimage3D dst_albedo;
+layout(r32ui, set = 0, binding = 2) uniform restrict writeonly uimage3D dst_facing;
+layout(r32ui, set = 0, binding = 3) uniform restrict writeonly uimage3D dst_light;
+layout(r32ui, set = 0, binding = 4) uniform restrict writeonly uimage3D dst_light_aniso;
+
+layout(set = 0, binding = 5, std430) restrict buffer readonly DispatchData {
+	uint x;
+	uint y;
+	uint z;
+	uint total_count;
+}
+dispatch_data;
+
+struct ProcessVoxel {
+	uint position; //xyz 7 bit packed, extra 11 bits for neigbours
+	uint albedo; //rgb bits 0-15 albedo, bits 16-21 are normal bits (set if geometry exists toward that side), extra 11 bits for neibhbours
+	uint light; //rgbe8985 encoded total saved light, extra 2 bits for neighbous
+	uint light_aniso; //55555 light anisotropy, extra 2 bits for neighbours
+	//total neighbours: 26
+};
+
+layout(set = 0, binding = 6, std430) restrict buffer readonly ProcessVoxels {
+	ProcessVoxel data[];
+}
+src_process_voxels;
+
+#endif
+
+#ifdef MODE_SCROLL_OCCLUSION
+
+layout(r8, set = 0, binding = 1) uniform restrict image3D dst_occlusion[8];
+layout(r16ui, set = 0, binding = 2) uniform restrict readonly uimage3D src_occlusion;
+
+#endif
+
+layout(push_constant, binding = 0, std430) uniform Params {
+	ivec3 scroll;
+
+	int grid_size;
+
+	ivec3 probe_offset;
+	int step_size;
+
+	bool half_size;
+	uint occlusion_index;
+	int cascade;
+	uint pad;
+}
+params;
+
+void main() {
+#ifdef MODE_SCROLL
+
+	// Pixel being shaded
+	int index = int(gl_GlobalInvocationID.x);
+	if (index >= dispatch_data.total_count) { //too big
+		return;
+	}
+
+	ivec3 read_pos = (ivec3(src_process_voxels.data[index].position) >> ivec3(0, 7, 14)) & ivec3(0x7F);
+	ivec3 write_pos = read_pos + params.scroll;
+
+	if (any(lessThan(write_pos, ivec3(0))) || any(greaterThanEqual(write_pos, ivec3(params.grid_size)))) {
+		return; //fits outside the 3D texture, dont do anything
+	}
+
+	uint albedo = ((src_process_voxels.data[index].albedo & 0x7FFF) << 1) | 1; //add solid bit
+	imageStore(dst_albedo, write_pos, uvec4(albedo));
+
+	uint facing = (src_process_voxels.data[index].albedo >> 15) & 0x3F; //6 anisotropic facing bits
+	imageStore(dst_facing, write_pos, uvec4(facing));
+
+	uint light = src_process_voxels.data[index].light & 0x3fffffff; //30 bits of RGBE8985
+	imageStore(dst_light, write_pos, uvec4(light));
+
+	uint light_aniso = src_process_voxels.data[index].light_aniso & 0x3fffffff; //30 bits of 6 anisotropic 5 bits values
+	imageStore(dst_light_aniso, write_pos, uvec4(light_aniso));
+
+#endif
+
+#ifdef MODE_SCROLL_OCCLUSION
+
+	ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
+	if (any(greaterThanEqual(pos, ivec3(params.grid_size) - abs(params.scroll)))) { //too large, do nothing
+		return;
+	}
+
+	ivec3 read_pos = pos + max(ivec3(0), -params.scroll);
+	ivec3 write_pos = pos + max(ivec3(0), params.scroll);
+
+	read_pos.z += params.cascade * params.grid_size;
+	uint occlusion = imageLoad(src_occlusion, read_pos).r;
+	read_pos.x += params.grid_size;
+	occlusion |= imageLoad(src_occlusion, read_pos).r << 16;
+
+	const uint occlusion_shift[8] = uint[](12, 8, 4, 0, 28, 24, 20, 16);
+
+	for (uint i = 0; i < 8; i++) {
+		float o = float((occlusion >> occlusion_shift[i]) & 0xF) / 15.0;
+		imageStore(dst_occlusion[i], write_pos, vec4(o));
+	}
+
+#endif
+
+#ifdef MODE_INITIALIZE_JUMP_FLOOD
+
+	ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
+
+	uint c = imageLoad(src_color, pos).r;
+	uvec4 v;
+	if (bool(c & 0x1)) {
+		//bit set means this is solid
+		v.xyz = uvec3(pos);
+		v.w = 255; //not zero means used
+	} else {
+		v.xyz = uvec3(0);
+		v.w = 0; // zero means unused
+	}
+
+	imageStore(dst_positions, pos, v);
+#endif
+
+#ifdef MODE_INITIALIZE_JUMP_FLOOD_HALF
+
+	ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
+	ivec3 base_pos = pos * 2;
+
+	//since we store in half size, lets kind of randomize what we store, so
+	//the half size jump flood has a bit better chance to find something
+	uvec4 closest[8];
+	int closest_count = 0;
+
+	for (uint i = 0; i < 8; i++) {
+		ivec3 src_pos = base_pos + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1));
+		uint c = imageLoad(src_color, src_pos).r;
+		if (bool(c & 1)) {
+			uvec4 v = uvec4(uvec3(src_pos), 255);
+			closest[closest_count] = v;
+			closest_count++;
+		}
+	}
+
+	if (closest_count == 0) {
+		imageStore(dst_positions, pos, uvec4(0));
+	} else {
+		ivec3 indexv = (pos & ivec3(1, 1, 1)) * ivec3(1, 2, 4);
+		int index = (indexv.x | indexv.y | indexv.z) % closest_count;
+		imageStore(dst_positions, pos, closest[index]);
+	}
+
+#endif
+
+#ifdef MODE_JUMPFLOOD
+
+	//regular jumpflood, efficent for large steps, inefficient for small steps
+	ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
+
+	vec3 posf = vec3(pos);
+
+	if (params.half_size) {
+		posf = posf * 2.0 + 0.5;
+	}
+
+	uvec4 p = imageLoad(src_positions, pos);
+
+	if (!params.half_size && p == uvec4(uvec3(pos), 255)) {
+		imageStore(dst_positions, pos, p);
+		return; //points to itself and valid, nothing better can be done, just pass
+	}
+
+	float p_dist;
+
+	if (p.w != 0) {
+		p_dist = distance(posf, vec3(p.xyz));
+	} else {
+		p_dist = 0.0; //should not matter
+	}
+
+	const uint offset_count = 26;
+	const ivec3 offsets[offset_count] = ivec3[](
+			ivec3(-1, -1, -1),
+			ivec3(-1, -1, 0),
+			ivec3(-1, -1, 1),
+			ivec3(-1, 0, -1),
+			ivec3(-1, 0, 0),
+			ivec3(-1, 0, 1),
+			ivec3(-1, 1, -1),
+			ivec3(-1, 1, 0),
+			ivec3(-1, 1, 1),
+			ivec3(0, -1, -1),
+			ivec3(0, -1, 0),
+			ivec3(0, -1, 1),
+			ivec3(0, 0, -1),
+			ivec3(0, 0, 1),
+			ivec3(0, 1, -1),
+			ivec3(0, 1, 0),
+			ivec3(0, 1, 1),
+			ivec3(1, -1, -1),
+			ivec3(1, -1, 0),
+			ivec3(1, -1, 1),
+			ivec3(1, 0, -1),
+			ivec3(1, 0, 0),
+			ivec3(1, 0, 1),
+			ivec3(1, 1, -1),
+			ivec3(1, 1, 0),
+			ivec3(1, 1, 1));
+
+	for (uint i = 0; i < offset_count; i++) {
+		ivec3 ofs = pos + offsets[i] * params.step_size;
+		if (any(lessThan(ofs, ivec3(0))) || any(greaterThanEqual(ofs, ivec3(params.grid_size)))) {
+			continue;
+		}
+		uvec4 q = imageLoad(src_positions, ofs);
+
+		if (q.w == 0) {
+			continue; //was not initialized yet, ignore
+		}
+
+		float q_dist = distance(posf, vec3(p.xyz));
+		if (p.w == 0 || q_dist < p_dist) {
+			p = q; //just replace because current is unused
+			p_dist = q_dist;
+		}
+	}
+
+	imageStore(dst_positions, pos, p);
+#endif
+
+#ifdef MODE_JUMPFLOOD_OPTIMIZED
+	//optimized version using shared compute memory
+
+	ivec3 group_offset = ivec3(gl_WorkGroupID.xyz) % params.step_size;
+	ivec3 group_pos = group_offset + (ivec3(gl_WorkGroupID.xyz) / params.step_size) * ivec3(GROUP_SIZE * params.step_size);
+
+	//load data into local group memory
+
+	if (all(lessThan(ivec3(gl_LocalInvocationID.xyz), ivec3((GROUP_SIZE + 2) / 2)))) {
+		//use this thread for loading, this method uses less threads for this but its simpler and less divergent
+		ivec3 base_pos = ivec3(gl_LocalInvocationID.xyz) * 2;
+		for (uint i = 0; i < 8; i++) {
+			ivec3 load_pos = base_pos + ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1));
+			ivec3 load_global_pos = group_pos + (load_pos - ivec3(1)) * params.step_size;
+			uvec4 q;
+			if (all(greaterThanEqual(load_global_pos, ivec3(0))) && all(lessThan(load_global_pos, ivec3(params.grid_size)))) {
+				q = imageLoad(src_positions, load_global_pos);
+			} else {
+				q = uvec4(0); //unused
+			}
+
+			group_store(load_pos, q);
+		}
+	}
+
+	ivec3 global_pos = group_pos + ivec3(gl_LocalInvocationID.xyz) * params.step_size;
+
+	if (any(lessThan(global_pos, ivec3(0))) || any(greaterThanEqual(global_pos, ivec3(params.grid_size)))) {
+		return; //do nothing else, end here because outside range
+	}
+
+	//sync
+	groupMemoryBarrier();
+	barrier();
+
+	ivec3 local_pos = ivec3(gl_LocalInvocationID.xyz) + ivec3(1);
+
+	const uint offset_count = 27;
+	const ivec3 offsets[offset_count] = ivec3[](
+			ivec3(-1, -1, -1),
+			ivec3(-1, -1, 0),
+			ivec3(-1, -1, 1),
+			ivec3(-1, 0, -1),
+			ivec3(-1, 0, 0),
+			ivec3(-1, 0, 1),
+			ivec3(-1, 1, -1),
+			ivec3(-1, 1, 0),
+			ivec3(-1, 1, 1),
+			ivec3(0, -1, -1),
+			ivec3(0, -1, 0),
+			ivec3(0, -1, 1),
+			ivec3(0, 0, -1),
+			ivec3(0, 0, 0),
+			ivec3(0, 0, 1),
+			ivec3(0, 1, -1),
+			ivec3(0, 1, 0),
+			ivec3(0, 1, 1),
+			ivec3(1, -1, -1),
+			ivec3(1, -1, 0),
+			ivec3(1, -1, 1),
+			ivec3(1, 0, -1),
+			ivec3(1, 0, 0),
+			ivec3(1, 0, 1),
+			ivec3(1, 1, -1),
+			ivec3(1, 1, 0),
+			ivec3(1, 1, 1));
+
+	//only makes sense if point is inside screen
+	uvec4 closest = uvec4(0);
+	float closest_dist = 0.0;
+
+	vec3 posf = vec3(global_pos);
+
+	if (params.half_size) {
+		posf = posf * 2.0 + 0.5;
+	}
+
+	for (uint i = 0; i < offset_count; i++) {
+		uvec4 point = group_load(local_pos + offsets[i]);
+
+		if (point.w == 0) {
+			continue; //was not initialized yet, ignore
+		}
+
+		float dist = distance(posf, vec3(point.xyz));
+		if (closest.w == 0 || dist < closest_dist) {
+			closest = point;
+			closest_dist = dist;
+		}
+	}
+
+	imageStore(dst_positions, global_pos, closest);
+
+#endif
+
+#ifdef MODE_UPSCALE_JUMP_FLOOD
+
+	ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
+
+	uint c = imageLoad(src_color, pos).r;
+	uvec4 v;
+	if (bool(c & 1)) {
+		//bit set means this is solid
+		v.xyz = uvec3(pos);
+		v.w = 255; //not zero means used
+	} else {
+		v = imageLoad(src_positions_half, pos >> 1);
+		float d = length(vec3(ivec3(v.xyz) - pos));
+
+		ivec3 vbase = ivec3(v.xyz - (v.xyz & uvec3(1)));
+
+		//search around if there is a better candidate from the same block
+		for (int i = 0; i < 8; i++) {
+			ivec3 bits = ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1));
+			ivec3 p = vbase + bits;
+
+			float d2 = length(vec3(p - pos));
+			if (d2 < d) { //check valid distance before test so we avoid a read
+				uint c2 = imageLoad(src_color, p).r;
+				if (bool(c2 & 1)) {
+					v.xyz = uvec3(p);
+					d = d2;
+				}
+			}
+		}
+
+		//could validate better position..
+	}
+
+	imageStore(dst_positions, pos, v);
+
+#endif
+
+#ifdef MODE_OCCLUSION
+
+	uint invocation_idx = uint(gl_LocalInvocationID.x);
+	ivec3 region = ivec3(gl_WorkGroupID);
+
+	ivec3 region_offset = -ivec3(OCCLUSION_SIZE);
+	region_offset += region * OCCLUSION_SIZE * 2;
+	region_offset += params.probe_offset * OCCLUSION_SIZE;
+
+	if (params.scroll != ivec3(0)) {
+		//validate scroll region
+		ivec3 region_offset_to = region_offset + ivec3(OCCLUSION_SIZE * 2);
+		uvec3 scroll_mask = uvec3(notEqual(params.scroll, ivec3(0))); //save which axes acre scrolling
+		ivec3 scroll_from = mix(ivec3(0), ivec3(params.grid_size) + params.scroll, lessThan(params.scroll, ivec3(0)));
+		ivec3 scroll_to = mix(ivec3(params.grid_size), params.scroll, greaterThan(params.scroll, ivec3(0)));
+
+		if ((uvec3(lessThanEqual(region_offset_to, scroll_from)) | uvec3(greaterThanEqual(region_offset, scroll_to))) * scroll_mask == scroll_mask) { //all axes that scroll are out, exit
+			return; //region outside scroll bounds, quit
+		}
+	}
+
+#define OCC_HALF_SIZE (OCCLUSION_SIZE / 2)
+
+	ivec3 local_ofs = ivec3(uvec3(invocation_idx % OCC_HALF_SIZE, (invocation_idx % (OCC_HALF_SIZE * OCC_HALF_SIZE)) / OCC_HALF_SIZE, invocation_idx / (OCC_HALF_SIZE * OCC_HALF_SIZE))) * 4;
+
+	/*	for(int i=0;i<64;i++) {
+		ivec3 offset = region_offset + local_ofs + ((ivec3(i) >> ivec3(0,2,4)) & ivec3(3,3,3));
+		uint facig =
+		if (all(greaterThanEqual(offset,ivec3(0))) && all(lessThan(offset,ivec3(params.grid_size)))) {*/
+
+	for (int i = 0; i < 16; i++) { //skip x, so it can be packed
+
+		ivec3 offset = local_ofs + ((ivec3(i * 4) >> ivec3(0, 2, 4)) & ivec3(3, 3, 3));
+
+		uint facing_pack = 0;
+		for (int j = 0; j < 4; j++) {
+			ivec3 foffset = region_offset + offset + ivec3(j, 0, 0);
+			if (all(greaterThanEqual(foffset, ivec3(0))) && all(lessThan(foffset, ivec3(params.grid_size)))) {
+				uint f = imageLoad(src_facing, foffset).r;
+				facing_pack |= f << (j * 8);
+			}
+		}
+
+		occlusion_facing[(offset.z * (OCCLUSION_SIZE * 2 * OCCLUSION_SIZE * 2) + offset.y * (OCCLUSION_SIZE * 2) + offset.x) / 4] = facing_pack;
+	}
+
+	//sync occlusion saved
+	groupMemoryBarrier();
+	barrier();
+
+	//process occlusion
+
+#define OCC_STEPS (OCCLUSION_SIZE * 3 - 2)
+#define OCC_HALF_STEPS (OCC_STEPS / 2)
+
+	for (int step = 0; step < OCC_STEPS; step++) {
+		bool shrink = step >= OCC_HALF_STEPS;
+		int occ_step = shrink ? OCC_HALF_STEPS - (step - OCC_HALF_STEPS) - 1 : step;
+
+		if (invocation_idx < group_size_offset[occ_step].x) {
+			uint pv = group_pos[group_size_offset[occ_step].y + invocation_idx];
+			ivec3 proc_abs = (ivec3(int(pv)) >> ivec3(0, 8, 16)) & ivec3(0xFF);
+
+			if (shrink) {
+				proc_abs = ivec3(OCCLUSION_SIZE) - proc_abs - ivec3(1);
+			}
+
+			for (int i = 0; i < 8; i++) {
+				ivec3 bits = ((ivec3(i) >> ivec3(0, 1, 2)) & ivec3(1, 1, 1));
+				ivec3 proc_sign = bits * 2 - 1;
+				ivec3 local_offset = ivec3(OCCLUSION_SIZE) + proc_abs * proc_sign - (ivec3(1) - bits);
+				ivec3 offset = local_offset + region_offset;
+				if (all(greaterThanEqual(offset, ivec3(0))) && all(lessThan(offset, ivec3(params.grid_size)))) {
+					float occ;
+
+					uint facing = get_facing(local_offset);
+
+					if (facing != 0) { //solid
+						occ = 0.0;
+					} else if (step == 0) {
+#if 0
+						occ = 0.0;
+						if (get_facing(local_offset - ivec3(proc_sign.x,0,0))==0) {
+							occ+=1.0;
+						}
+						if (get_facing(local_offset - ivec3(0,proc_sign.y,0))==0) {
+							occ+=1.0;
+						}
+						if (get_facing(local_offset - ivec3(0,0,proc_sign.z))==0) {
+							occ+=1.0;
+						}
+						/*
+						if (get_facing(local_offset - proc_sign)==0) {
+							occ+=1.0;
+						}*/
+
+						occ/=3.0;
+#endif
+						occ = 1.0;
+
+					} else {
+						ivec3 read_dir = -proc_sign;
+
+						ivec3 major_axis;
+						if (proc_abs.x < proc_abs.y) {
+							if (proc_abs.z < proc_abs.y) {
+								major_axis = ivec3(0, 1, 0);
+							} else {
+								major_axis = ivec3(0, 0, 1);
+							}
+						} else {
+							if (proc_abs.z < proc_abs.x) {
+								major_axis = ivec3(1, 0, 0);
+							} else {
+								major_axis = ivec3(0, 0, 1);
+							}
+						}
+
+						float avg = 0.0;
+						occ = 0.0;
+
+						ivec3 read_x = offset + ivec3(read_dir.x, 0, 0) + (proc_abs.x == 0 ? major_axis * read_dir : ivec3(0));
+						ivec3 read_y = offset + ivec3(0, read_dir.y, 0) + (proc_abs.y == 0 ? major_axis * read_dir : ivec3(0));
+						ivec3 read_z = offset + ivec3(0, 0, read_dir.z) + (proc_abs.z == 0 ? major_axis * read_dir : ivec3(0));
+
+						uint facing_x = get_facing(read_x - region_offset);
+						if (facing_x == 0) {
+							if (all(greaterThanEqual(read_x, ivec3(0))) && all(lessThan(read_x, ivec3(params.grid_size)))) {
+								occ += imageLoad(dst_occlusion[params.occlusion_index], read_x).r;
+								avg += 1.0;
+							}
+						} else {
+							if (proc_abs.x != 0) { //do not occlude from voxels in the opposite octant
+								avg += 1.0;
+							}
+						}
+
+						uint facing_y = get_facing(read_y - region_offset);
+						if (facing_y == 0) {
+							if (all(greaterThanEqual(read_y, ivec3(0))) && all(lessThan(read_y, ivec3(params.grid_size)))) {
+								occ += imageLoad(dst_occlusion[params.occlusion_index], read_y).r;
+								avg += 1.0;
+							}
+						} else {
+							if (proc_abs.y != 0) {
+								avg += 1.0;
+							}
+						}
+
+						uint facing_z = get_facing(read_z - region_offset);
+						if (facing_z == 0) {
+							if (all(greaterThanEqual(read_z, ivec3(0))) && all(lessThan(read_z, ivec3(params.grid_size)))) {
+								occ += imageLoad(dst_occlusion[params.occlusion_index], read_z).r;
+								avg += 1.0;
+							}
+						} else {
+							if (proc_abs.z != 0) {
+								avg += 1.0;
+							}
+						}
+
+						if (avg > 0.0) {
+							occ /= avg;
+						}
+					}
+
+					imageStore(dst_occlusion[params.occlusion_index], offset, vec4(occ));
+				}
+			}
+		}
+
+		groupMemoryBarrier();
+		barrier();
+	}
+	/*
+		for(int s=0;s<8;s++) {
+			ivec3 local_pos = pos * 2 + ((ivec3(s) >> ivec3(2,1,0)) & ivec3(1,1,1));
+			for(int i=0;i<8;i++) {
+				ivec3 offset = local_pos + ((ivec3(i) >> ivec3(2,1,0)) & ivec3(1,1,1)) * OCCLUSION_SIZE;
+
+				ivec3 global_offset = region_offset + offset;
+
+				if (any(lessThan(global_offset,ivec3(0))) || any(greaterThanEqual(global_offset,ivec3(params.grid_size)))) {
+					continue; // do not process
+				}
+
+				ivec3 proc_pos = offset - ivec3(OCCLUSION_SIZE);
+				proc_pos += mix(ivec3(0),ivec3(1),greaterThanEqual(proc_pos,ivec3(0)));
+
+				ivec3 proc_abs = abs(proc_pos);
+				ivec3 proc_pass = proc_abs - ivec3(1);
+
+				if (proc_pass.x+proc_pass.y+proc_pass.z != step) {
+					continue;
+				}
+
+				offset = global_offset;
+
+				float occ;
+
+				uint facing = imageLoad(src_facing,offset).r; //any bit present in facing means this is solid
+
+				if (facing != 0) { //solid
+					occ = 0.0;
+				} else if (step==0) {
+					occ = 1.0; //first step
+				} else {
+					ivec3 read_dir = -sign(proc_pos);
+
+					ivec3 major_axis;
+					if (proc_pass.x < proc_pass.y) {
+						if (proc_pass.z < proc_pass.y) {
+							major_axis = ivec3(0,1,0);
+						} else {
+							major_axis = ivec3(0,0,1);
+						}
+					} else {
+						if (proc_pass.z < proc_pass.x) {
+							major_axis = ivec3(1,0,0);
+						} else {
+							major_axis = ivec3(0,0,1);
+						}
+					}
+
+					float avg = 0.0;
+					occ = 0.0;
+
+					ivec3 read_x = offset + ivec3(read_dir.x,0,0) + (proc_pass.x == 0 ? major_axis * read_dir : ivec3(0));
+					ivec3 read_y = offset + ivec3(0,read_dir.y,0) + (proc_pass.y == 0 ? major_axis * read_dir : ivec3(0));
+					ivec3 read_z = offset + ivec3(0,0,read_dir.z) + (proc_pass.z == 0 ? major_axis * read_dir : ivec3(0));
+
+					if (all(greaterThanEqual(read_x,ivec3(0))) && all(lessThan(read_x,ivec3(params.grid_size)))) {
+
+						uint f = imageLoad(src_facing,read_x).r;
+						if (f==0) { //non solid
+							occ += imageLoad(dst_occlusion[params.occlusion_index],read_x).r;
+							avg+=1.0;
+						}
+					}
+
+					if (all(greaterThanEqual(read_y,ivec3(0))) && all(lessThan(read_y,ivec3(params.grid_size)))) {
+
+						uint f = imageLoad(src_facing,read_y).r;
+						if (f==0) {//non solid
+							occ += imageLoad(dst_occlusion[params.occlusion_index],read_y).r;
+							avg+=1.0;
+						}
+					}
+
+					if (all(greaterThanEqual(read_z,ivec3(0))) && all(lessThan(read_z,ivec3(params.grid_size)))) {
+
+						uint f = imageLoad(src_facing,read_z).r;
+						if (f==0) {//non solid
+							occ += imageLoad(dst_occlusion[params.occlusion_index],read_z).r;
+							avg+=1.0;
+						}
+					}
+
+					if (avg > 0.0) {
+						occ/=avg;
+					}
+				}
+
+				imageStore(dst_occlusion[params.occlusion_index],offset,vec4(occ));
+
+			}
+		}
+
+		groupMemoryBarrier();
+		barrier();
+
+	}
+*/
+#if 1
+	//bias solid voxels away
+
+	for (int i = 0; i < 64; i++) {
+		ivec3 local_offset = local_ofs + ((ivec3(i) >> ivec3(0, 2, 4)) & ivec3(3, 3, 3));
+		ivec3 offset = region_offset + local_offset;
+
+		if (all(greaterThanEqual(offset, ivec3(0))) && all(lessThan(offset, ivec3(params.grid_size)))) {
+			uint facing = get_facing(local_offset);
+
+			if (facing != 0) {
+				//only work on solids
+
+				ivec3 proc_pos = local_offset - ivec3(OCCLUSION_SIZE);
+				proc_pos += mix(ivec3(0), ivec3(1), greaterThanEqual(proc_pos, ivec3(0)));
+
+				float avg = 0.0;
+				float occ = 0.0;
+
+				ivec3 read_dir = -sign(proc_pos);
+				ivec3 read_dir_x = ivec3(read_dir.x, 0, 0);
+				ivec3 read_dir_y = ivec3(0, read_dir.y, 0);
+				ivec3 read_dir_z = ivec3(0, 0, read_dir.z);
+				//solid
+#if 0
+
+				uvec3 facing_pos_base = (uvec3(facing) >> uvec3(0,1,2)) & uvec3(1,1,1);
+				uvec3 facing_neg_base = (uvec3(facing) >> uvec3(3,4,5)) & uvec3(1,1,1);
+				uvec3 facing_pos=  facing_pos_base &((~facing_neg_base)&uvec3(1,1,1));
+				uvec3 facing_neg=  facing_neg_base &((~facing_pos_base)&uvec3(1,1,1));
+#else
+				uvec3 facing_pos = (uvec3(facing) >> uvec3(0, 1, 2)) & uvec3(1, 1, 1);
+				uvec3 facing_neg = (uvec3(facing) >> uvec3(3, 4, 5)) & uvec3(1, 1, 1);
+#endif
+				bvec3 read_valid = bvec3(mix(facing_neg, facing_pos, greaterThan(read_dir, ivec3(0))));
+
+				//sides
+				if (read_valid.x) {
+					ivec3 read_offset = local_offset + read_dir_x;
+					uint f = get_facing(read_offset);
+					if (f == 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r;
+							avg += 1.0;
+						}
+					}
+				}
+
+				if (read_valid.y) {
+					ivec3 read_offset = local_offset + read_dir_y;
+					uint f = get_facing(read_offset);
+					if (f == 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r;
+							avg += 1.0;
+						}
+					}
+				}
+
+				if (read_valid.z) {
+					ivec3 read_offset = local_offset + read_dir_z;
+					uint f = get_facing(read_offset);
+					if (f == 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r;
+							avg += 1.0;
+						}
+					}
+				}
+
+				//adjacents
+
+				if (all(read_valid.yz)) {
+					ivec3 read_offset = local_offset + read_dir_y + read_dir_z;
+					uint f = get_facing(read_offset);
+					if (f == 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r;
+							avg += 1.0;
+						}
+					}
+				}
+
+				if (all(read_valid.xz)) {
+					ivec3 read_offset = local_offset + read_dir_x + read_dir_z;
+					uint f = get_facing(read_offset);
+					if (f == 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r;
+							avg += 1.0;
+						}
+					}
+				}
+
+				if (all(read_valid.xy)) {
+					ivec3 read_offset = local_offset + read_dir_x + read_dir_y;
+					uint f = get_facing(read_offset);
+					if (f == 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r;
+							avg += 1.0;
+						}
+					}
+				}
+
+				//diagonal
+
+				if (all(read_valid)) {
+					ivec3 read_offset = local_offset + read_dir;
+					uint f = get_facing(read_offset);
+					if (f == 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occ += imageLoad(dst_occlusion[params.occlusion_index], read_offset).r;
+							avg += 1.0;
+						}
+					}
+				}
+
+				if (avg > 0.0) {
+					occ /= avg;
+				}
+
+				imageStore(dst_occlusion[params.occlusion_index], offset, vec4(occ));
+			}
+		}
+	}
+
+#endif
+
+#if 1
+	groupMemoryBarrier();
+	barrier();
+
+	for (int i = 0; i < 64; i++) {
+		ivec3 local_offset = local_ofs + ((ivec3(i) >> ivec3(0, 2, 4)) & ivec3(3, 3, 3));
+		ivec3 offset = region_offset + local_offset;
+
+		if (all(greaterThanEqual(offset, ivec3(0))) && all(lessThan(offset, ivec3(params.grid_size)))) {
+			uint facing = get_facing(local_offset);
+
+			if (facing == 0) {
+				ivec3 proc_pos = local_offset - ivec3(OCCLUSION_SIZE);
+				proc_pos += mix(ivec3(0), ivec3(1), greaterThanEqual(proc_pos, ivec3(0)));
+
+				ivec3 proc_abs = abs(proc_pos);
+
+				ivec3 read_dir = sign(proc_pos); //opposite direction
+				ivec3 read_dir_x = ivec3(read_dir.x, 0, 0);
+				ivec3 read_dir_y = ivec3(0, read_dir.y, 0);
+				ivec3 read_dir_z = ivec3(0, 0, read_dir.z);
+				//solid
+				uvec3 read_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), greaterThan(read_dir, ivec3(0))); //match positive with negative normals
+				uvec3 block_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), lessThan(read_dir, ivec3(0))); //match positive with negative normals
+
+				block_mask = uvec3(0);
+
+				float visible = 0.0;
+				float occlude_total = 0.0;
+
+				if (proc_abs.x < OCCLUSION_SIZE) {
+					ivec3 read_offset = local_offset + read_dir_x;
+					uint x_mask = get_facing(read_offset);
+					if (x_mask != 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occlude_total += 1.0;
+							if (bool(x_mask & read_mask.x) && !bool(x_mask & block_mask.x)) {
+								visible += 1.0;
+							}
+						}
+					}
+				}
+
+				if (proc_abs.y < OCCLUSION_SIZE) {
+					ivec3 read_offset = local_offset + read_dir_y;
+					uint y_mask = get_facing(read_offset);
+					if (y_mask != 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occlude_total += 1.0;
+							if (bool(y_mask & read_mask.y) && !bool(y_mask & block_mask.y)) {
+								visible += 1.0;
+							}
+						}
+					}
+				}
+
+				if (proc_abs.z < OCCLUSION_SIZE) {
+					ivec3 read_offset = local_offset + read_dir_z;
+					uint z_mask = get_facing(read_offset);
+					if (z_mask != 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occlude_total += 1.0;
+							if (bool(z_mask & read_mask.z) && !bool(z_mask & block_mask.z)) {
+								visible += 1.0;
+							}
+						}
+					}
+				}
+
+				//if near the cartesian plane, test in opposite direction too
+
+				read_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), lessThan(read_dir, ivec3(0))); //match negative with positive normals
+				block_mask = mix(uvec3(1, 2, 4), uvec3(8, 16, 32), greaterThan(read_dir, ivec3(0))); //match negative with positive normals
+				block_mask = uvec3(0);
+
+				if (proc_abs.x == 1) {
+					ivec3 read_offset = local_offset - read_dir_x;
+					uint x_mask = get_facing(read_offset);
+					if (x_mask != 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occlude_total += 1.0;
+							if (bool(x_mask & read_mask.x) && !bool(x_mask & block_mask.x)) {
+								visible += 1.0;
+							}
+						}
+					}
+				}
+
+				if (proc_abs.y == 1) {
+					ivec3 read_offset = local_offset - read_dir_y;
+					uint y_mask = get_facing(read_offset);
+					if (y_mask != 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occlude_total += 1.0;
+							if (bool(y_mask & read_mask.y) && !bool(y_mask & block_mask.y)) {
+								visible += 1.0;
+							}
+						}
+					}
+				}
+
+				if (proc_abs.z == 1) {
+					ivec3 read_offset = local_offset - read_dir_z;
+					uint z_mask = get_facing(read_offset);
+					if (z_mask != 0) {
+						read_offset += region_offset;
+						if (all(greaterThanEqual(read_offset, ivec3(0))) && all(lessThan(read_offset, ivec3(params.grid_size)))) {
+							occlude_total += 1.0;
+							if (bool(z_mask & read_mask.z) && !bool(z_mask & block_mask.z)) {
+								visible += 1.0;
+							}
+						}
+					}
+				}
+
+				if (occlude_total > 0.0) {
+					float occ = imageLoad(dst_occlusion[params.occlusion_index], offset).r;
+					occ *= visible / occlude_total;
+					imageStore(dst_occlusion[params.occlusion_index], offset, vec4(occ));
+				}
+			}
+		}
+	}
+
+#endif
+
+	/*
+	for(int i=0;i<8;i++) {
+		ivec3 local_offset = local_pos + ((ivec3(i) >> ivec3(2,1,0)) & ivec3(1,1,1)) * OCCLUSION_SIZE;
+		ivec3 offset = local_offset - ivec3(OCCLUSION_SIZE); //looking around probe, so starts negative
+		offset += region * OCCLUSION_SIZE * 2; //offset by region
+		offset += params.probe_offset * OCCLUSION_SIZE; // offset by probe offset
+		if (all(greaterThanEqual(offset,ivec3(0))) && all(lessThan(offset,ivec3(params.grid_size)))) {
+			imageStore(dst_occlusion[params.occlusion_index],offset,vec4( occlusion_data[ to_linear(local_offset) ]  ));
+			//imageStore(dst_occlusion[params.occlusion_index],offset,vec4( occlusion_solid[ to_linear(local_offset) ] ));
+		}
+	}
+*/
+
+#endif
+
+#ifdef MODE_STORE
+
+	ivec3 local = ivec3(gl_LocalInvocationID.xyz);
+	ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
+	// store SDF
+	uvec4 p = imageLoad(src_positions, pos);
+
+	bool solid = false;
+	float d;
+	if (ivec3(p.xyz) == pos) {
+		//solid block
+		d = 0;
+		solid = true;
+	} else {
+		//distance block
+		d = 1.0 + length(vec3(p.xyz) - vec3(pos));
+	}
+
+	d /= 255.0;
+
+	imageStore(dst_sdf, pos, vec4(d));
+
+	// STORE OCCLUSION
+
+	uint occlusion = 0;
+	const uint occlusion_shift[8] = uint[](12, 8, 4, 0, 28, 24, 20, 16);
+	for (int i = 0; i < 8; i++) {
+		float occ = imageLoad(src_occlusion[i], pos).r;
+		occlusion |= uint(clamp(occ * 15.0, 0.0, 15.0)) << occlusion_shift[i];
+	}
+	{
+		ivec3 occ_pos = pos;
+		occ_pos.z += params.cascade * params.grid_size;
+		imageStore(dst_occlusion, occ_pos, uvec4(occlusion & 0xFFFF));
+		occ_pos.x += params.grid_size;
+		imageStore(dst_occlusion, occ_pos, uvec4(occlusion >> 16));
+	}
+
+	// STORE POSITIONS
+
+	if (local == ivec3(0)) {
+		store_position_count = 0; //base one stores as zero, the others wait
+	}
+
+	groupMemoryBarrier();
+	barrier();
+
+	if (solid) {
+		uint index = atomicAdd(store_position_count, 1);
+		// At least do the conversion work in parallel
+		store_positions[index].position = uint(pos.x | (pos.y << 7) | (pos.z << 14));
+
+		//see around which voxels point to this one, add them to the list
+		uint bit_index = 0;
+		uint neighbour_bits = 0;
+		for (int i = -1; i <= 1; i++) {
+			for (int j = -1; j <= 1; j++) {
+				for (int k = -1; k <= 1; k++) {
+					if (i == 0 && j == 0 && k == 0) {
+						continue;
+					}
+					ivec3 npos = pos + ivec3(i, j, k);
+					if (all(greaterThanEqual(npos, ivec3(0))) && all(lessThan(npos, ivec3(params.grid_size)))) {
+						p = imageLoad(src_positions, npos);
+						if (ivec3(p.xyz) == pos) {
+							neighbour_bits |= (1 << bit_index);
+						}
+					}
+					bit_index++;
+				}
+			}
+		}
+
+		uint rgb = imageLoad(src_albedo, pos).r;
+		uint facing = imageLoad(src_facing, pos).r;
+
+		store_positions[index].albedo = rgb >> 1; //store as it comes (555) to avoid precision loss (and move away the alpha bit)
+		store_positions[index].albedo |= (facing & 0x3F) << 15; // store facing in bits 15-21
+
+		store_positions[index].albedo |= neighbour_bits << 21; //store lower 11 bits of neighbours with remaining albedo
+		store_positions[index].position |= (neighbour_bits >> 11) << 21; //store 11 bits more of neighbours with position
+
+		store_positions[index].light = imageLoad(src_light, pos).r;
+		store_positions[index].light_aniso = imageLoad(src_light_aniso, pos).r;
+		//add neighbours
+		store_positions[index].light |= (neighbour_bits >> 22) << 30; //store 2 bits more of neighbours with light
+		store_positions[index].light_aniso |= (neighbour_bits >> 24) << 30; //store 2 bits more of neighbours with aniso
+	}
+
+	groupMemoryBarrier();
+	barrier();
+
+	// global increment only once per group, to reduce pressure
+
+	if (local == ivec3(0) && store_position_count > 0) {
+		store_from_index = atomicAdd(dispatch_data.total_count, store_position_count);
+		uint group_count = (store_from_index + store_position_count - 1) / 64 + 1;
+		atomicMax(dispatch_data.x, group_count);
+	}
+
+	groupMemoryBarrier();
+	barrier();
+
+	uint read_index = uint(local.z * 4 * 4 + local.y * 4 + local.x);
+	uint write_index = store_from_index + read_index;
+
+	if (read_index < store_position_count) {
+		dst_process_voxels.data[write_index] = store_positions[read_index];
+	}
+
+	if (pos == ivec3(0)) {
+		//this thread clears y and z
+		dispatch_data.y = 1;
+		dispatch_data.z = 1;
+	}
+#endif
+}

+ 16 - 4
servers/rendering/rendering_device.cpp

@@ -212,10 +212,14 @@ RID RenderingDevice::_render_pipeline_create(RID p_shader, FramebufferFormatID p
 	return render_pipeline_create(p_shader, p_framebuffer_format, p_vertex_format, p_render_primitive, rasterization_state, multisample_state, depth_stencil_state, color_blend_state, p_dynamic_state_flags);
 }
 
-Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) {
+Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) {
 	Vector<DrawListID> splits;
 	splits.resize(p_splits);
-	draw_list_begin_split(p_framebuffer, p_splits, splits.ptrw(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region);
+	Vector<RID> stextures;
+	for (int i = 0; i < p_storage_textures.size(); i++) {
+		stextures.push_back(p_storage_textures[i]);
+	}
+	draw_list_begin_split(p_framebuffer, p_splits, splits.ptrw(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures);
 
 	Vector<int64_t> split_ids;
 	split_ids.resize(splits.size());
@@ -236,6 +240,10 @@ void RenderingDevice::_compute_list_set_push_constant(ComputeListID p_list, cons
 	compute_list_set_push_constant(p_list, p_data.ptr(), p_data_size);
 }
 
+void RenderingDevice::compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads, uint32_t p_x_local_group, uint32_t p_y_local_group, uint32_t p_z_local_group) {
+	compute_list_dispatch(p_list, (p_x_threads - 1) / p_x_local_group + 1, (p_y_threads - 1) / p_y_local_group + 1, (p_z_threads - 1) / p_z_local_group + 1);
+}
+
 void RenderingDevice::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("texture_create", "format", "view", "data"), &RenderingDevice::_texture_create, DEFVAL(Array()));
 	ClassDB::bind_method(D_METHOD("texture_create_shared", "view", "with_texture"), &RenderingDevice::_texture_create_shared);
@@ -254,8 +262,10 @@ void RenderingDevice::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "sync_with_draw"), &RenderingDevice::texture_resolve_multisample, DEFVAL(false));
 
 	ClassDB::bind_method(D_METHOD("framebuffer_format_create", "attachments"), &RenderingDevice::_framebuffer_format_create);
+	ClassDB::bind_method(D_METHOD("framebuffer_format_create_empty", "size"), &RenderingDevice::framebuffer_format_create_empty);
 	ClassDB::bind_method(D_METHOD("framebuffer_format_get_texture_samples", "format"), &RenderingDevice::framebuffer_format_get_texture_samples);
 	ClassDB::bind_method(D_METHOD("framebuffer_create", "textures", "validate_with_format"), &RenderingDevice::_framebuffer_create, DEFVAL(INVALID_FORMAT_ID));
+	ClassDB::bind_method(D_METHOD("framebuffer_create_empty", "size", "validate_with_format"), &RenderingDevice::framebuffer_create_empty, DEFVAL(INVALID_FORMAT_ID));
 	ClassDB::bind_method(D_METHOD("framebuffer_get_format", "framebuffer"), &RenderingDevice::framebuffer_get_format);
 
 	ClassDB::bind_method(D_METHOD("sampler_create", "state"), &RenderingDevice::_sampler_create);
@@ -292,8 +302,8 @@ void RenderingDevice::_bind_methods() {
 
 	ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color()));
 
-	ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2i()));
-	ClassDB::bind_method(D_METHOD("draw_list_begin_split", "framebuffer", "splits", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::_draw_list_begin_split, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2i()));
+	ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2i()), DEFVAL(TypedArray<RID>()));
+	ClassDB::bind_method(D_METHOD("draw_list_begin_split", "framebuffer", "splits", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_split, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2i()), DEFVAL(TypedArray<RID>()));
 
 	ClassDB::bind_method(D_METHOD("draw_list_bind_render_pipeline", "draw_list", "render_pipeline"), &RenderingDevice::draw_list_bind_render_pipeline);
 	ClassDB::bind_method(D_METHOD("draw_list_bind_uniform_set", "draw_list", "uniform_set", "set_index"), &RenderingDevice::draw_list_bind_uniform_set);
@@ -625,6 +635,8 @@ void RenderingDevice::_bind_methods() {
 	BIND_ENUM_CONSTANT(INDEX_BUFFER_FORMAT_UINT16);
 	BIND_ENUM_CONSTANT(INDEX_BUFFER_FORMAT_UINT32);
 
+	BIND_ENUM_CONSTANT(STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT);
+
 	BIND_ENUM_CONSTANT(UNIFORM_TYPE_SAMPLER); //for sampling only (sampler GLSL type)
 	BIND_ENUM_CONSTANT(UNIFORM_TYPE_SAMPLER_WITH_TEXTURE); // for sampling only); but includes a texture); (samplerXX GLSL type)); first a sampler then a texture
 	BIND_ENUM_CONSTANT(UNIFORM_TYPE_TEXTURE); //only texture); (textureXX GLSL type)

+ 15 - 4
servers/rendering/rendering_device.h

@@ -467,9 +467,11 @@ public:
 
 	// This ID is warranted to be unique for the same formats, does not need to be freed
 	virtual FramebufferFormatID framebuffer_format_create(const Vector<AttachmentFormat> &p_format) = 0;
+	virtual FramebufferFormatID framebuffer_format_create_empty(const Size2i &p_size) = 0;
 	virtual TextureSamples framebuffer_format_get_texture_samples(FramebufferFormatID p_format) = 0;
 
 	virtual RID framebuffer_create(const Vector<RID> &p_texture_attachments, FramebufferFormatID p_format_check = INVALID_ID) = 0;
+	virtual RID framebuffer_create_empty(const Size2i &p_size, FramebufferFormatID p_format_check = INVALID_ID) = 0;
 
 	virtual FramebufferFormatID framebuffer_get_format(RID p_framebuffer) = 0;
 
@@ -618,8 +620,12 @@ public:
 		UNIFORM_TYPE_MAX
 	};
 
+	enum StorageBufferUsage {
+		STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT = 1
+	};
+
 	virtual RID uniform_buffer_create(uint32_t p_size_bytes, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0;
-	virtual RID storage_buffer_create(uint32_t p_size, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0;
+	virtual RID storage_buffer_create(uint32_t p_size, const Vector<uint8_t> &p_data = Vector<uint8_t>(), uint32_t p_usage = 0) = 0;
 	virtual RID texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data = Vector<uint8_t>()) = 0;
 
 	struct Uniform {
@@ -940,8 +946,8 @@ public:
 	typedef int64_t DrawListID;
 
 	virtual DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()) = 0;
-	virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()) = 0;
-	virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()) = 0;
+	virtual DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()) = 0;
+	virtual Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()) = 0;
 
 	virtual void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) = 0;
 	virtual void draw_list_bind_uniform_set(DrawListID p_list, RID p_uniform_set, uint32_t p_index) = 0;
@@ -968,10 +974,14 @@ public:
 	virtual void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index) = 0;
 	virtual void compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size) = 0;
 	virtual void compute_list_dispatch(ComputeListID p_list, uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) = 0;
+	virtual void compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads, uint32_t p_x_local_group, uint32_t p_y_local_group, uint32_t p_z_local_group);
+	virtual void compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset) = 0;
 	virtual void compute_list_add_barrier(ComputeListID p_list) = 0;
 
 	virtual void compute_list_end() = 0;
 
+	virtual void full_barrier() = 0;
+
 	/***************/
 	/**** FREE! ****/
 	/***************/
@@ -1070,7 +1080,7 @@ protected:
 
 	RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, int p_dynamic_state_flags = 0);
 
-	Vector<int64_t> _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2());
+	Vector<int64_t> _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>());
 	void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size);
 	void _compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size);
 };
@@ -1089,6 +1099,7 @@ VARIANT_ENUM_CAST(RenderingDevice::SamplerRepeatMode)
 VARIANT_ENUM_CAST(RenderingDevice::SamplerBorderColor)
 VARIANT_ENUM_CAST(RenderingDevice::VertexFrequency)
 VARIANT_ENUM_CAST(RenderingDevice::IndexBufferFormat)
+VARIANT_ENUM_CAST(RenderingDevice::StorageBufferUsage)
 VARIANT_ENUM_CAST(RenderingDevice::UniformType)
 VARIANT_ENUM_CAST(RenderingDevice::RenderPrimitive)
 VARIANT_ENUM_CAST(RenderingDevice::PolygonCullMode)

+ 4 - 0
servers/rendering/rendering_server_raster.cpp

@@ -222,6 +222,10 @@ bool RenderingServerRaster::has_feature(Features p_feature) const {
 	return false;
 }
 
+void RenderingServerRaster::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) {
+	RSG::scene_render->sdfgi_set_debug_probe_select(p_position, p_dir);
+}
+
 RID RenderingServerRaster::get_test_cube() {
 	if (!test_cube.is_valid()) {
 		test_cube = _make_test_cube();

+ 13 - 5
servers/rendering/rendering_server_raster.h

@@ -322,7 +322,8 @@ public:
 	BIND2(light_set_negative, RID, bool)
 	BIND2(light_set_cull_mask, RID, uint32_t)
 	BIND2(light_set_reverse_cull_face_mode, RID, bool)
-	BIND2(light_set_use_gi, RID, bool)
+	BIND2(light_set_bake_mode, RID, LightBakeMode)
+	BIND2(light_set_max_sdfgi_cascade, RID, uint32_t)
 
 	BIND2(light_omni_set_shadow_mode, RID, LightOmniShadowMode)
 
@@ -336,9 +337,9 @@ public:
 
 	BIND2(reflection_probe_set_update_mode, RID, ReflectionProbeUpdateMode)
 	BIND2(reflection_probe_set_intensity, RID, float)
-	BIND2(reflection_probe_set_interior_ambient, RID, const Color &)
-	BIND2(reflection_probe_set_interior_ambient_energy, RID, float)
-	BIND2(reflection_probe_set_interior_ambient_probe_contribution, RID, float)
+	BIND2(reflection_probe_set_ambient_color, RID, const Color &)
+	BIND2(reflection_probe_set_ambient_energy, RID, float)
+	BIND2(reflection_probe_set_ambient_mode, RID, ReflectionProbeAmbientMode)
 	BIND2(reflection_probe_set_max_distance, RID, float)
 	BIND2(reflection_probe_set_extents, RID, const Vector3 &)
 	BIND2(reflection_probe_set_origin_offset, RID, const Vector3 &)
@@ -523,6 +524,7 @@ public:
 #define BINDBASE RSG::scene_render
 
 	BIND1(directional_shadow_atlas_set_size, int)
+	BIND1(gi_probe_set_quality, GIProbeQuality)
 
 	/* SKY API */
 
@@ -564,9 +566,13 @@ public:
 	BIND7(environment_set_fog_depth, RID, bool, float, float, float, bool, float)
 	BIND5(environment_set_fog_height, RID, bool, float, float, float)
 
+	BIND12(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, bool, bool, bool, float, float, float)
+	BIND1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount)
+	BIND1(environment_set_sdfgi_frames_to_converge, EnvironmentSDFGIFramesToConverge)
+
 	BIND3R(Ref<Image>, environment_bake_panorama, RID, bool, const Size2i &)
 
-	BIND2(screen_space_roughness_limiter_set_active, bool, float)
+	BIND3(screen_space_roughness_limiter_set_active, bool, float, float)
 	BIND1(sub_surface_scattering_set_quality, SubSurfaceScatteringQuality)
 	BIND2(sub_surface_scattering_set_scale, float, float)
 
@@ -793,6 +799,8 @@ public:
 
 	virtual bool is_low_end() const;
 
+	virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir);
+
 	RenderingServerRaster();
 	~RenderingServerRaster();
 

+ 124 - 17
servers/rendering/rendering_server_scene.cpp

@@ -369,6 +369,11 @@ void RenderingServerScene::instance_set_base(RID p_instance, RID p_base) {
 		switch (instance->base_type) {
 			case RS::INSTANCE_LIGHT: {
 				InstanceLightData *light = static_cast<InstanceLightData *>(instance->base_data);
+
+				if (RSG::storage->light_get_type(instance->base) != RS::LIGHT_DIRECTIONAL && light->bake_mode == RS::LIGHT_BAKE_DYNAMIC) {
+					instance->scenario->dynamic_lights.erase(light->instance);
+				}
+
 #ifdef DEBUG_ENABLED
 				if (light->geometries.size()) {
 					ERR_PRINT("BUG, indexing did not unpair geometries from light.");
@@ -976,7 +981,26 @@ void RenderingServerScene::_update_instance(Instance *p_instance) {
 		InstanceLightData *light = static_cast<InstanceLightData *>(p_instance->base_data);
 
 		RSG::scene_render->light_instance_set_transform(light->instance, p_instance->transform);
+		RSG::scene_render->light_instance_set_aabb(light->instance, p_instance->transform.xform(p_instance->aabb));
 		light->shadow_dirty = true;
+
+		RS::LightBakeMode bake_mode = RSG::storage->light_get_bake_mode(p_instance->base);
+		if (RSG::storage->light_get_type(p_instance->base) != RS::LIGHT_DIRECTIONAL && bake_mode != light->bake_mode) {
+			if (light->bake_mode == RS::LIGHT_BAKE_DYNAMIC) {
+				p_instance->scenario->dynamic_lights.erase(light->instance);
+			}
+
+			light->bake_mode = bake_mode;
+
+			if (light->bake_mode == RS::LIGHT_BAKE_DYNAMIC) {
+				p_instance->scenario->dynamic_lights.push_back(light->instance);
+			}
+		}
+
+		uint32_t max_sdfgi_cascade = RSG::storage->light_get_max_sdfgi_cascade(p_instance->base);
+		if (light->max_sdfgi_cascade != max_sdfgi_cascade) {
+			light->max_sdfgi_cascade = max_sdfgi_cascade; //should most likely make sdfgi dirty in scenario
+		}
 	}
 
 	if (p_instance->base_type == RS::INSTANCE_REFLECTION_PROBE) {
@@ -1788,8 +1812,10 @@ void RenderingServerScene::render_camera(RID p_render_buffers, RID p_camera, RID
 		} break;
 	}
 
-	_prepare_scene(camera->transform, camera_matrix, ortho, camera->vaspect, camera->env, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
-	_render_scene(p_render_buffers, camera->transform, camera_matrix, ortho, camera->env, camera->effects, p_scenario, p_shadow_atlas, RID(), -1);
+	RID environment = _render_get_environment(p_camera, p_scenario);
+
+	_prepare_scene(camera->transform, camera_matrix, ortho, camera->vaspect, p_render_buffers, environment, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
+	_render_scene(p_render_buffers, camera->transform, camera_matrix, ortho, environment, camera->effects, p_scenario, p_shadow_atlas, RID(), -1);
 #endif
 }
 
@@ -1808,6 +1834,8 @@ void RenderingServerScene::render_camera(RID p_render_buffers, Ref<XRInterface>
 	Transform world_origin = XRServer::get_singleton()->get_world_origin();
 	Transform cam_transform = p_interface->get_transform_for_eye(p_eye, world_origin);
 
+	RID environment = _render_get_environment(p_camera, p_scenario);
+
 	// For stereo render we only prepare for our left eye and then reuse the outcome for our right eye
 	if (p_eye == XRInterface::EYE_LEFT) {
 		// Center our transform, we assume basis is equal.
@@ -1865,17 +1893,17 @@ void RenderingServerScene::render_camera(RID p_render_buffers, Ref<XRInterface>
 		mono_transform *= apply_z_shift;
 
 		// now prepare our scene with our adjusted transform projection matrix
-		_prepare_scene(mono_transform, combined_matrix, false, false, camera->env, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
+		_prepare_scene(mono_transform, combined_matrix, false, false, p_render_buffers, environment, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
 	} else if (p_eye == XRInterface::EYE_MONO) {
 		// For mono render, prepare as per usual
-		_prepare_scene(cam_transform, camera_matrix, false, false, camera->env, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
+		_prepare_scene(cam_transform, camera_matrix, false, false, p_render_buffers, environment, camera->visible_layers, p_scenario, p_shadow_atlas, RID());
 	}
 
 	// And render our scene...
-	_render_scene(p_render_buffers, cam_transform, camera_matrix, false, camera->env, camera->effects, p_scenario, p_shadow_atlas, RID(), -1);
+	_render_scene(p_render_buffers, cam_transform, camera_matrix, false, environment, camera->effects, p_scenario, p_shadow_atlas, RID(), -1);
 };
 
-void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_force_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, bool p_using_shadows) {
+void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, bool p_using_shadows) {
 	// Note, in stereo rendering:
 	// - p_cam_transform will be a transform in the middle of our two eyes
 	// - p_cam_projection is a wider frustrum that encompasses both eyes
@@ -1887,6 +1915,10 @@ void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const
 
 	RSG::scene_render->set_scene_pass(render_pass);
 
+	if (p_render_buffers.is_valid()) {
+		RSG::scene_render->sdfgi_update(p_render_buffers, p_environment, p_cam_transform.origin); //update conditions for SDFGI (whether its used or not)
+	}
+
 	RENDER_TIMESTAMP("Frustum Culling");
 
 	//rasterizer->set_camera(camera->transform, camera_matrix,ortho);
@@ -2224,22 +2256,97 @@ void RenderingServerScene::_prepare_scene(const Transform p_cam_transform, const
 			}
 		}
 	}
+
+	/* UPDATE SDFGI */
+
+	if (p_render_buffers.is_valid()) {
+		uint32_t cascade_index[8];
+		uint32_t cascade_sizes[8];
+		const RID *cascade_ptrs[8];
+		uint32_t cascade_count = 0;
+		uint32_t sdfgi_light_cull_count = 0;
+
+		uint32_t prev_cascade = 0xFFFFFFFF;
+		for (int i = 0; i < RSG::scene_render->sdfgi_get_pending_region_count(p_render_buffers); i++) {
+			AABB region = RSG::scene_render->sdfgi_get_pending_region_bounds(p_render_buffers, i);
+			uint32_t region_cascade = RSG::scene_render->sdfgi_get_pending_region_cascade(p_render_buffers, i);
+
+			if (region_cascade != prev_cascade) {
+				cascade_sizes[cascade_count] = 0;
+				cascade_index[cascade_count] = region_cascade;
+				cascade_ptrs[cascade_count] = &sdfgi_light_cull_result[sdfgi_light_cull_count];
+				cascade_count++;
+				sdfgi_light_cull_pass++;
+				prev_cascade = region_cascade;
+			}
+			uint32_t sdfgi_cull_count = scenario->octree.cull_aabb(region, instance_shadow_cull_result, MAX_INSTANCE_CULL);
+
+			for (uint32_t j = 0; j < sdfgi_cull_count; j++) {
+				Instance *ins = instance_shadow_cull_result[j];
+
+				bool keep = false;
+
+				if (ins->base_type == RS::INSTANCE_LIGHT && ins->visible) {
+					InstanceLightData *instance_light = (InstanceLightData *)ins->base_data;
+					if (instance_light->bake_mode != RS::LIGHT_BAKE_STATIC || region_cascade > instance_light->max_sdfgi_cascade) {
+						continue;
+					}
+
+					if (sdfgi_light_cull_pass != instance_light->sdfgi_cascade_light_pass && sdfgi_light_cull_count < MAX_LIGHTS_CULLED) {
+						instance_light->sdfgi_cascade_light_pass = sdfgi_light_cull_pass;
+						sdfgi_light_cull_result[sdfgi_light_cull_count++] = instance_light->instance;
+						cascade_sizes[cascade_count - 1]++;
+					}
+				} else if ((1 << ins->base_type) & RS::INSTANCE_GEOMETRY_MASK) {
+					if (ins->baked_light) {
+						keep = true;
+					}
+				}
+
+				if (!keep) {
+					// remove, no reason to keep
+					sdfgi_cull_count--;
+					SWAP(instance_shadow_cull_result[j], instance_shadow_cull_result[sdfgi_cull_count]);
+					j--;
+				}
+			}
+
+			RSG::scene_render->render_sdfgi(p_render_buffers, i, (RasterizerScene::InstanceBase **)instance_shadow_cull_result, sdfgi_cull_count);
+			//have to save updated cascades, then update static lights.
+		}
+
+		if (sdfgi_light_cull_count) {
+			RSG::scene_render->render_sdfgi_static_lights(p_render_buffers, cascade_count, cascade_index, cascade_ptrs, cascade_sizes);
+		}
+
+		RSG::scene_render->sdfgi_update_probes(p_render_buffers, p_environment, directional_light_ptr, directional_light_count, scenario->dynamic_lights.ptr(), scenario->dynamic_lights.size());
+	}
 }
 
-void RenderingServerScene::_render_scene(RID p_render_buffers, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_force_camera_effects, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
-	Scenario *scenario = scenario_owner.getornull(p_scenario);
+RID RenderingServerScene::_render_get_environment(RID p_camera, RID p_scenario) {
+	Camera *camera = camera_owner.getornull(p_camera);
+	if (camera && RSG::scene_render->is_environment(camera->env)) {
+		return camera->env;
+	}
 
-	/* ENVIRONMENT */
+	Scenario *scenario = scenario_owner.getornull(p_scenario);
+	if (!scenario) {
+		return RID();
+	}
+	if (RSG::scene_render->is_environment(scenario->environment)) {
+		return scenario->environment;
+	}
 
-	RID environment;
-	if (p_force_environment.is_valid()) { //camera has more environment priority
-		environment = p_force_environment;
-	} else if (scenario->environment.is_valid()) {
-		environment = scenario->environment;
-	} else {
-		environment = scenario->fallback_environment;
+	if (RSG::scene_render->is_environment(scenario->fallback_environment)) {
+		return scenario->fallback_environment;
 	}
 
+	return RID();
+}
+
+void RenderingServerScene::_render_scene(RID p_render_buffers, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_environment, RID p_force_camera_effects, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass) {
+	Scenario *scenario = scenario_owner.getornull(p_scenario);
+
 	RID camera_effects;
 	if (p_force_camera_effects.is_valid()) {
 		camera_effects = p_force_camera_effects;
@@ -2249,7 +2356,7 @@ void RenderingServerScene::_render_scene(RID p_render_buffers, const Transform p
 	/* PROCESS GEOMETRY AND DRAW SCENE */
 
 	RENDER_TIMESTAMP("Render Scene ");
-	RSG::scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, instance_cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, gi_probe_instance_cull_result, gi_probe_cull_count, decal_instance_cull_result, decal_cull_count, (RasterizerScene::InstanceBase **)lightmap_cull_result, lightmap_cull_count, environment, camera_effects, p_shadow_atlas, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass);
+	RSG::scene_render->render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_orthogonal, (RasterizerScene::InstanceBase **)instance_cull_result, instance_cull_count, light_instance_cull_result, light_cull_count + directional_light_count, reflection_probe_instance_cull_result, reflection_probe_cull_count, gi_probe_instance_cull_result, gi_probe_cull_count, decal_instance_cull_result, decal_cull_count, (RasterizerScene::InstanceBase **)lightmap_cull_result, lightmap_cull_count, p_environment, camera_effects, p_shadow_atlas, p_reflection_probe.is_valid() ? RID() : scenario->reflection_atlas, p_reflection_probe, p_reflection_probe_pass);
 }
 
 void RenderingServerScene::render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas) {

+ 16 - 2
servers/rendering/rendering_server_scene.h

@@ -33,6 +33,8 @@
 
 #include "servers/rendering/rasterizer.h"
 
+#include "core/local_vector.h"
+#include "core/math/geometry_3d.h"
 #include "core/math/octree.h"
 #include "core/os/semaphore.h"
 #include "core/os/thread.h"
@@ -121,6 +123,8 @@ public:
 
 		SelfList<Instance>::List instances;
 
+		LocalVector<RID> dynamic_lights;
+
 		Scenario() { debug = RS::SCENARIO_DEBUG_DISABLED; }
 	};
 
@@ -309,7 +313,13 @@ public:
 
 		Instance *baked_light;
 
+		RS::LightBakeMode bake_mode;
+		uint32_t max_sdfgi_cascade = 2;
+
+		uint64_t sdfgi_cascade_light_pass = 0;
+
 		InstanceLightData() {
+			bake_mode = RS::LIGHT_BAKE_DISABLED;
 			shadow_dirty = true;
 			D = nullptr;
 			last_version = 0;
@@ -379,7 +389,9 @@ public:
 	Instance *instance_cull_result[MAX_INSTANCE_CULL];
 	Instance *instance_shadow_cull_result[MAX_INSTANCE_CULL]; //used for generating shadowmaps
 	Instance *light_cull_result[MAX_LIGHTS_CULLED];
+	RID sdfgi_light_cull_result[MAX_LIGHTS_CULLED];
 	RID light_instance_cull_result[MAX_LIGHTS_CULLED];
+	uint64_t sdfgi_light_cull_pass = 0;
 	int light_cull_count;
 	int directional_light_count;
 	RID reflection_probe_instance_cull_result[MAX_REFLECTION_PROBES_CULLED];
@@ -438,9 +450,11 @@ public:
 
 	_FORCE_INLINE_ bool _light_instance_update_shadow(Instance *p_instance, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_shadow_atlas, Scenario *p_scenario);
 
+	RID _render_get_environment(RID p_camera, RID p_scenario);
+
 	bool _render_reflection_probe_step(Instance *p_instance, int p_step);
-	void _prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_force_environment, RID p_force_camera_effects, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, bool p_using_shadows = true);
-	void _render_scene(RID p_render_buffers, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_force_environment, RID p_force_camera_effects, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
+	void _prepare_scene(const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, bool p_cam_vaspect, RID p_render_buffers, RID p_environment, uint32_t p_visible_layers, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, bool p_using_shadows = true);
+	void _render_scene(RID p_render_buffers, const Transform p_cam_transform, const CameraMatrix &p_cam_projection, bool p_cam_orthogonal, RID p_environment, RID p_force_camera_effects, RID p_scenario, RID p_shadow_atlas, RID p_reflection_probe, int p_reflection_probe_pass);
 	void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas);
 
 	void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas);

+ 16 - 5
servers/rendering/rendering_server_wrap_mt.h

@@ -237,7 +237,8 @@ public:
 	FUNC2(light_set_negative, RID, bool)
 	FUNC2(light_set_cull_mask, RID, uint32_t)
 	FUNC2(light_set_reverse_cull_face_mode, RID, bool)
-	FUNC2(light_set_use_gi, RID, bool)
+	FUNC2(light_set_bake_mode, RID, LightBakeMode)
+	FUNC2(light_set_max_sdfgi_cascade, RID, uint32_t)
 
 	FUNC2(light_omni_set_shadow_mode, RID, LightOmniShadowMode)
 
@@ -251,9 +252,9 @@ public:
 
 	FUNC2(reflection_probe_set_update_mode, RID, ReflectionProbeUpdateMode)
 	FUNC2(reflection_probe_set_intensity, RID, float)
-	FUNC2(reflection_probe_set_interior_ambient, RID, const Color &)
-	FUNC2(reflection_probe_set_interior_ambient_energy, RID, float)
-	FUNC2(reflection_probe_set_interior_ambient_probe_contribution, RID, float)
+	FUNC2(reflection_probe_set_ambient_color, RID, const Color &)
+	FUNC2(reflection_probe_set_ambient_energy, RID, float)
+	FUNC2(reflection_probe_set_ambient_mode, RID, ReflectionProbeAmbientMode)
 	FUNC2(reflection_probe_set_max_distance, RID, float)
 	FUNC2(reflection_probe_set_extents, RID, const Vector3 &)
 	FUNC2(reflection_probe_set_origin_offset, RID, const Vector3 &)
@@ -321,6 +322,8 @@ public:
 	FUNC2(gi_probe_set_anisotropy_strength, RID, float)
 	FUNC1RC(float, gi_probe_get_anisotropy_strength, RID)
 
+	FUNC1(gi_probe_set_quality, GIProbeQuality)
+
 	/* LIGHTMAP CAPTURE */
 
 	FUNCRID(lightmap)
@@ -465,6 +468,10 @@ public:
 
 	FUNC2(environment_set_ssao_quality, EnvironmentSSAOQuality, bool)
 
+	FUNC12(environment_set_sdfgi, RID, bool, EnvironmentSDFGICascades, float, EnvironmentSDFGIYScale, bool, bool, bool, bool, float, float, float)
+	FUNC1(environment_set_sdfgi_ray_count, EnvironmentSDFGIRayCount)
+	FUNC1(environment_set_sdfgi_frames_to_converge, EnvironmentSDFGIFramesToConverge)
+
 	FUNC11(environment_set_glow, RID, bool, int, float, float, float, float, EnvironmentGlowBlendMode, float, float, float)
 	FUNC1(environment_glow_set_use_bicubic_upscale, bool)
 
@@ -478,7 +485,7 @@ public:
 
 	FUNC3R(Ref<Image>, environment_bake_panorama, RID, bool, const Size2i &)
 
-	FUNC2(screen_space_roughness_limiter_set_active, bool, float)
+	FUNC3(screen_space_roughness_limiter_set_active, bool, float, float)
 	FUNC1(sub_surface_scattering_set_quality, SubSurfaceScatteringQuality)
 	FUNC2(sub_surface_scattering_set_scale, float, float)
 
@@ -714,6 +721,10 @@ public:
 		return rendering_server->get_frame_profile();
 	}
 
+	virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) {
+		rendering_server->sdfgi_set_debug_probe_select(p_position, p_dir);
+	}
+
 	RenderingServerWrapMT(RenderingServer *p_contained, bool p_create_thread);
 	~RenderingServerWrapMT();
 

+ 1 - 0
servers/rendering/shader_types.cpp

@@ -114,6 +114,7 @@ ShaderTypes::ShaderTypes() {
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["AO_LIGHT_AFFECT"] = ShaderLanguage::TYPE_FLOAT;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["EMISSION"] = ShaderLanguage::TYPE_VEC3;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D;
+	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["NORMAL_ROUGHNESS_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH_TEXTURE"] = ShaderLanguage::TYPE_SAMPLER2D;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["DEPTH"] = ShaderLanguage::TYPE_FLOAT;
 	shader_modes[RS::SHADER_SPATIAL].functions["fragment"].built_ins["SCREEN_UV"] = ShaderLanguage::TYPE_VEC2;

+ 18 - 10
servers/rendering_server.cpp

@@ -1608,7 +1608,7 @@ void RenderingServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("light_set_negative", "light", "enable"), &RenderingServer::light_set_negative);
 	ClassDB::bind_method(D_METHOD("light_set_cull_mask", "light", "mask"), &RenderingServer::light_set_cull_mask);
 	ClassDB::bind_method(D_METHOD("light_set_reverse_cull_face_mode", "light", "enabled"), &RenderingServer::light_set_reverse_cull_face_mode);
-	ClassDB::bind_method(D_METHOD("light_set_use_gi", "light", "enabled"), &RenderingServer::light_set_use_gi);
+	ClassDB::bind_method(D_METHOD("light_set_bake_mode", "light", "bake_mode"), &RenderingServer::light_set_bake_mode);
 
 	ClassDB::bind_method(D_METHOD("light_omni_set_shadow_mode", "light", "mode"), &RenderingServer::light_omni_set_shadow_mode);
 
@@ -1619,9 +1619,9 @@ void RenderingServer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("reflection_probe_create"), &RenderingServer::reflection_probe_create);
 	ClassDB::bind_method(D_METHOD("reflection_probe_set_update_mode", "probe", "mode"), &RenderingServer::reflection_probe_set_update_mode);
 	ClassDB::bind_method(D_METHOD("reflection_probe_set_intensity", "probe", "intensity"), &RenderingServer::reflection_probe_set_intensity);
-	ClassDB::bind_method(D_METHOD("reflection_probe_set_interior_ambient", "probe", "color"), &RenderingServer::reflection_probe_set_interior_ambient);
-	ClassDB::bind_method(D_METHOD("reflection_probe_set_interior_ambient_energy", "probe", "energy"), &RenderingServer::reflection_probe_set_interior_ambient_energy);
-	ClassDB::bind_method(D_METHOD("reflection_probe_set_interior_ambient_probe_contribution", "probe", "contrib"), &RenderingServer::reflection_probe_set_interior_ambient_probe_contribution);
+	ClassDB::bind_method(D_METHOD("reflection_probe_set_ambient_mode", "probe", "mode"), &RenderingServer::reflection_probe_set_ambient_mode);
+	ClassDB::bind_method(D_METHOD("reflection_probe_set_ambient_color", "probe", "color"), &RenderingServer::reflection_probe_set_ambient_color);
+	ClassDB::bind_method(D_METHOD("reflection_probe_set_ambient_energy", "probe", "energy"), &RenderingServer::reflection_probe_set_ambient_energy);
 	ClassDB::bind_method(D_METHOD("reflection_probe_set_max_distance", "probe", "distance"), &RenderingServer::reflection_probe_set_max_distance);
 	ClassDB::bind_method(D_METHOD("reflection_probe_set_extents", "probe", "extents"), &RenderingServer::reflection_probe_set_extents);
 	ClassDB::bind_method(D_METHOD("reflection_probe_set_origin_offset", "probe", "offset"), &RenderingServer::reflection_probe_set_origin_offset);
@@ -2060,9 +2060,11 @@ void RenderingServer::_bind_methods() {
 	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS);
 	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE);
 	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SSAO);
-	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_ROUGHNESS_LIMITER);
 	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_PSSM_SPLITS);
 	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_DECAL_ATLAS);
+	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SDFGI);
+	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_SDFGI_PROBES);
+	BIND_ENUM_CONSTANT(VIEWPORT_DEBUG_DRAW_GI_BUFFER);
 
 	BIND_ENUM_CONSTANT(SKY_MODE_QUALITY);
 	BIND_ENUM_CONSTANT(SKY_MODE_REALTIME);
@@ -2346,7 +2348,7 @@ RenderingServer::RenderingServer() {
 
 	GLOBAL_DEF("rendering/quality/gi_probes/anisotropic", false);
 	GLOBAL_DEF("rendering/quality/gi_probes/quality", 1);
-	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/gi_probes/quality", PropertyInfo(Variant::INT, "rendering/quality/gi_probes/quality", PROPERTY_HINT_ENUM, "Lowest (1 Cone - Fast),Medium (4 Cones - Average),High (6 Cones - Slow)"));
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/gi_probes/quality", PropertyInfo(Variant::INT, "rendering/quality/gi_probes/quality", PROPERTY_HINT_ENUM, "Low (4 Cones - Fast),High (6 Cones - Slow)"));
 
 	GLOBAL_DEF("rendering/quality/shading/force_vertex_shading", false);
 	GLOBAL_DEF("rendering/quality/shading/force_vertex_shading.mobile", true);
@@ -2372,10 +2374,11 @@ RenderingServer::RenderingServer() {
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/ssao/quality", PropertyInfo(Variant::INT, "rendering/quality/ssao/quality", PROPERTY_HINT_ENUM, "Low (Fast),Medium (Average),High (Slow),Ultra (Slower)"));
 	GLOBAL_DEF("rendering/quality/ssao/half_size", false);
 
-	GLOBAL_DEF("rendering/quality/screen_filters/screen_space_roughness_limiter", 0);
-	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/screen_space_roughness_limiter", PropertyInfo(Variant::INT, "rendering/quality/screen_filters/screen_space_roughness_limiter", PROPERTY_HINT_ENUM, "Disabled (Fast),Enabled (Average)"));
-	GLOBAL_DEF("rendering/quality/screen_filters/screen_space_roughness_limiter_curve", 1.0);
-	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/screen_space_roughness_limiter_curve", PropertyInfo(Variant::FLOAT, "rendering/quality/screen_filters/screen_space_roughness_limiter_curve", PROPERTY_HINT_EXP_EASING, "0.01,8,0.01"));
+	GLOBAL_DEF("rendering/quality/screen_filters/screen_space_roughness_limiter_enable", true);
+	GLOBAL_DEF("rendering/quality/screen_filters/screen_space_roughness_limiter_amount", 0.25);
+	GLOBAL_DEF("rendering/quality/screen_filters/screen_space_roughness_limiter_limit", 0.18);
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/screen_space_roughness_limiter_amount", PropertyInfo(Variant::FLOAT, "rendering/quality/screen_filters/screen_space_roughness_limiter_amount", PROPERTY_HINT_RANGE, "0.01,4.0,0.01"));
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/screen_filters/screen_space_roughness_limiter_limit", PropertyInfo(Variant::FLOAT, "rendering/quality/screen_filters/screen_space_roughness_limiter_limit", PROPERTY_HINT_RANGE, "0.01,1.0,0.01"));
 
 	GLOBAL_DEF("rendering/quality/glow/upscale_mode", 1);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/quality/glow/upscale_mode", PropertyInfo(Variant::INT, "rendering/quality/glow/upscale_mode", PROPERTY_HINT_ENUM, "Linear (Fast),Bicubic (Slow)"));
@@ -2395,6 +2398,11 @@ RenderingServer::RenderingServer() {
 
 	GLOBAL_DEF("rendering/lightmapper/probe_capture_update_speed", 15);
 	ProjectSettings::get_singleton()->set_custom_property_info("rendering/lightmapper/probe_capture_update_speed", PropertyInfo(Variant::FLOAT, "rendering/lightmapper/probe_capture_update_speed", PROPERTY_HINT_RANGE, "0.001,256,0.001"));
+
+	GLOBAL_DEF("rendering/sdfgi/probe_ray_count", 2);
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/probe_ray_count", PropertyInfo(Variant::INT, "rendering/sdfgi/probe_ray_count", PROPERTY_HINT_ENUM, "8 (Fastest),16,32,64,96,128 (Slowest)"));
+	GLOBAL_DEF("rendering/sdfgi/frames_to_converge", 1);
+	ProjectSettings::get_singleton()->set_custom_property_info("rendering/sdfgi/frames_to_converge", PropertyInfo(Variant::INT, "rendering/sdfgi/frames_to_converge", PROPERTY_HINT_ENUM, "5 (Less Latency but Lower Quality),10,15,20,25,30 (More Latency but Higher Quality)"));
 }
 
 RenderingServer::~RenderingServer() {

+ 73 - 6
servers/rendering_server.h

@@ -406,7 +406,15 @@ public:
 	virtual void light_set_negative(RID p_light, bool p_enable) = 0;
 	virtual void light_set_cull_mask(RID p_light, uint32_t p_mask) = 0;
 	virtual void light_set_reverse_cull_face_mode(RID p_light, bool p_enabled) = 0;
-	virtual void light_set_use_gi(RID p_light, bool p_enable) = 0;
+
+	enum LightBakeMode {
+		LIGHT_BAKE_DISABLED,
+		LIGHT_BAKE_DYNAMIC,
+		LIGHT_BAKE_STATIC,
+	};
+
+	virtual void light_set_bake_mode(RID p_light, LightBakeMode p_bake_mode) = 0;
+	virtual void light_set_max_sdfgi_cascade(RID p_light, uint32_t p_cascade) = 0;
 
 	// omni light
 	enum LightOmniShadowMode {
@@ -445,9 +453,16 @@ public:
 
 	virtual void reflection_probe_set_update_mode(RID p_probe, ReflectionProbeUpdateMode p_mode) = 0;
 	virtual void reflection_probe_set_intensity(RID p_probe, float p_intensity) = 0;
-	virtual void reflection_probe_set_interior_ambient(RID p_probe, const Color &p_color) = 0;
-	virtual void reflection_probe_set_interior_ambient_energy(RID p_probe, float p_energy) = 0;
-	virtual void reflection_probe_set_interior_ambient_probe_contribution(RID p_probe, float p_contrib) = 0;
+
+	enum ReflectionProbeAmbientMode {
+		REFLECTION_PROBE_AMBIENT_DISABLED,
+		REFLECTION_PROBE_AMBIENT_ENVIRONMENT,
+		REFLECTION_PROBE_AMBIENT_COLOR
+	};
+
+	virtual void reflection_probe_set_ambient_mode(RID p_probe, ReflectionProbeAmbientMode p_mode) = 0;
+	virtual void reflection_probe_set_ambient_color(RID p_probe, const Color &p_color) = 0;
+	virtual void reflection_probe_set_ambient_energy(RID p_probe, float p_energy) = 0;
 	virtual void reflection_probe_set_max_distance(RID p_probe, float p_distance) = 0;
 	virtual void reflection_probe_set_extents(RID p_probe, const Vector3 &p_extents) = 0;
 	virtual void reflection_probe_set_origin_offset(RID p_probe, const Vector3 &p_offset) = 0;
@@ -522,6 +537,13 @@ public:
 	virtual void gi_probe_set_anisotropy_strength(RID p_gi_probe, float p_strength) = 0;
 	virtual float gi_probe_get_anisotropy_strength(RID p_gi_probe) const = 0;
 
+	enum GIProbeQuality {
+		GI_PROBE_QUALITY_LOW,
+		GI_PROBE_QUALITY_HIGH,
+	};
+
+	virtual void gi_probe_set_quality(GIProbeQuality) = 0;
+
 	/* LIGHTMAP */
 
 	virtual RID lightmap_create() = 0;
@@ -690,9 +712,12 @@ public:
 		VIEWPORT_DEBUG_DRAW_DIRECTIONAL_SHADOW_ATLAS,
 		VIEWPORT_DEBUG_DRAW_SCENE_LUMINANCE,
 		VIEWPORT_DEBUG_DRAW_SSAO,
-		VIEWPORT_DEBUG_DRAW_ROUGHNESS_LIMITER,
 		VIEWPORT_DEBUG_DRAW_PSSM_SPLITS,
 		VIEWPORT_DEBUG_DRAW_DECAL_ATLAS,
+		VIEWPORT_DEBUG_DRAW_SDFGI,
+		VIEWPORT_DEBUG_DRAW_SDFGI_PROBES,
+		VIEWPORT_DEBUG_DRAW_GI_BUFFER,
+
 	};
 
 	virtual void viewport_set_debug_draw(RID p_viewport, ViewportDebugDraw p_draw) = 0;
@@ -807,13 +832,51 @@ public:
 
 	virtual void environment_set_ssao_quality(EnvironmentSSAOQuality p_quality, bool p_half_size) = 0;
 
+	enum EnvironmentSDFGICascades {
+		ENV_SDFGI_CASCADES_4,
+		ENV_SDFGI_CASCADES_6,
+		ENV_SDFGI_CASCADES_8,
+	};
+
+	enum EnvironmentSDFGIYScale {
+		ENV_SDFGI_Y_SCALE_DISABLED,
+		ENV_SDFGI_Y_SCALE_75_PERCENT,
+		ENV_SDFGI_Y_SCALE_50_PERCENT
+	};
+
+	virtual void environment_set_sdfgi(RID p_env, bool p_enable, EnvironmentSDFGICascades p_cascades, float p_min_cell_size, EnvironmentSDFGIYScale p_y_scale, bool p_use_occlusion, bool p_use_multibounce, bool p_read_sky, bool p_enhance_ssr, float p_energy, float p_normal_bias, float p_probe_bias) = 0;
+
+	enum EnvironmentSDFGIRayCount {
+		ENV_SDFGI_RAY_COUNT_8,
+		ENV_SDFGI_RAY_COUNT_16,
+		ENV_SDFGI_RAY_COUNT_32,
+		ENV_SDFGI_RAY_COUNT_64,
+		ENV_SDFGI_RAY_COUNT_96,
+		ENV_SDFGI_RAY_COUNT_128,
+		ENV_SDFGI_RAY_COUNT_MAX,
+	};
+
+	virtual void environment_set_sdfgi_ray_count(EnvironmentSDFGIRayCount p_ray_count) = 0;
+
+	enum EnvironmentSDFGIFramesToConverge {
+		ENV_SDFGI_CONVERGE_IN_5_FRAMES,
+		ENV_SDFGI_CONVERGE_IN_10_FRAMES,
+		ENV_SDFGI_CONVERGE_IN_15_FRAMES,
+		ENV_SDFGI_CONVERGE_IN_20_FRAMES,
+		ENV_SDFGI_CONVERGE_IN_25_FRAMES,
+		ENV_SDFGI_CONVERGE_IN_30_FRAMES,
+		ENV_SDFGI_CONVERGE_MAX
+	};
+
+	virtual void environment_set_sdfgi_frames_to_converge(EnvironmentSDFGIFramesToConverge p_frames) = 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;
 	virtual void environment_set_fog_height(RID p_env, bool p_enable, float p_min_height, float p_max_height, float p_height_curve) = 0;
 
 	virtual Ref<Image> environment_bake_panorama(RID p_env, bool p_bake_irradiance, const Size2i &p_size) = 0;
 
-	virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) = 0;
+	virtual void screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) = 0;
 
 	enum SubSurfaceScatteringQuality {
 		SUB_SURFACE_SCATTERING_QUALITY_DISABLED,
@@ -1207,6 +1270,8 @@ public:
 	virtual RID get_test_texture();
 	virtual RID get_white_texture();
 
+	virtual void sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) = 0;
+
 	virtual RID make_sphere_mesh(int p_lats, int p_lons, float p_radius);
 
 	virtual void mesh_add_surface_from_mesh_data(RID p_mesh, const Geometry3D::MeshData &p_mesh_data);
@@ -1248,10 +1313,12 @@ VARIANT_ENUM_CAST(RenderingServer::BlendShapeMode);
 VARIANT_ENUM_CAST(RenderingServer::MultimeshTransformFormat);
 VARIANT_ENUM_CAST(RenderingServer::LightType);
 VARIANT_ENUM_CAST(RenderingServer::LightParam);
+VARIANT_ENUM_CAST(RenderingServer::LightBakeMode);
 VARIANT_ENUM_CAST(RenderingServer::LightOmniShadowMode);
 VARIANT_ENUM_CAST(RenderingServer::LightDirectionalShadowMode);
 VARIANT_ENUM_CAST(RenderingServer::LightDirectionalShadowDepthRangeMode);
 VARIANT_ENUM_CAST(RenderingServer::ReflectionProbeUpdateMode);
+VARIANT_ENUM_CAST(RenderingServer::ReflectionProbeAmbientMode);
 VARIANT_ENUM_CAST(RenderingServer::DecalTexture);
 VARIANT_ENUM_CAST(RenderingServer::ParticlesDrawOrder);
 VARIANT_ENUM_CAST(RenderingServer::ViewportUpdateMode);

Some files were not shown because too many files changed in this diff