diff options
Diffstat (limited to 'servers/rendering/rasterizer_rd')
22 files changed, 8013 insertions, 875 deletions
diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp index 303cb7ad42..0c036b886c 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.cpp +++ b/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(©_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, ©_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; diff --git a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h b/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h index 8a55d2d13c..80849654de 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_effects_rd.h +++ b/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(); }; diff --git a/servers/rendering/rasterizer_rd/rasterizer_rd.h b/servers/rendering/rasterizer_rd/rasterizer_rd.h index cb53a531ac..cdcc6bfd73 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_rd.h +++ b/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; diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp index 7d351f249a..890ada019f 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.cpp @@ -268,8 +268,8 @@ void RasterizerSceneHighEndRD::ShaderData::set_code(const String &p_code) { blend_state_blend.attachments.push_back(blend_attachment); RD::PipelineColorBlendState blend_state_opaque = RD::PipelineColorBlendState::create_disabled(1); RD::PipelineColorBlendState blend_state_opaque_specular = RD::PipelineColorBlendState::create_disabled(2); - RD::PipelineColorBlendState blend_state_depth_normal = RD::PipelineColorBlendState::create_disabled(1); - RD::PipelineColorBlendState blend_state_depth_normal_roughness = RD::PipelineColorBlendState::create_disabled(2); + RD::PipelineColorBlendState blend_state_depth_normal_roughness = RD::PipelineColorBlendState::create_disabled(1); + RD::PipelineColorBlendState blend_state_depth_normal_roughness_giprobe = RD::PipelineColorBlendState::create_disabled(2); //update pipelines @@ -310,12 +310,12 @@ void RasterizerSceneHighEndRD::ShaderData::set_code(const String &p_code) { RD::PipelineDepthStencilState depth_stencil = depth_stencil_state; if (uses_alpha || uses_blend_alpha) { - if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_VCT_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { + if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { blend_state = blend_state_blend; if (depth_draw == DEPTH_DRAW_OPAQUE) { depth_stencil.enable_depth_write = false; //alpha does not draw depth } - } else if (uses_depth_pre_pass && (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL)) { + } else if (uses_depth_pre_pass && (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP || k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS || k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL)) { if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { //none, blend state contains nothing } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { @@ -328,20 +328,23 @@ void RasterizerSceneHighEndRD::ShaderData::set_code(const String &p_code) { continue; // do not use this version (will error if using it is attempted) } } else { - if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_VCT_COLOR_PASS || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { + if (k == SHADER_VERSION_COLOR_PASS || k == SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI || k == SHADER_VERSION_LIGHTMAP_COLOR_PASS) { blend_state = blend_state_opaque; } else if (k == SHADER_VERSION_DEPTH_PASS || k == SHADER_VERSION_DEPTH_PASS_DP) { //none, leave empty - } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL) { - blend_state = blend_state_depth_normal; } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS) { blend_state = blend_state_depth_normal_roughness; + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_GIPROBE) { + blend_state = blend_state_depth_normal_roughness_giprobe; } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL) { blend_state = RD::PipelineColorBlendState::create_disabled(5); //writes to normal and roughness in opaque way - + } else if (k == SHADER_VERSION_DEPTH_PASS_WITH_SDF) { + blend_state = RD::PipelineColorBlendState(); //no color targets for SDF } else { //specular write blend_state = blend_state_opaque_specular; + depth_stencil.enable_depth_test = false; + depth_stencil.enable_depth_write = false; } } @@ -607,7 +610,78 @@ void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::ensure_specular() { } } +void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::ensure_gi() { + if (!reflection_buffer.is_valid()) { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf.width = width; + tf.height = height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + + reflection_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + ambient_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + } +} + +void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::ensure_giprobe() { + if (!giprobe_buffer.is_valid()) { + RD::TextureFormat tf; + tf.format = RD::DATA_FORMAT_R8G8_UINT; + tf.width = width; + tf.height = height; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + + if (msaa != RS::VIEWPORT_MSAA_DISABLED) { + RD::TextureFormat tf_aa = tf; + tf_aa.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + tf_aa.samples = texture_samples; + giprobe_buffer_msaa = RD::get_singleton()->texture_create(tf_aa, RD::TextureView()); + } else { + tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + } + + tf.usage_bits |= RD::TEXTURE_USAGE_STORAGE_BIT; + + giprobe_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + + Vector<RID> fb; + if (msaa != RS::VIEWPORT_MSAA_DISABLED) { + fb.push_back(depth_msaa); + fb.push_back(normal_roughness_buffer_msaa); + fb.push_back(giprobe_buffer_msaa); + } else { + fb.push_back(depth); + fb.push_back(normal_roughness_buffer); + fb.push_back(giprobe_buffer); + } + + depth_normal_roughness_giprobe_fb = RD::get_singleton()->framebuffer_create(fb); + } +} + void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::clear() { + if (ambient_buffer != RID() && ambient_buffer != color) { + RD::get_singleton()->free(ambient_buffer); + ambient_buffer = RID(); + } + + if (reflection_buffer != RID() && reflection_buffer != specular) { + RD::get_singleton()->free(reflection_buffer); + reflection_buffer = RID(); + } + + if (giprobe_buffer != RID()) { + RD::get_singleton()->free(giprobe_buffer); + giprobe_buffer = RID(); + + if (giprobe_buffer_msaa.is_valid()) { + RD::get_singleton()->free(giprobe_buffer_msaa); + giprobe_buffer_msaa = RID(); + } + + depth_normal_roughness_giprobe_fb = RID(); + } + if (color_msaa.is_valid()) { RD::get_singleton()->free(color_msaa); color_msaa = RID(); @@ -634,24 +708,18 @@ void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::clear() { color_fb = RID(); depth_fb = RID(); - if (normal_buffer.is_valid()) { - RD::get_singleton()->free(normal_buffer); - if (normal_buffer_msaa.is_valid()) { - RD::get_singleton()->free(normal_buffer_msaa); - normal_buffer_msaa = RID(); + if (normal_roughness_buffer.is_valid()) { + RD::get_singleton()->free(normal_roughness_buffer); + if (normal_roughness_buffer_msaa.is_valid()) { + RD::get_singleton()->free(normal_roughness_buffer_msaa); + normal_roughness_buffer_msaa = RID(); } - normal_buffer = RID(); - depth_normal_fb = RID(); + normal_roughness_buffer = RID(); + depth_normal_roughness_fb = RID(); } - if (roughness_buffer.is_valid()) { - RD::get_singleton()->free(roughness_buffer); - if (roughness_buffer_msaa.is_valid()) { - RD::get_singleton()->free(roughness_buffer_msaa); - roughness_buffer_msaa = RID(); - } - roughness_buffer = RID(); - depth_normal_roughness_fb = RID(); + if (!render_sdfgi_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_sdfgi_uniform_set)) { + RD::get_singleton()->free(render_sdfgi_uniform_set); } } @@ -686,7 +754,7 @@ void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::configure(RID p_color_bu tf.width = p_width; tf.height = p_height; tf.type = RD::TEXTURE_TYPE_2D; - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; RD::TextureSamples ts[RS::VIEWPORT_MSAA_MAX] = { RD::TEXTURE_SAMPLES_1, @@ -702,7 +770,7 @@ void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::configure(RID p_color_bu color_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; - tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + tf.usage_bits = RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; depth_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); @@ -722,80 +790,38 @@ void RasterizerSceneHighEndRD::RenderBufferDataHighEnd::configure(RID p_color_bu } } -void RasterizerSceneHighEndRD::_allocate_normal_texture(RenderBufferDataHighEnd *rb) { - if (rb->normal_buffer.is_valid()) { +void RasterizerSceneHighEndRD::_allocate_normal_roughness_texture(RenderBufferDataHighEnd *rb) { + if (rb->normal_roughness_buffer.is_valid()) { return; } RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_A2B10G10R10_UNORM_PACK32; + tf.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; tf.width = rb->width; tf.height = rb->height; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; - } else { - tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - } - - rb->normal_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - if (rb->msaa == RS::VIEWPORT_MSAA_DISABLED) { - Vector<RID> fb; - fb.push_back(rb->depth); - fb.push_back(rb->normal_buffer); - rb->depth_normal_fb = RD::get_singleton()->framebuffer_create(fb); - } else { - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; - tf.samples = rb->texture_samples; - rb->normal_buffer_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - Vector<RID> fb; - fb.push_back(rb->depth_msaa); - fb.push_back(rb->normal_buffer_msaa); - rb->depth_normal_fb = RD::get_singleton()->framebuffer_create(fb); - } - - _render_buffers_clear_uniform_set(rb); -} - -void RasterizerSceneHighEndRD::_allocate_roughness_texture(RenderBufferDataHighEnd *rb) { - if (rb->roughness_buffer.is_valid()) { - return; - } - - ERR_FAIL_COND(rb->normal_buffer.is_null()); - - RD::TextureFormat tf; - tf.format = RD::DATA_FORMAT_R8_UNORM; - tf.width = rb->width; - tf.height = rb->height; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; - - if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; } else { tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; } - rb->roughness_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); + rb->normal_roughness_buffer = RD::get_singleton()->texture_create(tf, RD::TextureView()); if (rb->msaa == RS::VIEWPORT_MSAA_DISABLED) { Vector<RID> fb; fb.push_back(rb->depth); - fb.push_back(rb->normal_buffer); - fb.push_back(rb->roughness_buffer); + fb.push_back(rb->normal_roughness_buffer); rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb); } else { - tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT; tf.samples = rb->texture_samples; - rb->roughness_buffer_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); + rb->normal_roughness_buffer_msaa = RD::get_singleton()->texture_create(tf, RD::TextureView()); Vector<RID> fb; fb.push_back(rb->depth_msaa); - fb.push_back(rb->normal_buffer_msaa); - fb.push_back(rb->roughness_buffer_msaa); + fb.push_back(rb->normal_roughness_buffer_msaa); rb->depth_normal_roughness_fb = RD::get_singleton()->framebuffer_create(fb); } @@ -813,7 +839,7 @@ bool RasterizerSceneHighEndRD::free(RID p_rid) { return false; } -void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth) { +void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, int p_element_count, bool p_for_depth, bool p_has_sdfgi, bool p_has_opaque_gi) { uint32_t lightmap_captures_used = 0; for (int i = 0; i < p_element_count; i++) { @@ -855,37 +881,7 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, continue; } - if (!e->instance->gi_probe_instances.empty()) { - uint32_t written = 0; - for (int j = 0; j < e->instance->gi_probe_instances.size(); j++) { - RID probe = e->instance->gi_probe_instances[j]; - int slot = gi_probe_instance_get_slot(probe); - if (slot < 0) { - continue; //unallocated, dont render - } - - if (render_pass != gi_probe_instance_get_render_pass(probe)) { - continue; //not rendered in this frame - } - - uint32_t index = gi_probe_instance_get_render_index(probe); - - if (written == 0) { - id.gi_offset = index; - id.flags |= INSTANCE_DATA_FLAG_USE_GIPROBE; - written = 1; - } else { - id.gi_offset = index << 16; - written = 2; - break; - } - } - if (written == 0) { - id.gi_offset = 0xFFFFFFFF; - } else if (written == 1) { - id.gi_offset |= 0xFFFF0000; - } - } else if (e->instance->lightmap) { + if (e->instance->lightmap) { int32_t lightmap_index = storage->lightmap_get_array_index(e->instance->lightmap->base); if (lightmap_index >= 0) { id.gi_offset = lightmap_index; @@ -916,8 +912,40 @@ void RasterizerSceneHighEndRD::_fill_instances(RenderList::Element **p_elements, id.gi_offset = lightmap_captures_used; lightmap_captures_used++; } + } else { - id.gi_offset = 0xFFFFFFFF; + if (p_has_opaque_gi) { + id.flags |= INSTANCE_DATA_FLAG_USE_GI_BUFFERS; + } + + if (!e->instance->gi_probe_instances.empty()) { + uint32_t written = 0; + for (int j = 0; j < e->instance->gi_probe_instances.size(); j++) { + RID probe = e->instance->gi_probe_instances[j]; + + uint32_t index = gi_probe_instance_get_render_index(probe); + + if (written == 0) { + id.gi_offset = index; + id.flags |= INSTANCE_DATA_FLAG_USE_GIPROBE; + written = 1; + } else { + id.gi_offset = index << 16; + written = 2; + break; + } + } + if (written == 0) { + id.gi_offset = 0xFFFFFFFF; + } else if (written == 1) { + id.gi_offset |= 0xFFFF0000; + } + } else { + if (p_has_sdfgi && (e->instance->baked_light || e->instance->dynamic_gi)) { + id.flags |= INSTANCE_DATA_FLAG_USE_SDFGI; + } + id.gi_offset = 0xFFFFFFFF; + } } } @@ -970,7 +998,7 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l //find cull variant ShaderData::CullVariant cull_variant; - if (p_pass_mode == PASS_MODE_DEPTH_MATERIAL || ((p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) && e->instance->cast_shadows == RS::SHADOW_CASTING_SETTING_DOUBLE_SIDED)) { + if (p_pass_mode == PASS_MODE_DEPTH_MATERIAL || p_pass_mode == PASS_MODE_SDF || ((p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_SHADOW_DP) && e->instance->cast_shadows == RS::SHADOW_CASTING_SETTING_DOUBLE_SIDED)) { cull_variant = ShaderData::CULL_VARIANT_DOUBLE_SIDED; } else { bool mirror = e->instance->mirror; @@ -1016,8 +1044,8 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l case PASS_MODE_COLOR_TRANSPARENT: { if (e->uses_lightmap) { shader_version = SHADER_VERSION_LIGHTMAP_COLOR_PASS; - } else if (e->uses_vct) { - shader_version = SHADER_VERSION_VCT_COLOR_PASS; + } else if (e->uses_forward_gi) { + shader_version = SHADER_VERSION_COLOR_PASS_WITH_FORWARD_GI; } else { shader_version = SHADER_VERSION_COLOR_PASS; } @@ -1025,8 +1053,6 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l case PASS_MODE_COLOR_SPECULAR: { if (e->uses_lightmap) { shader_version = SHADER_VERSION_LIGHTMAP_COLOR_PASS_WITH_SEPARATE_SPECULAR; - } else if (e->uses_vct) { - shader_version = SHADER_VERSION_VCT_COLOR_PASS_WITH_SEPARATE_SPECULAR; } else { shader_version = SHADER_VERSION_COLOR_PASS_WITH_SEPARATE_SPECULAR; } @@ -1038,15 +1064,18 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l case PASS_MODE_SHADOW_DP: { shader_version = SHADER_VERSION_DEPTH_PASS_DP; } break; - case PASS_MODE_DEPTH_NORMAL: { - shader_version = SHADER_VERSION_DEPTH_PASS_WITH_NORMAL; - } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { shader_version = SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS; } break; + case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE: { + shader_version = SHADER_VERSION_DEPTH_PASS_WITH_NORMAL_AND_ROUGHNESS_AND_GIPROBE; + } break; case PASS_MODE_DEPTH_MATERIAL: { shader_version = SHADER_VERSION_DEPTH_PASS_WITH_MATERIAL; } break; + case PASS_MODE_SDF: { + shader_version = SHADER_VERSION_DEPTH_PASS_WITH_SDF; + } break; } RenderPipelineVertexFormatCacheRD *pipeline = nullptr; @@ -1134,7 +1163,7 @@ void RasterizerSceneHighEndRD::_render_list(RenderingDevice::DrawListID p_draw_l } } -void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const CameraMatrix &p_cam_projection, const Transform &p_cam_transform, RID p_reflection_probe, bool p_no_fog, const Size2 &p_screen_pixel_size, RID p_shadow_atlas, bool p_flip_y, const Color &p_default_bg_color, float p_znear, float p_zfar, bool p_opaque_render_buffers, bool p_pancake_shadows) { +void RasterizerSceneHighEndRD::_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, bool p_pancake_shadows) { //CameraMatrix projection = p_cam_projection; //projection.flip_y(); // Vulkan and modern APIs use Y-Down CameraMatrix correction; @@ -1178,6 +1207,70 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const Camer //time global variables scene_state.ubo.time = time; + scene_state.ubo.gi_upscale_for_msaa = false; + + if (p_render_buffers.is_valid()) { + RenderBufferDataHighEnd *render_buffers = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + if (render_buffers->msaa != RS::VIEWPORT_MSAA_DISABLED) { + scene_state.ubo.gi_upscale_for_msaa = true; + } + } +#if 0 + if (p_render_buffers.is_valid() && render_buffers_is_sdfgi_enabled(p_render_buffers)) { + + scene_state.ubo.sdfgi_cascade_count = render_buffers_get_sdfgi_cascade_count(p_render_buffers); + scene_state.ubo.sdfgi_probe_axis_size = render_buffers_get_sdfgi_cascade_probe_count(p_render_buffers); + scene_state.ubo.sdfgi_cascade_probe_size[0] = scene_state.ubo.sdfgi_probe_axis_size - 1; //float version for performance + scene_state.ubo.sdfgi_cascade_probe_size[1] = scene_state.ubo.sdfgi_probe_axis_size - 1; + scene_state.ubo.sdfgi_cascade_probe_size[2] = scene_state.ubo.sdfgi_probe_axis_size - 1; + + float csize = render_buffers_get_sdfgi_cascade_size(p_render_buffers); + scene_state.ubo.sdfgi_probe_to_uvw = 1.0 / float(scene_state.ubo.sdfgi_cascade_probe_size[0]); + float occ_bias = 0.0; + scene_state.ubo.sdfgi_occlusion_bias = occ_bias / csize; + scene_state.ubo.sdfgi_use_occlusion = render_buffers_is_sdfgi_using_occlusion(p_render_buffers); + scene_state.ubo.sdfgi_energy = render_buffers_get_sdfgi_energy(p_render_buffers); + + float cascade_voxel_size = (csize / scene_state.ubo.sdfgi_cascade_probe_size[0]); + float occlusion_clamp = (cascade_voxel_size - 0.5) / cascade_voxel_size; + scene_state.ubo.sdfgi_occlusion_clamp[0] = occlusion_clamp; + scene_state.ubo.sdfgi_occlusion_clamp[1] = occlusion_clamp; + scene_state.ubo.sdfgi_occlusion_clamp[2] = occlusion_clamp; + scene_state.ubo.sdfgi_normal_bias = (render_buffers_get_sdfgi_normal_bias(p_render_buffers) / csize) * scene_state.ubo.sdfgi_cascade_probe_size[0]; + + //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; + + uint32_t oct_size = sdfgi_get_lightprobe_octahedron_size(); + + scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[0] = 1.0 / ((oct_size + 2) * scene_state.ubo.sdfgi_probe_axis_size * scene_state.ubo.sdfgi_probe_axis_size); + scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[1] = 1.0 / ((oct_size + 2) * scene_state.ubo.sdfgi_probe_axis_size); + scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[2] = 1.0; + + scene_state.ubo.sdfgi_probe_uv_offset[0] = float(oct_size + 2) * scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[0]; + scene_state.ubo.sdfgi_probe_uv_offset[1] = float(oct_size + 2) * scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[1]; + scene_state.ubo.sdfgi_probe_uv_offset[2] = float((oct_size + 2) * scene_state.ubo.sdfgi_probe_axis_size) * scene_state.ubo.sdfgi_lightprobe_tex_pixel_size[0]; + + scene_state.ubo.sdfgi_occlusion_renormalize[0] = 0.5; + scene_state.ubo.sdfgi_occlusion_renormalize[1] = 1.0; + scene_state.ubo.sdfgi_occlusion_renormalize[2] = 1.0 / float(scene_state.ubo.sdfgi_cascade_count); + + for (uint32_t i = 0; i < scene_state.ubo.sdfgi_cascade_count; i++) { + SceneState::UBO::SDFGICascade &c = scene_state.ubo.sdfgi_cascades[i]; + Vector3 pos = render_buffers_get_sdfgi_cascade_offset(p_render_buffers, i); + pos -= p_cam_transform.origin; //make pos local to camera, to reduce numerical error + c.position[0] = pos.x; + c.position[1] = pos.y; + c.position[2] = pos.z; + c.to_probe = 1.0 / render_buffers_get_sdfgi_cascade_probe_size(p_render_buffers, i); + + Vector3i probe_ofs = render_buffers_get_sdfgi_cascade_probe_offset(p_render_buffers, i); + c.probe_world_offset[0] = probe_ofs.x; + c.probe_world_offset[1] = probe_ofs.y; + c.probe_world_offset[2] = probe_ofs.z; + } + } +#endif if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_UNSHADED) { scene_state.ubo.use_ambient_light = true; scene_state.ubo.ambient_light_color_energy[0] = 1; @@ -1260,11 +1353,13 @@ void RasterizerSceneHighEndRD::_setup_environment(RID p_environment, const Camer } scene_state.ubo.roughness_limiter_enabled = p_opaque_render_buffers && screen_space_roughness_limiter_is_active(); + scene_state.ubo.roughness_limiter_amount = screen_space_roughness_limiter_get_amount(); + scene_state.ubo.roughness_limiter_limit = screen_space_roughness_limiter_get_limit(); RD::get_singleton()->buffer_update(scene_state.uniform_buffer, 0, sizeof(SceneState::UBO), &scene_state.ubo, true); } -void RasterizerSceneHighEndRD::_add_geometry(InstanceBase *p_instance, uint32_t p_surface, RID p_material, PassMode p_pass_mode, uint32_t p_geometry_index) { +void RasterizerSceneHighEndRD::_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) { RID m_src; m_src = p_instance->material_override.is_valid() ? p_instance->material_override : p_material; @@ -1293,18 +1388,18 @@ void RasterizerSceneHighEndRD::_add_geometry(InstanceBase *p_instance, uint32_t ERR_FAIL_COND(!material); - _add_geometry_with_material(p_instance, p_surface, material, m_src, p_pass_mode, p_geometry_index); + _add_geometry_with_material(p_instance, p_surface, material, m_src, p_pass_mode, p_geometry_index, p_using_sdfgi); while (material->next_pass.is_valid()) { material = (MaterialData *)storage->material_get_data(material->next_pass, RasterizerStorageRD::SHADER_TYPE_3D); if (!material || !material->shader_data->valid) { break; + _add_geometry_with_material(p_instance, p_surface, material, material->next_pass, p_pass_mode, p_geometry_index, p_using_sdfgi); } - _add_geometry_with_material(p_instance, p_surface, material, material->next_pass, p_pass_mode, p_geometry_index); } } -void RasterizerSceneHighEndRD::_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) { +void RasterizerSceneHighEndRD::_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) { bool has_read_screen_alpha = p_material->shader_data->uses_screen_texture || p_material->shader_data->uses_depth_texture || p_material->shader_data->uses_normal_texture; bool has_base_alpha = (p_material->shader_data->uses_alpha || has_read_screen_alpha); bool has_blend_alpha = p_material->shader_data->uses_blend_alpha; @@ -1332,13 +1427,11 @@ void RasterizerSceneHighEndRD::_add_geometry_with_material(InstanceBase *p_insta return; } - if (p_pass_mode != PASS_MODE_DEPTH_MATERIAL && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass) { + if ((p_pass_mode != PASS_MODE_DEPTH_MATERIAL && p_pass_mode != PASS_MODE_SDF) && !p_material->shader_data->writes_modelview_or_projection && !p_material->shader_data->uses_vertex && !p_material->shader_data->uses_discard && !p_material->shader_data->uses_depth_pre_pass) { //shader does not use discard and does not write a vertex position, use generic material if (p_pass_mode == PASS_MODE_SHADOW || p_pass_mode == PASS_MODE_DEPTH) { p_material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); - } else if (p_pass_mode == PASS_MODE_DEPTH_NORMAL && !p_material->shader_data->uses_normal) { - p_material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); - } else if (p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS && !p_material->shader_data->uses_normal && !p_material->shader_data->uses_roughness) { + } else if ((p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || p_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE) && !p_material->shader_data->uses_normal && !p_material->shader_data->uses_roughness) { p_material = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); } } @@ -1346,7 +1439,9 @@ void RasterizerSceneHighEndRD::_add_geometry_with_material(InstanceBase *p_insta has_alpha = false; } - RenderList::Element *e = (has_alpha || p_material->shader_data->depth_test == ShaderData::DEPTH_TEST_DISABLED) ? render_list.add_alpha_element() : render_list.add_element(); + has_alpha = has_alpha || p_material->shader_data->depth_test == ShaderData::DEPTH_TEST_DISABLED; + + RenderList::Element *e = has_alpha ? render_list.add_alpha_element() : render_list.add_element(); if (!e) { return; @@ -1373,7 +1468,7 @@ void RasterizerSceneHighEndRD::_add_geometry_with_material(InstanceBase *p_insta e->material_index = e->material->index; e->uses_instancing = e->instance->base_type == RS::INSTANCE_MULTIMESH; e->uses_lightmap = e->instance->lightmap != nullptr || !e->instance->lightmap_sh.empty(); - e->uses_vct = e->instance->gi_probe_instances.size(); + e->uses_forward_gi = has_alpha && (e->instance->gi_probe_instances.size() || p_using_sdfgi); e->shader_index = e->shader_index; e->depth_layer = e->instance->depth_layer; e->priority = p_material->priority; @@ -1383,7 +1478,7 @@ void RasterizerSceneHighEndRD::_add_geometry_with_material(InstanceBase *p_insta } } -void RasterizerSceneHighEndRD::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_no_gi) { +void RasterizerSceneHighEndRD::_fill_render_list(InstanceBase **p_cull_result, int p_cull_count, PassMode p_pass_mode, bool p_using_sdfgi) { scene_state.current_shader_index = 0; scene_state.current_material_index = 0; scene_state.used_sss = false; @@ -1415,7 +1510,7 @@ void RasterizerSceneHighEndRD::_fill_render_list(InstanceBase **p_cull_result, i RID material = inst_materials[j].is_valid() ? inst_materials[j] : materials[j]; uint32_t surface_index = storage->mesh_surface_get_render_pass_index(inst->base, j, render_pass, &geometry_index); - _add_geometry(inst, j, material, p_pass_mode, surface_index); + _add_geometry(inst, j, material, p_pass_mode, surface_index, p_using_sdfgi); } //mesh->last_pass=frame; @@ -1443,7 +1538,7 @@ void RasterizerSceneHighEndRD::_fill_render_list(InstanceBase **p_cull_result, i for (uint32_t j = 0; j < surface_count; j++) { uint32_t surface_index = storage->mesh_surface_get_multimesh_render_pass_index(mesh, j, render_pass, &geometry_index); - _add_geometry(inst, j, materials[j], p_pass_mode, surface_index); + _add_geometry(inst, j, materials[j], p_pass_mode, surface_index, p_using_sdfgi); } } break; @@ -1525,30 +1620,13 @@ void RasterizerSceneHighEndRD::_setup_reflections(RID *p_reflection_probe_cull_r reflection_ubo.params[2] = interior ? 1.0 : 0.0; reflection_ubo.params[3] = box_projection ? 1.0 : 0.0; - if (interior) { - Color ambient_linear = storage->reflection_probe_get_interior_ambient(base_probe).to_linear(); - float interior_ambient_energy = storage->reflection_probe_get_interior_ambient_energy(base_probe); - float interior_ambient_probe_contrib = storage->reflection_probe_get_interior_ambient_probe_contribution(base_probe); - reflection_ubo.ambient[0] = ambient_linear.r * interior_ambient_energy; - reflection_ubo.ambient[1] = ambient_linear.g * interior_ambient_energy; - reflection_ubo.ambient[2] = ambient_linear.b * interior_ambient_energy; - reflection_ubo.ambient[3] = interior_ambient_probe_contrib; - } else { - Color ambient_linear = storage->reflection_probe_get_interior_ambient(base_probe).to_linear(); - if (is_environment(p_environment)) { - Color env_ambient_color = environment_get_ambient_light_color(p_environment).to_linear(); - float env_ambient_energy = environment_get_ambient_light_energy(p_environment); - ambient_linear = env_ambient_color; - ambient_linear.r *= env_ambient_energy; - ambient_linear.g *= env_ambient_energy; - ambient_linear.b *= env_ambient_energy; - } - - reflection_ubo.ambient[0] = ambient_linear.r; - reflection_ubo.ambient[1] = ambient_linear.g; - reflection_ubo.ambient[2] = ambient_linear.b; - reflection_ubo.ambient[3] = 0; //not used in exterior mode, since it just blends with regular ambient light - } + Color ambient_linear = storage->reflection_probe_get_ambient_color(base_probe).to_linear(); + float interior_ambient_energy = storage->reflection_probe_get_ambient_color_energy(base_probe); + uint32_t ambient_mode = storage->reflection_probe_get_ambient_mode(base_probe); + reflection_ubo.ambient[0] = ambient_linear.r * interior_ambient_energy; + reflection_ubo.ambient[1] = ambient_linear.g * interior_ambient_energy; + reflection_ubo.ambient[2] = ambient_linear.b * interior_ambient_energy; + reflection_ubo.ambient_mode = ambient_mode; Transform transform = reflection_probe_instance_get_transform(rpi); Transform proj = (p_camera_inverse_transform * transform).inverse(); @@ -1583,59 +1661,6 @@ void RasterizerSceneHighEndRD::_setup_lightmaps(InstanceBase **p_lightmap_cull_r } } -void RasterizerSceneHighEndRD::_setup_gi_probes(RID *p_gi_probe_probe_cull_result, int p_gi_probe_probe_cull_count, const Transform &p_camera_transform) { - int index = 0; - - for (int i = 0; i < p_gi_probe_probe_cull_count; i++) { - RID rpi = p_gi_probe_probe_cull_result[i]; - - if (index >= (int)scene_state.max_gi_probes) { - continue; - } - - int slot = gi_probe_instance_get_slot(rpi); - if (slot < 0) { - continue; //not usable - } - - RID base_probe = gi_probe_instance_get_base_probe(rpi); - - GIProbeData &gi_probe_ubo = scene_state.gi_probes[index]; - - Transform to_cell = gi_probe_instance_get_transform_to_cell(rpi) * p_camera_transform; - - store_transform(to_cell, gi_probe_ubo.xform); - - Vector3 bounds = storage->gi_probe_get_octree_size(base_probe); - - gi_probe_ubo.bounds[0] = bounds.x; - gi_probe_ubo.bounds[1] = bounds.y; - gi_probe_ubo.bounds[2] = bounds.z; - - gi_probe_ubo.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe); - gi_probe_ubo.bias = storage->gi_probe_get_bias(base_probe); - gi_probe_ubo.normal_bias = storage->gi_probe_get_normal_bias(base_probe); - gi_probe_ubo.blend_ambient = !storage->gi_probe_is_interior(base_probe); - gi_probe_ubo.texture_slot = gi_probe_instance_get_slot(rpi); - gi_probe_ubo.anisotropy_strength = storage->gi_probe_get_anisotropy_strength(base_probe); - gi_probe_ubo.ao = storage->gi_probe_get_ao(base_probe); - gi_probe_ubo.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f); - - if (gi_probe_is_anisotropic()) { - gi_probe_ubo.texture_slot *= 3; - } - - gi_probe_instance_set_render_index(rpi, index); - gi_probe_instance_set_render_pass(rpi, render_pass); - - index++; - } - - if (index) { - RD::get_singleton()->buffer_update(scene_state.gi_probe_buffer, 0, index * sizeof(GIProbeData), scene_state.gi_probes, true); - } -} - void RasterizerSceneHighEndRD::_setup_lights(RID *p_light_cull_result, int p_light_cull_count, const Transform &p_camera_inverse_transform, RID p_shadow_atlas, bool p_using_shadows) { uint32_t light_count = 0; scene_state.ubo.directional_light_count = 0; @@ -2156,6 +2181,8 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor Vector<Color> depth_pass_clear; bool using_separate_specular = false; bool using_ssr = false; + bool using_sdfgi = false; + bool using_giprobe = false; if (render_buffer) { screen_pixel_size.width = 1.0 / render_buffer->width; @@ -2165,43 +2192,55 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor opaque_framebuffer = render_buffer->color_fb; - if (p_environment.is_valid() && environment_is_ssr_enabled(p_environment)) { - depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; - render_buffer->ensure_specular(); - using_separate_specular = true; - using_ssr = true; - opaque_specular_framebuffer = render_buffer->color_specular_fb; - } else if (screen_space_roughness_limiter_is_active()) { - depth_pass_mode = PASS_MODE_DEPTH_NORMAL; - //we need to allocate both these, if not allocated - _allocate_normal_texture(render_buffer); - _allocate_roughness_texture(render_buffer); + if (p_gi_probe_cull_count > 0) { + using_giprobe = true; + render_buffer->ensure_gi(); + } + + if (!p_environment.is_valid() && using_giprobe) { + depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE; + + } else if (p_environment.is_valid() && (environment_is_ssr_enabled(p_environment) || environment_is_sdfgi_enabled(p_environment) || using_giprobe)) { + if (environment_is_sdfgi_enabled(p_environment)) { + depth_pass_mode = using_giprobe ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE : PASS_MODE_DEPTH_NORMAL_ROUGHNESS; // also giprobe + using_sdfgi = true; + render_buffer->ensure_gi(); + } else { + depth_pass_mode = using_giprobe ? PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE : PASS_MODE_DEPTH_NORMAL_ROUGHNESS; + } + + if (environment_is_ssr_enabled(p_environment)) { + render_buffer->ensure_specular(); + using_separate_specular = true; + using_ssr = true; + opaque_specular_framebuffer = render_buffer->color_specular_fb; + } + } else if (p_environment.is_valid() && (environment_is_ssao_enabled(p_environment) || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER)) { - depth_pass_mode = PASS_MODE_DEPTH_NORMAL; + depth_pass_mode = PASS_MODE_DEPTH_NORMAL_ROUGHNESS; } switch (depth_pass_mode) { case PASS_MODE_DEPTH: { depth_framebuffer = render_buffer->depth_fb; } break; - case PASS_MODE_DEPTH_NORMAL: { - _allocate_normal_texture(render_buffer); - depth_framebuffer = render_buffer->depth_normal_fb; - depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); - } break; case PASS_MODE_DEPTH_NORMAL_ROUGHNESS: { - _allocate_normal_texture(render_buffer); - _allocate_roughness_texture(render_buffer); + _allocate_normal_roughness_texture(render_buffer); depth_framebuffer = render_buffer->depth_normal_roughness_fb; depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); - depth_pass_clear.push_back(Color()); + } break; + case PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE: { + _allocate_normal_roughness_texture(render_buffer); + render_buffer->ensure_giprobe(); + depth_framebuffer = render_buffer->depth_normal_roughness_giprobe_fb; + depth_pass_clear.push_back(Color(0.5, 0.5, 0.5, 0)); + depth_pass_clear.push_back(Color(0, 0, 0, 0)); } break; default: { }; } alpha_framebuffer = opaque_framebuffer; - } else if (p_reflection_probe.is_valid()) { uint32_t resolution = reflection_probe_instance_get_resolution(p_reflection_probe); screen_pixel_size.width = 1.0 / resolution; @@ -2216,7 +2255,6 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor if (storage->reflection_probe_is_interior(reflection_probe_instance_get_probe(p_reflection_probe))) { p_environment = RID(); //no environment on interiors } - } else { ERR_FAIL(); //bug? } @@ -2226,16 +2264,15 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor _setup_lights(p_light_cull_result, p_light_cull_count, p_cam_transform.affine_inverse(), p_shadow_atlas, using_shadows); _setup_decals(p_decal_cull_result, p_decal_cull_count, p_cam_transform.affine_inverse()); _setup_reflections(p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_cam_transform.affine_inverse(), p_environment); - _setup_gi_probes(p_gi_probe_cull_result, p_gi_probe_cull_count, p_cam_transform); _setup_lightmaps(p_lightmap_cull_result, p_lightmap_cull_count, p_cam_transform); - _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); + _setup_environment(p_environment, p_render_buffer, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); cluster_builder.bake_cluster(); //bake to cluster _update_render_base_uniform_set(); //may have changed due to the above (light buffer enlarged, as an example) render_list.clear(); - _fill_render_list(p_cull_result, p_cull_count, PASS_MODE_COLOR, render_buffer == nullptr); + _fill_render_list(p_cull_result, p_cull_count, PASS_MODE_COLOR, using_sdfgi); bool using_sss = render_buffer && scene_state.used_sss && sub_surface_scattering_get_quality() != RS::SUB_SURFACE_SCATTERING_QUALITY_DISABLED; @@ -2307,47 +2344,46 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor clear_color = p_default_bg_color; } - _setup_view_dependant_uniform_set(p_shadow_atlas, p_reflection_atlas); + _setup_view_dependant_uniform_set(p_shadow_atlas, p_reflection_atlas, p_gi_probe_cull_result, p_gi_probe_cull_count); render_list.sort_by_key(false); - _fill_instances(render_list.elements, render_list.element_count, false); + _fill_instances(render_list.elements, render_list.element_count, false, false, using_sdfgi || using_giprobe); bool debug_giprobes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_ALBEDO || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_LIGHTING || get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_GI_PROBE_EMISSION; + bool debug_sdfgi_probes = get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_SDFGI_PROBES; bool depth_pre_pass = depth_framebuffer.is_valid(); RID render_buffers_uniform_set; bool using_ssao = depth_pre_pass && p_render_buffer.is_valid() && p_environment.is_valid() && environment_is_ssao_enabled(p_environment); - + bool continue_depth = false; if (depth_pre_pass) { //depth pre pass RENDER_TIMESTAMP("Render Depth Pre-Pass"); - bool finish_depth = using_ssao; + bool finish_depth = using_ssao || using_sdfgi || using_giprobe; RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, depth_pass_clear); _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(depth_framebuffer), render_list.elements, render_list.element_count, false, depth_pass_mode, render_buffer == nullptr, radiance_uniform_set, RID(), get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME); RD::get_singleton()->draw_list_end(); if (render_buffer && render_buffer->msaa != RS::VIEWPORT_MSAA_DISABLED) { - if (finish_depth) { + if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_GIPROBE) { + static int texture_samples[RS::VIEWPORT_MSAA_MAX] = { 1, 2, 4, 8, 16 }; + storage->get_effects()->resolve_gi(render_buffer->depth_msaa, render_buffer->normal_roughness_buffer_msaa, using_giprobe ? render_buffer->giprobe_buffer_msaa : RID(), render_buffer->depth, render_buffer->normal_roughness_buffer, using_giprobe ? render_buffer->giprobe_buffer : RID(), Vector2i(render_buffer->width, render_buffer->height), texture_samples[render_buffer->msaa]); + } else if (finish_depth) { RD::get_singleton()->texture_resolve_multisample(render_buffer->depth_msaa, render_buffer->depth, true); } - - if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->normal_buffer_msaa, render_buffer->normal_buffer, true); - if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS) { - RD::get_singleton()->texture_resolve_multisample(render_buffer->roughness_buffer_msaa, render_buffer->roughness_buffer, true); - } - } } + + continue_depth = !finish_depth; } if (using_ssao) { - _process_ssao(p_render_buffer, p_environment, render_buffer->normal_buffer, p_cam_projection); + _process_ssao(p_render_buffer, p_environment, render_buffer->normal_roughness_buffer, p_cam_projection); } - if (p_render_buffer.is_valid() && screen_space_roughness_limiter_is_active()) { - storage->get_effects()->roughness_limit(render_buffer->normal_buffer, render_buffer->roughness_buffer, Size2(render_buffer->width, render_buffer->height), screen_space_roughness_limiter_get_curve()); + if (using_sdfgi || using_giprobe) { + _process_gi(p_render_buffer, render_buffer->normal_roughness_buffer, render_buffer->ambient_buffer, render_buffer->reflection_buffer, render_buffer->giprobe_buffer, p_environment, p_cam_projection, p_cam_transform, p_gi_probe_cull_result, p_gi_probe_cull_count); } if (p_render_buffer.is_valid()) { @@ -2356,7 +2392,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor render_buffers_uniform_set = render_buffer->uniform_set; } - _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), p_render_buffer.is_valid()); + _setup_environment(p_environment, p_render_buffer, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), p_render_buffer.is_valid()); RENDER_TIMESTAMP("Render Opaque Pass"); @@ -2364,8 +2400,8 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor bool can_continue_depth = !scene_state.used_depth_texture && !using_ssr && !using_sss; { - bool will_continue_color = (can_continue_color || draw_sky || debug_giprobes); - bool will_continue_depth = (can_continue_depth || draw_sky || debug_giprobes); + bool will_continue_color = (can_continue_color || draw_sky || debug_giprobes || debug_sdfgi_probes); + bool will_continue_depth = (can_continue_depth || draw_sky || debug_giprobes || debug_sdfgi_probes); //regular forward for now Vector<Color> c; @@ -2379,7 +2415,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor } RID framebuffer = using_separate_specular ? opaque_specular_framebuffer : opaque_framebuffer; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? (using_ssao ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CONTINUE) : RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, depth_pre_pass ? (continue_depth ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CONTINUE) : RD::INITIAL_ACTION_CLEAR, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, c, 1.0, 0); _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(framebuffer), render_list.elements, render_list.element_count, false, using_separate_specular ? PASS_MODE_COLOR_SPECULAR : PASS_MODE_COLOR, render_buffer == nullptr, radiance_uniform_set, render_buffers_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME); RD::get_singleton()->draw_list_end(); @@ -2405,6 +2441,19 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor RD::get_singleton()->draw_list_end(); } + if (debug_sdfgi_probes) { + //debug giprobes + bool will_continue_color = (can_continue_color || draw_sky); + bool will_continue_depth = (can_continue_depth || draw_sky); + + CameraMatrix dc; + dc.set_depth_correction(true); + CameraMatrix cm = (dc * p_cam_projection) * CameraMatrix(p_cam_transform.affine_inverse()); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(opaque_framebuffer, RD::INITIAL_ACTION_CONTINUE, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); + _debug_sdfgi_probes(p_render_buffer, draw_list, opaque_framebuffer, cm); + RD::get_singleton()->draw_list_end(); + } + if (draw_sky) { RENDER_TIMESTAMP("Render Sky"); @@ -2437,7 +2486,7 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor if (using_ssr) { RENDER_TIMESTAMP("Screen Space Reflection"); - _process_ssr(p_render_buffer, render_buffer->color_fb, render_buffer->normal_buffer, render_buffer->roughness_buffer, render_buffer->specular, render_buffer->specular, Color(0, 0, 0, 1), p_environment, p_cam_projection, render_buffer->msaa == RS::VIEWPORT_MSAA_DISABLED); + _process_ssr(p_render_buffer, render_buffer->color_fb, render_buffer->normal_roughness_buffer, render_buffer->specular, render_buffer->specular, Color(0, 0, 0, 1), p_environment, p_cam_projection, render_buffer->msaa == RS::VIEWPORT_MSAA_DISABLED); } else { //just mix specular back RENDER_TIMESTAMP("Merge Specular"); @@ -2447,11 +2496,11 @@ void RasterizerSceneHighEndRD::_render_scene(RID p_render_buffer, const Transfor RENDER_TIMESTAMP("Render Transparent Pass"); - _setup_environment(p_environment, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); + _setup_environment(p_environment, p_render_buffer, p_cam_projection, p_cam_transform, p_reflection_probe, p_reflection_probe.is_valid(), screen_pixel_size, p_shadow_atlas, !p_reflection_probe.is_valid(), p_default_bg_color, p_cam_projection.get_z_near(), p_cam_projection.get_z_far(), false); render_list.sort_by_reverse_depth_and_priority(true); - _fill_instances(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false); + _fill_instances(&render_list.elements[render_list.max_elements - render_list.alpha_element_count], render_list.alpha_element_count, false, using_sdfgi); { RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); @@ -2473,15 +2522,15 @@ void RasterizerSceneHighEndRD::_render_shadow(RID p_framebuffer, InstanceBase ** scene_state.ubo.dual_paraboloid_side = p_use_dp_flip ? -1 : 1; - _setup_environment(RID(), p_projection, p_transform, RID(), true, Vector2(1, 1), RID(), true, Color(), 0, p_zfar, false, p_use_pancake); + _setup_environment(RID(), RID(), p_projection, p_transform, RID(), true, Vector2(1, 1), RID(), true, Color(), 0, p_zfar, false, p_use_pancake); render_list.clear(); PassMode pass_mode = p_use_dp ? PASS_MODE_SHADOW_DP : PASS_MODE_SHADOW; - _fill_render_list(p_cull_result, p_cull_count, pass_mode, true); + _fill_render_list(p_cull_result, p_cull_count, pass_mode); - _setup_view_dependant_uniform_set(RID(), RID()); + _setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0); RENDER_TIMESTAMP("Render Shadow"); @@ -2507,14 +2556,14 @@ void RasterizerSceneHighEndRD::_render_material(const Transform &p_cam_transform scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = true; - _setup_environment(RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0); + _setup_environment(RID(), RID(), p_cam_projection, p_cam_transform, RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0); render_list.clear(); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; - _fill_render_list(p_cull_result, p_cull_count, pass_mode, true); + _fill_render_list(p_cull_result, p_cull_count, pass_mode); - _setup_view_dependant_uniform_set(RID(), RID()); + _setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0); RENDER_TIMESTAMP("Render Material"); @@ -2546,14 +2595,14 @@ void RasterizerSceneHighEndRD::_render_uv2(InstanceBase **p_cull_result, int p_c scene_state.ubo.dual_paraboloid_side = 0; scene_state.ubo.material_uv2_mode = true; - _setup_environment(RID(), CameraMatrix(), Transform(), RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0); + _setup_environment(RID(), RID(), CameraMatrix(), Transform(), RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0); render_list.clear(); PassMode pass_mode = PASS_MODE_DEPTH_MATERIAL; - _fill_render_list(p_cull_result, p_cull_count, pass_mode, true); + _fill_render_list(p_cull_result, p_cull_count, pass_mode); - _setup_view_dependant_uniform_set(RID(), RID()); + _setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0); RENDER_TIMESTAMP("Render Material"); @@ -2597,6 +2646,121 @@ void RasterizerSceneHighEndRD::_render_uv2(InstanceBase **p_cull_result, int p_c } } +void RasterizerSceneHighEndRD::_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) { + RENDER_TIMESTAMP("Render SDFGI"); + + _update_render_base_uniform_set(); + + RenderBufferDataHighEnd *render_buffer = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + ERR_FAIL_COND(!render_buffer); + + render_pass++; + render_list.clear(); + + PassMode pass_mode = PASS_MODE_SDF; + _fill_render_list(p_cull_result, p_cull_count, pass_mode); + render_list.sort_by_key(false); + _fill_instances(render_list.elements, render_list.element_count, true); + + _setup_view_dependant_uniform_set(RID(), RID(), nullptr, 0); + + Vector3 half_extents = p_bounds.size * 0.5; + Vector3 center = p_bounds.position + half_extents; + + if (render_buffer->render_sdfgi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(render_buffer->render_sdfgi_uniform_set)) { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 0; + u.ids.push_back(p_albedo_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(p_emission_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(p_emission_aniso_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 3; + u.ids.push_back(p_geom_facing_texture); + uniforms.push_back(u); + } + + render_buffer->render_sdfgi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_sdfgi_rd, RENDER_BUFFERS_UNIFORM_SET); + } + + Vector<RID> sbs; + sbs.push_back(p_albedo_texture); + sbs.push_back(p_emission_texture); + sbs.push_back(p_emission_aniso_texture); + sbs.push_back(p_geom_facing_texture); + + //print_line("re-render " + p_from + " - " + p_size + " bounds " + p_bounds); + for (int i = 0; i < 3; i++) { + scene_state.ubo.sdf_offset[i] = p_from[i]; + scene_state.ubo.sdf_size[i] = p_size[i]; + } + + for (int i = 0; i < 3; i++) { + Vector3 axis; + axis[i] = 1.0; + Vector3 up, right; + int right_axis = (i + 1) % 3; + int up_axis = (i + 2) % 3; + up[up_axis] = 1.0; + right[right_axis] = 1.0; + + Size2i fb_size; + fb_size.x = p_size[right_axis]; + fb_size.y = p_size[up_axis]; + + Transform cam_xform; + cam_xform.origin = center + axis * half_extents; + cam_xform.basis.set_axis(0, right); + cam_xform.basis.set_axis(1, up); + cam_xform.basis.set_axis(2, axis); + + //print_line("pass: " + itos(i) + " xform " + cam_xform); + + float h_size = half_extents[right_axis]; + float v_size = half_extents[up_axis]; + float d_size = half_extents[i] * 2.0; + CameraMatrix camera_proj; + camera_proj.set_orthogonal(-h_size, h_size, -v_size, v_size, 0, d_size); + //print_line("pass: " + itos(i) + " cam hsize: " + rtos(h_size) + " vsize: " + rtos(v_size) + " dsize " + rtos(d_size)); + + Transform to_bounds; + to_bounds.origin = p_bounds.position; + to_bounds.basis.scale(p_bounds.size); + + store_transform(to_bounds.affine_inverse() * cam_xform, scene_state.ubo.sdf_to_bounds); + + _setup_environment(RID(), RID(), camera_proj, cam_xform, RID(), true, Vector2(1, 1), RID(), false, Color(), 0, 0); + + Map<Size2i, RID>::Element *E = sdfgi_framebuffer_size_cache.find(fb_size); + if (!E) { + RID fb = RD::get_singleton()->framebuffer_create_empty(fb_size); + E = sdfgi_framebuffer_size_cache.insert(fb_size, fb); + } + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(E->get(), RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2(), sbs); + _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(E->get()), render_list.elements, render_list.element_count, true, pass_mode, true, RID(), render_buffer->render_sdfgi_uniform_set, false); //second regular triangles + RD::get_singleton()->draw_list_end(); + } +} + void RasterizerSceneHighEndRD::_base_uniforms_changed() { if (!render_base_uniform_set.is_null() && RD::get_singleton()->uniform_set_is_valid(render_base_uniform_set)) { RD::get_singleton()->free(render_base_uniform_set); @@ -2682,49 +2846,6 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() { } { RD::Uniform u; - u.binding = 8; - u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; - u.ids.push_back(scene_state.gi_probe_buffer); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.binding = 9; - u.type = RD::UNIFORM_TYPE_TEXTURE; - int slot_count = gi_probe_get_slots().size(); - if (gi_probe_is_anisotropic()) { - u.ids.resize(slot_count * 3); - } else { - u.ids.resize(slot_count); - } - - for (int i = 0; i < slot_count; i++) { - RID probe = gi_probe_get_slots()[i]; - - if (gi_probe_is_anisotropic()) { - if (probe.is_null()) { - RID empty_tex = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); - u.ids.write[i * 3 + 0] = empty_tex; - u.ids.write[i * 3 + 1] = empty_tex; - u.ids.write[i * 3 + 2] = empty_tex; - } else { - u.ids.write[i * 3 + 0] = gi_probe_instance_get_texture(probe); - u.ids.write[i * 3 + 1] = gi_probe_instance_get_aniso_texture(probe, 0); - u.ids.write[i * 3 + 2] = gi_probe_instance_get_aniso_texture(probe, 1); - } - } else { - if (probe.is_null()) { - u.ids.write[i] = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); - } else { - u.ids.write[i] = gi_probe_instance_get_texture(probe); - } - } - } - - uniforms.push_back(u); - } - { - RD::Uniform u; u.binding = 10; u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.ids.push_back(scene_state.lightmap_buffer); @@ -2803,11 +2924,19 @@ void RasterizerSceneHighEndRD::_update_render_base_uniform_set() { uniforms.push_back(u); } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 20; + u.ids.push_back(sdfgi_get_ubo()); + uniforms.push_back(u); + } + render_base_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, SCENE_UNIFORM_SET); } } -void RasterizerSceneHighEndRD::_setup_view_dependant_uniform_set(RID p_shadow_atlas, RID p_reflection_atlas) { +void RasterizerSceneHighEndRD::_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) { if (view_dependant_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(view_dependant_uniform_set)) { RD::get_singleton()->free(view_dependant_uniform_set); } @@ -2844,6 +2973,25 @@ void RasterizerSceneHighEndRD::_setup_view_dependant_uniform_set(RID p_shadow_at uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID default_tex = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + for (int i = 0; i < MAX_GI_PROBES; i++) { + if (i < p_gi_probe_cull_count) { + RID tex = gi_probe_instance_get_texture(p_gi_probe_cull_result[i]); + if (!tex.is_valid()) { + tex = default_tex; + } + u.ids.push_back(tex); + } else { + u.ids.push_back(default_tex); + } + } + + uniforms.push_back(u); + } view_dependant_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, VIEW_DEPENDANT_UNIFORM_SET); } @@ -2860,16 +3008,22 @@ void RasterizerSceneHighEndRD::_render_buffers_uniform_set_changed(RID p_render_ _render_buffers_clear_uniform_set(rb); } -RID RasterizerSceneHighEndRD::_render_buffers_get_roughness_texture(RID p_render_buffers) { +RID RasterizerSceneHighEndRD::_render_buffers_get_normal_texture(RID p_render_buffers) { RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); - return rb->roughness_buffer; + return rb->normal_roughness_buffer; } -RID RasterizerSceneHighEndRD::_render_buffers_get_normal_texture(RID p_render_buffers) { +RID RasterizerSceneHighEndRD::_render_buffers_get_ambient_texture(RID p_render_buffers) { RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); - return rb->normal_buffer; + return rb->ambient_buffer; +} + +RID RasterizerSceneHighEndRD::_render_buffers_get_reflection_texture(RID p_render_buffers) { + RenderBufferDataHighEnd *rb = (RenderBufferDataHighEnd *)render_buffers_get_data(p_render_buffers); + + return rb->reflection_buffer; } void RasterizerSceneHighEndRD::_update_render_buffers_uniform_set(RID p_render_buffers) { @@ -2898,30 +3052,70 @@ void RasterizerSceneHighEndRD::_update_render_buffers_uniform_set(RID p_render_b RD::Uniform u; u.binding = 2; u.type = RD::UNIFORM_TYPE_TEXTURE; - RID texture = rb->normal_buffer.is_valid() ? rb->normal_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL); + RID texture = rb->normal_roughness_buffer.is_valid() ? rb->normal_roughness_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_NORMAL); u.ids.push_back(texture); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 3; + u.binding = 4; u.type = RD::UNIFORM_TYPE_TEXTURE; - RID texture = rb->roughness_buffer.is_valid() ? rb->roughness_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + RID aot = render_buffers_get_ao_texture(p_render_buffers); + RID texture = aot.is_valid() ? aot : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); u.ids.push_back(texture); uniforms.push_back(u); } { RD::Uniform u; - u.binding = 4; + u.binding = 5; u.type = RD::UNIFORM_TYPE_TEXTURE; - RID aot = render_buffers_get_ao_texture(p_render_buffers); - RID texture = aot.is_valid() ? aot : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + RID texture = rb->ambient_buffer.is_valid() ? rb->ambient_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); u.ids.push_back(texture); uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 6; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = rb->reflection_buffer.is_valid() ? rb->reflection_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + u.ids.push_back(texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 7; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID t; + if (render_buffers_is_sdfgi_enabled(p_render_buffers)) { + t = render_buffers_get_sdfgi_irradiance_probes(p_render_buffers); + } else { + t = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); + } + u.ids.push_back(t); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 8; + u.type = RD::UNIFORM_TYPE_TEXTURE; + if (render_buffers_is_sdfgi_enabled(p_render_buffers)) { + u.ids.push_back(render_buffers_get_sdfgi_occlusion_texture(p_render_buffers)); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 9; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(render_buffers_get_gi_probe_buffer(p_render_buffers)); + uniforms.push_back(u); + } + rb->uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); } } @@ -2946,6 +3140,7 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag if (is_using_radiance_cubemap_array()) { defines += "\n#define USE_RADIANCE_CUBEMAP_ARRAY \n"; } + defines += "\n#define SDFGI_OCT_SIZE " + itos(sdfgi_get_lightprobe_octahedron_size()) + "\n"; uint32_t uniform_max_size = RD::get_singleton()->limit_get(RD::LIMIT_MAX_UNIFORM_BUFFER_SIZE); @@ -2977,36 +3172,6 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag scene_state.directional_light_buffer = RD::get_singleton()->uniform_buffer_create(directional_light_buffer_size); defines += "\n#define MAX_DIRECTIONAL_LIGHT_DATA_STRUCTS " + itos(scene_state.max_directional_lights) + "\n"; } - - { //giprobes - int slot_count = gi_probe_get_slots().size(); - if (gi_probe_is_anisotropic()) { - slot_count *= 3; - defines += "\n#define GI_PROBE_USE_ANISOTROPY\n"; - } - - if (gi_probe_get_quality() == GIPROBE_QUALITY_ULTRA_LOW) { - defines += "\n#define GI_PROBE_LOW_QUALITY\n"; - } else if (gi_probe_get_quality() == GIPROBE_QUALITY_HIGH) { - defines += "\n#define GI_PROBE_HIGH_QUALITY\n"; - } - - defines += "\n#define MAX_GI_PROBE_TEXTURES " + itos(slot_count) + "\n"; - - uint32_t giprobe_buffer_size; - if (uniform_max_size < 65536) { - //Yes, you guessed right, ARM again - giprobe_buffer_size = uniform_max_size; - } else { - giprobe_buffer_size = 65536; - } - - giprobe_buffer_size = MIN(sizeof(GIProbeData) * gi_probe_get_slots().size(), giprobe_buffer_size); - scene_state.max_gi_probes = giprobe_buffer_size / sizeof(GIProbeData); - scene_state.gi_probes = memnew_arr(GIProbeData, scene_state.max_gi_probes); - scene_state.gi_probe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GIProbeData) * scene_state.max_gi_probes); - defines += "\n#define MAX_GI_PROBES " + itos(scene_state.max_gi_probes) + "\n"; - } { //lightmaps scene_state.max_lightmaps = storage->lightmap_array_get_size(); @@ -3036,13 +3201,13 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag Vector<String> shader_versions; shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_DUAL_PARABOLOID\n"); - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL\n"); - shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL\n#define MODE_RENDER_ROUGHNESS\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_NORMAL_ROUGHNESS\n#define MODE_RENDER_GIPROBE\n"); shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_MATERIAL\n"); + shader_versions.push_back("\n#define MODE_RENDER_DEPTH\n#define MODE_RENDER_SDF\n"); shader_versions.push_back(""); + shader_versions.push_back("\n#define USE_FORWARD_GI\n"); shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n"); - shader_versions.push_back("\n#define USE_VOXEL_CONE_TRACING\n"); - shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_VOXEL_CONE_TRACING\n"); shader_versions.push_back("\n#define USE_LIGHTMAP\n"); shader_versions.push_back("\n#define MODE_MULTIPLE_RENDER_TARGETS\n#define USE_LIGHTMAP\n"); shader.scene_shader.initialize(shader_versions, defines); @@ -3109,7 +3274,7 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag actions.renames["SCREEN_UV"] = "screen_uv"; actions.renames["SCREEN_TEXTURE"] = "color_buffer"; actions.renames["DEPTH_TEXTURE"] = "depth_buffer"; - actions.renames["NORMAL_TEXTURE"] = "normal_buffer"; + actions.renames["NORMAL_ROUGHNESS_TEXTURE"] = "normal_roughness_buffer"; actions.renames["DEPTH"] = "gl_FragDepth"; actions.renames["OUTPUT_IS_SRGB"] = "true"; @@ -3219,6 +3384,7 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag MaterialData *md = (MaterialData *)storage->material_get_data(default_material, RasterizerStorageRD::SHADER_TYPE_3D); default_shader_rd = shader.scene_shader.version_get_shader(md->shader_data->version, SHADER_VERSION_COLOR_PASS); + default_shader_sdfgi_rd = shader.scene_shader.version_get_shader(md->shader_data->version, SHADER_VERSION_DEPTH_PASS_WITH_SDF); } { @@ -3268,7 +3434,7 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag { //render buffers Vector<RD::Uniform> uniforms; - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 7; i++) { RD::Uniform u; u.binding = i; u.type = RD::UNIFORM_TYPE_TEXTURE; @@ -3276,6 +3442,28 @@ RasterizerSceneHighEndRD::RasterizerSceneHighEndRD(RasterizerStorageRD *p_storag u.ids.push_back(texture); uniforms.push_back(u); } + { + RD::Uniform u; + u.binding = 7; + u.type = RD::UNIFORM_TYPE_TEXTURE; + RID texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE); + u.ids.push_back(texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 8; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 9; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(render_buffers_get_default_gi_probe_buffer()); + uniforms.push_back(u); + } default_render_buffers_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, default_shader_rd, RENDER_BUFFERS_UNIFORM_SET); } @@ -3307,7 +3495,6 @@ RasterizerSceneHighEndRD::~RasterizerSceneHighEndRD() { { RD::get_singleton()->free(scene_state.uniform_buffer); RD::get_singleton()->free(scene_state.instance_buffer); - RD::get_singleton()->free(scene_state.gi_probe_buffer); RD::get_singleton()->free(scene_state.directional_light_buffer); RD::get_singleton()->free(scene_state.light_buffer); RD::get_singleton()->free(scene_state.lightmap_buffer); @@ -3315,7 +3502,6 @@ RasterizerSceneHighEndRD::~RasterizerSceneHighEndRD() { RD::get_singleton()->free(scene_state.reflection_buffer); RD::get_singleton()->free(scene_state.decal_buffer); memdelete_arr(scene_state.instances); - memdelete_arr(scene_state.gi_probes); memdelete_arr(scene_state.directional_lights); memdelete_arr(scene_state.lights); memdelete_arr(scene_state.lightmaps); @@ -3323,4 +3509,9 @@ RasterizerSceneHighEndRD::~RasterizerSceneHighEndRD() { memdelete_arr(scene_state.reflections); memdelete_arr(scene_state.decals); } + + while (sdfgi_framebuffer_size_cache.front()) { + RD::get_singleton()->free(sdfgi_framebuffer_size_cache.front()->get()); + sdfgi_framebuffer_size_cache.erase(sdfgi_framebuffer_size_cache.front()); + } } diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h index 8438a4f730..cb03da48c1 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_high_end_rd.h +++ b/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); diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp index 689552be2f..898cb39204 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp +++ b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.cpp @@ -32,8 +32,8 @@ #include "core/os/os.h" #include "core/project_settings.h" +#include "rasterizer_rd.h" #include "servers/rendering/rendering_server_raster.h" - uint64_t RasterizerSceneRD::auto_exposure_counter = 2; void get_vogel_disk(float *r_kernel, int p_sample_count) { @@ -195,6 +195,1541 @@ void RasterizerSceneRD::_update_reflection_mipmaps(ReflectionData &rd) { } } +void RasterizerSceneRD::_sdfgi_erase(RenderBuffers *rb) { + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + const SDFGI::Cascade &c = rb->sdfgi->cascades[i]; + RD::get_singleton()->free(c.light_data); + RD::get_singleton()->free(c.light_aniso_0_tex); + RD::get_singleton()->free(c.light_aniso_1_tex); + RD::get_singleton()->free(c.sdf_tex); + RD::get_singleton()->free(c.solid_cell_dispatch_buffer); + RD::get_singleton()->free(c.solid_cell_buffer); + RD::get_singleton()->free(c.lightprobe_history_tex); + RD::get_singleton()->free(c.lightprobe_average_tex); + RD::get_singleton()->free(c.lights_buffer); + } + + RD::get_singleton()->free(rb->sdfgi->render_albedo); + RD::get_singleton()->free(rb->sdfgi->render_emission); + RD::get_singleton()->free(rb->sdfgi->render_emission_aniso); + + RD::get_singleton()->free(rb->sdfgi->render_sdf[0]); + RD::get_singleton()->free(rb->sdfgi->render_sdf[1]); + + RD::get_singleton()->free(rb->sdfgi->render_sdf_half[0]); + RD::get_singleton()->free(rb->sdfgi->render_sdf_half[1]); + + for (int i = 0; i < 8; i++) { + RD::get_singleton()->free(rb->sdfgi->render_occlusion[i]); + } + + RD::get_singleton()->free(rb->sdfgi->render_geom_facing); + + RD::get_singleton()->free(rb->sdfgi->lightprobe_data); + RD::get_singleton()->free(rb->sdfgi->lightprobe_history_scroll); + RD::get_singleton()->free(rb->sdfgi->occlusion_data); + + RD::get_singleton()->free(rb->sdfgi->cascades_ubo); + + memdelete(rb->sdfgi); + + rb->sdfgi = nullptr; +} + +const Vector3i RasterizerSceneRD::SDFGI::Cascade::DIRTY_ALL = Vector3i(0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF); + +void RasterizerSceneRD::sdfgi_update(RID p_render_buffers, RID p_environment, const Vector3 &p_world_position) { + Environent *env = environment_owner.getornull(p_environment); + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + bool needs_sdfgi = env && env->sdfgi_enabled; + + if (!needs_sdfgi) { + if (rb->sdfgi != nullptr) { + //erase it + _sdfgi_erase(rb); + _render_buffers_uniform_set_changed(p_render_buffers); + } + return; + } + + static const uint32_t history_frames_to_converge[RS::ENV_SDFGI_CONVERGE_MAX] = { 5, 10, 15, 20, 25, 30 }; + uint32_t requested_history_size = history_frames_to_converge[sdfgi_frames_to_converge]; + + if (rb->sdfgi && (rb->sdfgi->cascade_mode != env->sdfgi_cascades || rb->sdfgi->min_cell_size != env->sdfgi_min_cell_size || requested_history_size != rb->sdfgi->history_size || rb->sdfgi->uses_occlusion != env->sdfgi_use_occlusion || rb->sdfgi->y_scale_mode != env->sdfgi_y_scale)) { + //configuration changed, erase + _sdfgi_erase(rb); + } + + SDFGI *sdfgi = rb->sdfgi; + if (sdfgi == nullptr) { + //re-create + rb->sdfgi = memnew(SDFGI); + sdfgi = rb->sdfgi; + sdfgi->cascade_mode = env->sdfgi_cascades; + sdfgi->min_cell_size = env->sdfgi_min_cell_size; + sdfgi->uses_occlusion = env->sdfgi_use_occlusion; + sdfgi->y_scale_mode = env->sdfgi_y_scale; + static const float y_scale[3] = { 1.0, 1.5, 2.0 }; + sdfgi->y_mult = y_scale[sdfgi->y_scale_mode]; + static const int cascasde_size[3] = { 4, 6, 8 }; + sdfgi->cascades.resize(cascasde_size[sdfgi->cascade_mode]); + sdfgi->probe_axis_count = SDFGI::PROBE_DIVISOR + 1; + sdfgi->solid_cell_ratio = sdfgi_solid_cell_ratio; + sdfgi->solid_cell_count = uint32_t(float(sdfgi->cascade_size * sdfgi->cascade_size * sdfgi->cascade_size) * sdfgi->solid_cell_ratio); + + float base_cell_size = sdfgi->min_cell_size; + + RD::TextureFormat tf_sdf; + tf_sdf.format = RD::DATA_FORMAT_R8_UNORM; + tf_sdf.width = sdfgi->cascade_size; // Always 64x64 + tf_sdf.height = sdfgi->cascade_size; + tf_sdf.depth = sdfgi->cascade_size; + tf_sdf.type = RD::TEXTURE_TYPE_3D; + tf_sdf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + + { + RD::TextureFormat tf_render = tf_sdf; + tf_render.format = RD::DATA_FORMAT_R16_UINT; + sdfgi->render_albedo = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + tf_render.format = RD::DATA_FORMAT_R32_UINT; + sdfgi->render_emission = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + sdfgi->render_emission_aniso = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + + tf_render.format = RD::DATA_FORMAT_R8_UNORM; //at least its easy to visualize + + for (int i = 0; i < 8; i++) { + sdfgi->render_occlusion[i] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + } + + tf_render.format = RD::DATA_FORMAT_R32_UINT; + sdfgi->render_geom_facing = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + + tf_render.format = RD::DATA_FORMAT_R8G8B8A8_UINT; + sdfgi->render_sdf[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + sdfgi->render_sdf[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + + tf_render.width /= 2; + tf_render.height /= 2; + tf_render.depth /= 2; + + sdfgi->render_sdf_half[0] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + sdfgi->render_sdf_half[1] = RD::get_singleton()->texture_create(tf_render, RD::TextureView()); + } + + RD::TextureFormat tf_occlusion = tf_sdf; + tf_occlusion.format = RD::DATA_FORMAT_R16_UINT; + tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R16_UINT); + tf_occlusion.shareable_formats.push_back(RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16); + tf_occlusion.depth *= sdfgi->cascades.size(); //use depth for occlusion slices + tf_occlusion.width *= 2; //use width for the other half + + RD::TextureFormat tf_light = tf_sdf; + tf_light.format = RD::DATA_FORMAT_R32_UINT; + tf_light.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT); + tf_light.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); + + RD::TextureFormat tf_aniso0 = tf_sdf; + tf_aniso0.format = RD::DATA_FORMAT_R8G8B8A8_UNORM; + RD::TextureFormat tf_aniso1 = tf_sdf; + tf_aniso1.format = RD::DATA_FORMAT_R8G8_UNORM; + + int passes = nearest_shift(sdfgi->cascade_size) - 1; + + //store lightprobe SH + RD::TextureFormat tf_probes; + tf_probes.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT; + tf_probes.width = sdfgi->probe_axis_count * sdfgi->probe_axis_count; + tf_probes.height = sdfgi->probe_axis_count * SDFGI::SH_SIZE; + tf_probes.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_CAN_COPY_FROM_BIT; + tf_probes.type = RD::TEXTURE_TYPE_2D_ARRAY; + + sdfgi->history_size = requested_history_size; + + RD::TextureFormat tf_probe_history = tf_probes; + tf_probe_history.format = RD::DATA_FORMAT_R16G16B16A16_SINT; //signed integer because SH are signed + tf_probe_history.array_layers = sdfgi->history_size; + + RD::TextureFormat tf_probe_average = tf_probes; + tf_probe_average.format = RD::DATA_FORMAT_R32G32B32A32_SINT; //signed integer because SH are signed + tf_probe_average.type = RD::TEXTURE_TYPE_2D_ARRAY; + tf_probe_average.array_layers = 1; + + sdfgi->lightprobe_history_scroll = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView()); + sdfgi->lightprobe_average_scroll = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView()); + + { + //octahedral lightprobes + RD::TextureFormat tf_octprobes = tf_probes; + tf_octprobes.array_layers = sdfgi->cascades.size() * 2; + tf_octprobes.format = RD::DATA_FORMAT_R32_UINT; //pack well with RGBE + tf_octprobes.width = sdfgi->probe_axis_count * sdfgi->probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2); + tf_octprobes.height = sdfgi->probe_axis_count * (SDFGI::LIGHTPROBE_OCT_SIZE + 2); + tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_R32_UINT); + tf_octprobes.shareable_formats.push_back(RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32); + //lightprobe texture is an octahedral texture + + sdfgi->lightprobe_data = RD::get_singleton()->texture_create(tf_octprobes, RD::TextureView()); + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; + sdfgi->lightprobe_texture = RD::get_singleton()->texture_create_shared(tv, sdfgi->lightprobe_data); + } + + sdfgi->cascades_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES); + + sdfgi->occlusion_data = RD::get_singleton()->texture_create(tf_occlusion, RD::TextureView()); + { + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_R4G4B4A4_UNORM_PACK16; + sdfgi->occlusion_texture = RD::get_singleton()->texture_create_shared(tv, sdfgi->occlusion_data); + } + + for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) { + SDFGI::Cascade &cascade = sdfgi->cascades[i]; + + /* 3D Textures */ + + cascade.sdf_tex = RD::get_singleton()->texture_create(tf_sdf, RD::TextureView()); + + cascade.light_data = RD::get_singleton()->texture_create(tf_light, RD::TextureView()); + + cascade.light_aniso_0_tex = RD::get_singleton()->texture_create(tf_aniso0, RD::TextureView()); + cascade.light_aniso_1_tex = RD::get_singleton()->texture_create(tf_aniso1, RD::TextureView()); + + { + RD::TextureView tv; + tv.format_override = RD::DATA_FORMAT_E5B9G9R9_UFLOAT_PACK32; + cascade.light_tex = RD::get_singleton()->texture_create_shared(tv, cascade.light_data); + + RD::get_singleton()->texture_clear(cascade.light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); + RD::get_singleton()->texture_clear(cascade.light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); + RD::get_singleton()->texture_clear(cascade.light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); + } + + cascade.cell_size = base_cell_size; + Vector3 world_position = p_world_position; + world_position.y *= sdfgi->y_mult; + int32_t probe_cells = sdfgi->cascade_size / SDFGI::PROBE_DIVISOR; + Vector3 probe_size = Vector3(1, 1, 1) * cascade.cell_size * probe_cells; + Vector3i probe_pos = Vector3i((world_position / probe_size + Vector3(0.5, 0.5, 0.5)).floor()); + cascade.position = probe_pos * probe_cells; + + cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL; + + base_cell_size *= 2.0; + + /* Probe History */ + + cascade.lightprobe_history_tex = RD::get_singleton()->texture_create(tf_probe_history, RD::TextureView()); + RD::get_singleton()->texture_clear(cascade.lightprobe_history_tex, Color(0, 0, 0, 0), 0, 1, 0, tf_probe_history.array_layers); //needs to be cleared for average to work + + cascade.lightprobe_average_tex = RD::get_singleton()->texture_create(tf_probe_average, RD::TextureView()); + RD::get_singleton()->texture_clear(cascade.lightprobe_average_tex, Color(0, 0, 0, 0), 0, 1, 0, 1); //needs to be cleared for average to work + + /* Buffers */ + + cascade.solid_cell_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDFGI::Cascade::SolidCell) * sdfgi->solid_cell_count); + cascade.solid_cell_dispatch_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector<uint8_t>(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); + cascade.lights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDGIShader::Light) * MAX(SDFGI::MAX_STATIC_LIGHTS, SDFGI::MAX_DYNAMIC_LIGHTS)); + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_sdf[(passes & 1) ? 1 : 0]); //if passes are even, we read from buffer 0, else we read from buffer 1 + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->render_albedo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 3; + for (int j = 0; j < 8; j++) { + u.ids.push_back(sdfgi->render_occlusion[j]); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 4; + u.ids.push_back(sdfgi->render_emission); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 5; + u.ids.push_back(sdfgi->render_emission_aniso); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 6; + u.ids.push_back(sdfgi->render_geom_facing); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 7; + u.ids.push_back(cascade.sdf_tex); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 8; + u.ids.push_back(sdfgi->occlusion_data); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 10; + u.ids.push_back(cascade.solid_cell_dispatch_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 11; + u.ids.push_back(cascade.solid_cell_buffer); + uniforms.push_back(u); + } + + cascade.sdf_store_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_STORE), 0); + } + + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_albedo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->render_geom_facing); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 3; + u.ids.push_back(sdfgi->render_emission); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 4; + u.ids.push_back(sdfgi->render_emission_aniso); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 5; + u.ids.push_back(cascade.solid_cell_dispatch_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 6; + u.ids.push_back(cascade.solid_cell_buffer); + uniforms.push_back(u); + } + + cascade.scroll_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_SCROLL), 0); + } + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + for (int j = 0; j < 8; j++) { + u.ids.push_back(sdfgi->render_occlusion[j]); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->occlusion_data); + uniforms.push_back(u); + } + + cascade.scroll_occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_SCROLL_OCCLUSION), 0); + } + } + + //direct light + for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) { + SDFGI::Cascade &cascade = sdfgi->cascades[i]; + + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.binding = 1; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (j < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[j].sdf_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 3; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.ids.push_back(cascade.solid_cell_dispatch_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 4; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.ids.push_back(cascade.solid_cell_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 5; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.ids.push_back(cascade.light_data); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 6; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.ids.push_back(cascade.light_aniso_0_tex); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 7; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.ids.push_back(cascade.light_aniso_1_tex); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 8; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(rb->sdfgi->cascades_ubo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 9; + u.type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.ids.push_back(cascade.lights_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 10; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(rb->sdfgi->lightprobe_texture); + uniforms.push_back(u); + } + + cascade.sdf_direct_light_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, 0), 0); + } + + //preprocess initialize uniform set + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_albedo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->render_sdf[0]); + uniforms.push_back(u); + } + + sdfgi->sdf_initialize_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE), 0); + } + + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_albedo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->render_sdf_half[0]); + uniforms.push_back(u); + } + + sdfgi->sdf_initialize_half_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF), 0); + } + + //jump flood uniform set + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_sdf[0]); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->render_sdf[1]); + uniforms.push_back(u); + } + + sdfgi->jump_flood_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0); + SWAP(uniforms.write[0].ids.write[0], uniforms.write[1].ids.write[0]); + sdfgi->jump_flood_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0); + } + //jump flood half uniform set + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_sdf_half[0]); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->render_sdf_half[1]); + uniforms.push_back(u); + } + + sdfgi->jump_flood_half_uniform_set[0] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0); + SWAP(uniforms.write[0].ids.write[0], uniforms.write[1].ids.write[0]); + sdfgi->jump_flood_half_uniform_set[1] = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD), 0); + } + + //upscale half size sdf + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_albedo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + u.ids.push_back(sdfgi->render_sdf_half[(passes & 1) ? 0 : 1]); //reverse pass order because half size + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 3; + u.ids.push_back(sdfgi->render_sdf[(passes & 1) ? 0 : 1]); //reverse pass order because it needs an extra JFA pass + uniforms.push_back(u); + } + + sdfgi->upscale_jfa_uniform_set_index = (passes & 1) ? 0 : 1; + sdfgi->sdf_upscale_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE), 0); + } + + //occlusion uniform set + { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 1; + u.ids.push_back(sdfgi->render_albedo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 2; + for (int i = 0; i < 8; i++) { + u.ids.push_back(sdfgi->render_occlusion[i]); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 3; + u.ids.push_back(sdfgi->render_geom_facing); + uniforms.push_back(u); + } + + sdfgi->occlusion_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, SDGIShader::PRE_PROCESS_OCCLUSION), 0); + } + + for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) { + //integrate uniform + + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.binding = 1; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (j < sdfgi->cascades.size()) { + u.ids.push_back(sdfgi->cascades[j].sdf_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (j < sdfgi->cascades.size()) { + u.ids.push_back(sdfgi->cascades[j].light_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 3; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (j < sdfgi->cascades.size()) { + u.ids.push_back(sdfgi->cascades[j].light_aniso_0_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 4; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (j < sdfgi->cascades.size()) { + u.ids.push_back(sdfgi->cascades[j].light_aniso_1_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 6; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 7; + u.ids.push_back(sdfgi->cascades_ubo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 8; + u.ids.push_back(sdfgi->lightprobe_data); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 9; + u.ids.push_back(sdfgi->cascades[i].lightprobe_history_tex); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 10; + u.ids.push_back(sdfgi->cascades[i].lightprobe_average_tex); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 11; + u.ids.push_back(sdfgi->lightprobe_history_scroll); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 12; + u.ids.push_back(sdfgi->lightprobe_average_scroll); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 13; + RID parent_average; + if (i < sdfgi->cascades.size() - 1) { + parent_average = sdfgi->cascades[i + 1].lightprobe_average_tex; + } else { + parent_average = sdfgi->cascades[i - 1].lightprobe_average_tex; //to use something, but it wont be used + } + u.ids.push_back(parent_average); + uniforms.push_back(u); + } + + sdfgi->cascades[i].integrate_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 0); + } + + sdfgi->uses_multibounce = env->sdfgi_use_multibounce; + sdfgi->energy = env->sdfgi_energy; + sdfgi->normal_bias = env->sdfgi_normal_bias; + sdfgi->probe_bias = env->sdfgi_probe_bias; + sdfgi->reads_sky = env->sdfgi_read_sky_light; + + _render_buffers_uniform_set_changed(p_render_buffers); + + return; //done. all levels will need to be rendered which its going to take a bit + } + + //check for updates + + sdfgi->uses_multibounce = env->sdfgi_use_multibounce; + sdfgi->energy = env->sdfgi_energy; + sdfgi->normal_bias = env->sdfgi_normal_bias; + sdfgi->probe_bias = env->sdfgi_probe_bias; + sdfgi->reads_sky = env->sdfgi_read_sky_light; + + int32_t drag_margin = (sdfgi->cascade_size / SDFGI::PROBE_DIVISOR) / 2; + + for (uint32_t i = 0; i < sdfgi->cascades.size(); i++) { + SDFGI::Cascade &cascade = sdfgi->cascades[i]; + cascade.dirty_regions = Vector3i(); + + Vector3 probe_half_size = Vector3(1, 1, 1) * cascade.cell_size * float(sdfgi->cascade_size / SDFGI::PROBE_DIVISOR) * 0.5; + probe_half_size = Vector3(0, 0, 0); + + Vector3 world_position = p_world_position; + world_position.y *= sdfgi->y_mult; + Vector3i pos_in_cascade = Vector3i((world_position + probe_half_size) / cascade.cell_size); + + for (int j = 0; j < 3; j++) { + if (pos_in_cascade[j] < cascade.position[j]) { + while (pos_in_cascade[j] < (cascade.position[j] - drag_margin)) { + cascade.position[j] -= drag_margin * 2; + cascade.dirty_regions[j] += drag_margin * 2; + } + } else if (pos_in_cascade[j] > cascade.position[j]) { + while (pos_in_cascade[j] > (cascade.position[j] + drag_margin)) { + cascade.position[j] += drag_margin * 2; + cascade.dirty_regions[j] -= drag_margin * 2; + } + } + + if (cascade.dirty_regions[j] == 0) { + continue; // not dirty + } else if (uint32_t(ABS(cascade.dirty_regions[j])) >= sdfgi->cascade_size) { + //moved too much, just redraw everything (make all dirty) + cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL; + break; + } + } + + if (cascade.dirty_regions != Vector3i() && cascade.dirty_regions != SDFGI::Cascade::DIRTY_ALL) { + //see how much the total dirty volume represents from the total volume + uint32_t total_volume = sdfgi->cascade_size * sdfgi->cascade_size * sdfgi->cascade_size; + uint32_t safe_volume = 1; + for (int j = 0; j < 3; j++) { + safe_volume *= sdfgi->cascade_size - ABS(cascade.dirty_regions[j]); + } + uint32_t dirty_volume = total_volume - safe_volume; + if (dirty_volume > (safe_volume / 2)) { + //more than half the volume is dirty, make all dirty so its only rendered once + cascade.dirty_regions = SDFGI::Cascade::DIRTY_ALL; + } + } + } +} + +int RasterizerSceneRD::sdfgi_get_pending_region_count(RID p_render_buffers) const { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + + ERR_FAIL_COND_V(rb == nullptr, 0); + + if (rb->sdfgi == nullptr) { + return 0; + } + + int dirty_count = 0; + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + const SDFGI::Cascade &c = rb->sdfgi->cascades[i]; + + if (c.dirty_regions == SDFGI::Cascade::DIRTY_ALL) { + dirty_count++; + } else { + for (int j = 0; j < 3; j++) { + if (c.dirty_regions[j] != 0) { + dirty_count++; + } + } + } + } + + return dirty_count; +} + +int RasterizerSceneRD::_sdfgi_get_pending_region_data(RID p_render_buffers, int p_region, Vector3i &r_local_offset, Vector3i &r_local_size, AABB &r_bounds) const { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(rb == nullptr, -1); + ERR_FAIL_COND_V(rb->sdfgi == nullptr, -1); + + int dirty_count = 0; + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + const SDFGI::Cascade &c = rb->sdfgi->cascades[i]; + + if (c.dirty_regions == SDFGI::Cascade::DIRTY_ALL) { + if (dirty_count == p_region) { + r_local_offset = Vector3i(); + r_local_size = Vector3i(1, 1, 1) * rb->sdfgi->cascade_size; + + r_bounds.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + c.position)) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1); + r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1); + return i; + } + dirty_count++; + } else { + for (int j = 0; j < 3; j++) { + if (c.dirty_regions[j] != 0) { + if (dirty_count == p_region) { + Vector3i from = Vector3i(0, 0, 0); + Vector3i to = Vector3i(1, 1, 1) * rb->sdfgi->cascade_size; + + if (c.dirty_regions[j] > 0) { + //fill from the beginning + to[j] = c.dirty_regions[j]; + } else { + //fill from the end + from[j] = to[j] + c.dirty_regions[j]; + } + + for (int k = 0; k < j; k++) { + // "chip" away previous regions to avoid re-voxelizing the same thing + if (c.dirty_regions[k] > 0) { + from[k] += c.dirty_regions[k]; + } else if (c.dirty_regions[k] < 0) { + to[k] += c.dirty_regions[k]; + } + } + + r_local_offset = from; + r_local_size = to - from; + + r_bounds.position = Vector3(from + Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + c.position) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1); + r_bounds.size = Vector3(r_local_size) * c.cell_size * Vector3(1, 1.0 / rb->sdfgi->y_mult, 1); + + return i; + } + + dirty_count++; + } + } + } + } + return -1; +} + +AABB RasterizerSceneRD::sdfgi_get_pending_region_bounds(RID p_render_buffers, int p_region) const { + AABB bounds; + Vector3i from; + Vector3i size; + + int c = _sdfgi_get_pending_region_data(p_render_buffers, p_region, from, size, bounds); + ERR_FAIL_COND_V(c == -1, AABB()); + return bounds; +} + +uint32_t RasterizerSceneRD::sdfgi_get_pending_region_cascade(RID p_render_buffers, int p_region) const { + AABB bounds; + Vector3i from; + Vector3i size; + + return _sdfgi_get_pending_region_data(p_render_buffers, p_region, from, size, bounds); +} + +void RasterizerSceneRD::_sdfgi_update_cascades(RID p_render_buffers) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(rb == nullptr); + if (rb->sdfgi == nullptr) { + return; + } + + //update cascades + SDFGI::Cascade::UBO cascade_data[SDFGI::MAX_CASCADES]; + int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR; + + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[i].position)) * rb->sdfgi->cascades[i].cell_size; + + cascade_data[i].offset[0] = pos.x; + cascade_data[i].offset[1] = pos.y; + cascade_data[i].offset[2] = pos.z; + cascade_data[i].to_cell = 1.0 / rb->sdfgi->cascades[i].cell_size; + cascade_data[i].probe_offset[0] = rb->sdfgi->cascades[i].position.x / probe_divisor; + cascade_data[i].probe_offset[1] = rb->sdfgi->cascades[i].position.y / probe_divisor; + cascade_data[i].probe_offset[2] = rb->sdfgi->cascades[i].position.z / probe_divisor; + cascade_data[i].pad = 0; + } + + RD::get_singleton()->buffer_update(rb->sdfgi->cascades_ubo, 0, sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES, cascade_data, true); +} + +void RasterizerSceneRD::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) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(rb == nullptr); + if (rb->sdfgi == nullptr) { + return; + } + Environent *env = environment_owner.getornull(p_environment); + + RENDER_TIMESTAMP(">SDFGI Update Probes"); + + /* Update Cascades UBO */ + _sdfgi_update_cascades(p_render_buffers); + /* Update Dynamic Lights Buffer */ + + RENDER_TIMESTAMP("Update Lights"); + + /* Update dynamic lights */ + + { + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_DYNAMIC]); + + SDGIShader::DirectLightPushConstant push_constant; + + push_constant.grid_size[0] = rb->sdfgi->cascade_size; + push_constant.grid_size[1] = rb->sdfgi->cascade_size; + push_constant.grid_size[2] = rb->sdfgi->cascade_size; + push_constant.max_cascades = rb->sdfgi->cascades.size(); + push_constant.probe_axis_size = rb->sdfgi->probe_axis_count; + push_constant.multibounce = rb->sdfgi->uses_multibounce; + push_constant.y_mult = rb->sdfgi->y_mult; + + push_constant.process_offset = 0; + push_constant.process_increment = 1; + + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + SDFGI::Cascade &cascade = rb->sdfgi->cascades[i]; + + { //fill light buffer + + SDGIShader::Light lights[SDFGI::MAX_DYNAMIC_LIGHTS]; + uint32_t idx = 0; + for (uint32_t j = 0; j < p_directional_light_count; j++) { + if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { + break; + } + + LightInstance *li = light_instance_owner.getornull(p_directional_light_instances[j]); + ERR_CONTINUE(!li); + Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); + dir.y *= rb->sdfgi->y_mult; + dir.normalize(); + lights[idx].direction[0] = dir.x; + lights[idx].direction[1] = dir.y; + lights[idx].direction[2] = dir.z; + Color color = storage->light_get_color(li->light); + color = color.to_linear(); + lights[idx].color[0] = color.r; + lights[idx].color[1] = color.g; + lights[idx].color[2] = color.b; + lights[idx].type = RS::LIGHT_DIRECTIONAL; + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].has_shadow = storage->light_has_shadow(li->light); + + idx++; + } + + AABB cascade_aabb; + cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + cascade.position)) * cascade.cell_size; + cascade_aabb.size = Vector3(1, 1, 1) * rb->sdfgi->cascade_size * cascade.cell_size; + + for (uint32_t j = 0; j < p_positional_light_count; j++) { + if (idx == SDFGI::MAX_DYNAMIC_LIGHTS) { + break; + } + + LightInstance *li = light_instance_owner.getornull(p_positional_light_instances[j]); + ERR_CONTINUE(!li); + + uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light); + if (i > max_sdfgi_cascade) { + continue; + } + + if (!cascade_aabb.intersects(li->aabb)) { + continue; + } + + Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); + //faster to not do this here + //dir.y *= rb->sdfgi->y_mult; + //dir.normalize(); + lights[idx].direction[0] = dir.x; + lights[idx].direction[1] = dir.y; + lights[idx].direction[2] = dir.z; + Vector3 pos = li->transform.origin; + pos.y *= rb->sdfgi->y_mult; + lights[idx].position[0] = pos.x; + lights[idx].position[1] = pos.y; + lights[idx].position[2] = pos.z; + Color color = storage->light_get_color(li->light); + color = color.to_linear(); + lights[idx].color[0] = color.r; + lights[idx].color[1] = color.g; + lights[idx].color[2] = color.b; + lights[idx].type = storage->light_get_type(li->light); + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].has_shadow = storage->light_has_shadow(li->light); + lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); + lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); + lights[idx].spot_angle = Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)); + lights[idx].spot_attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION); + + idx++; + } + + if (idx > 0) { + RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights, true); + } + push_constant.light_count = idx; + } + + push_constant.cascade = i; + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascade.sdf_direct_light_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::DirectLightPushConstant)); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.solid_cell_dispatch_buffer, 0); + } + RD::get_singleton()->compute_list_end(); + } + + RENDER_TIMESTAMP("Raytrace"); + + SDGIShader::IntegratePushConstant push_constant; + push_constant.grid_size[1] = rb->sdfgi->cascade_size; + push_constant.grid_size[2] = rb->sdfgi->cascade_size; + push_constant.grid_size[0] = rb->sdfgi->cascade_size; + push_constant.max_cascades = rb->sdfgi->cascades.size(); + push_constant.probe_axis_size = rb->sdfgi->probe_axis_count; + push_constant.history_index = rb->sdfgi->render_pass % rb->sdfgi->history_size; + push_constant.history_size = rb->sdfgi->history_size; + static const uint32_t ray_count[RS::ENV_SDFGI_RAY_COUNT_MAX] = { 8, 16, 32, 64, 96, 128 }; + push_constant.ray_count = ray_count[sdfgi_ray_count]; + push_constant.ray_bias = rb->sdfgi->probe_bias; + push_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count; + push_constant.image_size[1] = rb->sdfgi->probe_axis_count; + + RID sky_uniform_set = sdfgi_shader.integrate_default_sky_uniform_set; + push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_DISABLED; + push_constant.y_mult = rb->sdfgi->y_mult; + + if (rb->sdfgi->reads_sky && env) { + push_constant.sky_energy = env->bg_energy; + + if (env->background == RS::ENV_BG_CLEAR_COLOR) { + push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_COLOR; + Color c = storage->get_default_clear_color().to_linear(); + push_constant.sky_color[0] = c.r; + push_constant.sky_color[1] = c.g; + push_constant.sky_color[2] = c.b; + } else if (env->background == RS::ENV_BG_COLOR) { + push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_COLOR; + Color c = env->bg_color; + push_constant.sky_color[0] = c.r; + push_constant.sky_color[1] = c.g; + push_constant.sky_color[2] = c.b; + + } else if (env->background == RS::ENV_BG_SKY) { + Sky *sky = sky_owner.getornull(env->sky); + if (sky && sky->radiance.is_valid()) { + if (sky->sdfgi_integrate_sky_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(sky->sdfgi_integrate_sky_uniform_set)) { + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 0; + u.ids.push_back(sky->radiance); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 1; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + sky->sdfgi_integrate_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1); + } + sky_uniform_set = sky->sdfgi_integrate_sky_uniform_set; + push_constant.sky_mode = SDGIShader::IntegratePushConstant::SKY_MODE_SKY; + } + } + } + + rb->sdfgi->render_pass++; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_PROCESS]); + + int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR; + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + push_constant.cascade = i; + push_constant.world_offset[0] = rb->sdfgi->cascades[i].position.x / probe_divisor; + push_constant.world_offset[1] = rb->sdfgi->cascades[i].position.y / probe_divisor; + push_constant.world_offset[2] = rb->sdfgi->cascades[i].position.z / probe_divisor; + + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[i].integrate_uniform_set, 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sky_uniform_set, 1); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::IntegratePushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count, rb->sdfgi->probe_axis_count, 1, 8, 8, 1); + } + + RD::get_singleton()->compute_list_add_barrier(compute_list); //wait until done + + // Then store values into the lightprobe texture. Separating these steps has a small performance hit, but it allows for multiple bounces + RENDER_TIMESTAMP("Average Probes"); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_STORE]); + + //convert to octahedral to store + push_constant.image_size[0] *= SDFGI::LIGHTPROBE_OCT_SIZE; + push_constant.image_size[1] *= SDFGI::LIGHTPROBE_OCT_SIZE; + + for (uint32_t i = 0; i < rb->sdfgi->cascades.size(); i++) { + push_constant.cascade = i; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[i].integrate_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::IntegratePushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, rb->sdfgi->probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1, 8, 8, 1); + } + + RD::get_singleton()->compute_list_end(); + + RENDER_TIMESTAMP("<SDFGI Update Probes"); +} + +void RasterizerSceneRD::_process_gi(RID p_render_buffers, RID p_normal_roughness_buffer, RID p_ambient_buffer, RID p_reflection_buffer, RID p_gi_probe_buffer, RID p_environment, const CameraMatrix &p_projection, const Transform &p_transform, RID *p_gi_probe_cull_result, int p_gi_probe_cull_count) { + RENDER_TIMESTAMP("Render GI"); + + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(rb == nullptr); + Environent *env = environment_owner.getornull(p_environment); + + GI::PushConstant push_constant; + + push_constant.screen_size[0] = rb->width; + push_constant.screen_size[1] = rb->height; + push_constant.z_near = p_projection.get_z_near(); + push_constant.z_far = p_projection.get_z_far(); + push_constant.orthogonal = p_projection.is_orthogonal(); + push_constant.proj_info[0] = -2.0f / (rb->width * p_projection.matrix[0][0]); + push_constant.proj_info[1] = -2.0f / (rb->height * p_projection.matrix[1][1]); + push_constant.proj_info[2] = (1.0f - p_projection.matrix[0][2]) / p_projection.matrix[0][0]; + push_constant.proj_info[3] = (1.0f + p_projection.matrix[1][2]) / p_projection.matrix[1][1]; + push_constant.max_giprobes = MIN(RenderBuffers::MAX_GIPROBES, p_gi_probe_cull_count); + push_constant.high_quality_vct = gi_probe_quality == RS::GI_PROBE_QUALITY_HIGH; + push_constant.use_sdfgi = rb->sdfgi != nullptr; + + if (env) { + push_constant.ao_color[0] = env->ao_color.r; + push_constant.ao_color[1] = env->ao_color.g; + push_constant.ao_color[2] = env->ao_color.b; + } else { + push_constant.ao_color[0] = 0; + push_constant.ao_color[1] = 0; + push_constant.ao_color[2] = 0; + } + + push_constant.cam_rotation[0] = p_transform.basis[0][0]; + push_constant.cam_rotation[1] = p_transform.basis[1][0]; + push_constant.cam_rotation[2] = p_transform.basis[2][0]; + push_constant.cam_rotation[3] = 0; + push_constant.cam_rotation[4] = p_transform.basis[0][1]; + push_constant.cam_rotation[5] = p_transform.basis[1][1]; + push_constant.cam_rotation[6] = p_transform.basis[2][1]; + push_constant.cam_rotation[7] = 0; + push_constant.cam_rotation[8] = p_transform.basis[0][2]; + push_constant.cam_rotation[9] = p_transform.basis[1][2]; + push_constant.cam_rotation[10] = p_transform.basis[2][2]; + push_constant.cam_rotation[11] = 0; + + if (rb->sdfgi) { + GI::SDFGIData sdfgi_data; + + sdfgi_data.grid_size[0] = rb->sdfgi->cascade_size; + sdfgi_data.grid_size[1] = rb->sdfgi->cascade_size; + sdfgi_data.grid_size[2] = rb->sdfgi->cascade_size; + + sdfgi_data.max_cascades = rb->sdfgi->cascades.size(); + sdfgi_data.probe_axis_size = rb->sdfgi->probe_axis_count; + sdfgi_data.cascade_probe_size[0] = sdfgi_data.probe_axis_size - 1; //float version for performance + sdfgi_data.cascade_probe_size[1] = sdfgi_data.probe_axis_size - 1; + sdfgi_data.cascade_probe_size[2] = sdfgi_data.probe_axis_size - 1; + + float csize = rb->sdfgi->cascade_size; + sdfgi_data.probe_to_uvw = 1.0 / float(sdfgi_data.cascade_probe_size[0]); + sdfgi_data.use_occlusion = rb->sdfgi->uses_occlusion; + //sdfgi_data.energy = rb->sdfgi->energy; + + sdfgi_data.y_mult = rb->sdfgi->y_mult; + + float cascade_voxel_size = (csize / sdfgi_data.cascade_probe_size[0]); + float occlusion_clamp = (cascade_voxel_size - 0.5) / cascade_voxel_size; + sdfgi_data.occlusion_clamp[0] = occlusion_clamp; + sdfgi_data.occlusion_clamp[1] = occlusion_clamp; + sdfgi_data.occlusion_clamp[2] = occlusion_clamp; + sdfgi_data.normal_bias = (rb->sdfgi->normal_bias / csize) * sdfgi_data.cascade_probe_size[0]; + + //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; + + uint32_t oct_size = SDFGI::LIGHTPROBE_OCT_SIZE; + + sdfgi_data.lightprobe_tex_pixel_size[0] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size * sdfgi_data.probe_axis_size); + sdfgi_data.lightprobe_tex_pixel_size[1] = 1.0 / ((oct_size + 2) * sdfgi_data.probe_axis_size); + sdfgi_data.lightprobe_tex_pixel_size[2] = 1.0; + + sdfgi_data.energy = rb->sdfgi->energy; + + sdfgi_data.lightprobe_uv_offset[0] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[0]; + sdfgi_data.lightprobe_uv_offset[1] = float(oct_size + 2) * sdfgi_data.lightprobe_tex_pixel_size[1]; + sdfgi_data.lightprobe_uv_offset[2] = float((oct_size + 2) * sdfgi_data.probe_axis_size) * sdfgi_data.lightprobe_tex_pixel_size[0]; + + sdfgi_data.occlusion_renormalize[0] = 0.5; + sdfgi_data.occlusion_renormalize[1] = 1.0; + sdfgi_data.occlusion_renormalize[2] = 1.0 / float(sdfgi_data.max_cascades); + + int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR; + + for (uint32_t i = 0; i < sdfgi_data.max_cascades; i++) { + GI::SDFGIData::ProbeCascadeData &c = sdfgi_data.cascades[i]; + Vector3 pos = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[i].position)) * rb->sdfgi->cascades[i].cell_size; + Vector3 cam_origin = p_transform.origin; + cam_origin.y *= rb->sdfgi->y_mult; + pos -= cam_origin; //make pos local to camera, to reduce numerical error + c.position[0] = pos.x; + c.position[1] = pos.y; + c.position[2] = pos.z; + c.to_probe = 1.0 / (float(rb->sdfgi->cascade_size) * rb->sdfgi->cascades[i].cell_size / float(rb->sdfgi->probe_axis_count - 1)); + + Vector3i probe_ofs = rb->sdfgi->cascades[i].position / probe_divisor; + c.probe_world_offset[0] = probe_ofs.x; + c.probe_world_offset[1] = probe_ofs.y; + c.probe_world_offset[2] = probe_ofs.z; + + c.to_cell = 1.0 / rb->sdfgi->cascades[i].cell_size; + } + + RD::get_singleton()->buffer_update(gi.sdfgi_ubo, 0, sizeof(GI::SDFGIData), &sdfgi_data, true); + } + + { + RID gi_probe_buffer = render_buffers_get_gi_probe_buffer(p_render_buffers); + GI::GIProbeData gi_probe_data[RenderBuffers::MAX_GIPROBES]; + + bool giprobes_changed = false; + + Transform to_camera; + to_camera.origin = p_transform.origin; //only translation, make local + + for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) { + RID texture; + if (i < p_gi_probe_cull_count) { + GIProbeInstance *gipi = gi_probe_instance_owner.getornull(p_gi_probe_cull_result[i]); + + if (gipi) { + texture = gipi->texture; + GI::GIProbeData &gipd = gi_probe_data[i]; + + RID base_probe = gipi->probe; + + Transform to_cell = storage->gi_probe_get_to_cell_xform(gipi->probe) * gipi->transform.affine_inverse() * to_camera; + + gipd.xform[0] = to_cell.basis.elements[0][0]; + gipd.xform[1] = to_cell.basis.elements[1][0]; + gipd.xform[2] = to_cell.basis.elements[2][0]; + gipd.xform[3] = 0; + gipd.xform[4] = to_cell.basis.elements[0][1]; + gipd.xform[5] = to_cell.basis.elements[1][1]; + gipd.xform[6] = to_cell.basis.elements[2][1]; + gipd.xform[7] = 0; + gipd.xform[8] = to_cell.basis.elements[0][2]; + gipd.xform[9] = to_cell.basis.elements[1][2]; + gipd.xform[10] = to_cell.basis.elements[2][2]; + gipd.xform[11] = 0; + gipd.xform[12] = to_cell.origin.x; + gipd.xform[13] = to_cell.origin.y; + gipd.xform[14] = to_cell.origin.z; + gipd.xform[15] = 1; + + Vector3 bounds = storage->gi_probe_get_octree_size(base_probe); + + gipd.bounds[0] = bounds.x; + gipd.bounds[1] = bounds.y; + gipd.bounds[2] = bounds.z; + + gipd.dynamic_range = storage->gi_probe_get_dynamic_range(base_probe) * storage->gi_probe_get_energy(base_probe); + gipd.bias = storage->gi_probe_get_bias(base_probe); + gipd.normal_bias = storage->gi_probe_get_normal_bias(base_probe); + gipd.blend_ambient = !storage->gi_probe_is_interior(base_probe); + gipd.anisotropy_strength = 0; + gipd.ao = storage->gi_probe_get_ao(base_probe); + gipd.ao_size = Math::pow(storage->gi_probe_get_ao_size(base_probe), 4.0f); + } + } + + if (texture == RID()) { + texture = storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE); + } + + if (texture != rb->giprobe_textures[i]) { + giprobes_changed = true; + rb->giprobe_textures[i] = texture; + } + } + + if (giprobes_changed) { + RD::get_singleton()->free(rb->gi_uniform_set); + rb->gi_uniform_set = RID(); + } + + if (p_gi_probe_cull_count > 0) { + RD::get_singleton()->buffer_update(gi_probe_buffer, 0, sizeof(GI::GIProbeData) * MIN(RenderBuffers::MAX_GIPROBES, p_gi_probe_cull_count), gi_probe_data, true); + } + } + + if (rb->gi_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(rb->gi_uniform_set)) { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.binding = 1; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[j].sdf_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[j].light_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 3; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[j].light_aniso_0_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 4; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t j = 0; j < SDFGI::MAX_CASCADES; j++) { + if (rb->sdfgi && j < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[j].light_aniso_1_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 5; + if (rb->sdfgi) { + u.ids.push_back(rb->sdfgi->occlusion_texture); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 6; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 7; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 9; + u.ids.push_back(p_ambient_buffer); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.binding = 10; + u.ids.push_back(p_reflection_buffer); + uniforms.push_back(u); + } + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 11; + if (rb->sdfgi) { + u.ids.push_back(rb->sdfgi->lightprobe_texture); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_2D_ARRAY_WHITE)); + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 12; + u.ids.push_back(rb->depth_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 13; + u.ids.push_back(p_normal_roughness_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 14; + RID buffer = p_gi_probe_buffer.is_valid() ? p_gi_probe_buffer : storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_BLACK); + u.ids.push_back(buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 15; + u.ids.push_back(gi.sdfgi_ubo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.binding = 16; + u.ids.push_back(rb->giprobe_buffer); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 17; + for (int i = 0; i < RenderBuffers::MAX_GIPROBES; i++) { + u.ids.push_back(rb->giprobe_textures[i]); + } + uniforms.push_back(u); + } + + rb->gi_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, gi.shader.version_get_shader(gi.shader_version, 0), 0); + } + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi.pipelines[0]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->gi_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(GI::PushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width, rb->height, 1, 8, 8, 1); + RD::get_singleton()->compute_list_end(); +} + RID RasterizerSceneRD::sky_create() { return sky_owner.make_rid(Sky()); } @@ -1291,6 +2826,31 @@ void RasterizerSceneRD::environment_glow_set_use_bicubic_upscale(bool p_enable) glow_bicubic_upscale = p_enable; } +void RasterizerSceneRD::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) { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND(!env); + + env->sdfgi_enabled = p_enable; + env->sdfgi_cascades = p_cascades; + env->sdfgi_min_cell_size = p_min_cell_size; + env->sdfgi_use_occlusion = p_use_occlusion; + env->sdfgi_use_multibounce = p_use_multibounce; + env->sdfgi_read_sky_light = p_read_sky; + env->sdfgi_enhance_ssr = p_enhance_ssr; + env->sdfgi_energy = p_energy; + env->sdfgi_normal_bias = p_normal_bias; + env->sdfgi_probe_bias = p_probe_bias; + env->sdfgi_y_scale = p_y_scale; +} + +void RasterizerSceneRD::environment_set_sdfgi_ray_count(RS::EnvironmentSDFGIRayCount p_ray_count) { + sdfgi_ray_count = p_ray_count; +} + +void RasterizerSceneRD::environment_set_sdfgi_frames_to_converge(RS::EnvironmentSDFGIFramesToConverge p_frames) { + sdfgi_frames_to_converge = p_frames; +} + void RasterizerSceneRD::environment_set_ssr(RID p_env, bool p_enable, int p_max_steps, float p_fade_int, float p_fade_out, float p_depth_tolerance) { Environent *env = environment_owner.getornull(p_env); ERR_FAIL_COND(!env); @@ -1351,6 +2911,11 @@ bool RasterizerSceneRD::environment_is_ssr_enabled(RID p_env) const { ERR_FAIL_COND_V(!env, false); return env->ssr_enabled; } +bool RasterizerSceneRD::environment_is_sdfgi_enabled(RID p_env) const { + Environent *env = environment_owner.getornull(p_env); + ERR_FAIL_COND_V(!env, false); + return env->sdfgi_enabled; +} bool RasterizerSceneRD::is_environment(RID p_env) const { return environment_owner.owns(p_env); @@ -2099,6 +3664,13 @@ void RasterizerSceneRD::light_instance_set_transform(RID p_light_instance, const light_instance->transform = p_transform; } +void RasterizerSceneRD::light_instance_set_aabb(RID p_light_instance, const AABB &p_aabb) { + LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); + ERR_FAIL_COND(!light_instance); + + light_instance->aabb = p_aabb; +} + void RasterizerSceneRD::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, float p_range_begin, const Vector2 &p_uv_scale) { LightInstance *light_instance = light_instance_owner.getornull(p_light_instance); ERR_FAIL_COND(!light_instance); @@ -2193,23 +3765,9 @@ void RasterizerSceneRD::decal_instance_set_transform(RID p_decal, const Transfor ///////////////////////////////// RID RasterizerSceneRD::gi_probe_instance_create(RID p_base) { - //find a free slot - int index = -1; - for (int i = 0; i < gi_probe_slots.size(); i++) { - if (gi_probe_slots[i] == RID()) { - index = i; - break; - } - } - - ERR_FAIL_COND_V(index == -1, RID()); - GIProbeInstance gi_probe; - gi_probe.slot = index; gi_probe.probe = p_base; RID rid = gi_probe_instance_owner.make_rid(gi_probe); - gi_probe_slots.write[index] = rid; - return rid; } @@ -2240,10 +3798,6 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc //need to re-create everything if (gi_probe->texture.is_valid()) { RD::get_singleton()->free(gi_probe->texture); - if (gi_probe_use_anisotropy) { - RD::get_singleton()->free(gi_probe->anisotropy_r16[0]); - RD::get_singleton()->free(gi_probe->anisotropy_r16[1]); - } RD::get_singleton()->free(gi_probe->write_buffer); gi_probe->mipmaps.clear(); } @@ -2275,47 +3829,18 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc RD::get_singleton()->texture_clear(gi_probe->texture, Color(0, 0, 0, 0), 0, levels.size(), 0, 1, false); - if (gi_probe_use_anisotropy) { - tf.format = RD::DATA_FORMAT_R16_UINT; - tf.shareable_formats.push_back(RD::DATA_FORMAT_R16_UINT); - tf.shareable_formats.push_back(RD::DATA_FORMAT_R5G6B5_UNORM_PACK16); - - //need to create R16 first, else driver does not like the storage bit for compute.. - gi_probe->anisotropy_r16[0] = RD::get_singleton()->texture_create(tf, RD::TextureView()); - gi_probe->anisotropy_r16[1] = RD::get_singleton()->texture_create(tf, RD::TextureView()); - - RD::TextureView tv; - tv.format_override = RD::DATA_FORMAT_R5G6B5_UNORM_PACK16; - gi_probe->anisotropy[0] = RD::get_singleton()->texture_create_shared(tv, gi_probe->anisotropy_r16[0]); - gi_probe->anisotropy[1] = RD::get_singleton()->texture_create_shared(tv, gi_probe->anisotropy_r16[1]); - - RD::get_singleton()->texture_clear(gi_probe->anisotropy[0], Color(0, 0, 0, 0), 0, levels.size(), 0, 1, false); - RD::get_singleton()->texture_clear(gi_probe->anisotropy[1], Color(0, 0, 0, 0), 0, levels.size(), 0, 1, false); - } - { int total_elements = 0; for (int i = 0; i < levels.size(); i++) { total_elements += levels[i]; } - if (gi_probe_use_anisotropy) { - total_elements *= 6; - } - gi_probe->write_buffer = RD::get_singleton()->storage_buffer_create(total_elements * 16); } for (int i = 0; i < levels.size(); i++) { GIProbeInstance::Mipmap mipmap; mipmap.texture = RD::get_singleton()->texture_create_shared_from_slice(RD::TextureView(), gi_probe->texture, 0, i, RD::TEXTURE_SLICE_3D); - if (gi_probe_use_anisotropy) { - RD::TextureView tv; - tv.format_override = RD::DATA_FORMAT_R16_UINT; - mipmap.anisotropy[0] = RD::get_singleton()->texture_create_shared_from_slice(tv, gi_probe->anisotropy[0], 0, i, RD::TEXTURE_SLICE_3D); - mipmap.anisotropy[1] = RD::get_singleton()->texture_create_shared_from_slice(tv, gi_probe->anisotropy[1], 0, i, RD::TEXTURE_SLICE_3D); - } - mipmap.level = levels.size() - i - 1; mipmap.cell_offset = 0; for (uint32_t j = 0; j < mipmap.level; j++) { @@ -2383,24 +3908,6 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc u.ids.push_back(gi_probe->texture); copy_uniforms.push_back(u); } - - if (gi_probe_use_anisotropy) { - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 7; - u.ids.push_back(gi_probe->anisotropy[0]); - copy_uniforms.push_back(u); - } - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 8; - u.ids.push_back(gi_probe->anisotropy[1]); - copy_uniforms.push_back(u); - } - } - mipmap.second_bounce_uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_SECOND_BOUNCE], 0); } else { mipmap.uniform_set = RD::get_singleton()->uniform_set_create(copy_uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_COMPUTE_MIPMAP], 0); @@ -2415,23 +3922,6 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc uniforms.push_back(u); } - if (gi_probe_use_anisotropy) { - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 6; - u.ids.push_back(mipmap.anisotropy[0]); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 7; - u.ids.push_back(mipmap.anisotropy[1]); - uniforms.push_back(u); - } - } - mipmap.write_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[GI_PROBE_SHADER_VERSION_WRITE_TEXTURE], 0); gi_probe->mipmaps.push_back(mipmap); @@ -2626,22 +4116,6 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].texture); uniforms.push_back(u); } - if (gi_probe_is_anisotropic()) { - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 12; - u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].anisotropy[0]); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_IMAGE; - u.binding = 13; - u.ids.push_back(gi_probe->mipmaps[dmap.mipmap].anisotropy[1]); - uniforms.push_back(u); - } - } } dmap.uniform_set = RD::get_singleton()->uniform_set_create(uniforms, giprobe_lighting_shader_version_shaders[(write && plot) ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE_PLOT : write ? GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_WRITE : GI_PROBE_SHADER_VERSION_DYNAMIC_SHRINK_PLOT], 0); @@ -2663,10 +4137,6 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc if (gi_probe->has_dynamic_object_data) { //if it has dynamic object data, it needs to be cleared RD::get_singleton()->texture_clear(gi_probe->texture, Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1, true); - if (gi_probe_is_anisotropic()) { - RD::get_singleton()->texture_clear(gi_probe->anisotropy[0], Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1, true); - RD::get_singleton()->texture_clear(gi_probe->anisotropy[1], Color(0, 0, 0, 0), 0, gi_probe->mipmaps.size(), 0, 1, true); - } } uint32_t light_count = 0; @@ -2733,7 +4203,7 @@ void RasterizerSceneRD::gi_probe_update(RID p_probe, bool p_update_light_instanc push_constant.propagation = storage->gi_probe_get_propagation(gi_probe->probe); push_constant.dynamic_range = storage->gi_probe_get_dynamic_range(gi_probe->probe); push_constant.light_count = light_count; - push_constant.aniso_strength = storage->gi_probe_get_anisotropy_strength(gi_probe->probe); + push_constant.aniso_strength = 0; /* print_line("probe update to version " + itos(gi_probe->last_probe_version)); print_line("propagation " + rtos(push_constant.propagation)); @@ -3067,23 +4537,6 @@ void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_lis uniforms.push_back(u); } - if (gi_probe_use_anisotropy) { - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 4; - u.ids.push_back(gi_probe->anisotropy[0]); - uniforms.push_back(u); - } - { - RD::Uniform u; - u.type = RD::UNIFORM_TYPE_TEXTURE; - u.binding = 5; - u.ids.push_back(gi_probe->anisotropy[1]); - uniforms.push_back(u); - } - } - int cell_count; if (!p_emission && p_lighting && gi_probe->has_dynamic_object_data) { cell_count = push_constant.bounds[0] * push_constant.bounds[1] * push_constant.bounds[2]; @@ -3098,12 +4551,140 @@ void RasterizerSceneRD::_debug_giprobe(RID p_gi_probe, RD::DrawListID p_draw_lis RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, 36); } -const Vector<RID> &RasterizerSceneRD::gi_probe_get_slots() const { - return gi_probe_slots; -} +void RasterizerSceneRD::_debug_sdfgi_probes(RID p_render_buffers, RD::DrawListID p_draw_list, RID p_framebuffer, const CameraMatrix &p_camera_with_transform) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + + if (!rb->sdfgi) { + return; //nothing to debug + } + + SDGIShader::DebugProbesPushConstant push_constant; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + push_constant.projection[i * 4 + j] = p_camera_with_transform.matrix[i][j]; + } + } + + //gen spheres from strips + uint32_t band_points = 16; + push_constant.band_power = 4; + push_constant.sections_in_band = ((band_points / 2) - 1); + push_constant.band_mask = band_points - 2; + push_constant.section_arc = (Math_PI * 2.0) / float(push_constant.sections_in_band); + push_constant.y_mult = rb->sdfgi->y_mult; + + uint32_t total_points = push_constant.sections_in_band * band_points; + uint32_t total_probes = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count; + + push_constant.grid_size[0] = rb->sdfgi->cascade_size; + push_constant.grid_size[1] = rb->sdfgi->cascade_size; + push_constant.grid_size[2] = rb->sdfgi->cascade_size; + push_constant.cascade = 0; + + push_constant.probe_axis_size = rb->sdfgi->probe_axis_count; + + if (!rb->sdfgi->debug_probes_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(rb->sdfgi->debug_probes_uniform_set)) { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.binding = 1; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(rb->sdfgi->cascades_ubo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(rb->sdfgi->lightprobe_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 3; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 4; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(rb->sdfgi->occlusion_texture); + uniforms.push_back(u); + } + + rb->sdfgi->debug_probes_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, 0), 0); + } + + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, sdfgi_shader.debug_probes_pipeline[SDGIShader::PROBE_DEBUG_PROBES].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, rb->sdfgi->debug_probes_uniform_set, 0); + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDGIShader::DebugProbesPushConstant)); + RD::get_singleton()->draw_list_draw(p_draw_list, false, total_probes, total_points); + + if (sdfgi_debug_probe_dir != Vector3()) { + print_line("CLICK DEBUG ME?"); + uint32_t cascade = 0; + Vector3 offset = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[cascade].position)) * rb->sdfgi->cascades[cascade].cell_size * Vector3(1.0, 1.0 / rb->sdfgi->y_mult, 1.0); + Vector3 probe_size = rb->sdfgi->cascades[cascade].cell_size * (rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR) * Vector3(1.0, 1.0 / rb->sdfgi->y_mult, 1.0); + Vector3 ray_from = sdfgi_debug_probe_pos; + Vector3 ray_to = sdfgi_debug_probe_pos + sdfgi_debug_probe_dir * rb->sdfgi->cascades[cascade].cell_size * Math::sqrt(3.0) * rb->sdfgi->cascade_size; + float sphere_radius = 0.2; + float closest_dist = 1e20; + sdfgi_debug_probe_enabled = false; + + Vector3i probe_from = rb->sdfgi->cascades[cascade].position / (rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR); + for (int i = 0; i < (SDFGI::PROBE_DIVISOR + 1); i++) { + for (int j = 0; j < (SDFGI::PROBE_DIVISOR + 1); j++) { + for (int k = 0; k < (SDFGI::PROBE_DIVISOR + 1); k++) { + Vector3 pos = offset + probe_size * Vector3(i, j, k); + Vector3 res; + if (Geometry3D::segment_intersects_sphere(ray_from, ray_to, pos, sphere_radius, &res)) { + float d = ray_from.distance_to(res); + if (d < closest_dist) { + closest_dist = d; + sdfgi_debug_probe_enabled = true; + sdfgi_debug_probe_index = probe_from + Vector3i(i, j, k); + } + } + } + } + } + + if (sdfgi_debug_probe_enabled) { + print_line("found: " + sdfgi_debug_probe_index); + } else { + print_line("no found"); + } + sdfgi_debug_probe_dir = Vector3(); + } + + if (sdfgi_debug_probe_enabled) { + uint32_t cascade = 0; + uint32_t probe_cells = (rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR); + Vector3i probe_from = rb->sdfgi->cascades[cascade].position / probe_cells; + Vector3i ofs = sdfgi_debug_probe_index - probe_from; + if (ofs.x < 0 || ofs.y < 0 || ofs.z < 0) { + return; + } + if (ofs.x > SDFGI::PROBE_DIVISOR || ofs.y > SDFGI::PROBE_DIVISOR || ofs.z > SDFGI::PROBE_DIVISOR) { + return; + } + + uint32_t mult = (SDFGI::PROBE_DIVISOR + 1); + uint32_t index = ofs.z * mult * mult + ofs.y * mult + ofs.x; + + push_constant.probe_debug_index = index; -RasterizerSceneRD::GIProbeQuality RasterizerSceneRD::gi_probe_get_quality() const { - return gi_probe_quality; + uint32_t cell_count = probe_cells * 2 * probe_cells * 2 * probe_cells * 2; + + RD::get_singleton()->draw_list_bind_render_pipeline(p_draw_list, sdfgi_shader.debug_probes_pipeline[SDGIShader::PROBE_DEBUG_VISIBILITY].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); + RD::get_singleton()->draw_list_bind_uniform_set(p_draw_list, rb->sdfgi->debug_probes_uniform_set, 0); + RD::get_singleton()->draw_list_set_push_constant(p_draw_list, &push_constant, sizeof(SDGIShader::DebugProbesPushConstant)); + RD::get_singleton()->draw_list_draw(p_draw_list, false, cell_count, total_points); + } } //////////////////////////////// @@ -3273,7 +4854,7 @@ void RasterizerSceneRD::_process_sss(RID p_render_buffers, const CameraMatrix &p storage->get_effects()->sub_surface_scattering(rb->texture, rb->blur[0].mipmaps[0].texture, rb->depth_texture, p_camera, Size2i(rb->width, rb->height), sss_scale, sss_depth_scale, sss_quality); } -void RasterizerSceneRD::_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 RasterizerSceneRD::_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) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); ERR_FAIL_COND(!rb); @@ -3322,7 +4903,7 @@ void RasterizerSceneRD::_process_ssr(RID p_render_buffers, RID p_dest_framebuffe _render_buffers_uniform_set_changed(p_render_buffers); } - storage->get_effects()->screen_space_reflection(rb->texture, p_normal_buffer, ssr_roughness_quality, p_roughness_buffer, rb->ssr.blur_radius[0], rb->ssr.blur_radius[1], p_metallic, p_metallic_mask, rb->depth_texture, rb->ssr.depth_scaled, rb->ssr.normal_scaled, rb->blur[0].mipmaps[1].texture, rb->blur[1].mipmaps[0].texture, Size2i(rb->width / 2, rb->height / 2), env->ssr_max_steps, env->ssr_fade_in, env->ssr_fade_out, env->ssr_depth_tolerance, p_projection); + storage->get_effects()->screen_space_reflection(rb->texture, p_normal_buffer, ssr_roughness_quality, rb->ssr.blur_radius[0], rb->ssr.blur_radius[1], p_metallic, p_metallic_mask, rb->depth_texture, rb->ssr.depth_scaled, rb->ssr.normal_scaled, rb->blur[0].mipmaps[1].texture, rb->blur[1].mipmaps[0].texture, Size2i(rb->width / 2, rb->height / 2), env->ssr_max_steps, env->ssr_fade_in, env->ssr_fade_out, env->ssr_depth_tolerance, p_projection); storage->get_effects()->merge_specular(p_dest_framebuffer, p_specular_buffer, p_use_additive ? RID() : rb->texture, rb->blur[0].mipmaps[1].texture); } @@ -3563,17 +5144,165 @@ void RasterizerSceneRD::_render_buffers_debug_draw(RID p_render_buffers, RID p_s effects->copy_to_fb_rect(ao_buf, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true); } - if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_ROUGHNESS_LIMITER && _render_buffers_get_roughness_texture(p_render_buffers).is_valid()) { + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(p_render_buffers).is_valid()) { Size2 rtsize = storage->render_target_get_size(rb->render_target); - effects->copy_to_fb_rect(_render_buffers_get_roughness_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, true); + effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false); } - if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_NORMAL_BUFFER && _render_buffers_get_normal_texture(p_render_buffers).is_valid()) { + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_GI_BUFFER && _render_buffers_get_ambient_texture(p_render_buffers).is_valid()) { Size2 rtsize = storage->render_target_get_size(rb->render_target); - effects->copy_to_fb_rect(_render_buffers_get_normal_texture(p_render_buffers), storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false); + RID ambient_texture = _render_buffers_get_ambient_texture(p_render_buffers); + RID reflection_texture = _render_buffers_get_reflection_texture(p_render_buffers); + effects->copy_to_fb_rect(ambient_texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), false, false, false, true, reflection_texture); } } +void RasterizerSceneRD::_sdfgi_debug_draw(RID p_render_buffers, const CameraMatrix &p_projection, const Transform &p_transform) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + + if (!rb->sdfgi) { + return; //eh + } + + if (!rb->sdfgi->debug_uniform_set.is_valid() || !RD::get_singleton()->uniform_set_is_valid(rb->sdfgi->debug_uniform_set)) { + Vector<RD::Uniform> uniforms; + { + RD::Uniform u; + u.binding = 1; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { + if (i < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[i].sdf_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 2; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { + if (i < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[i].light_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 3; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { + if (i < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[i].light_aniso_0_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 4; + u.type = RD::UNIFORM_TYPE_TEXTURE; + for (uint32_t i = 0; i < SDFGI::MAX_CASCADES; i++) { + if (i < rb->sdfgi->cascades.size()) { + u.ids.push_back(rb->sdfgi->cascades[i].light_aniso_1_tex); + } else { + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + } + } + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 5; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(rb->sdfgi->occlusion_texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 8; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 9; + u.type = RD::UNIFORM_TYPE_UNIFORM_BUFFER; + u.ids.push_back(rb->sdfgi->cascades_ubo); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 10; + u.type = RD::UNIFORM_TYPE_IMAGE; + u.ids.push_back(rb->texture); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.binding = 11; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.ids.push_back(rb->sdfgi->lightprobe_texture); + uniforms.push_back(u); + } + rb->sdfgi->debug_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.debug_shader_version, 0); + } + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.debug_pipeline); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->debug_uniform_set, 0); + + SDGIShader::DebugPushConstant push_constant; + push_constant.grid_size[0] = rb->sdfgi->cascade_size; + push_constant.grid_size[1] = rb->sdfgi->cascade_size; + push_constant.grid_size[2] = rb->sdfgi->cascade_size; + push_constant.max_cascades = rb->sdfgi->cascades.size(); + push_constant.screen_size[0] = rb->width; + push_constant.screen_size[1] = rb->height; + push_constant.probe_axis_size = rb->sdfgi->probe_axis_count; + push_constant.use_occlusion = rb->sdfgi->uses_occlusion; + push_constant.y_mult = rb->sdfgi->y_mult; + + Vector2 vp_half = p_projection.get_viewport_half_extents(); + push_constant.cam_extent[0] = vp_half.x; + push_constant.cam_extent[1] = vp_half.y; + push_constant.cam_extent[2] = -p_projection.get_z_near(); + + push_constant.cam_transform[0] = p_transform.basis.elements[0][0]; + push_constant.cam_transform[1] = p_transform.basis.elements[1][0]; + push_constant.cam_transform[2] = p_transform.basis.elements[2][0]; + push_constant.cam_transform[3] = 0; + push_constant.cam_transform[4] = p_transform.basis.elements[0][1]; + push_constant.cam_transform[5] = p_transform.basis.elements[1][1]; + push_constant.cam_transform[6] = p_transform.basis.elements[2][1]; + push_constant.cam_transform[7] = 0; + push_constant.cam_transform[8] = p_transform.basis.elements[0][2]; + push_constant.cam_transform[9] = p_transform.basis.elements[1][2]; + push_constant.cam_transform[10] = p_transform.basis.elements[2][2]; + push_constant.cam_transform[11] = 0; + push_constant.cam_transform[12] = p_transform.origin.x; + push_constant.cam_transform[13] = p_transform.origin.y; + push_constant.cam_transform[14] = p_transform.origin.z; + push_constant.cam_transform[15] = 1; + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::DebugPushConstant)); + + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->width, rb->height, 1, 8, 8, 1); + RD::get_singleton()->compute_list_end(); + + Size2 rtsize = storage->render_target_get_size(rb->render_target); + storage->get_effects()->copy_to_fb_rect(rb->texture, storage->render_target_get_rd_framebuffer(rb->render_target), Rect2(Vector2(), rtsize), true); +} + RID RasterizerSceneRD::render_buffers_get_back_buffer_texture(RID p_render_buffers) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); ERR_FAIL_COND_V(!rb, RID()); @@ -3590,6 +5319,113 @@ RID RasterizerSceneRD::render_buffers_get_ao_texture(RID p_render_buffers) { return rb->ssao.ao_full.is_valid() ? rb->ssao.ao_full : rb->ssao.ao[0]; } +RID RasterizerSceneRD::render_buffers_get_gi_probe_buffer(RID p_render_buffers) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, RID()); + if (rb->giprobe_buffer.is_null()) { + rb->giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES); + } + return rb->giprobe_buffer; +} + +RID RasterizerSceneRD::render_buffers_get_default_gi_probe_buffer() { + return default_giprobe_buffer; +} + +uint32_t RasterizerSceneRD::render_buffers_get_sdfgi_cascade_count(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, 0); + ERR_FAIL_COND_V(!rb->sdfgi, 0); + + return rb->sdfgi->cascades.size(); +} +bool RasterizerSceneRD::render_buffers_is_sdfgi_enabled(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, false); + + return rb->sdfgi != nullptr; +} +RID RasterizerSceneRD::render_buffers_get_sdfgi_irradiance_probes(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, RID()); + ERR_FAIL_COND_V(!rb->sdfgi, RID()); + + return rb->sdfgi->lightprobe_texture; +} + +Vector3 RasterizerSceneRD::render_buffers_get_sdfgi_cascade_offset(RID p_render_buffers, uint32_t p_cascade) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, Vector3()); + ERR_FAIL_COND_V(!rb->sdfgi, Vector3()); + ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), Vector3()); + + return Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + rb->sdfgi->cascades[p_cascade].position)) * rb->sdfgi->cascades[p_cascade].cell_size; +} + +Vector3i RasterizerSceneRD::render_buffers_get_sdfgi_cascade_probe_offset(RID p_render_buffers, uint32_t p_cascade) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, Vector3i()); + ERR_FAIL_COND_V(!rb->sdfgi, Vector3i()); + ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), Vector3i()); + int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR; + + return rb->sdfgi->cascades[p_cascade].position / probe_divisor; +} + +float RasterizerSceneRD::render_buffers_get_sdfgi_normal_bias(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, 0); + ERR_FAIL_COND_V(!rb->sdfgi, 0); + + return rb->sdfgi->normal_bias; +} +float RasterizerSceneRD::render_buffers_get_sdfgi_cascade_probe_size(RID p_render_buffers, uint32_t p_cascade) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, 0); + ERR_FAIL_COND_V(!rb->sdfgi, 0); + ERR_FAIL_UNSIGNED_INDEX_V(p_cascade, rb->sdfgi->cascades.size(), 0); + + return float(rb->sdfgi->cascade_size) * rb->sdfgi->cascades[p_cascade].cell_size / float(rb->sdfgi->probe_axis_count - 1); +} +uint32_t RasterizerSceneRD::render_buffers_get_sdfgi_cascade_probe_count(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, 0); + ERR_FAIL_COND_V(!rb->sdfgi, 0); + + return rb->sdfgi->probe_axis_count; +} + +uint32_t RasterizerSceneRD::render_buffers_get_sdfgi_cascade_size(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, 0); + ERR_FAIL_COND_V(!rb->sdfgi, 0); + + return rb->sdfgi->cascade_size; +} + +bool RasterizerSceneRD::render_buffers_is_sdfgi_using_occlusion(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, false); + ERR_FAIL_COND_V(!rb->sdfgi, false); + + return rb->sdfgi->uses_occlusion; +} + +float RasterizerSceneRD::render_buffers_get_sdfgi_energy(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, 0); + ERR_FAIL_COND_V(!rb->sdfgi, false); + + return rb->sdfgi->energy; +} +RID RasterizerSceneRD::render_buffers_get_sdfgi_occlusion_texture(RID p_render_buffers) const { + const RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND_V(!rb, RID()); + ERR_FAIL_COND_V(!rb->sdfgi, RID()); + + return rb->sdfgi->occlusion_texture; +} + void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_render_target, int p_width, int p_height, RS::ViewportMSAA p_msaa, RenderingServer::ViewportScreenSpaceAA p_screen_space_aa) { RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); rb->width = p_width; @@ -3606,7 +5442,7 @@ void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_ren tf.height = rb->height; tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; } else { tf.usage_bits |= RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; } @@ -3616,12 +5452,20 @@ void RasterizerSceneRD::render_buffers_configure(RID p_render_buffers, RID p_ren { RD::TextureFormat tf; - tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; + if (rb->msaa == RS::VIEWPORT_MSAA_DISABLED) { + tf.format = RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_D24_UNORM_S8_UINT, RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) ? RD::DATA_FORMAT_D24_UNORM_S8_UINT : RD::DATA_FORMAT_D32_SFLOAT_S8_UINT; + } else { + tf.format = RD::DATA_FORMAT_R32_SFLOAT; + } + tf.width = p_width; tf.height = p_height; - tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + tf.usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT; + if (rb->msaa != RS::VIEWPORT_MSAA_DISABLED) { - tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + tf.usage_bits |= RD::TEXTURE_USAGE_CAN_COPY_TO_BIT | RD::TEXTURE_USAGE_STORAGE_BIT; + } else { + tf.usage_bits |= RD::TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } rb->depth_texture = RD::get_singleton()->texture_create(tf, RD::TextureView()); @@ -3748,6 +5592,14 @@ void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_ca clear_color = storage->get_default_clear_color(); } + //assign render indices to giprobes + for (int i = 0; i < p_gi_probe_cull_count; i++) { + GIProbeInstance *giprobe_inst = gi_probe_instance_owner.getornull(p_gi_probe_cull_result[i]); + if (giprobe_inst) { + giprobe_inst->render_index = i; + } + } + _render_scene(p_render_buffers, p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_light_cull_result, p_light_cull_count, p_reflection_probe_cull_result, p_reflection_probe_cull_count, p_gi_probe_cull_result, p_gi_probe_cull_count, p_decal_cull_result, p_decal_cull_count, p_lightmap_cull_result, p_lightmap_cull_count, p_environment, p_camera_effects, p_shadow_atlas, p_reflection_atlas, p_reflection_probe, p_reflection_probe_pass, clear_color); if (p_render_buffers.is_valid()) { @@ -3755,6 +5607,9 @@ void RasterizerSceneRD::render_scene(RID p_render_buffers, const Transform &p_ca _render_buffers_post_process_and_tonemap(p_render_buffers, p_environment, p_camera_effects, p_cam_projection); _render_buffers_debug_draw(p_render_buffers, p_shadow_atlas); + if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_SDFGI) { + _sdfgi_debug_draw(p_render_buffers, p_cam_projection, p_cam_transform); + } } } @@ -3938,11 +5793,455 @@ void RasterizerSceneRD::render_material(const Transform &p_cam_transform, const _render_material(p_cam_transform, p_cam_projection, p_cam_ortogonal, p_cull_result, p_cull_count, p_framebuffer, p_region); } +void RasterizerSceneRD::render_sdfgi(RID p_render_buffers, int p_region, InstanceBase **p_cull_result, int p_cull_count) { + //print_line("rendering region " + itos(p_region)); + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + ERR_FAIL_COND(!rb->sdfgi); + AABB bounds; + Vector3i from; + Vector3i size; + + int cascade_prev = _sdfgi_get_pending_region_data(p_render_buffers, p_region - 1, from, size, bounds); + int cascade_next = _sdfgi_get_pending_region_data(p_render_buffers, p_region + 1, from, size, bounds); + int cascade = _sdfgi_get_pending_region_data(p_render_buffers, p_region, from, size, bounds); + ERR_FAIL_COND(cascade < 0); + + if (cascade_prev != cascade) { + //initialize render + RD::get_singleton()->texture_clear(rb->sdfgi->render_albedo, Color(0, 0, 0, 0), 0, 1, 0, 1, true); + RD::get_singleton()->texture_clear(rb->sdfgi->render_emission, Color(0, 0, 0, 0), 0, 1, 0, 1, true); + RD::get_singleton()->texture_clear(rb->sdfgi->render_emission_aniso, Color(0, 0, 0, 0), 0, 1, 0, 1, true); + RD::get_singleton()->texture_clear(rb->sdfgi->render_geom_facing, Color(0, 0, 0, 0), 0, 1, 0, 1, true); + } + + //print_line("rendering cascade " + itos(p_region) + " objects: " + itos(p_cull_count) + " bounds: " + bounds + " from: " + from + " size: " + size + " cell size: " + rtos(rb->sdfgi->cascades[cascade].cell_size)); + _render_sdfgi(p_render_buffers, from, size, bounds, p_cull_result, p_cull_count, rb->sdfgi->render_albedo, rb->sdfgi->render_emission, rb->sdfgi->render_emission_aniso, rb->sdfgi->render_geom_facing); + + if (cascade_next != cascade) { + RENDER_TIMESTAMP(">SDFGI Update SDF"); + //done rendering! must update SDF + //clear dispatch indirect data + + SDGIShader::PreprocessPushConstant push_constant; + zeromem(&push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + + RENDER_TIMESTAMP("Scroll SDF"); + + //scroll + if (rb->sdfgi->cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) { + //for scroll + Vector3i dirty = rb->sdfgi->cascades[cascade].dirty_regions; + push_constant.scroll[0] = dirty.x; + push_constant.scroll[1] = dirty.y; + push_constant.scroll[2] = dirty.z; + } else { + //for no scroll + push_constant.scroll[0] = 0; + push_constant.scroll[1] = 0; + push_constant.scroll[2] = 0; + } + push_constant.grid_size = rb->sdfgi->cascade_size; + push_constant.cascade = cascade; + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + if (rb->sdfgi->cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) { + //must pre scroll existing data because not all is dirty + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_SCROLL]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].scroll_uniform_set, 0); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, rb->sdfgi->cascades[cascade].solid_cell_dispatch_buffer, 0); + // no barrier do all together + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_SCROLL_OCCLUSION]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].scroll_occlusion_uniform_set, 0); + + Vector3i dirty = rb->sdfgi->cascades[cascade].dirty_regions; + Vector3i groups; + groups.x = rb->sdfgi->cascade_size - ABS(dirty.x); + groups.y = rb->sdfgi->cascade_size - ABS(dirty.y); + groups.z = rb->sdfgi->cascade_size - ABS(dirty.z); + + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, groups.x, groups.y, groups.z, 4, 4, 4); + + //no barrier, continue together + + { + //scroll probes and their history also + + SDGIShader::IntegratePushConstant ipush_constant; + ipush_constant.grid_size[1] = rb->sdfgi->cascade_size; + ipush_constant.grid_size[2] = rb->sdfgi->cascade_size; + ipush_constant.grid_size[0] = rb->sdfgi->cascade_size; + ipush_constant.max_cascades = rb->sdfgi->cascades.size(); + ipush_constant.probe_axis_size = rb->sdfgi->probe_axis_count; + ipush_constant.history_index = 0; + ipush_constant.history_size = rb->sdfgi->history_size; + ipush_constant.ray_count = 0; + ipush_constant.ray_bias = 0; + ipush_constant.sky_mode = 0; + ipush_constant.sky_energy = 0; + ipush_constant.sky_color[0] = 0; + ipush_constant.sky_color[1] = 0; + ipush_constant.sky_color[2] = 0; + ipush_constant.y_mult = rb->sdfgi->y_mult; + + ipush_constant.image_size[0] = rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count; + ipush_constant.image_size[1] = rb->sdfgi->probe_axis_count; + ipush_constant.image_size[1] = rb->sdfgi->probe_axis_count; + + int32_t probe_divisor = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR; + ipush_constant.cascade = cascade; + ipush_constant.world_offset[0] = rb->sdfgi->cascades[cascade].position.x / probe_divisor; + ipush_constant.world_offset[1] = rb->sdfgi->cascades[cascade].position.y / probe_divisor; + ipush_constant.world_offset[2] = rb->sdfgi->cascades[cascade].position.z / probe_divisor; + + ipush_constant.scroll[0] = dirty.x / probe_divisor; + ipush_constant.scroll[1] = dirty.y / probe_divisor; + ipush_constant.scroll[2] = dirty.z / probe_divisor; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_SCROLL]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].integrate_uniform_set, 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count, rb->sdfgi->probe_axis_count, 1, 8, 8, 1); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.integrate_pipeline[SDGIShader::INTEGRATE_MODE_SCROLL_STORE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].integrate_uniform_set, 0); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, sdfgi_shader.integrate_default_sky_uniform_set, 1); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &ipush_constant, sizeof(SDGIShader::IntegratePushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->probe_axis_count * rb->sdfgi->probe_axis_count, rb->sdfgi->probe_axis_count, 1, 8, 8, 1); + } + + //ok finally barrier + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + + //clear dispatch indirect data + uint32_t dispatch_indirct_data[4] = { 0, 0, 0, 0 }; + RD::get_singleton()->buffer_update(rb->sdfgi->cascades[cascade].solid_cell_dispatch_buffer, 0, sizeof(uint32_t) * 4, dispatch_indirct_data, true); + + bool half_size = true; //much faster, very little differnce + static const int optimized_jf_group_size = 8; + + if (half_size) { + push_constant.grid_size >>= 1; + + uint32_t cascade_half_size = rb->sdfgi->cascade_size >> 1; + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE_HALF]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->sdf_initialize_half_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size, 4, 4, 4); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + //must start with regular jumpflood + + push_constant.half_size = true; + { + RENDER_TIMESTAMP("SDFGI Jump Flood (Half Size)"); + + uint32_t s = cascade_half_size; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD]); + + int jf_us = 0; + //start with regular jump flood for very coarse reads, as this is impossible to optimize + while (s > 1) { + s /= 2; + push_constant.step_size = s; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_half_uniform_set[jf_us], 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size, 4, 4, 4); + RD::get_singleton()->compute_list_add_barrier(compute_list); + jf_us = jf_us == 0 ? 1 : 0; + + if (cascade_half_size / (s / 2) >= optimized_jf_group_size) { + break; + } + } + + RENDER_TIMESTAMP("SDFGI Jump Flood Optimized (Half Size)"); + + //continue with optimized jump flood for smaller reads + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]); + while (s > 1) { + s /= 2; + push_constant.step_size = s; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_half_uniform_set[jf_us], 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, cascade_half_size, cascade_half_size, cascade_half_size, optimized_jf_group_size, optimized_jf_group_size, optimized_jf_group_size); + RD::get_singleton()->compute_list_add_barrier(compute_list); + jf_us = jf_us == 0 ? 1 : 0; + } + } + + // restore grid size for last passes + push_constant.grid_size = rb->sdfgi->cascade_size; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_UPSCALE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->sdf_upscale_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, 4, 4, 4); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + //run one pass of fullsize jumpflood to fix up half size arctifacts + + push_constant.half_size = false; + push_constant.step_size = 1; + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_uniform_set[rb->sdfgi->upscale_jfa_uniform_set_index], 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, optimized_jf_group_size, optimized_jf_group_size, optimized_jf_group_size); + RD::get_singleton()->compute_list_add_barrier(compute_list); + + } else { + //full size jumpflood + RENDER_TIMESTAMP("SDFGI Jump Flood"); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_INITIALIZE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->sdf_initialize_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, 4, 4, 4); + + RD::get_singleton()->compute_list_add_barrier(compute_list); + + push_constant.half_size = false; + { + uint32_t s = rb->sdfgi->cascade_size; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD]); + + int jf_us = 0; + //start with regular jump flood for very coarse reads, as this is impossible to optimize + while (s > 1) { + s /= 2; + push_constant.step_size = s; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_uniform_set[jf_us], 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, 4, 4, 4); + RD::get_singleton()->compute_list_add_barrier(compute_list); + jf_us = jf_us == 0 ? 1 : 0; + + if (rb->sdfgi->cascade_size / (s / 2) >= optimized_jf_group_size) { + break; + } + } + + RENDER_TIMESTAMP("SDFGI Jump Flood Optimized"); + + //continue with optimized jump flood for smaller reads + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_JUMP_FLOOD_OPTIMIZED]); + while (s > 1) { + s /= 2; + push_constant.step_size = s; + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->jump_flood_uniform_set[jf_us], 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, optimized_jf_group_size, optimized_jf_group_size, optimized_jf_group_size); + RD::get_singleton()->compute_list_add_barrier(compute_list); + jf_us = jf_us == 0 ? 1 : 0; + } + } + } + + RENDER_TIMESTAMP("SDFGI Occlusion"); + + // occlusion + { + uint32_t probe_size = rb->sdfgi->cascade_size / SDFGI::PROBE_DIVISOR; + Vector3i probe_global_pos = rb->sdfgi->cascades[cascade].position / probe_size; + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_OCCLUSION]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->occlusion_uniform_set, 0); + for (int i = 0; i < 8; i++) { + //dispatch all at once for performance + Vector3i offset(i & 1, (i >> 1) & 1, (i >> 2) & 1); + + if ((probe_global_pos.x & 1) != 0) { + offset.x = (offset.x + 1) & 1; + } + if ((probe_global_pos.y & 1) != 0) { + offset.y = (offset.y + 1) & 1; + } + if ((probe_global_pos.z & 1) != 0) { + offset.z = (offset.z + 1) & 1; + } + push_constant.probe_offset[0] = offset.x; + push_constant.probe_offset[1] = offset.y; + push_constant.probe_offset[2] = offset.z; + push_constant.occlusion_index = i; + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + + Vector3i groups = Vector3i(probe_size + 1, probe_size + 1, probe_size + 1) - offset; //if offseted, its one less probe per axis to compute + RD::get_singleton()->compute_list_dispatch(compute_list, groups.x, groups.y, groups.z); + } + RD::get_singleton()->compute_list_add_barrier(compute_list); + } + + RENDER_TIMESTAMP("SDFGI Store"); + + // store + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.preprocess_pipeline[SDGIShader::PRE_PROCESS_STORE]); + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, rb->sdfgi->cascades[cascade].sdf_store_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDGIShader::PreprocessPushConstant)); + RD::get_singleton()->compute_list_dispatch_threads(compute_list, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, 4, 4, 4); + + RD::get_singleton()->compute_list_end(); + + //clear these textures, as they will have previous garbage on next draw + RD::get_singleton()->texture_clear(rb->sdfgi->cascades[cascade].light_tex, Color(0, 0, 0, 0), 0, 1, 0, 1, true); + RD::get_singleton()->texture_clear(rb->sdfgi->cascades[cascade].light_aniso_0_tex, Color(0, 0, 0, 0), 0, 1, 0, 1, true); + RD::get_singleton()->texture_clear(rb->sdfgi->cascades[cascade].light_aniso_1_tex, Color(0, 0, 0, 0), 0, 1, 0, 1, true); + +#if 0 + Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rb->sdfgi->cascades[cascade].sdf, 0); + Ref<Image> img; + img.instance(); + for (uint32_t i = 0; i < rb->sdfgi->cascade_size; i++) { + Vector<uint8_t> subarr = data.subarray(128 * 128 * i, 128 * 128 * (i + 1) - 1); + img->create(rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, false, Image::FORMAT_L8, subarr); + img->save_png("res://cascade_sdf_" + itos(cascade) + "_" + itos(i) + ".png"); + } + + //finalize render and update sdf +#endif + +#if 0 + Vector<uint8_t> data = RD::get_singleton()->texture_get_data(rb->sdfgi->render_albedo, 0); + Ref<Image> img; + img.instance(); + for (uint32_t i = 0; i < rb->sdfgi->cascade_size; i++) { + Vector<uint8_t> subarr = data.subarray(128 * 128 * i * 2, 128 * 128 * (i + 1) * 2 - 1); + img->create(rb->sdfgi->cascade_size, rb->sdfgi->cascade_size, false, Image::FORMAT_RGB565, subarr); + img->convert(Image::FORMAT_RGBA8); + img->save_png("res://cascade_" + itos(cascade) + "_" + itos(i) + ".png"); + } + + //finalize render and update sdf +#endif + + RENDER_TIMESTAMP("<SDFGI Update SDF"); + } +} + +void RasterizerSceneRD::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) { + RenderBuffers *rb = render_buffers_owner.getornull(p_render_buffers); + ERR_FAIL_COND(!rb); + ERR_FAIL_COND(!rb->sdfgi); + + ERR_FAIL_COND(p_positional_light_cull_count == 0); + + _sdfgi_update_cascades(p_render_buffers); //need cascades updated for this + + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); + + RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, sdfgi_shader.direct_light_pipeline[SDGIShader::DIRECT_LIGHT_MODE_STATIC]); + + SDGIShader::DirectLightPushConstant dl_push_constant; + + dl_push_constant.grid_size[0] = rb->sdfgi->cascade_size; + dl_push_constant.grid_size[1] = rb->sdfgi->cascade_size; + dl_push_constant.grid_size[2] = rb->sdfgi->cascade_size; + dl_push_constant.max_cascades = rb->sdfgi->cascades.size(); + dl_push_constant.probe_axis_size = rb->sdfgi->probe_axis_count; + dl_push_constant.multibounce = false; // this is static light, do not multibounce yet + dl_push_constant.y_mult = rb->sdfgi->y_mult; + + //all must be processed + dl_push_constant.process_offset = 0; + dl_push_constant.process_increment = 1; + + SDGIShader::Light lights[SDFGI::MAX_STATIC_LIGHTS]; + + for (uint32_t i = 0; i < p_cascade_count; i++) { + ERR_CONTINUE(p_cascade_indices[i] >= rb->sdfgi->cascades.size()); + + SDFGI::Cascade &cc = rb->sdfgi->cascades[p_cascade_indices[i]]; + + { //fill light buffer + + AABB cascade_aabb; + cascade_aabb.position = Vector3((Vector3i(1, 1, 1) * -int32_t(rb->sdfgi->cascade_size >> 1) + cc.position)) * cc.cell_size; + cascade_aabb.size = Vector3(1, 1, 1) * rb->sdfgi->cascade_size * cc.cell_size; + + int idx = 0; + + for (uint32_t j = 0; j < p_positional_light_cull_count[i]; j++) { + if (idx == SDFGI::MAX_STATIC_LIGHTS) { + break; + } + + LightInstance *li = light_instance_owner.getornull(p_positional_light_cull_result[i][j]); + ERR_CONTINUE(!li); + + uint32_t max_sdfgi_cascade = storage->light_get_max_sdfgi_cascade(li->light); + if (p_cascade_indices[i] > max_sdfgi_cascade) { + continue; + } + + if (!cascade_aabb.intersects(li->aabb)) { + continue; + } + + lights[idx].type = storage->light_get_type(li->light); + + Vector3 dir = -li->transform.basis.get_axis(Vector3::AXIS_Z); + if (lights[idx].type == RS::LIGHT_DIRECTIONAL) { + dir.y *= rb->sdfgi->y_mult; //only makes sense for directional + dir.normalize(); + } + lights[idx].direction[0] = dir.x; + lights[idx].direction[1] = dir.y; + lights[idx].direction[2] = dir.z; + Vector3 pos = li->transform.origin; + pos.y *= rb->sdfgi->y_mult; + lights[idx].position[0] = pos.x; + lights[idx].position[1] = pos.y; + lights[idx].position[2] = pos.z; + Color color = storage->light_get_color(li->light); + color = color.to_linear(); + lights[idx].color[0] = color.r; + lights[idx].color[1] = color.g; + lights[idx].color[2] = color.b; + lights[idx].energy = storage->light_get_param(li->light, RS::LIGHT_PARAM_ENERGY); + lights[idx].has_shadow = storage->light_has_shadow(li->light); + lights[idx].attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_ATTENUATION); + lights[idx].radius = storage->light_get_param(li->light, RS::LIGHT_PARAM_RANGE); + lights[idx].spot_angle = Math::deg2rad(storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ANGLE)); + lights[idx].spot_attenuation = storage->light_get_param(li->light, RS::LIGHT_PARAM_SPOT_ATTENUATION); + + idx++; + } + + if (idx > 0) { + RD::get_singleton()->buffer_update(cc.lights_buffer, 0, idx * sizeof(SDGIShader::Light), lights, true); + } + dl_push_constant.light_count = idx; + } + + dl_push_constant.cascade = p_cascade_indices[i]; + + if (dl_push_constant.light_count > 0) { + RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cc.sdf_direct_light_uniform_set, 0); + RD::get_singleton()->compute_list_set_push_constant(compute_list, &dl_push_constant, sizeof(SDGIShader::DirectLightPushConstant)); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cc.solid_cell_dispatch_buffer, 0); + } + } + + RD::get_singleton()->compute_list_end(); +} + bool RasterizerSceneRD::free(RID p_rid) { if (render_buffers_owner.owns(p_rid)) { RenderBuffers *rb = render_buffers_owner.getornull(p_rid); _free_render_buffer_data(rb); memdelete(rb->data); + if (rb->sdfgi) { + _sdfgi_erase(rb); + } render_buffers_owner.free(p_rid); } else if (environment_owner.owns(p_rid)) { //not much to delete, just free it @@ -3966,18 +6265,12 @@ bool RasterizerSceneRD::free(RID p_rid) { RD::get_singleton()->free(gi_probe->texture); RD::get_singleton()->free(gi_probe->write_buffer); } - if (gi_probe->anisotropy[0].is_valid()) { - RD::get_singleton()->free(gi_probe->anisotropy[0]); - RD::get_singleton()->free(gi_probe->anisotropy[1]); - } for (int i = 0; i < gi_probe->dynamic_maps.size(); i++) { RD::get_singleton()->free(gi_probe->dynamic_maps[i].texture); RD::get_singleton()->free(gi_probe->dynamic_maps[i].depth); } - gi_probe_slots.write[gi_probe->slot] = RID(); - gi_probe_instance_owner.free(p_rid); } else if (sky_owner.owns(p_rid)) { _update_dirty_skys(); @@ -4050,17 +6343,22 @@ void RasterizerSceneRD::set_time(double p_time, double p_step) { time_step = p_step; } -void RasterizerSceneRD::screen_space_roughness_limiter_set_active(bool p_enable, float p_curve) { +void RasterizerSceneRD::screen_space_roughness_limiter_set_active(bool p_enable, float p_amount, float p_limit) { screen_space_roughness_limiter = p_enable; - screen_space_roughness_limiter_curve = p_curve; + screen_space_roughness_limiter_amount = p_amount; + screen_space_roughness_limiter_limit = p_limit; } bool RasterizerSceneRD::screen_space_roughness_limiter_is_active() const { return screen_space_roughness_limiter; } -float RasterizerSceneRD::screen_space_roughness_limiter_get_curve() const { - return screen_space_roughness_limiter_curve; +float RasterizerSceneRD::screen_space_roughness_limiter_get_amount() const { + return screen_space_roughness_limiter_amount; +} + +float RasterizerSceneRD::screen_space_roughness_limiter_get_limit() const { + return screen_space_roughness_limiter_limit; } TypedArray<Image> RasterizerSceneRD::bake_render_uv2(RID p_base, const Vector<RID> &p_material_overrides, const Size2i &p_image_size) { @@ -4154,6 +6452,11 @@ TypedArray<Image> RasterizerSceneRD::bake_render_uv2(RID p_base, const Vector<RI return ret; } +void RasterizerSceneRD::sdfgi_set_debug_probe_select(const Vector3 &p_position, const Vector3 &p_dir) { + sdfgi_debug_probe_pos = p_position; + sdfgi_debug_probe_dir = p_dir; +} + RasterizerSceneRD *RasterizerSceneRD::singleton = nullptr; RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { @@ -4165,7 +6468,7 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { sky_use_cubemap_array = GLOBAL_GET("rendering/quality/reflections/texture_array_reflections"); // sky_use_cubemap_array = false; - uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); + //uint32_t textures_per_stage = RD::get_singleton()->limit_get(RD::LIMIT_MAX_TEXTURES_PER_SHADER_STAGE); { //kinda complicated to compute the amount of slots, we try to use as many as we can @@ -4174,34 +6477,9 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { gi_probe_lights = memnew_arr(GIProbeLight, gi_probe_max_lights); gi_probe_lights_uniform = RD::get_singleton()->uniform_buffer_create(gi_probe_max_lights * sizeof(GIProbeLight)); - - gi_probe_use_anisotropy = GLOBAL_GET("rendering/quality/gi_probes/anisotropic"); - gi_probe_quality = GIProbeQuality(CLAMP(int(GLOBAL_GET("rendering/quality/gi_probes/quality")), 0, 2)); - - if (textures_per_stage <= 16) { - gi_probe_slots.resize(2); //thats all you can get - gi_probe_use_anisotropy = false; - } else if (textures_per_stage <= 31) { - gi_probe_slots.resize(4); //thats all you can get, iOS - gi_probe_use_anisotropy = false; - } else if (textures_per_stage <= 128) { - gi_probe_slots.resize(32); //old intel - gi_probe_use_anisotropy = false; - } else if (textures_per_stage <= 256) { - gi_probe_slots.resize(64); //old intel too - gi_probe_use_anisotropy = false; - } else { - if (gi_probe_use_anisotropy) { - gi_probe_slots.resize(1024 / 3); //needs 3 textures - } else { - gi_probe_slots.resize(1024); //modern intel, nvidia, 8192 or greater - } - } + gi_probe_quality = RS::GIProbeQuality(CLAMP(int(GLOBAL_GET("rendering/quality/gi_probes/quality")), 0, 1)); String defines = "\n#define MAX_LIGHTS " + itos(gi_probe_max_lights) + "\n"; - if (gi_probe_use_anisotropy) { - defines += "\n#define MODE_ANISOTROPIC\n"; - } Vector<String> versions; versions.push_back("\n#define MODE_COMPUTE_LIGHT\n"); @@ -4223,9 +6501,6 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { { String defines; - if (gi_probe_use_anisotropy) { - defines += "\n#define USE_ANISOTROPY\n"; - } Vector<String> versions; versions.push_back("\n#define MODE_DEBUG_COLOR\n"); versions.push_back("\n#define MODE_DEBUG_LIGHT\n"); @@ -4373,11 +6648,131 @@ RasterizerSceneRD::RasterizerSceneRD(RasterizerStorageRD *p_storage) { sky_scene_state.sampler_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sky_shader.default_shader_rd, SKY_SET_SAMPLERS); } + { + Vector<String> preprocess_modes; + preprocess_modes.push_back("\n#define MODE_SCROLL\n"); + preprocess_modes.push_back("\n#define MODE_SCROLL_OCCLUSION\n"); + preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD\n"); + preprocess_modes.push_back("\n#define MODE_INITIALIZE_JUMP_FLOOD_HALF\n"); + preprocess_modes.push_back("\n#define MODE_JUMPFLOOD\n"); + preprocess_modes.push_back("\n#define MODE_JUMPFLOOD_OPTIMIZED\n"); + preprocess_modes.push_back("\n#define MODE_UPSCALE_JUMP_FLOOD\n"); + preprocess_modes.push_back("\n#define MODE_OCCLUSION\n"); + preprocess_modes.push_back("\n#define MODE_STORE\n"); + String defines = "\n#define OCCLUSION_SIZE " + itos(SDFGI::CASCADE_SIZE / SDFGI::PROBE_DIVISOR) + "\n"; + sdfgi_shader.preprocess.initialize(preprocess_modes, defines); + sdfgi_shader.preprocess_shader = sdfgi_shader.preprocess.version_create(); + for (int i = 0; i < SDGIShader::PRE_PROCESS_MAX; i++) { + sdfgi_shader.preprocess_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.preprocess.version_get_shader(sdfgi_shader.preprocess_shader, i)); + } + } + + { + //calculate tables + String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + + Vector<String> direct_light_modes; + direct_light_modes.push_back("\n#define MODE_PROCESS_STATIC\n"); + direct_light_modes.push_back("\n#define MODE_PROCESS_DYNAMIC\n"); + sdfgi_shader.direct_light.initialize(direct_light_modes, defines); + sdfgi_shader.direct_light_shader = sdfgi_shader.direct_light.version_create(); + for (int i = 0; i < SDGIShader::DIRECT_LIGHT_MODE_MAX; i++) { + sdfgi_shader.direct_light_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.direct_light.version_get_shader(sdfgi_shader.direct_light_shader, i)); + } + } + + { + //calculate tables + String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + defines += "\n#define SH_SIZE " + itos(SDFGI::SH_SIZE) + "\n"; + + Vector<String> integrate_modes; + integrate_modes.push_back("\n#define MODE_PROCESS\n"); + integrate_modes.push_back("\n#define MODE_STORE\n"); + integrate_modes.push_back("\n#define MODE_SCROLL\n"); + integrate_modes.push_back("\n#define MODE_SCROLL_STORE\n"); + sdfgi_shader.integrate.initialize(integrate_modes, defines); + sdfgi_shader.integrate_shader = sdfgi_shader.integrate.version_create(); + + for (int i = 0; i < SDGIShader::INTEGRATE_MODE_MAX; i++) { + sdfgi_shader.integrate_pipeline[i] = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, i)); + } + + { + Vector<RD::Uniform> uniforms; + + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_TEXTURE; + u.binding = 0; + u.ids.push_back(storage->texture_rd_get_default(RasterizerStorageRD::DEFAULT_RD_TEXTURE_3D_WHITE)); + uniforms.push_back(u); + } + { + RD::Uniform u; + u.type = RD::UNIFORM_TYPE_SAMPLER; + u.binding = 1; + u.ids.push_back(storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR_WITH_MIPMAPS, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED)); + uniforms.push_back(u); + } + + sdfgi_shader.integrate_default_sky_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, sdfgi_shader.integrate.version_get_shader(sdfgi_shader.integrate_shader, 0), 1); + } + } + { + //calculate tables + String defines = "\n#define SDFGI_OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + Vector<String> gi_modes; + gi_modes.push_back(""); + gi.shader.initialize(gi_modes, defines); + gi.shader_version = gi.shader.version_create(); + for (int i = 0; i < GI::MODE_MAX; i++) { + gi.pipelines[i] = RD::get_singleton()->compute_pipeline_create(gi.shader.version_get_shader(gi.shader_version, i)); + } + + gi.sdfgi_ubo = RD::get_singleton()->uniform_buffer_create(sizeof(GI::SDFGIData)); + } + { + String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + Vector<String> debug_modes; + debug_modes.push_back(""); + sdfgi_shader.debug.initialize(debug_modes, defines); + sdfgi_shader.debug_shader = sdfgi_shader.debug.version_create(); + sdfgi_shader.debug_shader_version = sdfgi_shader.debug.version_get_shader(sdfgi_shader.debug_shader, 0); + sdfgi_shader.debug_pipeline = RD::get_singleton()->compute_pipeline_create(sdfgi_shader.debug_shader_version); + } + { + String defines = "\n#define OCT_SIZE " + itos(SDFGI::LIGHTPROBE_OCT_SIZE) + "\n"; + + Vector<String> versions; + versions.push_back("\n#define MODE_PROBES\n"); + versions.push_back("\n#define MODE_VISIBILITY\n"); + + sdfgi_shader.debug_probes.initialize(versions, defines); + sdfgi_shader.debug_probes_shader = sdfgi_shader.debug_probes.version_create(); + + { + RD::PipelineRasterizationState rs; + rs.cull_mode = RD::POLYGON_CULL_DISABLED; + RD::PipelineDepthStencilState ds; + ds.enable_depth_test = true; + ds.enable_depth_write = true; + ds.depth_compare_operator = RD::COMPARE_OP_LESS_OR_EQUAL; + for (int i = 0; i < SDGIShader::PROBE_DEBUG_MAX; i++) { + RID debug_probes_shader_version = sdfgi_shader.debug_probes.version_get_shader(sdfgi_shader.debug_probes_shader, i); + sdfgi_shader.debug_probes_pipeline[i].setup(debug_probes_shader_version, RD::RENDER_PRIMITIVE_TRIANGLE_STRIPS, rs, RD::PipelineMultisampleState(), ds, RD::PipelineColorBlendState::create_disabled(), 0); + } + } + } + + default_giprobe_buffer = RD::get_singleton()->uniform_buffer_create(sizeof(GI::GIProbeData) * RenderBuffers::MAX_GIPROBES); + camera_effects_set_dof_blur_bokeh_shape(RS::DOFBokehShape(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_shape")))); camera_effects_set_dof_blur_quality(RS::DOFBlurQuality(int(GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_bokeh_quality"))), GLOBAL_GET("rendering/quality/depth_of_field/depth_of_field_use_jitter")); environment_set_ssao_quality(RS::EnvironmentSSAOQuality(int(GLOBAL_GET("rendering/quality/ssao/quality"))), GLOBAL_GET("rendering/quality/ssao/half_size")); - screen_space_roughness_limiter = GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter"); - screen_space_roughness_limiter_curve = GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_curve"); + screen_space_roughness_limiter = GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_enabled"); + screen_space_roughness_limiter_amount = GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_amount"); + screen_space_roughness_limiter_limit = GLOBAL_GET("rendering/quality/screen_filters/screen_space_roughness_limiter_limit"); glow_bicubic_upscale = int(GLOBAL_GET("rendering/quality/glow/upscale_mode")) > 0; ssr_roughness_quality = RS::EnvironmentSSRRoughnessQuality(int(GLOBAL_GET("rendering/quality/screen_space_reflection/roughness_quality"))); sss_quality = RS::SubSurfaceScatteringQuality(int(GLOBAL_GET("rendering/quality/subsurface_scattering/subsurface_scattering_quality"))); @@ -4406,6 +6801,8 @@ RasterizerSceneRD::~RasterizerSceneRD() { RD::get_singleton()->free(sky_scene_state.light_uniform_set); } + RD::get_singleton()->free(default_giprobe_buffer); + RD::get_singleton()->free(gi_probe_lights_uniform); giprobe_debug_shader.version_free(giprobe_debug_shader_version); giprobe_shader.version_free(giprobe_lighting_shader_version); diff --git a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h b/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h index 781dbd50cc..88c2f5a5e6 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_scene_rd.h +++ b/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(); }; diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp index a5151d1ff8..8f3e2c25f9 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.cpp +++ b/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 diff --git a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h b/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h index fe9377192e..b1146f1386 100644 --- a/servers/rendering/rasterizer_rd/rasterizer_storage_rd.h +++ b/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); diff --git a/servers/rendering/rasterizer_rd/shaders/SCsub b/servers/rendering/rasterizer_rd/shaders/SCsub index a454d144aa..67f4edc626 100644 --- a/servers/rendering/rasterizer_rd/shaders/SCsub +++ b/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") diff --git a/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl b/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl index b1cfe1e91e..9751e13b4e 100644 --- a/servers/rendering/rasterizer_rd/shaders/copy_to_fb.glsl +++ b/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; } diff --git a/servers/rendering/rasterizer_rd/shaders/gi.glsl b/servers/rendering/rasterizer_rd/shaders/gi.glsl new file mode 100644 index 0000000000..a1939f75ad --- /dev/null +++ b/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); +} diff --git a/servers/rendering/rasterizer_rd/shaders/resolve.glsl b/servers/rendering/rasterizer_rd/shaders/resolve.glsl new file mode 100644 index 0000000000..9429a66dc9 --- /dev/null +++ b/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 +} diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl index 9f42b0f814..9a94a7914e 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end.glsl +++ b/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 diff --git a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl b/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl index 1cac12406a..1244599097 100644 --- a/servers/rendering/rasterizer_rd/shaders/scene_high_end_inc.glsl +++ b/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) */ diff --git a/servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl b/servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl index 084f28d932..a8ee33a664 100644 --- a/servers/rendering/rasterizer_rd/shaders/screen_space_reflection.glsl +++ b/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; diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_debug.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_debug.glsl new file mode 100644 index 0000000000..813ea29fa1 --- /dev/null +++ b/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)); +} diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_debug_probes.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_debug_probes.glsl new file mode 100644 index 0000000000..08da283dad --- /dev/null +++ b/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 +} diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_direct_light.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_direct_light.glsl new file mode 100644 index 0000000000..c4b29216d5 --- /dev/null +++ b/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 +} diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_fields.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_fields.glsl new file mode 100644 index 0000000000..eec0a90c0d --- /dev/null +++ b/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 +} diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_integrate.glsl new file mode 100644 index 0000000000..e4779aafaf --- /dev/null +++ b/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 +} diff --git a/servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl b/servers/rendering/rasterizer_rd/shaders/sdfgi_preprocess.glsl new file mode 100644 index 0000000000..7a523d3a3c --- /dev/null +++ b/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 +} |