1
1
This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc
Clément Foucault d371dfbe2c DRW: Change binding API
Using bind() overload is too error prone.
2022-08-30 21:27:02 +02:00

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