Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_post_process/src/motion_blur/mod.rs
6596 views
1
//! Per-object, per-pixel motion blur.
2
//!
3
//! Add the [`MotionBlur`] component to a camera to enable motion blur.
4
5
use bevy_app::{App, Plugin};
6
use bevy_asset::embedded_asset;
7
use bevy_camera::Camera;
8
use bevy_core_pipeline::{
9
core_3d::graph::{Core3d, Node3d},
10
prepass::{DepthPrepass, MotionVectorPrepass},
11
};
12
use bevy_ecs::{
13
component::Component,
14
query::{QueryItem, With},
15
reflect::ReflectComponent,
16
schedule::IntoScheduleConfigs,
17
};
18
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
19
use bevy_render::{
20
extract_component::{ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin},
21
render_graph::{RenderGraphExt, ViewNodeRunner},
22
render_resource::{ShaderType, SpecializedRenderPipelines},
23
Render, RenderApp, RenderStartup, RenderSystems,
24
};
25
26
pub mod node;
27
pub mod pipeline;
28
29
/// A component that enables and configures motion blur when added to a camera.
30
///
31
/// Motion blur is an effect that simulates how moving objects blur as they change position during
32
/// the exposure of film, a sensor, or an eyeball.
33
///
34
/// Because rendering simulates discrete steps in time, we use per-pixel motion vectors to estimate
35
/// the path of objects between frames. This kind of implementation has some artifacts:
36
/// - Fast moving objects in front of a stationary object or when in front of empty space, will not
37
/// have their edges blurred.
38
/// - Transparent objects do not write to depth or motion vectors, so they cannot be blurred.
39
///
40
/// Other approaches, such as *A Reconstruction Filter for Plausible Motion Blur* produce more
41
/// correct results, but are more expensive and complex, and have other kinds of artifacts. This
42
/// implementation is relatively inexpensive and effective.
43
///
44
/// # Usage
45
///
46
/// Add the [`MotionBlur`] component to a camera to enable and configure motion blur for that
47
/// camera.
48
///
49
/// ```
50
/// # use bevy_post_process::motion_blur::MotionBlur;
51
/// # use bevy_camera::Camera3d;
52
/// # use bevy_ecs::prelude::*;
53
/// # fn test(mut commands: Commands) {
54
/// commands.spawn((
55
/// Camera3d::default(),
56
/// MotionBlur::default(),
57
/// ));
58
/// # }
59
/// ````
60
#[derive(Reflect, Component, Clone)]
61
#[reflect(Component, Default, Clone)]
62
#[require(DepthPrepass, MotionVectorPrepass)]
63
pub struct MotionBlur {
64
/// The strength of motion blur from `0.0` to `1.0`.
65
///
66
/// The shutter angle describes the fraction of a frame that a camera's shutter is open and
67
/// exposing the film/sensor. For 24fps cinematic film, a shutter angle of 0.5 (180 degrees) is
68
/// common. This means that the shutter was open for half of the frame, or 1/48th of a second.
69
/// The lower the shutter angle, the less exposure time and thus less blur.
70
///
71
/// A value greater than one is non-physical and results in an object's blur stretching further
72
/// than it traveled in that frame. This might be a desirable effect for artistic reasons, but
73
/// consider allowing users to opt out of this.
74
///
75
/// This value is intentionally tied to framerate to avoid the aforementioned non-physical
76
/// over-blurring. If you want to emulate a cinematic look, your options are:
77
/// - Framelimit your app to 24fps, and set the shutter angle to 0.5 (180 deg). Note that
78
/// depending on artistic intent or the action of a scene, it is common to set the shutter
79
/// angle between 0.125 (45 deg) and 0.5 (180 deg). This is the most faithful way to
80
/// reproduce the look of film.
81
/// - Set the shutter angle greater than one. For example, to emulate the blur strength of
82
/// film while rendering at 60fps, you would set the shutter angle to `60/24 * 0.5 = 1.25`.
83
/// Note that this will result in artifacts where the motion of objects will stretch further
84
/// than they moved between frames; users may find this distracting.
85
pub shutter_angle: f32,
86
/// The quality of motion blur, corresponding to the number of per-pixel samples taken in each
87
/// direction during blur.
88
///
89
/// Setting this to `1` results in each pixel being sampled once in the leading direction, once
90
/// in the trailing direction, and once in the middle, for a total of 3 samples (`1 * 2 + 1`).
91
/// Setting this to `3` will result in `3 * 2 + 1 = 7` samples. Setting this to `0` is
92
/// equivalent to disabling motion blur.
93
pub samples: u32,
94
}
95
96
impl Default for MotionBlur {
97
fn default() -> Self {
98
Self {
99
shutter_angle: 0.5,
100
samples: 1,
101
}
102
}
103
}
104
105
impl ExtractComponent for MotionBlur {
106
type QueryData = &'static Self;
107
type QueryFilter = With<Camera>;
108
type Out = MotionBlurUniform;
109
110
fn extract_component(item: QueryItem<Self::QueryData>) -> Option<Self::Out> {
111
Some(MotionBlurUniform {
112
shutter_angle: item.shutter_angle,
113
samples: item.samples,
114
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
115
_webgl2_padding: Default::default(),
116
})
117
}
118
}
119
120
#[doc(hidden)]
121
#[derive(Component, ShaderType, Clone)]
122
pub struct MotionBlurUniform {
123
shutter_angle: f32,
124
samples: u32,
125
#[cfg(all(feature = "webgl", target_arch = "wasm32", not(feature = "webgpu")))]
126
// WebGL2 structs must be 16 byte aligned.
127
_webgl2_padding: bevy_math::Vec2,
128
}
129
130
/// Adds support for per-object motion blur to the app. See [`MotionBlur`] for details.
131
#[derive(Default)]
132
pub struct MotionBlurPlugin;
133
impl Plugin for MotionBlurPlugin {
134
fn build(&self, app: &mut App) {
135
embedded_asset!(app, "motion_blur.wgsl");
136
137
app.add_plugins((
138
ExtractComponentPlugin::<MotionBlur>::default(),
139
UniformComponentPlugin::<MotionBlurUniform>::default(),
140
));
141
142
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
143
return;
144
};
145
146
render_app
147
.init_resource::<SpecializedRenderPipelines<pipeline::MotionBlurPipeline>>()
148
.add_systems(RenderStartup, pipeline::init_motion_blur_pipeline)
149
.add_systems(
150
Render,
151
pipeline::prepare_motion_blur_pipelines.in_set(RenderSystems::Prepare),
152
);
153
154
render_app
155
.add_render_graph_node::<ViewNodeRunner<node::MotionBlurNode>>(
156
Core3d,
157
Node3d::MotionBlur,
158
)
159
.add_render_graph_edges(
160
Core3d,
161
(
162
Node3d::EndMainPass,
163
Node3d::MotionBlur,
164
Node3d::Bloom, // we want blurred areas to bloom and tonemap properly.
165
),
166
);
167
}
168
}
169
170