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:
JMS55 2023-10-27 16:13:14 -07:00 committed by GitHub
parent c5087fef3c
commit b208388af9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 33 deletions

View File

@ -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,
};
} }
} }

View File

@ -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));
} }
} }

View File

@ -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

View File

@ -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()));
} }
} }
} }