Smaller TAA fixes (#10200)
Extracted the easy stuff from #8974 . # Problem 1. Commands from `update_previous_view_projections` would crash when matching entities were despawned. 2. `TaaPipelineId` and `draw_3d_graph` module were not public. 3. When the motion vectors pointed to pixels that are now off screen, a smearing artifact could occur. # Solution 1. Use `try_insert` command instead. 2. Make them public, renaming to `TemporalAntiAliasPipelineId`. 3. Check for this case, and ignore history for pixels that are off-screen.
This commit is contained in:
		
							parent
							
								
									c5087fef3c
								
							
						
					
					
						commit
						b208388af9
					
				| @ -21,7 +21,10 @@ pub use skybox::Skybox; | |||||||
| /// Experimental features that are not yet finished. Please report any issues you encounter!
 | /// Experimental features that are not yet finished. Please report any issues you encounter!
 | ||||||
| pub mod experimental { | pub mod experimental { | ||||||
|     pub mod taa { |     pub mod taa { | ||||||
|         pub use crate::taa::*; |         pub use crate::taa::{ | ||||||
|  |             TemporalAntiAliasBundle, TemporalAntiAliasNode, TemporalAntiAliasPlugin, | ||||||
|  |             TemporalAntiAliasSettings, | ||||||
|  |         }; | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -35,7 +35,7 @@ use bevy_render::{ | |||||||
|     ExtractSchedule, MainWorld, Render, RenderApp, RenderSet, |     ExtractSchedule, MainWorld, Render, RenderApp, RenderSet, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| mod draw_3d_graph { | pub mod draw_3d_graph { | ||||||
|     pub mod node { |     pub mod node { | ||||||
|         /// Label for the TAA render node.
 |         /// Label for the TAA render node.
 | ||||||
|         pub const TAA: &str = "taa"; |         pub const TAA: &str = "taa"; | ||||||
| @ -61,7 +61,7 @@ impl Plugin for TemporalAntiAliasPlugin { | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         render_app |         render_app | ||||||
|             .init_resource::<SpecializedRenderPipelines<TAAPipeline>>() |             .init_resource::<SpecializedRenderPipelines<TaaPipeline>>() | ||||||
|             .add_systems(ExtractSchedule, extract_taa_settings) |             .add_systems(ExtractSchedule, extract_taa_settings) | ||||||
|             .add_systems( |             .add_systems( | ||||||
|                 Render, |                 Render, | ||||||
| @ -71,7 +71,10 @@ impl Plugin for TemporalAntiAliasPlugin { | |||||||
|                     prepare_taa_history_textures.in_set(RenderSet::PrepareResources), |                     prepare_taa_history_textures.in_set(RenderSet::PrepareResources), | ||||||
|                 ), |                 ), | ||||||
|             ) |             ) | ||||||
|             .add_render_graph_node::<ViewNodeRunner<TAANode>>(CORE_3D, draw_3d_graph::node::TAA) |             .add_render_graph_node::<ViewNodeRunner<TemporalAntiAliasNode>>( | ||||||
|  |                 CORE_3D, | ||||||
|  |                 draw_3d_graph::node::TAA, | ||||||
|  |             ) | ||||||
|             .add_render_graph_edges( |             .add_render_graph_edges( | ||||||
|                 CORE_3D, |                 CORE_3D, | ||||||
|                 &[ |                 &[ | ||||||
| @ -88,7 +91,7 @@ impl Plugin for TemporalAntiAliasPlugin { | |||||||
|             return; |             return; | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         render_app.init_resource::<TAAPipeline>(); |         render_app.init_resource::<TaaPipeline>(); | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -110,14 +113,13 @@ pub struct TemporalAntiAliasBundle { | |||||||
| /// # Tradeoffs
 | /// # Tradeoffs
 | ||||||
| ///
 | ///
 | ||||||
| /// Pros:
 | /// Pros:
 | ||||||
|  | /// * Filters more types of aliasing than MSAA, such as textures and singular bright pixels (specular aliasing)
 | ||||||
| /// * Cost scales with screen/view resolution, unlike MSAA which scales with number of triangles
 | /// * Cost scales with screen/view resolution, unlike MSAA which scales with number of triangles
 | ||||||
| /// * Filters more types of aliasing than MSAA, such as textures and singular bright pixels
 | /// * Greatly increases the quality of stochastic rendering techniques such as SSAO, certain shadow map sampling methods, etc
 | ||||||
| /// * Greatly increases the quality of stochastic rendering techniques such as SSAO, shadow mapping, etc
 |  | ||||||
| ///
 | ///
 | ||||||
| /// Cons:
 | /// Cons:
 | ||||||
| /// * Chance of "ghosting" - ghostly trails left behind moving objects
 | /// * Chance of "ghosting" - ghostly trails left behind moving objects
 | ||||||
| /// * Thin geometry, lighting detail, or texture lines may flicker or disappear
 | /// * Thin geometry, lighting detail, or texture lines may flicker noisily or disappear
 | ||||||
| /// * Slightly blurs the image, leading to a softer look (using an additional sharpening pass can reduce this)
 |  | ||||||
| ///
 | ///
 | ||||||
| /// Because TAA blends past frames with the current frame, when the frames differ too much
 | /// Because TAA blends past frames with the current frame, when the frames differ too much
 | ||||||
| /// (such as with fast moving objects or camera cuts), ghosting artifacts may occur.
 | /// (such as with fast moving objects or camera cuts), ghosting artifacts may occur.
 | ||||||
| @ -130,7 +132,7 @@ pub struct TemporalAntiAliasBundle { | |||||||
| /// and add the [`DepthPrepass`], [`MotionVectorPrepass`], and [`TemporalJitter`]
 | /// and add the [`DepthPrepass`], [`MotionVectorPrepass`], and [`TemporalJitter`]
 | ||||||
| /// components to your camera.
 | /// components to your camera.
 | ||||||
| ///
 | ///
 | ||||||
| /// Cannot be used with [`bevy_render::camera::OrthographicProjection`].
 | /// [Currently](https://github.com/bevyengine/bevy/issues/8423) cannot be used with [`bevy_render::camera::OrthographicProjection`].
 | ||||||
| ///
 | ///
 | ||||||
| /// Currently does not support skinned meshes and morph targets.
 | /// Currently does not support skinned meshes and morph targets.
 | ||||||
| /// There will probably be ghosting artifacts if used with them.
 | /// There will probably be ghosting artifacts if used with them.
 | ||||||
| @ -151,7 +153,7 @@ pub struct TemporalAntiAliasSettings { | |||||||
|     /// representative of the current frame, such as in sudden camera cuts.
 |     /// representative of the current frame, such as in sudden camera cuts.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// After setting this to true, it will automatically be toggled
 |     /// After setting this to true, it will automatically be toggled
 | ||||||
|     /// back to false after one frame.
 |     /// back to false at the end of the frame.
 | ||||||
|     pub reset: bool, |     pub reset: bool, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -161,16 +163,17 @@ impl Default for TemporalAntiAliasSettings { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Render [`bevy_render::render_graph::Node`] used by temporal anti-aliasing.
 | ||||||
| #[derive(Default)] | #[derive(Default)] | ||||||
| struct TAANode; | pub struct TemporalAntiAliasNode; | ||||||
| 
 | 
 | ||||||
| impl ViewNode for TAANode { | impl ViewNode for TemporalAntiAliasNode { | ||||||
|     type ViewQuery = ( |     type ViewQuery = ( | ||||||
|         &'static ExtractedCamera, |         &'static ExtractedCamera, | ||||||
|         &'static ViewTarget, |         &'static ViewTarget, | ||||||
|         &'static TAAHistoryTextures, |         &'static TemporalAntiAliasHistoryTextures, | ||||||
|         &'static ViewPrepassTextures, |         &'static ViewPrepassTextures, | ||||||
|         &'static TAAPipelineId, |         &'static TemporalAntiAliasPipelineId, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     fn run( |     fn run( | ||||||
| @ -183,7 +186,7 @@ impl ViewNode for TAANode { | |||||||
|         world: &World, |         world: &World, | ||||||
|     ) -> Result<(), NodeRunError> { |     ) -> Result<(), NodeRunError> { | ||||||
|         let (Some(pipelines), Some(pipeline_cache)) = ( |         let (Some(pipelines), Some(pipeline_cache)) = ( | ||||||
|             world.get_resource::<TAAPipeline>(), |             world.get_resource::<TaaPipeline>(), | ||||||
|             world.get_resource::<PipelineCache>(), |             world.get_resource::<PipelineCache>(), | ||||||
|         ) else { |         ) else { | ||||||
|             return Ok(()); |             return Ok(()); | ||||||
| @ -240,13 +243,13 @@ impl ViewNode for TAANode { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Resource)] | #[derive(Resource)] | ||||||
| struct TAAPipeline { | struct TaaPipeline { | ||||||
|     taa_bind_group_layout: BindGroupLayout, |     taa_bind_group_layout: BindGroupLayout, | ||||||
|     nearest_sampler: Sampler, |     nearest_sampler: Sampler, | ||||||
|     linear_sampler: Sampler, |     linear_sampler: Sampler, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl FromWorld for TAAPipeline { | impl FromWorld for TaaPipeline { | ||||||
|     fn from_world(world: &mut World) -> Self { |     fn from_world(world: &mut World) -> Self { | ||||||
|         let render_device = world.resource::<RenderDevice>(); |         let render_device = world.resource::<RenderDevice>(); | ||||||
| 
 | 
 | ||||||
| @ -328,7 +331,7 @@ impl FromWorld for TAAPipeline { | |||||||
|                 ], |                 ], | ||||||
|             }); |             }); | ||||||
| 
 | 
 | ||||||
|         TAAPipeline { |         TaaPipeline { | ||||||
|             taa_bind_group_layout, |             taa_bind_group_layout, | ||||||
|             nearest_sampler, |             nearest_sampler, | ||||||
|             linear_sampler, |             linear_sampler, | ||||||
| @ -337,13 +340,13 @@ impl FromWorld for TAAPipeline { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(PartialEq, Eq, Hash, Clone)] | #[derive(PartialEq, Eq, Hash, Clone)] | ||||||
| struct TAAPipelineKey { | struct TaaPipelineKey { | ||||||
|     hdr: bool, |     hdr: bool, | ||||||
|     reset: bool, |     reset: bool, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl SpecializedRenderPipeline for TAAPipeline { | impl SpecializedRenderPipeline for TaaPipeline { | ||||||
|     type Key = TAAPipelineKey; |     type Key = TaaPipelineKey; | ||||||
| 
 | 
 | ||||||
|     fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { |     fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { | ||||||
|         let mut shader_defs = vec![]; |         let mut shader_defs = vec![]; | ||||||
| @ -440,7 +443,7 @@ fn prepare_taa_jitter_and_mip_bias( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Component)] | #[derive(Component)] | ||||||
| struct TAAHistoryTextures { | pub struct TemporalAntiAliasHistoryTextures { | ||||||
|     write: CachedTexture, |     write: CachedTexture, | ||||||
|     read: CachedTexture, |     read: CachedTexture, | ||||||
| } | } | ||||||
| @ -480,12 +483,12 @@ fn prepare_taa_history_textures( | |||||||
|             let history_2_texture = texture_cache.get(&render_device, texture_descriptor); |             let history_2_texture = texture_cache.get(&render_device, texture_descriptor); | ||||||
| 
 | 
 | ||||||
|             let textures = if frame_count.0 % 2 == 0 { |             let textures = if frame_count.0 % 2 == 0 { | ||||||
|                 TAAHistoryTextures { |                 TemporalAntiAliasHistoryTextures { | ||||||
|                     write: history_1_texture, |                     write: history_1_texture, | ||||||
|                     read: history_2_texture, |                     read: history_2_texture, | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 TAAHistoryTextures { |                 TemporalAntiAliasHistoryTextures { | ||||||
|                     write: history_2_texture, |                     write: history_2_texture, | ||||||
|                     read: history_1_texture, |                     read: history_1_texture, | ||||||
|                 } |                 } | ||||||
| @ -497,17 +500,17 @@ fn prepare_taa_history_textures( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(Component)] | #[derive(Component)] | ||||||
| struct TAAPipelineId(CachedRenderPipelineId); | pub struct TemporalAntiAliasPipelineId(CachedRenderPipelineId); | ||||||
| 
 | 
 | ||||||
| fn prepare_taa_pipelines( | fn prepare_taa_pipelines( | ||||||
|     mut commands: Commands, |     mut commands: Commands, | ||||||
|     pipeline_cache: Res<PipelineCache>, |     pipeline_cache: Res<PipelineCache>, | ||||||
|     mut pipelines: ResMut<SpecializedRenderPipelines<TAAPipeline>>, |     mut pipelines: ResMut<SpecializedRenderPipelines<TaaPipeline>>, | ||||||
|     pipeline: Res<TAAPipeline>, |     pipeline: Res<TaaPipeline>, | ||||||
|     views: Query<(Entity, &ExtractedView, &TemporalAntiAliasSettings)>, |     views: Query<(Entity, &ExtractedView, &TemporalAntiAliasSettings)>, | ||||||
| ) { | ) { | ||||||
|     for (entity, view, taa_settings) in &views { |     for (entity, view, taa_settings) in &views { | ||||||
|         let mut pipeline_key = TAAPipelineKey { |         let mut pipeline_key = TaaPipelineKey { | ||||||
|             hdr: view.hdr, |             hdr: view.hdr, | ||||||
|             reset: taa_settings.reset, |             reset: taa_settings.reset, | ||||||
|         }; |         }; | ||||||
| @ -519,6 +522,8 @@ fn prepare_taa_pipelines( | |||||||
|             pipelines.specialize(&pipeline_cache, &pipeline, pipeline_key); |             pipelines.specialize(&pipeline_cache, &pipeline, pipeline_key); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         commands.entity(entity).insert(TAAPipelineId(pipeline_id)); |         commands | ||||||
|  |             .entity(entity) | ||||||
|  |             .insert(TemporalAntiAliasPipelineId(pipeline_id)); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -175,7 +175,14 @@ fn taa(@location(0) uv: vec2<f32>) -> Output { | |||||||
|     // Blend current and past sample |     // Blend current and past sample | ||||||
|     // Use more of the history if we're confident in it (reduces noise when there is no motion) |     // Use more of the history if we're confident in it (reduces noise when there is no motion) | ||||||
|     // https://hhoppe.com/supersample.pdf, section 4.1 |     // https://hhoppe.com/supersample.pdf, section 4.1 | ||||||
|     let current_color_factor = clamp(1.0 / history_confidence, MIN_HISTORY_BLEND_RATE, DEFAULT_HISTORY_BLEND_RATE); |     var current_color_factor = clamp(1.0 / history_confidence, MIN_HISTORY_BLEND_RATE, DEFAULT_HISTORY_BLEND_RATE); | ||||||
|  | 
 | ||||||
|  |     // Reject history when motion vectors point off screen | ||||||
|  |     if any(saturate(history_uv) != history_uv) { | ||||||
|  |         current_color_factor = 1.0; | ||||||
|  |         history_confidence = 1.0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     current_color = mix(history_color, current_color, current_color_factor); |     current_color = mix(history_color, current_color, current_color_factor); | ||||||
| #endif // #ifndef RESET | #endif // #ifndef RESET | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -185,7 +185,7 @@ pub fn update_previous_view_projections( | |||||||
|     query: Query<(Entity, &Camera, &GlobalTransform), (With<Camera3d>, With<MotionVectorPrepass>)>, |     query: Query<(Entity, &Camera, &GlobalTransform), (With<Camera3d>, With<MotionVectorPrepass>)>, | ||||||
| ) { | ) { | ||||||
|     for (entity, camera, camera_transform) in &query { |     for (entity, camera, camera_transform) in &query { | ||||||
|         commands.entity(entity).insert(PreviousViewProjection { |         commands.entity(entity).try_insert(PreviousViewProjection { | ||||||
|             view_proj: camera.projection_matrix() * camera_transform.compute_matrix().inverse(), |             view_proj: camera.projection_matrix() * camera_transform.compute_matrix().inverse(), | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| @ -205,7 +205,7 @@ pub fn update_mesh_previous_global_transforms( | |||||||
|         for (entity, transform) in &meshes { |         for (entity, transform) in &meshes { | ||||||
|             commands |             commands | ||||||
|                 .entity(entity) |                 .entity(entity) | ||||||
|                 .insert(PreviousGlobalTransform(transform.affine())); |                 .try_insert(PreviousGlobalTransform(transform.affine())); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 JMS55
						JMS55