Bias texture mipmaps (#7614)
# Objective - Closes #7323 - Reduce texture blurriness for TAA ## Solution - Add a `MipBias` component and view uniform. - Switch material `textureSample()` calls to `textureSampleBias()`. - Add a `-1.0` bias to TAA. --- ## Changelog - Added `MipBias` camera component, mostly for internal use. --------- Co-authored-by: François <mockersf@gmail.com> Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
c6170d48f9
commit
724e69bff4
@ -10,14 +10,14 @@ use bevy_core::FrameCount;
|
|||||||
use bevy_ecs::{
|
use bevy_ecs::{
|
||||||
prelude::{Bundle, Component, Entity},
|
prelude::{Bundle, Component, Entity},
|
||||||
query::{QueryItem, With},
|
query::{QueryItem, With},
|
||||||
schedule::IntoSystemConfigs,
|
schedule::{apply_deferred, IntoSystemConfigs},
|
||||||
system::{Commands, Query, Res, ResMut, Resource},
|
system::{Commands, Query, Res, ResMut, Resource},
|
||||||
world::{FromWorld, World},
|
world::{FromWorld, World},
|
||||||
};
|
};
|
||||||
use bevy_math::vec2;
|
use bevy_math::vec2;
|
||||||
use bevy_reflect::{Reflect, TypeUuid};
|
use bevy_reflect::{Reflect, TypeUuid};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
camera::{ExtractedCamera, TemporalJitter},
|
camera::{ExtractedCamera, MipBias, TemporalJitter},
|
||||||
prelude::{Camera, Projection},
|
prelude::{Camera, Projection},
|
||||||
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
|
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
|
||||||
render_resource::{
|
render_resource::{
|
||||||
@ -65,7 +65,8 @@ impl Plugin for TemporalAntiAliasPlugin {
|
|||||||
.add_systems(
|
.add_systems(
|
||||||
Render,
|
Render,
|
||||||
(
|
(
|
||||||
prepare_taa_jitter
|
(prepare_taa_jitter_and_mip_bias, apply_deferred)
|
||||||
|
.chain()
|
||||||
.before(prepare_view_uniforms)
|
.before(prepare_view_uniforms)
|
||||||
.in_set(RenderSet::Prepare),
|
.in_set(RenderSet::Prepare),
|
||||||
prepare_taa_history_textures.in_set(RenderSet::Prepare),
|
prepare_taa_history_textures.in_set(RenderSet::Prepare),
|
||||||
@ -140,6 +141,8 @@ pub struct TemporalAntiAliasBundle {
|
|||||||
/// are added using a third party library, the library must either:
|
/// are added using a third party library, the library must either:
|
||||||
/// 1. Write particle motion vectors to the motion vectors prepass texture
|
/// 1. Write particle motion vectors to the motion vectors prepass texture
|
||||||
/// 2. Render particles after TAA
|
/// 2. Render particles after TAA
|
||||||
|
///
|
||||||
|
/// If no [`MipBias`] component is attached to the camera, TAA will add a MipBias(-1.0) component.
|
||||||
#[derive(Component, Reflect, Clone)]
|
#[derive(Component, Reflect, Clone)]
|
||||||
pub struct TemporalAntiAliasSettings {
|
pub struct TemporalAntiAliasSettings {
|
||||||
/// Set to true to delete the saved temporal history (past frames).
|
/// Set to true to delete the saved temporal history (past frames).
|
||||||
@ -436,9 +439,13 @@ fn extract_taa_settings(mut commands: Commands, mut main_world: ResMut<MainWorld
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_taa_jitter(
|
fn prepare_taa_jitter_and_mip_bias(
|
||||||
frame_count: Res<FrameCount>,
|
frame_count: Res<FrameCount>,
|
||||||
mut query: Query<&mut TemporalJitter, With<TemporalAntiAliasSettings>>,
|
mut query: Query<
|
||||||
|
(Entity, &mut TemporalJitter, Option<&MipBias>),
|
||||||
|
With<TemporalAntiAliasSettings>,
|
||||||
|
>,
|
||||||
|
mut commands: Commands,
|
||||||
) {
|
) {
|
||||||
// Halton sequence (2, 3) - 0.5, skipping i = 0
|
// Halton sequence (2, 3) - 0.5, skipping i = 0
|
||||||
let halton_sequence = [
|
let halton_sequence = [
|
||||||
@ -454,8 +461,12 @@ fn prepare_taa_jitter(
|
|||||||
|
|
||||||
let offset = halton_sequence[frame_count.0 as usize % halton_sequence.len()];
|
let offset = halton_sequence[frame_count.0 as usize % halton_sequence.len()];
|
||||||
|
|
||||||
for mut jitter in &mut query {
|
for (entity, mut jitter, mip_bias) in &mut query {
|
||||||
jitter.offset = offset;
|
jitter.offset = offset;
|
||||||
|
|
||||||
|
if mip_bias.is_none() {
|
||||||
|
commands.entity(entity).insert(MipBias(-1.0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -55,7 +55,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
if ((material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u) {
|
||||||
output_color = output_color * textureSample(base_color_texture, base_color_sampler, uv);
|
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, uv, view.mip_bias);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
var emissive: vec4<f32> = material.emissive;
|
var emissive: vec4<f32> = material.emissive;
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
if ((material.flags & STANDARD_MATERIAL_FLAGS_EMISSIVE_TEXTURE_BIT) != 0u) {
|
||||||
emissive = vec4<f32>(emissive.rgb * textureSample(emissive_texture, emissive_sampler, uv).rgb, 1.0);
|
emissive = vec4<f32>(emissive.rgb * textureSampleBias(emissive_texture, emissive_sampler, uv, view.mip_bias).rgb, 1.0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
pbr_input.material.emissive = emissive;
|
pbr_input.material.emissive = emissive;
|
||||||
@ -83,7 +83,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
var perceptual_roughness: f32 = material.perceptual_roughness;
|
var perceptual_roughness: f32 = material.perceptual_roughness;
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
if ((material.flags & STANDARD_MATERIAL_FLAGS_METALLIC_ROUGHNESS_TEXTURE_BIT) != 0u) {
|
||||||
let metallic_roughness = textureSample(metallic_roughness_texture, metallic_roughness_sampler, uv);
|
let metallic_roughness = textureSampleBias(metallic_roughness_texture, metallic_roughness_sampler, uv, view.mip_bias);
|
||||||
// Sampling from GLTF standard channels for now
|
// Sampling from GLTF standard channels for now
|
||||||
metallic = metallic * metallic_roughness.b;
|
metallic = metallic * metallic_roughness.b;
|
||||||
perceptual_roughness = perceptual_roughness * metallic_roughness.g;
|
perceptual_roughness = perceptual_roughness * metallic_roughness.g;
|
||||||
@ -96,7 +96,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
|
|||||||
var occlusion: vec3<f32> = vec3(1.0);
|
var occlusion: vec3<f32> = vec3(1.0);
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
if ((material.flags & STANDARD_MATERIAL_FLAGS_OCCLUSION_TEXTURE_BIT) != 0u) {
|
||||||
occlusion = vec3(textureSample(occlusion_texture, occlusion_sampler, in.uv).r);
|
occlusion = vec3(textureSampleBias(occlusion_texture, occlusion_sampler, in.uv, view.mip_bias).r);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
|
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION
|
||||||
|
|||||||
@ -82,7 +82,7 @@ fn apply_normal_mapping(
|
|||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
#ifdef STANDARDMATERIAL_NORMAL_MAP
|
||||||
// Nt is the tangent-space normal.
|
// Nt is the tangent-space normal.
|
||||||
var Nt = textureSample(normal_map_texture, normal_map_sampler, uv).rgb;
|
var Nt = textureSampleBias(normal_map_texture, normal_map_sampler, uv, view.mip_bias).rgb;
|
||||||
if (standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
|
if (standard_material_flags & STANDARD_MATERIAL_FLAGS_TWO_COMPONENT_NORMAL_MAP) != 0u {
|
||||||
// Only use the xy components and derive z for 2-component normal maps.
|
// Only use the xy components and derive z for 2-component normal maps.
|
||||||
Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
|
Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ fn prepass_alpha_discard(in: FragmentInput) {
|
|||||||
|
|
||||||
#ifdef VERTEX_UVS
|
#ifdef VERTEX_UVS
|
||||||
if (material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
|
if (material.flags & STANDARD_MATERIAL_FLAGS_BASE_COLOR_TEXTURE_BIT) != 0u {
|
||||||
output_color = output_color * textureSample(base_color_texture, base_color_sampler, in.uv);
|
output_color = output_color * textureSampleBias(base_color_texture, base_color_sampler, in.uv, view.mip_bias);
|
||||||
}
|
}
|
||||||
#endif // VERTEX_UVS
|
#endif // VERTEX_UVS
|
||||||
|
|
||||||
|
|||||||
@ -771,3 +771,9 @@ impl TemporalJitter {
|
|||||||
projection.z_axis.y += jitter.y;
|
projection.z_axis.y += jitter.y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Camera component specifying a mip bias to apply when sampling from material textures.
|
||||||
|
///
|
||||||
|
/// Often used in conjunction with antialiasing post-process effects to reduce textures blurriness.
|
||||||
|
#[derive(Component)]
|
||||||
|
pub struct MipBias(pub f32);
|
||||||
|
|||||||
@ -6,7 +6,7 @@ pub use visibility::*;
|
|||||||
pub use window::*;
|
pub use window::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
camera::{ExtractedCamera, ManualTextureViews, TemporalJitter},
|
camera::{ExtractedCamera, ManualTextureViews, MipBias, TemporalJitter},
|
||||||
extract_resource::{ExtractResource, ExtractResourcePlugin},
|
extract_resource::{ExtractResource, ExtractResourcePlugin},
|
||||||
prelude::{Image, Shader},
|
prelude::{Image, Shader},
|
||||||
render_asset::RenderAssets,
|
render_asset::RenderAssets,
|
||||||
@ -173,6 +173,7 @@ pub struct ViewUniform {
|
|||||||
// viewport(x_origin, y_origin, width, height)
|
// viewport(x_origin, y_origin, width, height)
|
||||||
viewport: Vec4,
|
viewport: Vec4,
|
||||||
color_grading: ColorGrading,
|
color_grading: ColorGrading,
|
||||||
|
mip_bias: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
@ -352,11 +353,16 @@ pub fn prepare_view_uniforms(
|
|||||||
render_device: Res<RenderDevice>,
|
render_device: Res<RenderDevice>,
|
||||||
render_queue: Res<RenderQueue>,
|
render_queue: Res<RenderQueue>,
|
||||||
mut view_uniforms: ResMut<ViewUniforms>,
|
mut view_uniforms: ResMut<ViewUniforms>,
|
||||||
views: Query<(Entity, &ExtractedView, Option<&TemporalJitter>)>,
|
views: Query<(
|
||||||
|
Entity,
|
||||||
|
&ExtractedView,
|
||||||
|
Option<&TemporalJitter>,
|
||||||
|
Option<&MipBias>,
|
||||||
|
)>,
|
||||||
) {
|
) {
|
||||||
view_uniforms.uniforms.clear();
|
view_uniforms.uniforms.clear();
|
||||||
|
|
||||||
for (entity, camera, temporal_jitter) in &views {
|
for (entity, camera, temporal_jitter, mip_bias) in &views {
|
||||||
let viewport = camera.viewport.as_vec4();
|
let viewport = camera.viewport.as_vec4();
|
||||||
let unjittered_projection = camera.projection;
|
let unjittered_projection = camera.projection;
|
||||||
let mut projection = unjittered_projection;
|
let mut projection = unjittered_projection;
|
||||||
@ -383,6 +389,7 @@ pub fn prepare_view_uniforms(
|
|||||||
world_position: camera.transform.translation(),
|
world_position: camera.transform.translation(),
|
||||||
viewport,
|
viewport,
|
||||||
color_grading: camera.color_grading,
|
color_grading: camera.color_grading,
|
||||||
|
mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0,
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -19,4 +19,5 @@ struct View {
|
|||||||
// viewport(x_origin, y_origin, width, height)
|
// viewport(x_origin, y_origin, width, height)
|
||||||
viewport: vec4<f32>,
|
viewport: vec4<f32>,
|
||||||
color_grading: ColorGrading,
|
color_grading: ColorGrading,
|
||||||
|
mip_bias: f32,
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user