257 lines
8.2 KiB
C++
257 lines
8.2 KiB
C++
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2021 Blender Foundation.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup eevee
|
|
*/
|
|
|
|
// #include "BLI_map.hh"
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
#include "eevee_instance.hh"
|
|
#include "eevee_motion_blur.hh"
|
|
// #include "eevee_sampling.hh"
|
|
// #include "eevee_shader_shared.hh"
|
|
// #include "eevee_velocity.hh"
|
|
|
|
namespace blender::eevee {
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name MotionBlurModule
|
|
*
|
|
* \{ */
|
|
|
|
void MotionBlurModule::init()
|
|
{
|
|
const Scene *scene = inst_.scene;
|
|
|
|
enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0;
|
|
|
|
if (!enabled_) {
|
|
motion_blur_fx_enabled_ = false;
|
|
return;
|
|
}
|
|
|
|
/* Take into account the steps needed for fx motion blur. */
|
|
int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1;
|
|
|
|
time_steps_.resize(steps_count);
|
|
|
|
initial_frame_ = scene->r.cfra;
|
|
initial_subframe_ = scene->r.subframe;
|
|
frame_time_ = initial_frame_ + initial_subframe_;
|
|
shutter_position_ = scene->eevee.motion_blur_position;
|
|
shutter_time_ = scene->eevee.motion_blur_shutter;
|
|
|
|
data_.depth_scale = scene->eevee.motion_blur_depth_scale;
|
|
motion_blur_fx_enabled_ = true; /* TODO(fclem): UI option. */
|
|
|
|
/* Viewport stops here. We only do Post-FX motion blur. */
|
|
if (inst_.is_viewport()) {
|
|
enabled_ = false;
|
|
return;
|
|
}
|
|
|
|
/* Without this there is the possibility of the curve table not being allocated. */
|
|
BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false);
|
|
|
|
Vector<float> cdf(CM_TABLE);
|
|
Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf);
|
|
Sampling::cdf_invert(cdf, time_steps_);
|
|
|
|
for (float &time : time_steps_) {
|
|
time = this->shutter_time_to_scene_time(time);
|
|
}
|
|
|
|
step_id_ = 1;
|
|
|
|
if (motion_blur_fx_enabled_) {
|
|
/* A bit weird but we have to sync the first 2 steps here because the step()
|
|
* function is only called after rendering a sample. */
|
|
inst_.velocity.step_sync(STEP_PREVIOUS, time_steps_[0]);
|
|
inst_.velocity.step_sync(STEP_NEXT, time_steps_[2]);
|
|
}
|
|
inst_.set_time(time_steps_[1]);
|
|
}
|
|
|
|
/* Runs after rendering a sample. */
|
|
void MotionBlurModule::step()
|
|
{
|
|
if (!enabled_) {
|
|
return;
|
|
}
|
|
|
|
if (inst_.sampling.finished()) {
|
|
/* Restore original frame number. This is because the render pipeline expects it. */
|
|
RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_);
|
|
}
|
|
else if (inst_.sampling.do_render_sync()) {
|
|
/* Time to change motion step. */
|
|
BLI_assert(time_steps_.size() > step_id_ + 2);
|
|
step_id_ += 2;
|
|
|
|
if (motion_blur_fx_enabled_) {
|
|
inst_.velocity.step_swap();
|
|
inst_.velocity.step_sync(eVelocityStep::STEP_NEXT, time_steps_[step_id_ + 1]);
|
|
}
|
|
inst_.set_time(time_steps_[step_id_]);
|
|
}
|
|
}
|
|
|
|
float MotionBlurModule::shutter_time_to_scene_time(float time)
|
|
{
|
|
switch (shutter_position_) {
|
|
case SCE_EEVEE_MB_START:
|
|
/* No offset. */
|
|
break;
|
|
case SCE_EEVEE_MB_CENTER:
|
|
time -= 0.5f;
|
|
break;
|
|
case SCE_EEVEE_MB_END:
|
|
time -= 1.0;
|
|
break;
|
|
default:
|
|
BLI_assert(!"Invalid motion blur position enum!");
|
|
break;
|
|
}
|
|
time *= shutter_time_;
|
|
time += frame_time_;
|
|
return time;
|
|
}
|
|
|
|
void MotionBlurModule::sync()
|
|
{
|
|
/* Disable motion blur in viewport when changing camera projection type.
|
|
* Avoids really high velocities. */
|
|
if (inst_.velocity.camera_changed_projection()) {
|
|
motion_blur_fx_enabled_ = false;
|
|
}
|
|
|
|
if (!motion_blur_fx_enabled_) {
|
|
return;
|
|
}
|
|
|
|
eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT;
|
|
RenderBuffers &render_buffers = inst_.render_buffers;
|
|
|
|
motion_blur_ps_.init();
|
|
inst_.velocity.bind_resources(&motion_blur_ps_);
|
|
inst_.sampling.bind_resources(&motion_blur_ps_);
|
|
{
|
|
/* Create max velocity tiles. */
|
|
PassSimple::Sub &sub = motion_blur_ps_.sub("TilesFlatten");
|
|
eShaderType shader = (inst_.is_viewport()) ? MOTION_BLUR_TILE_FLATTEN_VIEWPORT :
|
|
MOTION_BLUR_TILE_FLATTEN_RENDER;
|
|
sub.shader_set(inst_.shaders.static_shader_get(shader));
|
|
sub.bind_ubo("motion_blur_buf", data_);
|
|
sub.bind_texture("depth_tx", &render_buffers.depth_tx);
|
|
sub.bind_image("velocity_img", &render_buffers.vector_tx);
|
|
sub.bind_image("out_tiles_img", &tiles_tx_);
|
|
sub.dispatch(&dispatch_flatten_size_);
|
|
sub.barrier(GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH);
|
|
}
|
|
{
|
|
/* Expand max velocity tiles by spreading them in their neighborhood. */
|
|
PassSimple::Sub &sub = motion_blur_ps_.sub("TilesDilate");
|
|
sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE));
|
|
sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_);
|
|
sub.bind_image("in_tiles_img", &tiles_tx_);
|
|
sub.dispatch(&dispatch_dilate_size_);
|
|
sub.barrier(GPU_BARRIER_SHADER_STORAGE);
|
|
}
|
|
{
|
|
/* Do the motion blur gather algorithm. */
|
|
PassSimple::Sub &sub = motion_blur_ps_.sub("ConvolveGather");
|
|
sub.shader_set(inst_.shaders.static_shader_get(MOTION_BLUR_GATHER));
|
|
sub.bind_ubo("motion_blur_buf", data_);
|
|
sub.bind_ssbo("tile_indirection_buf", tile_indirection_buf_);
|
|
sub.bind_texture("depth_tx", &render_buffers.depth_tx, no_filter);
|
|
sub.bind_texture("velocity_tx", &render_buffers.vector_tx, no_filter);
|
|
sub.bind_texture("in_color_tx", &input_color_tx_, no_filter);
|
|
sub.bind_image("in_tiles_img", &tiles_tx_);
|
|
sub.bind_image("out_color_img", &output_color_tx_);
|
|
|
|
sub.dispatch(&dispatch_gather_size_);
|
|
sub.barrier(GPU_BARRIER_TEXTURE_FETCH);
|
|
}
|
|
}
|
|
|
|
void MotionBlurModule::render(View &view, GPUTexture **input_tx, GPUTexture **output_tx)
|
|
{
|
|
if (!motion_blur_fx_enabled_) {
|
|
return;
|
|
}
|
|
|
|
const Texture &depth_tx = inst_.render_buffers.depth_tx;
|
|
|
|
int2 extent = {depth_tx.width(), depth_tx.height()};
|
|
int2 tiles_extent = math::divide_ceil(extent, int2(MOTION_BLUR_TILE_SIZE));
|
|
|
|
if (inst_.is_viewport()) {
|
|
float frame_delta = fabsf(inst_.velocity.step_time_delta_get(STEP_PREVIOUS, STEP_CURRENT));
|
|
/* Avoid highly disturbing blurs, during navigation with high shutter time. */
|
|
if (frame_delta > 0.0f && !DRW_state_is_navigating()) {
|
|
/* Rescale motion blur intensity to be shutter time relative and avoid long streak when we
|
|
* have frame skipping. Always try to stick to what the render frame would look like. */
|
|
data_.motion_scale = float2(shutter_time_ / frame_delta);
|
|
}
|
|
else {
|
|
/* There is no time change. Motion only comes from viewport navigation and object transform.
|
|
* Apply motion blur as smoothing and only blur towards last frame. */
|
|
data_.motion_scale = float2(1.0f, 0.0f);
|
|
|
|
if (was_navigating_ != DRW_state_is_navigating()) {
|
|
/* Special case for navigation events that only last for one frame (for instance mouse
|
|
* scroll for zooming). For this case we have to wait for the next frame before enabling
|
|
* the navigation motion blur. */
|
|
was_navigating_ = DRW_state_is_navigating();
|
|
return;
|
|
}
|
|
}
|
|
was_navigating_ = DRW_state_is_navigating();
|
|
|
|
/* Change texture swizzling to avoid complexity in gather pass shader. */
|
|
GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgrg");
|
|
}
|
|
else {
|
|
data_.motion_scale = float2(1.0f);
|
|
}
|
|
/* Second motion vector is stored inverted. */
|
|
data_.motion_scale.y = -data_.motion_scale.y;
|
|
data_.target_size_inv = 1.0f / float2(extent);
|
|
data_.push_update();
|
|
|
|
input_color_tx_ = *input_tx;
|
|
output_color_tx_ = *output_tx;
|
|
|
|
dispatch_flatten_size_ = int3(tiles_extent, 1);
|
|
dispatch_dilate_size_ = int3(math::divide_ceil(tiles_extent, int2(MOTION_BLUR_GROUP_SIZE)), 1);
|
|
dispatch_gather_size_ = int3(math::divide_ceil(extent, int2(MOTION_BLUR_GROUP_SIZE)), 1);
|
|
|
|
DRW_stats_group_start("Motion Blur");
|
|
|
|
tiles_tx_.acquire(tiles_extent, GPU_RGBA16F);
|
|
|
|
GPU_storagebuf_clear_to_zero(tile_indirection_buf_);
|
|
|
|
inst_.manager->submit(motion_blur_ps_, view);
|
|
|
|
tiles_tx_.release();
|
|
|
|
DRW_stats_group_end();
|
|
|
|
if (inst_.is_viewport()) {
|
|
/* Reset swizzle since this texture might be reused in other places. */
|
|
GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgba");
|
|
}
|
|
|
|
/* Swap buffers so that next effect has the right input. */
|
|
*input_tx = output_color_tx_;
|
|
*output_tx = input_color_tx_;
|
|
}
|
|
|
|
/** \} */
|
|
|
|
} // namespace blender::eevee
|