From e7d3a7c2aa72789e132fe878514cb24ce89d2dcc Mon Sep 17 00:00:00 2001 From: Dario Date: Thu, 17 Aug 2023 09:32:33 -0300 Subject: [PATCH] Improve visual feedback when using the motion vectors debug view option. Replaces the current method of showing the raw values of the motion vectors buffer to display a grid of lines instead with a new shader. --- .../renderer_rd/effects/debug_effects.cpp | 36 +++++++++ .../renderer_rd/effects/debug_effects.h | 14 ++++ .../renderer_rd/renderer_scene_render_rd.cpp | 3 +- .../shaders/effects/motion_vectors.glsl | 80 +++++++++++++++++++ 4 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp index 8cd3c224831..3d26a9a8df7 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.cpp +++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp @@ -51,6 +51,17 @@ DebugEffects::DebugEffects() { raster_state.wireframe = true; shadow_frustum.pipelines[SFP_WIREFRAME].setup(shadow_frustum.shader.version_get_shader(shadow_frustum.shader_version, 0), RD::RENDER_PRIMITIVE_LINES, raster_state, RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_disabled(), 0); } + + { + // Motion Vectors debug shader. + Vector modes; + modes.push_back(""); + + motion_vectors.shader.initialize(modes); + motion_vectors.shader_version = motion_vectors.shader.version_create(); + + motion_vectors.pipeline.setup(motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0), RD::RENDER_PRIMITIVE_TRIANGLES, RD::PipelineRasterizationState(), RD::PipelineMultisampleState(), RD::PipelineDepthStencilState(), RD::PipelineColorBlendState::create_blend(), 0); + } } void DebugEffects::_create_frustum_arrays() { @@ -163,6 +174,8 @@ DebugEffects::~DebugEffects() { if (frustum.lines_buffer.is_valid()) { RD::get_singleton()->free(frustum.lines_buffer); // Array gets freed as dependency. } + + motion_vectors.shader.version_free(motion_vectors.shader_version); } void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect) { @@ -326,3 +339,26 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj } } } + +void DebugEffects::draw_motion_vectors(RID p_velocity, RID p_dest_fb, Size2i p_velocity_size) { + MaterialStorage *material_storage = MaterialStorage::get_singleton(); + ERR_FAIL_NULL(material_storage); + + UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); + ERR_FAIL_NULL(uniform_set_cache); + + RID default_sampler = material_storage->sampler_rd_get_default(RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR, RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED); + RD::Uniform u_source_velocity(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector({ default_sampler, p_velocity })); + + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, motion_vectors.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb), false, RD::get_singleton()->draw_list_get_current_pass())); + + motion_vectors.push_constant.velocity_resolution[0] = p_velocity_size.width; + motion_vectors.push_constant.velocity_resolution[1] = p_velocity_size.height; + + RID shader = motion_vectors.shader.version_get_shader(motion_vectors.shader_version, 0); + RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_velocity), 0); + RD::get_singleton()->draw_list_set_push_constant(draw_list, &motion_vectors.push_constant, sizeof(MotionVectorsPushConstant)); + RD::get_singleton()->draw_list_draw(draw_list, false, 1u, 3u); + RD::get_singleton()->draw_list_end(); +} diff --git a/servers/rendering/renderer_rd/effects/debug_effects.h b/servers/rendering/renderer_rd/effects/debug_effects.h index 21b7b03f849..ae32d949120 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.h +++ b/servers/rendering/renderer_rd/effects/debug_effects.h @@ -32,6 +32,7 @@ #define DEBUG_EFFECTS_RD_H #include "servers/rendering/renderer_rd/pipeline_cache_rd.h" +#include "servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl.gen.h" #include "servers/rendering/renderer_rd/shaders/effects/shadow_frustum.glsl.gen.h" #include "servers/rendering/renderer_scene_render.h" @@ -70,6 +71,18 @@ private: PipelineCacheRD pipelines[SFP_MAX]; } shadow_frustum; + struct MotionVectorsPushConstant { + float velocity_resolution[2]; + float pad[2]; + }; + + struct { + MotionVectorsShaderRD shader; + RID shader_version; + PipelineCacheRD pipeline; + MotionVectorsPushConstant push_constant; + } motion_vectors; + void _create_frustum_arrays(); protected: @@ -78,6 +91,7 @@ public: ~DebugEffects(); void draw_shadow_frustum(RID p_light, const Projection &p_cam_projection, const Transform3D &p_cam_transform, RID p_dest_fb, const Rect2 p_rect); + void draw_motion_vectors(RID p_velocity, RID p_dest_fb, Size2i p_velocity_size); }; } // namespace RendererRD diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 9d4d266a7a7..20e24dba0eb 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -745,8 +745,7 @@ void RendererSceneRenderRD::_render_buffers_debug_draw(const RenderDataRD *p_ren } if (debug_draw == RS::VIEWPORT_DEBUG_DRAW_MOTION_VECTORS && _render_buffers_get_velocity_texture(rb).is_valid()) { - Size2i rtsize = texture_storage->render_target_get_size(render_target); - copy_effects->copy_to_fb_rect(_render_buffers_get_velocity_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), Rect2(Vector2(), rtsize), false, false); + debug_effects->draw_motion_vectors(_render_buffers_get_velocity_texture(rb), texture_storage->render_target_get_rd_framebuffer(render_target), rb->get_internal_size()); } } diff --git a/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl b/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl new file mode 100644 index 00000000000..80e4f51565c --- /dev/null +++ b/servers/rendering/renderer_rd/shaders/effects/motion_vectors.glsl @@ -0,0 +1,80 @@ +#[vertex] + +#version 450 + +#VERSION_DEFINES + +layout(location = 0) out vec2 uv_interp; + +void main() { + vec2 base_arr[3] = vec2[](vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0)); + gl_Position = vec4(base_arr[gl_VertexIndex], 0.0, 1.0); + uv_interp = clamp(gl_Position.xy, vec2(0.0, 0.0), vec2(1.0, 1.0)) * 2.0; // saturate(x) * 2.0 +} + +#[fragment] + +#version 450 + +#VERSION_DEFINES + +layout(location = 0) in vec2 uv_interp; + +layout(set = 0, binding = 0) uniform sampler2D source_velocity; + +layout(location = 0) out vec4 frag_color; + +layout(push_constant, std430) uniform Params { + vec2 resolution; +} +params; + +// Based on distance to line segment from https://www.shadertoy.com/view/3tdSDj + +float line_segment(in vec2 p, in vec2 a, in vec2 b) { + vec2 aspect = vec2(params.resolution.x / params.resolution.y, 1.0f); + vec2 ba = (b - a) * aspect; + vec2 pa = (p - a) * aspect; + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0f, 1.0f); + return length(pa - h * ba) * (params.resolution.y / 2.0f); +} + +void main() { + // Retrieve motion vector data. + float cell_size = 32.0f; + float circle_radius = 2.0f; + vec3 nan_color = vec3(1.0f, 0.0f, 0.0f); + vec3 active_color = vec3(1.0f, 0.8f, 0.1f); + vec3 inactive_color = vec3(0.5f, 0.5f, 0.5f); + vec2 pos_pixel = uv_interp * params.resolution; + vec2 cell_pos_pixel = floor(pos_pixel / cell_size) * cell_size + (cell_size * 0.5f); + vec2 cell_pos_uv = cell_pos_pixel / params.resolution; + vec2 cell_pos_previous_uv = cell_pos_uv + textureLod(source_velocity, cell_pos_uv, 0.0f).xy; + + // Draw the shapes. + float epsilon = 1e-6f; + vec2 cell_pos_delta_uv = cell_pos_uv - cell_pos_previous_uv; + bool motion_active = length(cell_pos_delta_uv) > epsilon; + vec3 color; + if (any(isnan(cell_pos_delta_uv))) { + color = nan_color; + } else if (motion_active) { + color = active_color; + } else { + color = inactive_color; + } + + float alpha; + if (length(cell_pos_pixel - pos_pixel) <= circle_radius) { + // Circle center. + alpha = 1.0f; + } else if (motion_active) { + // Motion vector line. + alpha = 1.0f - line_segment(uv_interp, cell_pos_uv, cell_pos_previous_uv); + } else { + // Ignore pixel. + alpha = 0.0f; + } + + frag_color = vec4(color, alpha); +}