first commit

This commit is contained in:
sphynx-owner
2024-07-07 21:36:54 +03:00
commit 616943d42b
5 changed files with 846 additions and 0 deletions

View File

@ -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.

View File

@ -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;
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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));
}