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:
JMS55 2023-06-22 16:55:05 -04:00 committed by GitHub
parent c6170d48f9
commit 724e69bff4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 40 additions and 15 deletions

View file

@ -10,14 +10,14 @@ use bevy_core::FrameCount;
use bevy_ecs::{
prelude::{Bundle, Component, Entity},
query::{QueryItem, With},
schedule::IntoSystemConfigs,
schedule::{apply_deferred, IntoSystemConfigs},
system::{Commands, Query, Res, ResMut, Resource},
world::{FromWorld, World},
};
use bevy_math::vec2;
use bevy_reflect::{Reflect, TypeUuid};
use bevy_render::{
camera::{ExtractedCamera, TemporalJitter},
camera::{ExtractedCamera, MipBias, TemporalJitter},
prelude::{Camera, Projection},
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
render_resource::{
@ -65,7 +65,8 @@ impl Plugin for TemporalAntiAliasPlugin {
.add_systems(
Render,
(
prepare_taa_jitter
(prepare_taa_jitter_and_mip_bias, apply_deferred)
.chain()
.before(prepare_view_uniforms)
.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:
/// 1. Write particle motion vectors to the motion vectors prepass texture
/// 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)]
pub struct TemporalAntiAliasSettings {
/// 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>,
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
let halton_sequence = [
@ -454,8 +461,12 @@ fn prepare_taa_jitter(
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;
if mip_bias.is_none() {
commands.entity(entity).insert(MipBias(-1.0));
}
}
}

View file

@ -55,7 +55,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
#endif
#ifdef VERTEX_UVS
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
@ -74,7 +74,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
var emissive: vec4<f32> = material.emissive;
#ifdef VERTEX_UVS
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
pbr_input.material.emissive = emissive;
@ -83,7 +83,7 @@ fn fragment(in: FragmentInput) -> @location(0) vec4<f32> {
var perceptual_roughness: f32 = material.perceptual_roughness;
#ifdef VERTEX_UVS
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
metallic = metallic * metallic_roughness.b;
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);
#ifdef VERTEX_UVS
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
#ifdef SCREEN_SPACE_AMBIENT_OCCLUSION

View file

@ -82,7 +82,7 @@ fn apply_normal_mapping(
#ifdef VERTEX_UVS
#ifdef STANDARDMATERIAL_NORMAL_MAP
// 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 {
// Only use the xy components and derive z for 2-component normal maps.
Nt = vec3<f32>(Nt.rg * 2.0 - 1.0, 0.0);

View file

@ -39,7 +39,7 @@ fn prepass_alpha_discard(in: FragmentInput) {
#ifdef VERTEX_UVS
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

View file

@ -771,3 +771,9 @@ impl TemporalJitter {
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);

View file

@ -6,7 +6,7 @@ pub use visibility::*;
pub use window::*;
use crate::{
camera::{ExtractedCamera, ManualTextureViews, TemporalJitter},
camera::{ExtractedCamera, ManualTextureViews, MipBias, TemporalJitter},
extract_resource::{ExtractResource, ExtractResourcePlugin},
prelude::{Image, Shader},
render_asset::RenderAssets,
@ -173,6 +173,7 @@ pub struct ViewUniform {
// viewport(x_origin, y_origin, width, height)
viewport: Vec4,
color_grading: ColorGrading,
mip_bias: f32,
}
#[derive(Resource, Default)]
@ -352,11 +353,16 @@ pub fn prepare_view_uniforms(
render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
mut view_uniforms: ResMut<ViewUniforms>,
views: Query<(Entity, &ExtractedView, Option<&TemporalJitter>)>,
views: Query<(
Entity,
&ExtractedView,
Option<&TemporalJitter>,
Option<&MipBias>,
)>,
) {
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 unjittered_projection = camera.projection;
let mut projection = unjittered_projection;
@ -383,6 +389,7 @@ pub fn prepare_view_uniforms(
world_position: camera.transform.translation(),
viewport,
color_grading: camera.color_grading,
mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0,
}),
};

View file

@ -19,4 +19,5 @@ struct View {
// viewport(x_origin, y_origin, width, height)
viewport: vec4<f32>,
color_grading: ColorGrading,
mip_bias: f32,
};