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