From 616943d42bcbba01e0d60ce2843dbd4319e82b49 Mon Sep 17 00:00:00 2001 From: sphynx-owner Date: Sun, 7 Jul 2024 21:36:54 +0300 Subject: [PATCH] first commit --- .../LICENSE-MotionBlurEffect | 21 ++ .../jfp_backtracking_experimental.glsl | 257 ++++++++++++++++ .../MyJumpFloodIteration/jump_flood_blur.gd | 290 ++++++++++++++++++ .../MyJumpFloodIteration/jump_flood_blur.glsl | 261 ++++++++++++++++ .../jump_flood_overlay.glsl | 17 + 5 files changed, 846 insertions(+) create mode 100644 addons/MyJumpFloodIteration/LICENSE-MotionBlurEffect create mode 100644 addons/MyJumpFloodIteration/jfp_backtracking_experimental.glsl create mode 100644 addons/MyJumpFloodIteration/jump_flood_blur.gd create mode 100644 addons/MyJumpFloodIteration/jump_flood_blur.glsl create mode 100644 addons/MyJumpFloodIteration/jump_flood_overlay.glsl diff --git a/addons/MyJumpFloodIteration/LICENSE-MotionBlurEffect b/addons/MyJumpFloodIteration/LICENSE-MotionBlurEffect new file mode 100644 index 0000000..2dea453 --- /dev/null +++ b/addons/MyJumpFloodIteration/LICENSE-MotionBlurEffect @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 sphynx-owner + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/addons/MyJumpFloodIteration/jfp_backtracking_experimental.glsl b/addons/MyJumpFloodIteration/jfp_backtracking_experimental.glsl new file mode 100644 index 0000000..b614956 --- /dev/null +++ b/addons/MyJumpFloodIteration/jfp_backtracking_experimental.glsl @@ -0,0 +1,257 @@ +#[compute] +#version 450 + +#define FLT_MAX 3.402823466e+38 +#define FLT_MIN 1.175494351e-38 +#define DBL_MAX 1.7976931348623158e+308 +#define DBL_MIN 2.2250738585072014e-308 + +layout(set = 0, binding = 0) uniform sampler2D depth_sampler; +layout(set = 0, binding = 1) uniform sampler2D velocity_sampler; +layout(rgba16f, set = 0, binding = 2) uniform image2D buffer_a; +layout(rgba16f, set = 0, binding = 3) uniform image2D buffer_b; + +layout(push_constant, std430) uniform Params +{ + int iteration_index; + int last_iteration_index; + int nan1; + int nan2; + float perpen_error_thresh; + float sample_step_multiplier; + float motion_blur_intensity; + float velocity_match_threshold; + float parallel_sensitivity; + float perpendicular_sensitivity; + float nan3; + float nan4; +} params; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; + +const int kernel_size = 9;//8;// + +const vec2 check_step_kernel[kernel_size] = { + vec2(0, 0), + vec2(1, 1), + vec2(0, 1), + vec2(-1, 1), + vec2(1, 0), + vec2(1, -1), + vec2(-1, 0), + vec2(-1, -1), + vec2(0, -1), +}; + +// near plane distance +float npd = 0.05; + +vec4 get_value(bool a, ivec2 uvi, ivec2 render_size) +{ + if ((uvi.x >= render_size.x) || (uvi.x < 0) || (uvi.y >= render_size.y) || (uvi.y < 0)) + { + return vec4(-1, -1, 0, 1); + } + + if(a) + { + return imageLoad(buffer_a, uvi); + } + + return imageLoad(buffer_b, uvi); +} + +void set_value(bool a, ivec2 uvi, vec4 value, ivec2 render_size) +{ + if ((uvi.x >= render_size.x) || (uvi.x < 0) || (uvi.y >= render_size.y) || (uvi.y < 0)) + { + return; + } + if(a) + { + imageStore(buffer_a, uvi, value); + return; + } + + imageStore(buffer_b, uvi, value); +} + +// Motion similarity +// ---------------------------------------------------------- +float get_motion_difference(vec2 V, vec2 V2, float parallel_sensitivity, float perpendicular_sensitivity) +{ + vec2 VO = V - V2; + double parallel = abs(dot(VO, V) / max(DBL_MIN, dot(V, V))); + vec2 perpen_V = vec2(V.y, -V.x); + double perpendicular = abs(dot(VO, perpen_V) / max(DBL_MIN, dot(V, V))); + float difference = float(parallel) * parallel_sensitivity + float(perpendicular) * perpendicular_sensitivity; + return clamp(difference, 0, 1); +} +// ---------------------------------------------------------- + +vec4 sample_fitness(vec2 uv_offset, vec4 uv_sample) +{ + vec2 sample_velocity = -uv_sample.xy; + + if (dot(sample_velocity, sample_velocity) <= FLT_MIN) + { + return vec4(FLT_MAX, FLT_MAX, FLT_MAX, 0); + } + +// if(dot(uv_offset, uv_offset) <= FLT_MIN) +// { +// uv_offset = normalize(sample_velocity) * FLT_MIN; +// } + + double velocity_space_distance = dot(sample_velocity, uv_offset) / max(FLT_MIN, dot(sample_velocity, sample_velocity)); + + double mid_point = params.motion_blur_intensity / 2; + + double absolute_velocity_space_distance = abs(velocity_space_distance - mid_point); + + double within_velocity_range = step(absolute_velocity_space_distance, mid_point); + + vec2 perpen_offset = vec2(uv_offset.y, -uv_offset.x); + + double side_offset = abs(dot(perpen_offset, sample_velocity)) / max(FLT_MIN, dot(sample_velocity, sample_velocity)); + + double within_perpen_error_range = step(side_offset, params.perpen_error_thresh * params.motion_blur_intensity); + + return vec4(absolute_velocity_space_distance, velocity_space_distance, uv_sample.z, within_velocity_range * within_perpen_error_range); +} + +bool is_sample_better(vec4 a, vec4 b) +{ + if((a.w == b.w) && (a.w == 1)) + { + return a.z < b.z; + } + + float nearer = a.z > b.z ? 1 : 0; + + return a.x * b.w * nearer < b.x * a.w; +} + +vec4 get_backtracked_sample(vec2 uvn, vec2 chosen_uv, vec2 chosen_velocity, vec4 best_sample_fitness, vec2 render_size) +{ + //return vec4(chosen_uv, best_sample_fitness.x, 0);// comment this to enable backtracking + + int step_count = 16; + + float smallest_step = 1 / max(render_size.x, render_size.y); + + float max_dilation_radius = pow(2, params.last_iteration_index) * params.sample_step_multiplier * smallest_step / (length(chosen_velocity) * params.motion_blur_intensity); + + float general_velocity_multiplier = min(best_sample_fitness.y, max_dilation_radius); + + for(int i = -step_count; i < step_count + 1; i++) + { + float velocity_multiplier = general_velocity_multiplier * (1 + float(i) / float(step_count)); + + if(velocity_multiplier > params.motion_blur_intensity + 0.2 || velocity_multiplier < FLT_MIN) + { + continue; + } + + vec2 new_sample = uvn - chosen_velocity * velocity_multiplier; + + if((new_sample.x < 0.) || (new_sample.x > 1.) || (new_sample.y < 0.) || (new_sample.y > 1.)) + { + continue; + } + + vec2 velocity_test = textureLod(velocity_sampler, new_sample, 0.0).xy; + + if(get_motion_difference(chosen_velocity, velocity_test, params.parallel_sensitivity, params.perpendicular_sensitivity) <= params.velocity_match_threshold) + { + chosen_uv = new_sample; + best_sample_fitness.x = velocity_multiplier; + return vec4(chosen_uv, best_sample_fitness.x, 0); + } + } + + return vec4(uvn, best_sample_fitness.x, 1); +} + +void main() +{ + ivec2 render_size = ivec2(textureSize(velocity_sampler, 0)); + ivec2 uvi = ivec2(gl_GlobalInvocationID.xy); + if ((uvi.x >= render_size.x) || (uvi.y >= render_size.y)) + { + return; + } + vec2 uvn = (vec2(uvi) + vec2(0.5)) / render_size; + + int iteration_index = params.iteration_index; + + float step_size = round(pow(2, params.last_iteration_index - iteration_index)); + + vec2 uv_step = vec2(step_size) * params.sample_step_multiplier / render_size; + + vec4 best_sample_fitness = vec4(FLT_MAX, FLT_MAX, FLT_MAX, 0); + + vec2 chosen_uv = uvn; + + vec2 chosen_velocity = vec2(0); + + bool set_a = !bool(step(0.5, float(iteration_index % 2))); + + for(int i = 0; i < kernel_size; i++) + { + if((true || params.iteration_index == 0) && i == 0) + { + continue; + } + + vec2 step_offset = check_step_kernel[i] * uv_step; + vec2 check_uv = uvn + step_offset; + + if((check_uv.x < 0.) || (check_uv.x > 1.) || (check_uv.y < 0.) || (check_uv.y > 1.)) + { + continue; + } + + if(iteration_index > 0) + { + ivec2 check_uv2 = ivec2(check_uv * render_size); + + vec4 buffer_load = get_value(!set_a, check_uv2, render_size); + + check_uv = buffer_load.xy; + + step_offset = check_uv - uvn; + } + + vec4 uv_sample = vec4(textureLod(velocity_sampler, check_uv, 0.0).xy, npd / textureLod(depth_sampler, check_uv, 0.0).x, 0); + + vec4 current_sample_fitness = sample_fitness(step_offset, uv_sample); + + if (is_sample_better(current_sample_fitness, best_sample_fitness)) + { + best_sample_fitness = current_sample_fitness; + chosen_uv = check_uv; + chosen_velocity = uv_sample.xy; + } + } + + if(iteration_index < params.last_iteration_index) + { + set_value(set_a, uvi, vec4(chosen_uv, best_sample_fitness.x, best_sample_fitness.w), render_size); + return; + } + + float depth = npd / textureLod(depth_sampler, uvn, 0.0).x; + + if(best_sample_fitness.w == 0 || depth < best_sample_fitness.z) + { + set_value(set_a, uvi, vec4(uvn, best_sample_fitness.x, 0), render_size); + return; + } + + vec4 backtracked_sample = get_backtracked_sample(uvn, chosen_uv, chosen_velocity, best_sample_fitness, render_size); + + set_value(set_a, uvi, backtracked_sample, render_size); + + return; +} \ No newline at end of file diff --git a/addons/MyJumpFloodIteration/jump_flood_blur.gd b/addons/MyJumpFloodIteration/jump_flood_blur.gd new file mode 100644 index 0000000..c507d48 --- /dev/null +++ b/addons/MyJumpFloodIteration/jump_flood_blur.gd @@ -0,0 +1,290 @@ +extends CompositorEffect +class_name MotionBlurSphynxJumpFlood + +@export_group("Motion Blur", "motion_blur_") +# diminishing returns over 16 +@export_range(4, 64) var motion_blur_samples: int = 8 +# you really don't want this over 0.5, but you can if you want to try +@export_range(0, 0.5, 0.001, "or_greater") var motion_blur_intensity: float = 1 +@export_range(0, 1) var motion_blur_center_fade: float = 0.0 + + +@export var blur_shader_file : RDShaderFile = preload("res://addons/MyJumpFloodIteration/jump_flood_blur.glsl"): + set(value): + blur_shader_file = value + _init() + +@export var overlay_shader_file : RDShaderFile = preload("res://addons/MyJumpFloodIteration/jump_flood_overlay.glsl"): + set(value): + overlay_shader_file = value + _init() + +@export var construction_pass : RDShaderFile = preload("res://addons/MyJumpFloodIteration/jfp_backtracking_experimental.glsl"): + set(value): + construction_pass = value + _init() + +## the portion of speed that is allowed for side bleed of velocities +## during the jfa dilation passes and before backtracking. Getting this a higher value +## would make it so that meshes at movement blur more reliably, but also bleed +## further perpendicularly to their velocity, thus wash elemets behind them out. +@export var perpen_error_threshold : float = 0.3 + +## an initial step size that can increase the dilation radius proportionally, at the +## sacrifice of some quality in the final resolution of the dilation +@export var sample_step_multiplier : float = 8 + +## how sensitive the backtracking for velocities be +@export var backtracking_velocity_match_threshold : float = 0.49 + +## how sensitively the backtracking should treat velocities that are a different +## length along that velocity +@export var backtracking_velocity_match_parallel_sensitivity : float = 0.5; + +## how sensitively the backtracking should treat velcoities that have perpendicular +## offset to that velocity +@export var backtracking_velcoity_match_perpendicular_sensitivity : float = 0.1; + +## the number of passes performed by the jump flood algorithm based dilation, +## each pass added doubles the maximum radius of dilation available +@export var JFA_pass_count : int = 3 + +## wether this motion blur stays the same intensity below +## target_constant_framerate +@export var framerate_independent : bool = true + +## if framerate_independent is enabled, the blur would simulate +## sutter speeds at that framerate, and up. +@export var target_constant_framerate : float = 30 + +var rd: RenderingDevice + +var linear_sampler: RID + +var construct_shader : RID +var construct_pipeline : RID + +var motion_blur_shader: RID +var motion_blur_pipeline: RID + +var overlay_shader: RID +var overlay_pipeline: RID + +var context: StringName = "MotionBlur" +var texture: StringName = "texture" + +var buffer_a : StringName = "buffer_a" +var buffer_b : StringName = "buffer_b" + +var past_color : StringName = "past_color" + +var velocity_3D : StringName = "velocity_3D" +var velocity_curl : StringName = "velocity_curl" + +var draw_debug : float = 0 + +var freeze : bool = false + +func _init(): + effect_callback_type = CompositorEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT + needs_motion_vectors = true + RenderingServer.call_on_render_thread(_initialize_compute) + +func _notification(what): + if what == NOTIFICATION_PREDELETE: + if linear_sampler.is_valid(): + rd.free_rid(linear_sampler) + if motion_blur_shader.is_valid(): + rd.free_rid(motion_blur_shader) + if overlay_shader.is_valid(): + rd.free_rid(overlay_shader) + +func _initialize_compute(): + rd = RenderingServer.get_rendering_device() + if !rd: + return + + var sampler_state := RDSamplerState.new() + sampler_state.min_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.mag_filter = RenderingDevice.SAMPLER_FILTER_LINEAR + sampler_state.repeat_u = RenderingDevice.SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE + sampler_state.repeat_v = RenderingDevice.SAMPLER_REPEAT_MODE_CLAMP_TO_EDGE + linear_sampler = rd.sampler_create(sampler_state) + + var construct_shader_spirv : RDShaderSPIRV = construction_pass.get_spirv() + construct_shader = rd.shader_create_from_spirv(construct_shader_spirv) + construct_pipeline = rd.compute_pipeline_create(construct_shader) + + var shader_spirv: RDShaderSPIRV = blur_shader_file.get_spirv() + motion_blur_shader = rd.shader_create_from_spirv(shader_spirv) + motion_blur_pipeline = rd.compute_pipeline_create(motion_blur_shader) + + var overlay_shader_spirv: RDShaderSPIRV = overlay_shader_file.get_spirv() + overlay_shader = rd.shader_create_from_spirv(overlay_shader_spirv) + overlay_pipeline = rd.compute_pipeline_create(overlay_shader) + +func get_image_uniform(image: RID, binding: int) -> RDUniform: + var uniform: RDUniform = RDUniform.new() + uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE + uniform.binding = binding + uniform.add_id(image) + return uniform + +func get_sampler_uniform(image: RID, binding: int) -> RDUniform: + var uniform: RDUniform = RDUniform.new() + uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_SAMPLER_WITH_TEXTURE + uniform.binding = binding + uniform.add_id(linear_sampler) + uniform.add_id(image) + return uniform + + + +var temp_motion_blur_intensity : float + +var previous_time : float = 0 + +func _render_callback(p_effect_callback_type, p_render_data): + var time : float = float(Time.get_ticks_msec()) / 1000 + + var delta_time : float = time - previous_time + + previous_time = time + + temp_motion_blur_intensity = motion_blur_intensity + + if framerate_independent: + var capped_frame_time : float = min(1 / target_constant_framerate, delta_time) + temp_motion_blur_intensity = motion_blur_intensity * capped_frame_time / delta_time + + if rd and p_effect_callback_type == CompositorEffect.EFFECT_CALLBACK_TYPE_POST_TRANSPARENT: + var render_scene_buffers: RenderSceneBuffersRD = p_render_data.get_render_scene_buffers() + var render_scene_data: RenderSceneDataRD = p_render_data.get_render_scene_data() + if render_scene_buffers and render_scene_data: + var render_size: Vector2 = render_scene_buffers.get_internal_size() + if render_size.x == 0.0 or render_size.y == 0.0: + return + + ensure_texture(texture, render_scene_buffers) + ensure_texture(buffer_a, render_scene_buffers, true)#, Vector2(0.25, 0.25)) + ensure_texture(buffer_b, render_scene_buffers, true)#, Vector2(0.25, 0.25)) + ensure_texture(past_color, render_scene_buffers) + + rd.draw_command_begin_label("Motion Blur", Color(1.0, 1.0, 1.0, 1.0)) + + var last_iteration_index : int = JFA_pass_count - 1; + + var push_constant: PackedFloat32Array = [ + motion_blur_samples, temp_motion_blur_intensity, + motion_blur_center_fade, draw_debug, + freeze, + Engine.get_frames_drawn() % 8, + last_iteration_index, + sample_step_multiplier + ] + + var view_count = render_scene_buffers.get_view_count() + for view in range(view_count): + var color_image := render_scene_buffers.get_color_layer(view) + var depth_image := render_scene_buffers.get_depth_layer(view) + var velocity_image := render_scene_buffers.get_velocity_layer(view) + var texture_image := render_scene_buffers.get_texture_slice(context, texture, view, 0, 1, 1) + var buffer_a_image := render_scene_buffers.get_texture_slice(context, buffer_a, view, 0, 1, 1) + var buffer_b_image := render_scene_buffers.get_texture_slice(context, buffer_b, view, 0, 1, 1) + var past_color_image := render_scene_buffers.get_texture_slice(context, past_color, view, 0, 1, 1) + rd.draw_command_begin_label("Construct blur " + str(view), Color(1.0, 1.0, 1.0, 1.0)) + + var tex_uniform_set + var compute_list + + var x_groups := floori((render_size.x - 1) / 8 + 1) + var y_groups := floori((render_size.y - 1) / 8 + 1) + + tex_uniform_set = UniformSetCacheRD.get_cache(construct_shader, 0, [ + get_sampler_uniform(depth_image, 0), + get_sampler_uniform(velocity_image, 1), + get_image_uniform(buffer_a_image, 2), + get_image_uniform(buffer_b_image, 3), + ]) + + compute_list = rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, construct_pipeline) + rd.compute_list_bind_uniform_set(compute_list, tex_uniform_set, 0) + + for i in JFA_pass_count: + var jf_push_constants : PackedInt32Array = [ + i, + last_iteration_index, + 16, + 16 + ] + + var jf_float_push_constants_test : PackedFloat32Array = [ + perpen_error_threshold, + sample_step_multiplier, + temp_motion_blur_intensity, + backtracking_velocity_match_threshold, + backtracking_velocity_match_parallel_sensitivity, + backtracking_velcoity_match_perpendicular_sensitivity, + 0, + 0 + ] + + var jf_byte_array = jf_push_constants.to_byte_array() + jf_byte_array.append_array(jf_float_push_constants_test.to_byte_array()) + + rd.compute_list_set_push_constant(compute_list, jf_byte_array, jf_byte_array.size()) + rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1) + + rd.compute_list_end() + + rd.draw_command_end_label() + + rd.draw_command_begin_label("Compute blur " + str(view), Color(1.0, 1.0, 1.0, 1.0)) + + tex_uniform_set = UniformSetCacheRD.get_cache(motion_blur_shader, 0, [ + get_sampler_uniform(color_image, 0), + get_sampler_uniform(depth_image, 1), + get_sampler_uniform(velocity_image, 2), + get_image_uniform(buffer_b_image if last_iteration_index % 2 else buffer_a_image, 3), + get_image_uniform(texture_image, 4), + get_image_uniform(past_color_image, 5), + ]) + + compute_list = rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, motion_blur_pipeline) + rd.compute_list_bind_uniform_set(compute_list, tex_uniform_set, 0) + rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4) + rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1) + rd.compute_list_end() + rd.draw_command_end_label() + + rd.draw_command_begin_label("Overlay result " + str(view), Color(1.0, 1.0, 1.0, 1.0)) + + tex_uniform_set = UniformSetCacheRD.get_cache(overlay_shader, 0, [ + get_sampler_uniform(texture_image, 0), + get_image_uniform(color_image, 1), + ]) + + compute_list = rd.compute_list_begin() + rd.compute_list_bind_compute_pipeline(compute_list, overlay_pipeline) + rd.compute_list_bind_uniform_set(compute_list, tex_uniform_set, 0) + rd.compute_list_dispatch(compute_list, x_groups, y_groups, 1) + rd.compute_list_end() + rd.draw_command_end_label() + + rd.draw_command_end_label() + + +func ensure_texture(texture_name : StringName, render_scene_buffers : RenderSceneBuffersRD, high_accuracy : bool = false, render_size_multiplier : Vector2 = Vector2(1, 1)): + var render_size : Vector2 = Vector2(render_scene_buffers.get_internal_size()) * render_size_multiplier + + if render_scene_buffers.has_texture(context, texture_name): + var tf: RDTextureFormat = render_scene_buffers.get_texture_format(context, texture_name) + if tf.width != render_size.x or tf.height != render_size.y: + render_scene_buffers.clear_context(context) + + if !render_scene_buffers.has_texture(context, texture_name): + var usage_bits: int = RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT | RenderingDevice.TEXTURE_USAGE_STORAGE_BIT + var texture_format = RenderingDevice.DATA_FORMAT_R32G32B32A32_SFLOAT if high_accuracy else RenderingDevice.DATA_FORMAT_R16G16B16A16_SFLOAT + render_scene_buffers.create_texture(context, texture_name, texture_format, usage_bits, RenderingDevice.TEXTURE_SAMPLES_1, render_size, 1, 1, true) diff --git a/addons/MyJumpFloodIteration/jump_flood_blur.glsl b/addons/MyJumpFloodIteration/jump_flood_blur.glsl new file mode 100644 index 0000000..da7c695 --- /dev/null +++ b/addons/MyJumpFloodIteration/jump_flood_blur.glsl @@ -0,0 +1,261 @@ +#[compute] +#version 450 + +#define FLT_MAX 3.402823466e+38 +#define FLT_MIN 1.175494351e-38 +#define DBL_MAX 1.7976931348623158e+308 +#define DBL_MIN 2.2250738585072014e-308 + +layout(set = 0, binding = 0) uniform sampler2D color_sampler; +layout(set = 0, binding = 1) uniform sampler2D depth_sampler; +layout(set = 0, binding = 2) uniform sampler2D vector_sampler; +layout(rgba16f, set = 0, binding = 3) uniform readonly image2D velocity_map; +layout(rgba16f, set = 0, binding = 4) uniform image2D output_image; +layout(rgba16f, set = 0, binding = 5) uniform image2D past_color_image; + +layout(push_constant, std430) uniform Params +{ + float motion_blur_samples; + float motion_blur_intensity; + float motion_blur_center_fade; + float debug; + float freeze; + float frame; + float last_iteration_index; + float sample_step_multiplier; +} params; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +// velocity similarity divisors +float vsim_parallel = 20; +float vsim_perpendicular = 20; + +// for velocity similarity check +float depth_bias = 0.1; + +// sample weight threshold +float sw_threshold = 0.1; + +// near plane distance +float npd = 0.05; + +// SOFT_Z_EXTENT +float sze = 0.1; + +// Helper functions +// -------------------------------------------- +vec2 get_depth_difference_at_derivative(vec2 uv, vec2 step_size) +{ + float base = textureLod(depth_sampler, uv, 0.0).x; + float x = textureLod(depth_sampler, uv + vec2(0, step_size.x), 0.0).x; + float y = textureLod(depth_sampler, uv + vec2(step_size.y, 0), 0.0).x; + return vec2(x - base, y - base); +} + +// from https://www.shadertoy.com/view/ftKfzc +float interleaved_gradient_noise(vec2 uv, int FrameId){ + uv += float(FrameId) * (vec2(47, 17) * 0.695); + + vec3 magic = vec3( 0.06711056, 0.00583715, 52.9829189 ); + + return fract(magic.z * fract(dot(uv, magic.xy))); +} + +float get_velocity_convergence(vec2 uv, vec2 step_size) +{ + vec2 base = textureLod(vector_sampler, uv, 0.0).xy; + vec2 x = textureLod(vector_sampler, uv + vec2(0, step_size.x), 0.0).xy; + vec2 y = textureLod(vector_sampler, uv + vec2(step_size.y, 0), 0.0).xy; + + return (dot(vec2(0, 1), vec2(x - base)) + dot(vec2(1, 0), vec2(y - base))); +} + +vec3 get_ndc_velocity(vec2 uv, vec2 render_size, float depth) +{ + float ndc_velocity_z = get_velocity_convergence(uv, vec2(1) / render_size) / depth; + + vec2 ndc_velocity_xy = textureLod(vector_sampler, uv, 0.0).xy; + + return vec3(ndc_velocity_xy, ndc_velocity_z); +} + +vec3 get_world_velocity(vec2 uv, vec2 render_size, float depth) +{ + return get_ndc_velocity(uv, render_size, depth) / depth; +} + + +vec3 get_velocity_curl_vector(vec2 uv, vec2 render_size) +{ + float depth = textureLod(depth_sampler, uv, 0.0).x; + + vec2 step_size = vec2(1) / render_size; + vec3 base = get_world_velocity(uv, render_size, depth); + vec3 x = get_world_velocity(uv + vec2(step_size.x, 0), render_size, depth); + vec3 y = get_world_velocity(uv + vec2(0, step_size.y), render_size, depth); + + vec2 depth_derivative = get_depth_difference_at_derivative(uv, step_size) / depth; + + vec3 x_vector = normalize(vec3(step_size.x, 0, 0)); + vec3 y_vector = normalize(vec3(0, step_size.y, 0)); + + vec3 cross_x = cross((x - base) / vec3(step_size, 0), x_vector); + vec3 cross_y = cross((y - base) / vec3(step_size, 0), y_vector); + + return cross_x + cross_y; +} + +float get_velocity_curl(vec2 uv, vec2 render_size) +{ + vec2 step_size = vec2(1) / render_size; + vec2 base = textureLod(vector_sampler, uv, 0.0).xy; + vec2 x = textureLod(vector_sampler, uv + vec2(0, step_size.x), 0.0).xy; + vec2 y = textureLod(vector_sampler, uv + vec2(step_size.y, 0), 0.0).xy; + + return (cross(vec3(0, 1, 0), vec3(x - base, 0) / vec3(step_size, 0)) + cross(vec3(1, 0, 0), vec3(y - base, 0) / vec3(step_size, 0))).z; +} +// ------------------------------------------------------- + +// McGuire's functions https://docs.google.com/document/d/1IIlAKTj-O01hcXEdGxTErQbCHO9iBmRx6oFUy_Jm0fI/edit +// ---------------------------------------------------------- +float soft_depth_compare(float depth_X, float depth_Y) +{ + return clamp(1 - (depth_X - depth_Y) / sze, 0, 1); +} + +float cone(vec2 X, vec2 Y, vec2 v) +{ + return clamp(1 - length(X - Y) / length(v), 0, 1); +} + +float cylinder(vec2 X, vec2 Y, vec2 v) +{ + return 1.0 + smoothstep(0.95 * length(v), 1.05 * length(v), length(X - Y)); +} +// ---------------------------------------------------------- + +// Motion similarity +// ---------------------------------------------------------- +float get_motion_difference(vec2 V, vec2 V2, float power) +{ + vec2 VO = V - V2; + float difference = dot(VO, V) / max(FLT_MIN, dot(V, V)); + return pow(clamp(difference, 0, 1), power); +} +// ---------------------------------------------------------- + +void main() +{ + ivec2 render_size = ivec2(textureSize(color_sampler, 0)); + ivec2 uvi = ivec2(gl_GlobalInvocationID.xy); + if ((uvi.x >= render_size.x) || (uvi.y >= render_size.y)) + { + return; + } + + if(params.freeze > 0) + { + imageStore(output_image, uvi, imageLoad(past_color_image, uvi)); + return; + } + + vec2 uvn = (vec2(uvi) + vec2(0.5)) / render_size; + + int iteration_count = int(params.motion_blur_samples); + + vec4 base = textureLod(color_sampler, uvn, 0.0); + + vec4 result_constructed_color = vec4(0); + + vec4 velocity_map_sample = imageLoad(velocity_map, uvi); + + vec3 velocity = -textureLod(vector_sampler, velocity_map_sample.xy, 0.0).xyz; + + vec3 naive_velocity = -textureLod(vector_sampler, uvn, 0.0).xyz; + + float max_dialtion_radius = pow(2, params.last_iteration_index) * params.sample_step_multiplier / max(render_size.x, render_size.y); + + if ((dot(velocity, velocity) == 0 || params.motion_blur_intensity == 0) && params.debug == 0) //(uvn.y > 0.5)// + { + imageStore(output_image, uvi, base); + imageStore(past_color_image, uvi, base); + return; + } + + float velocity_step_coef = min(params.motion_blur_intensity, max_dialtion_radius / (length(velocity) * params.motion_blur_intensity)) / max(1.0, params.motion_blur_samples - 1.0) * (1 + (interleaved_gradient_noise(uvi, int(params.frame)) - 0.5)); + + vec3 sample_step = velocity * velocity_step_coef; + + vec4 velocity_map_sample_step = vec4(0); + + //float d = 1.0 - min(1.0, 2.0 * distance(uvn, vec2(0.5))); + //sample_step *= 1.0 - d * params.fade_padding.x; + + float total_weight = 1;// max(0.0001, length(naive_velocity)); + + vec2 offset = vec2(0.0); + + vec4 col = base * total_weight; + + float depth = max(FLT_MIN, textureLod(depth_sampler, velocity_map_sample.xy, 0.0).x); + + float naive_depth = max(FLT_MIN, textureLod(depth_sampler, uvn, 0.0).x); + + for (int i = 1; i < iteration_count; i++) + { + offset += sample_step.xy;// * interleaved_gradient_noise(uvi, int(params.frame) + i); + + vec2 uvo = uvn + offset; + + if (any(notEqual(uvo, clamp(uvo, vec2(0.0), vec2(1.0))))) + { + break; + } + + velocity_map_sample_step = imageLoad(velocity_map, ivec2(uvo * render_size)); + + vec3 current_velocity = -textureLod(vector_sampler, velocity_map_sample_step.xy, 0.0).xyz; + + float current_depth = max(FLT_MIN, textureLod(depth_sampler, velocity_map_sample_step.xy, 0.0).x); + + float sample_weight = 1; + + float motion_difference = get_motion_difference(velocity.xy, current_velocity.xy, 0.1); + + float foreground = soft_depth_compare(npd / current_depth, npd / depth); + + sample_weight *= 1 - (foreground * motion_difference); + + total_weight += sample_weight; + + col += textureLod(color_sampler, uvo, 0.0) * sample_weight; + } + + col /= total_weight; + + if (params.debug == 0) + { + imageStore(output_image, uvi, col); + imageStore(past_color_image, uvi, col); + return; + } + + vec4 tl_col = vec4(abs(textureLod(vector_sampler, uvn, 0.0).xy) * 10, 0, 1); + + vec4 tr_col = vec4(abs(velocity.xy) * 10, 0, 1); + + vec4 bl_col = vec4(abs(velocity_map_sample.xyw - vec3(uvn, 0)) * vec3(10, 10, 1), 1); + + vec4 br_col = col; + + //imageStore(past_color_image, uvi, imageLoad(output_image, uvi)); + + imageStore(output_image, uvi / 2, tl_col); + imageStore(output_image, uvi / 2 + ivec2(vec2(0.5, 0.5) * render_size), br_col); + imageStore(output_image, uvi / 2 + ivec2(vec2(0.0, 0.5) * render_size), bl_col); + imageStore(output_image, uvi / 2 + ivec2(vec2(0.5, 0.0) * render_size), tr_col); + imageStore(past_color_image, uvi / 2, tl_col); + imageStore(past_color_image, uvi / 2 + ivec2(vec2(0.5, 0.5) * render_size), br_col); + imageStore(past_color_image, uvi / 2 + ivec2(vec2(0.0, 0.5) * render_size), bl_col); + imageStore(past_color_image, uvi / 2 + ivec2(vec2(0.5, 0.0) * render_size), tr_col); +} \ No newline at end of file diff --git a/addons/MyJumpFloodIteration/jump_flood_overlay.glsl b/addons/MyJumpFloodIteration/jump_flood_overlay.glsl new file mode 100644 index 0000000..30c9794 --- /dev/null +++ b/addons/MyJumpFloodIteration/jump_flood_overlay.glsl @@ -0,0 +1,17 @@ +#[compute] +#version 450 + +layout(set = 0, binding = 0) uniform sampler2D blur_sampler; +layout(rgba16f, set = 0, binding = 1) uniform image2D color_image; + +layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in; +void main() +{ + ivec2 render_size = ivec2(textureSize(blur_sampler, 0)); + ivec2 uv = ivec2(gl_GlobalInvocationID.xy); + if ((uv.x >= render_size.x) || (uv.y >= render_size.y)) + { + return; + } + imageStore(color_image, uv, textureLod(blur_sampler, (vec2(uv) + 0.5) / render_size, 0.0)); +}