Skip to content

Commit

Permalink
Temporal Antialiasing (TAA) (#7291)
Browse files Browse the repository at this point in the history
![image](https://user-images.githubusercontent.com/47158642/214374911-412f0986-3927-4f7a-9a6c-413bdee6b389.png)

# Objective

- Implement an alternative antialias technique
- TAA scales based off of view resolution, not geometry complexity
- TAA filters textures, firefly pixels, and other aliasing not covered
by MSAA
- TAA additionally will reduce noise / increase quality in future
stochastic rendering techniques
- Closes #3663

## Solution

- Add a temporal jitter component
- Add a motion vector prepass
- Add a TemporalAntialias component and plugin
- Combine existing MSAA and FXAA examples and add TAA

## Followup Work
- Prepass motion vector support for skinned meshes
- Move uniforms needed for motion vectors into a separate bind group,
instead of using different bind group layouts
- Reuse previous frame's GPU view buffer for motion vectors, instead of
recomputing
- Mip biasing for sharper textures, and or unjitter texture UVs
#7323
- Compute shader for better performance
- Investigate FSR techniques
  - Historical depth based disocclusion tests, for geometry disocclusion
  - Historical luminance/hue based tests, for shading disocclusion
- Pixel "locks" to reduce blending rate / revamp history confidence
mechanism
- Orthographic camera support for TemporalJitter
- Figure out COD's 1-tap bicubic filter

---

## Changelog

- Added MotionVectorPrepass and TemporalJitter
- Added TemporalAntialiasPlugin, TemporalAntialiasBundle, and
TemporalAntialiasSettings

---------

Co-authored-by: IceSentry <[email protected]>
Co-authored-by: IceSentry <[email protected]>
Co-authored-by: Robert Swain <[email protected]>
Co-authored-by: Daniel Chia <[email protected]>
Co-authored-by: robtfm <[email protected]>
Co-authored-by: Brandon Dyer <[email protected]>
Co-authored-by: Edgar Geier <[email protected]>
  • Loading branch information
8 people authored Mar 27, 2023
1 parent 3d8c768 commit 53667de
Show file tree
Hide file tree
Showing 29 changed files with 1,830 additions and 500 deletions.
30 changes: 10 additions & 20 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,16 @@ description = "A scene showcasing the built-in 3D shapes"
category = "3D Rendering"
wasm = true

[[example]]
name = "anti_aliasing"
path = "examples/3d/anti_aliasing.rs"

[package.metadata.example.anti_aliasing]
name = "Anti-aliasing"
description = "Compares different anti-aliasing methods"
category = "3D Rendering"
wasm = false

[[example]]
name = "3d_gizmos"
path = "examples/3d/3d_gizmos.rs"
Expand Down Expand Up @@ -515,26 +525,6 @@ description = "Compares tonemapping options"
category = "3D Rendering"
wasm = true

[[example]]
name = "fxaa"
path = "examples/3d/fxaa.rs"

[package.metadata.example.fxaa]
name = "FXAA"
description = "Compares MSAA (Multi-Sample Anti-Aliasing) and FXAA (Fast Approximate Anti-Aliasing)"
category = "3D Rendering"
wasm = true

[[example]]
name = "msaa"
path = "examples/3d/msaa.rs"

[package.metadata.example.msaa]
name = "MSAA"
description = "Configures MSAA (Multi-Sample Anti-Aliasing) for smoother edges"
category = "3D Rendering"
wasm = true

[[example]]
name = "orthographic"
path = "examples/3d/orthographic.rs"
Expand Down
4 changes: 4 additions & 0 deletions assets/shaders/show_prepass.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
struct ShowPrepassSettings {
show_depth: u32,
show_normals: u32,
show_motion_vectors: u32,
padding_1: u32,
padding_2: u32,
}
Expand All @@ -23,6 +24,9 @@ fn fragment(
} else if settings.show_normals == 1u {
let normal = prepass_normal(frag_coord, sample_index);
return vec4(normal, 1.0);
} else if settings.show_motion_vectors == 1u {
let motion_vector = prepass_motion_vector(frag_coord, sample_index);
return vec4(motion_vector / globals.delta_time, 0.0, 1.0);
}

return vec4(0.0);
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ tonemapping_luts = []
# bevy
bevy_app = { path = "../bevy_app", version = "0.11.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.11.0-dev" }
bevy_core = { path = "../bevy_core", version = "0.11.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.11.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.11.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.11.0-dev" }
Expand Down
5 changes: 0 additions & 5 deletions crates/bevy_core_pipeline/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ use bevy_render::{
view::ViewTarget,
Render, RenderApp, RenderSet,
};
#[cfg(feature = "trace")]
use bevy_utils::tracing::info_span;
use downsampling_pipeline::{
prepare_downsampling_pipeline, BloomDownsamplingPipeline, BloomDownsamplingPipelineIds,
BloomUniforms,
Expand Down Expand Up @@ -150,9 +148,6 @@ impl Node for BloomNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
#[cfg(feature = "trace")]
let _bloom_span = info_span!("bloom").entered();

let downsampling_pipeline_res = world.resource::<BloomDownsamplingPipeline>();
let pipeline_cache = world.resource::<PipelineCache>();
let uniforms = world.resource::<ComponentUniforms<BloomUniforms>>();
Expand Down
9 changes: 7 additions & 2 deletions crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
clear_color::{ClearColor, ClearColorConfig},
core_3d::{AlphaMask3d, Camera3d, Opaque3d, Transparent3d},
prepass::{DepthPrepass, NormalPrepass},
prepass::{DepthPrepass, MotionVectorPrepass, NormalPrepass},
};
use bevy_ecs::prelude::*;
use bevy_render::{
Expand Down Expand Up @@ -29,6 +29,7 @@ pub struct MainPass3dNode {
&'static ViewDepthTexture,
Option<&'static DepthPrepass>,
Option<&'static NormalPrepass>,
Option<&'static MotionVectorPrepass>,
),
With<ExtractedView>,
>,
Expand Down Expand Up @@ -64,6 +65,7 @@ impl Node for MainPass3dNode {
depth,
depth_prepass,
normal_prepass,
motion_vector_prepass,
)) = self.query.get_manual(world, view_entity) else {
// No window
return Ok(());
Expand Down Expand Up @@ -94,7 +96,10 @@ impl Node for MainPass3dNode {
view: &depth.view,
// NOTE: The opaque main pass loads the depth buffer and possibly overwrites it
depth_ops: Some(Operations {
load: if depth_prepass.is_some() || normal_prepass.is_some() {
load: if depth_prepass.is_some()
|| normal_prepass.is_some()
|| motion_vector_prepass.is_some()
{
// if any prepass runs, it will generate a depth buffer so we should use it,
// even if only the normal_prepass is used.
Camera3dDepthLoadOp::Load
Expand Down
8 changes: 8 additions & 0 deletions crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ pub mod fullscreen_vertex_shader;
pub mod fxaa;
pub mod msaa_writeback;
pub mod prepass;
mod taa;
pub mod tonemapping;
pub mod upscaling;

/// Experimental features that are not yet finished. Please report any issues you encounter!
pub mod experimental {
pub mod taa {
pub use crate::taa::*;
}
}

pub mod prelude {
#[doc(hidden)]
pub use crate::{
Expand Down
17 changes: 13 additions & 4 deletions crates/bevy_core_pipeline/src/prepass/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//! Run a prepass before the main pass to generate depth and/or normals texture, sometimes called a thin g-buffer.
//! Run a prepass before the main pass to generate depth, normals, and/or motion vectors textures, sometimes called a thin g-buffer.
//! These textures are useful for various screen-space effects and reducing overdraw in the main pass.
//!
//! The prepass only runs for opaque meshes or meshes with an alpha mask. Transparent meshes are ignored.
Expand All @@ -7,6 +7,7 @@
//!
//! [`DepthPrepass`]
//! [`NormalPrepass`]
//! [`MotionVectorPrepass`]
//!
//! The textures are automatically added to the default mesh view bindings. You can also get the raw textures
//! by querying the [`ViewPrepassTextures`] component on any camera with a prepass component.
Expand All @@ -15,9 +16,9 @@
//! to a separate texture unless the [`DepthPrepass`] is activated. This means that if any prepass component is present
//! it will always create a depth buffer that will be used by the main pass.
//!
//! When using the default mesh view bindings you should be able to use `prepass_depth()`
//! and `prepass_normal()` to load the related textures. These functions are defined in `bevy_pbr::prepass_utils`.
//! See the `shader_prepass` example that shows how to use it.
//! When using the default mesh view bindings you should be able to use `prepass_depth()`,
//! `prepass_normal()`, and `prepass_motion_vector()` to load the related textures.
//! These functions are defined in `bevy_pbr::prepass_utils`. See the `shader_prepass` example that shows how to use them.
//!
//! The prepass runs for each `Material`. You can control if the prepass should run per-material by setting the `prepass_enabled`
//! flag on the `MaterialPlugin`.
Expand All @@ -39,6 +40,7 @@ use bevy_utils::FloatOrd;

pub const DEPTH_PREPASS_FORMAT: TextureFormat = TextureFormat::Depth32Float;
pub const NORMAL_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgb10a2Unorm;
pub const MOTION_VECTOR_PREPASS_FORMAT: TextureFormat = TextureFormat::Rg16Float;

/// If added to a [`crate::prelude::Camera3d`] then depth values will be copied to a separate texture available to the main pass.
#[derive(Component, Default, Reflect)]
Expand All @@ -49,6 +51,10 @@ pub struct DepthPrepass;
#[derive(Component, Default, Reflect)]
pub struct NormalPrepass;

/// If added to a [`crate::prelude::Camera3d`] then screen space motion vectors will be copied to a separate texture available to the main pass.
#[derive(Component, Default, Reflect)]
pub struct MotionVectorPrepass;

/// Textures that are written to by the prepass.
///
/// This component will only be present if any of the relevant prepass components are also present.
Expand All @@ -60,6 +66,9 @@ pub struct ViewPrepassTextures {
/// The normals texture generated by the prepass.
/// Exists only if [`NormalPrepass`] is added to the `ViewTarget`
pub normal: Option<CachedTexture>,
/// The motion vectors texture generated by the prepass.
/// Exists only if [`MotionVectorPrepass`] is added to the `ViewTarget`
pub motion_vectors: Option<CachedTexture>,
/// The size of the textures.
pub size: Extent3d,
}
Expand Down
29 changes: 24 additions & 5 deletions crates/bevy_core_pipeline/src/prepass/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,34 @@ impl Node for PrepassNode {
};

let mut color_attachments = vec![];
if let Some(view_normals_texture) = &view_prepass_textures.normal {
color_attachments.push(Some(RenderPassColorAttachment {
view: &view_normals_texture.default_view,
color_attachments.push(
view_prepass_textures
.normal
.as_ref()
.map(|view_normals_texture| RenderPassColorAttachment {
view: &view_normals_texture.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::BLACK.into()),
store: true,
},
}),
);
color_attachments.push(view_prepass_textures.motion_vectors.as_ref().map(
|view_motion_vectors_texture| RenderPassColorAttachment {
view: &view_motion_vectors_texture.default_view,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::BLACK.into()),
// Blue channel doesn't matter, but set to 1.0 for possible faster clear
// https://gpuopen.com/performance/#clears
load: LoadOp::Clear(Color::rgb_linear(1.0, 1.0, 1.0).into()),
store: true,
},
}));
},
));
if color_attachments.iter().all(Option::is_none) {
// all attachments are none: clear the attachment list so that no fragment shader is required
color_attachments.clear();
}

{
Expand Down
Loading

0 comments on commit 53667de

Please sign in to comment.