Blender V5.0
eevee_motion_blur.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BKE_colortools.hh"
10
11#include "RE_engine.h"
12
13#include "GPU_debug.hh"
14
15#include "eevee_instance.hh"
16#include "eevee_motion_blur.hh"
17
18namespace blender::eevee {
19
20/* -------------------------------------------------------------------- */
24
26{
27 const Scene *scene = inst_.scene;
28 const ViewLayer *view_layer = inst_.view_layer;
29
30 /* Disable on viewport outside of animation playback,
31 * since it can get distracting while editing the scene. */
32 enabled_ = (scene->r.mode & R_MBLUR) != 0 && (inst_.is_image_render || inst_.is_playback);
33 if (enabled_) {
34 enabled_ = (view_layer->layflag & SCE_LAY_MOTION_BLUR) != 0;
35 }
36
37 if (!enabled_) {
38 motion_blur_fx_enabled_ = false;
39 return;
40 }
41
42 /* Take into account the steps needed for fx motion blur. */
43 int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1;
44
45 time_steps_.resize(steps_count);
46
47 initial_frame_ = scene->r.cfra;
48 initial_subframe_ = scene->r.subframe;
49 frame_time_ = initial_frame_ + initial_subframe_;
50 shutter_position_ = scene->r.motion_blur_position;
51 shutter_time_ = scene->r.motion_blur_shutter;
52
53 data_.depth_scale = scene->eevee.motion_blur_depth_scale;
54 motion_blur_fx_enabled_ = true; /* TODO(fclem): UI option. */
55
56 /* Viewport stops here. We only do Post-FX motion blur. */
57 if (inst_.is_viewport()) {
58 enabled_ = false;
59 return;
60 }
61
62 /* Without this there is the possibility of the curve table not being allocated. */
64
67 Sampling::cdf_invert(cdf, time_steps_);
68
69 for (float &time : time_steps_) {
70 time = this->shutter_time_to_scene_time(time);
71 }
72
73 step_id_ = 1;
74
75 if (motion_blur_fx_enabled_) {
76 /* A bit weird but we have to sync the first 2 steps here because the step()
77 * function is only called after rendering a sample. */
78 inst_.velocity.step_sync(STEP_PREVIOUS, time_steps_[0]);
79 inst_.velocity.step_sync(STEP_NEXT, time_steps_[2]);
80 /* Let the main sync loop handle the current step. */
81 }
82 inst_.set_time(time_steps_[1]);
83}
84
86{
87 if (!enabled_) {
88 return;
89 }
90
91 if (inst_.sampling.finished()) {
92 /* Restore original frame number. This is because the render pipeline expects it. */
93 RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_);
94 }
95 else if (inst_.sampling.do_render_sync()) {
96 /* Time to change motion step. */
97 BLI_assert(time_steps_.size() > step_id_ + 2);
98 step_id_ += 2;
99
100 if (motion_blur_fx_enabled_) {
101 inst_.velocity.step_swap();
102 inst_.velocity.step_sync(eVelocityStep::STEP_NEXT, time_steps_[step_id_ + 1]);
103 }
104 inst_.set_time(time_steps_[step_id_]);
105 }
106}
107
108float MotionBlurModule::shutter_time_to_scene_time(float time)
109{
110 switch (shutter_position_) {
111 case SCE_MB_START:
112 /* No offset. */
113 break;
114 case SCE_MB_CENTER:
115 time -= 0.5f;
116 break;
117 case SCE_MB_END:
118 time -= 1.0;
119 break;
120 default:
121 BLI_assert_msg(false, "Invalid motion blur position enum!");
122 break;
123 }
124 time *= shutter_time_;
125 time += frame_time_;
126 return time;
127}
128
130{
131 /* Disable motion blur in viewport when changing camera projection type.
132 * Avoids really high velocities. */
133 if (inst_.velocity.camera_changed_projection() ||
134 (inst_.is_viewport() && (inst_.camera.overscan_changed() || inst_.camera.camera_changed())))
135 {
136 motion_blur_fx_enabled_ = false;
137 }
138
139 if (!motion_blur_fx_enabled_) {
140 return;
141 }
142
144 RenderBuffers &render_buffers = inst_.render_buffers;
145
146 motion_blur_ps_.init();
147 motion_blur_ps_.bind_resources(inst_.velocity);
148 motion_blur_ps_.bind_resources(inst_.sampling);
149 {
150 /* Create max velocity tiles. */
151 PassSimple::Sub &sub = motion_blur_ps_.sub("TilesFlatten");
152 gpu::TextureFormat vector_tx_format = inst_.render_buffers.vector_tx_format();
153 eShaderType shader = vector_tx_format == gpu::TextureFormat::SFLOAT_16_16 ?
156 sub.shader_set(inst_.shaders.static_shader_get(shader));
157 sub.bind_ubo("motion_blur_buf", data_);
158 sub.bind_texture("depth_tx", &render_buffers.depth_tx);
159 sub.bind_image("velocity_img", &render_buffers.vector_tx);
160 sub.bind_image("out_tiles_img", &tiles_tx_);
161 sub.dispatch(&dispatch_flatten_size_);
163 }
164 {
165 /* Expand max velocity tiles by spreading them in their neighborhood. */
166 PassSimple::Sub &sub = motion_blur_ps_.sub("TilesDilate");
167 sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE));
168 sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_);
169 sub.bind_image("in_tiles_img", &tiles_tx_);
170 sub.dispatch(&dispatch_dilate_size_);
172 }
173 {
174 /* Do the motion blur gather algorithm. */
175 PassSimple::Sub &sub = motion_blur_ps_.sub("ConvolveGather");
176 sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_GATHER));
177 sub.bind_ubo("motion_blur_buf", data_);
178 sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_);
179 sub.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
180 sub.bind_texture("velocity_tx", &render_buffers.vector_tx, no_filter);
181 sub.bind_texture("in_color_tx", &input_color_tx_, no_filter);
182 sub.bind_image("in_tiles_img", &tiles_tx_);
183 sub.bind_image("out_color_img", &output_color_tx_);
184
185 sub.dispatch(&dispatch_gather_size_);
187 }
188}
189
191{
192 if (!motion_blur_fx_enabled_) {
193 return;
194 }
195
196 const Texture &depth_tx = inst_.render_buffers.depth_tx;
197
198 int2 extent = {depth_tx.width(), depth_tx.height()};
199 int2 tiles_extent = math::divide_ceil(extent, int2(MOTION_BLUR_TILE_SIZE));
200
201 if (inst_.is_viewport()) {
202 float frame_delta = fabsf(inst_.velocity.step_time_delta_get(STEP_PREVIOUS, STEP_CURRENT));
203 /* Avoid highly disturbing blurs, during navigation with high shutter time. */
204 if (frame_delta > 0.0f && !inst_.is_navigating) {
205 /* Rescale motion blur intensity to be shutter time relative and avoid long streak when we
206 * have frame skipping. Always try to stick to what the render frame would look like. */
207 data_.motion_scale = float2(shutter_time_ / frame_delta);
208 }
209 else {
210 /* There is no time change. Motion only comes from viewport navigation and object transform.
211 * Apply motion blur as smoothing and only blur towards last frame. */
212 data_.motion_scale = float2(1.0f, 0.0f);
213
214 if (was_navigating_ != inst_.is_navigating) {
215 /* Special case for navigation events that only last for one frame (for instance mouse
216 * scroll for zooming). For this case we have to wait for the next frame before enabling
217 * the navigation motion blur. */
218 was_navigating_ = inst_.is_navigating;
219 return;
220 }
221 }
222 was_navigating_ = inst_.is_navigating;
223 }
224 else {
225 data_.motion_scale = float2(1.0f);
226 }
227 /* Second motion vector is stored inverted. */
228 data_.motion_scale.y = -data_.motion_scale.y;
229 data_.target_size_inv = 1.0f / float2(extent);
230 data_.push_update();
231
232 input_color_tx_ = *input_tx;
233 output_color_tx_ = *output_tx;
234
235 dispatch_flatten_size_ = int3(tiles_extent, 1);
236 dispatch_dilate_size_ = int3(math::divide_ceil(tiles_extent, int2(MOTION_BLUR_GROUP_SIZE)), 1);
237 dispatch_gather_size_ = int3(math::divide_ceil(extent, int2(MOTION_BLUR_GROUP_SIZE)), 1);
238
239 GPU_debug_group_begin("Motion Blur");
240
241 tiles_tx_.acquire(tiles_extent, gpu::TextureFormat::SFLOAT_16_16_16_16);
242
243 tile_indirection_buf_.clear_to_zero();
244
245 inst_.manager->submit(motion_blur_ps_, view);
246
247 tiles_tx_.release();
248
250
251 /* Swap buffers so that next effect has the right input. */
252 *input_tx = output_color_tx_;
253 *output_tx = input_color_tx_;
254}
255
257
258} // namespace blender::eevee
void BKE_curvemapping_changed(CurveMapping *cumap, bool rem_doubles)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE int max_ii(int a, int b)
#define CM_TABLE
@ SCE_LAY_MOTION_BLUR
@ SCE_MB_START
@ SCE_MB_END
@ SCE_MB_CENTER
@ R_MBLUR
static AppView * view
void GPU_debug_group_end()
Definition gpu_debug.cc:33
void GPU_debug_group_begin(const char *name)
Definition gpu_debug.cc:22
@ GPU_BARRIER_SHADER_STORAGE
Definition GPU_state.hh:48
@ GPU_BARRIER_TEXTURE_FETCH
Definition GPU_state.hh:37
@ GPU_BARRIER_SHADER_IMAGE_ACCESS
Definition GPU_state.hh:35
void shader_set(gpu::Shader *shader)
void bind_texture(const char *name, gpu::Texture *texture, GPUSamplerState state=sampler_auto)
void bind_image(const char *name, gpu::Texture *image)
void dispatch(int group_len)
void barrier(GPUBarrier type)
void bind_ubo(const char *name, gpu::UniformBuf *buffer)
void bind_ssbo(const char *name, gpu::StorageBuf *buffer)
detail::PassBase< command::DrawCommandBuf > Sub
Definition draw_pass.hh:499
void render(View &view, gpu::Texture **input_tx, gpu::Texture **output_tx)
static void cdf_invert(Vector< float > &cdf, Vector< float > &inverted_cdf)
static void cdf_from_curvemapping(const CurveMapping &curve, Vector< float > &cdf)
#define MOTION_BLUR_GROUP_SIZE
#define MOTION_BLUR_TILE_SIZE
void RE_engine_frame_set(RenderEngine *engine, int frame, float subframe)
VecBase< T, Size > divide_ceil(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
VecBase< int32_t, 3 > int3
#define fabsf
static constexpr GPUSamplerState default_sampler()
float motion_blur_shutter
struct CurveMapping mblur_shutter_curve
float motion_blur_depth_scale
struct RenderData r
struct SceneEEVEE eevee