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