bevy_post_process/motion_blur/
node.rs1use bevy_ecs::{query::QueryItem, world::World};
2use bevy_render::{
3 diagnostic::RecordDiagnostics,
4 extract_component::ComponentUniforms,
5 globals::GlobalsBuffer,
6 render_graph::{NodeRunError, RenderGraphContext, ViewNode},
7 render_resource::{
8 BindGroupEntries, Operations, PipelineCache, RenderPassColorAttachment,
9 RenderPassDescriptor,
10 },
11 renderer::RenderContext,
12 view::{Msaa, ViewTarget},
13};
14
15use bevy_core_pipeline::prepass::ViewPrepassTextures;
16
17use super::{
18 pipeline::{MotionBlurPipeline, MotionBlurPipelineId},
19 MotionBlurUniform,
20};
21
22#[derive(Default)]
23pub struct MotionBlurNode;
24
25impl ViewNode for MotionBlurNode {
26 type ViewQuery = (
27 &'static ViewTarget,
28 &'static MotionBlurPipelineId,
29 &'static ViewPrepassTextures,
30 &'static MotionBlurUniform,
31 &'static Msaa,
32 );
33 fn run(
34 &self,
35 _graph: &mut RenderGraphContext,
36 render_context: &mut RenderContext,
37 (view_target, pipeline_id, prepass_textures, motion_blur, msaa): QueryItem<Self::ViewQuery>,
38 world: &World,
39 ) -> Result<(), NodeRunError> {
40 if motion_blur.samples == 0 || motion_blur.shutter_angle <= 0.0 {
41 return Ok(()); }
43
44 let motion_blur_pipeline = world.resource::<MotionBlurPipeline>();
45 let pipeline_cache = world.resource::<PipelineCache>();
46 let settings_uniforms = world.resource::<ComponentUniforms<MotionBlurUniform>>();
47 let Some(pipeline) = pipeline_cache.get_render_pipeline(pipeline_id.0) else {
48 return Ok(());
49 };
50
51 let Some(settings_binding) = settings_uniforms.uniforms().binding() else {
52 return Ok(());
53 };
54 let (Some(prepass_motion_vectors_texture), Some(prepass_depth_texture)) =
55 (&prepass_textures.motion_vectors, &prepass_textures.depth)
56 else {
57 return Ok(());
58 };
59 let Some(globals_uniforms) = world.resource::<GlobalsBuffer>().buffer.binding() else {
60 return Ok(());
61 };
62
63 let diagnostics = render_context.diagnostic_recorder();
64
65 let post_process = view_target.post_process_write();
66
67 let layout = if msaa.samples() == 1 {
68 &motion_blur_pipeline.layout
69 } else {
70 &motion_blur_pipeline.layout_msaa
71 };
72
73 let bind_group = render_context.render_device().create_bind_group(
74 Some("motion_blur_bind_group"),
75 layout,
76 &BindGroupEntries::sequential((
77 post_process.source,
78 &prepass_motion_vectors_texture.texture.default_view,
79 &prepass_depth_texture.texture.default_view,
80 &motion_blur_pipeline.sampler,
81 settings_binding.clone(),
82 globals_uniforms.clone(),
83 )),
84 );
85
86 let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
87 label: Some("motion_blur"),
88 color_attachments: &[Some(RenderPassColorAttachment {
89 view: post_process.destination,
90 depth_slice: None,
91 resolve_target: None,
92 ops: Operations::default(),
93 })],
94 depth_stencil_attachment: None,
95 timestamp_writes: None,
96 occlusion_query_set: None,
97 });
98 let pass_span = diagnostics.pass_span(&mut render_pass, "motion_blur");
99
100 render_pass.set_render_pipeline(pipeline);
101 render_pass.set_bind_group(0, &bind_group, &[]);
102 render_pass.draw(0..3, 0..1);
103
104 pass_span.end(&mut render_pass);
105
106 Ok(())
107 }
108}