Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1256,6 +1256,17 @@ category = "3D Rendering"
# TAA not supported by WebGL
wasm = false

[[example]]
name = "specular_anti_aliasing"
path = "examples/3d/specular_anti_aliasing.rs"
doc-scrape-examples = true

[package.metadata.example.specular_anti_aliasing]
name = "Specular Anti-aliasing"
description = "Demonstrates the geometric specular anti-aliasing toggle on StandardMaterial"
category = "3D Rendering"
wasm = false

[[example]]
name = "atmospheric_fog"
path = "examples/3d/atmospheric_fog.rs"
Expand Down
45 changes: 45 additions & 0 deletions crates/bevy_pbr/src/pbr_material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,37 @@ pub struct StandardMaterial {
/// Whether to enable fog for this material.
pub fog_enabled: bool,

/// Whether to enable geometric specular aa.
///
/// When enabled, the PBR shader widens the roughness based on the
/// screen space variance of the shading normal, suppressing the
/// shimmering specular "fireflies" that high freq normal maps and
/// sharp geometric curvature produce at low roughness...
///
/// Based on Tokuyoshi and Kaplanyan, "Improved Geometric Specular
/// Antialiasing" (I3D 2019):
/// <https://www.jp.square-enix.com/tech/library/pdf/ImprovedGeometricSpecularAA.pdf>.
///
/// Defaults to `false`.
pub specular_anti_aliasing: bool,

/// Strength of [`StandardMaterial::specular_anti_aliasing`], the
/// screen-space variance σ of the filtering kernel. Higher values
/// widen the roughness more aggressively.
///
/// Only has an effect when [`StandardMaterial::specular_anti_aliasing`]
/// is `true`. Defaults to `0.5`.
pub specular_anti_aliasing_screen_space_variance: f32,

/// Clamping threshold κ for [`StandardMaterial::specular_anti_aliasing`].
/// Caps the maximum amount of squared roughness the filter is allowed
/// to add, preventing over-filtering where screen-space derivatives
/// spike.
///
/// Only has an effect when [`StandardMaterial::specular_anti_aliasing`]
/// is `true`. Defaults to `0.18`.
pub specular_anti_aliasing_threshold: f32,

/// How to apply the alpha channel of the `base_color_texture`.
///
/// See [`AlphaMode`] for details. Defaults to [`AlphaMode::Opaque`].
Expand Down Expand Up @@ -926,6 +957,9 @@ impl Default for StandardMaterial {
cull_mode: Some(Face::Back),
unlit: false,
fog_enabled: true,
specular_anti_aliasing: false,
specular_anti_aliasing_screen_space_variance: 0.5,
specular_anti_aliasing_threshold: 0.18,
alpha_mode: AlphaMode::Opaque,
depth_bias: 0.0,
depth_map: None,
Expand Down Expand Up @@ -989,6 +1023,7 @@ bitflags::bitflags! {
const ANISOTROPY_TEXTURE = 1 << 17;
const SPECULAR_TEXTURE = 1 << 18;
const SPECULAR_TINT_TEXTURE = 1 << 19;
const SPECULAR_ANTI_ALIASING = 1 << 20;
const ALPHA_MODE_RESERVED_BITS = Self::ALPHA_MODE_MASK_BITS << Self::ALPHA_MODE_SHIFT_BITS; // ← Bitmask reserving bits for the `AlphaMode`
const ALPHA_MODE_OPAQUE = 0 << Self::ALPHA_MODE_SHIFT_BITS; // ← Values are just sequential values bitshifted into
const ALPHA_MODE_MASK = 1 << Self::ALPHA_MODE_SHIFT_BITS; // the bitmask, and can range from 0 to 7.
Expand Down Expand Up @@ -1061,6 +1096,10 @@ pub struct StandardMaterialUniform {
pub max_relief_mapping_search_steps: u32,
/// ID for specifying which deferred lighting pass should be used for rendering this material, if any.
pub deferred_lighting_pass_id: u32,
/// Screen-space variance (σ) for geometric specular anti-aliasing.
pub specular_anti_aliasing_screen_space_variance: f32,
/// Clamping threshold (κ) for geometric specular anti-aliasing.
pub specular_anti_aliasing_threshold: f32,
}

impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
Expand Down Expand Up @@ -1090,6 +1129,9 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
if self.fog_enabled {
flags |= StandardMaterialFlags::FOG_ENABLED;
}
if self.specular_anti_aliasing {
flags |= StandardMaterialFlags::SPECULAR_ANTI_ALIASING;
}
if self.depth_map.is_some() {
flags |= StandardMaterialFlags::DEPTH_MAP;
}
Expand Down Expand Up @@ -1208,6 +1250,9 @@ impl AsBindGroupShaderType<StandardMaterialUniform> for StandardMaterial {
max_relief_mapping_search_steps: self.parallax_mapping_method.max_steps(),
deferred_lighting_pass_id: self.deferred_lighting_pass_id as u32,
uv_transform: self.uv_transform.into(),
specular_anti_aliasing_screen_space_variance: self
.specular_anti_aliasing_screen_space_variance,
specular_anti_aliasing_threshold: self.specular_anti_aliasing_threshold,
}
}
}
Expand Down
38 changes: 38 additions & 0 deletions crates/bevy_pbr/src/render/pbr_fragment.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,44 @@ pbr_input.material.uv_transform = uv_transform;

pbr_input.lightmap_light = lightmap(in.uv_b, lightmap_exposure, in.instance_index);
#endif

// Geometric specular aa.
// Tokuyoshi and Kaplanyan, "Improved Geometric Specular Antialiasing" (I3D 2019).
#ifndef MESHLET_MESH_MATERIAL_PASS
if ((flags & pbr_types::STANDARD_MATERIAL_FLAGS_SPECULAR_ANTI_ALIASING_BIT) != 0u) {
#ifdef BINDLESS
let sigma = pbr_bindings::material_array[material_indices[slot].material].specular_anti_aliasing_screen_space_variance;
let kappa = pbr_bindings::material_array[material_indices[slot].material].specular_anti_aliasing_threshold;
#else // BINDLESS
let sigma = pbr_bindings::material.specular_anti_aliasing_screen_space_variance;
let kappa = pbr_bindings::material.specular_anti_aliasing_threshold;
#endif // BINDLESS
let sigma2 = sigma * sigma;

let dndx = dpdx(pbr_input.N);
let dndy = dpdy(pbr_input.N);
let variance = sigma2 * (dot(dndx, dndx) + dot(dndy, dndy));
let kernel_roughness = min(variance, kappa);

// perceptual_roughness^2 is the GGX alpha. Widen alpha^2 by the kernel and convert back.
let alpha = pbr_input.material.perceptual_roughness *
pbr_input.material.perceptual_roughness;
pbr_input.material.perceptual_roughness =
sqrt(sqrt(saturate(alpha * alpha + kernel_roughness)));

#ifdef STANDARD_MATERIAL_CLEARCOAT
let dcdx = dpdx(pbr_input.clearcoat_N);
let dcdy = dpdy(pbr_input.clearcoat_N);
let cc_variance = sigma2 * (dot(dcdx, dcdx) + dot(dcdy, dcdy));
let cc_kernel = min(cc_variance, kappa);

let cc_alpha = pbr_input.material.clearcoat_perceptual_roughness *
pbr_input.material.clearcoat_perceptual_roughness;
pbr_input.material.clearcoat_perceptual_roughness =
sqrt(sqrt(saturate(cc_alpha * cc_alpha + cc_kernel)));
#endif // STANDARD_MATERIAL_CLEARCOAT
}
#endif // MESHLET_MESH_MATERIAL_PASS
}

return pbr_input;
Expand Down
5 changes: 5 additions & 0 deletions crates/bevy_pbr/src/render/pbr_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ struct StandardMaterial {
max_relief_mapping_search_steps: u32,
/// ID for specifying which deferred lighting pass should be used for rendering this material, if any.
deferred_lighting_pass_id: u32,
specular_anti_aliasing_screen_space_variance: f32,
specular_anti_aliasing_threshold: f32,
};

// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Expand All @@ -54,6 +56,7 @@ const STANDARD_MATERIAL_FLAGS_CLEARCOAT_NORMAL_TEXTURE_BIT: u32 = 1u << 16u
const STANDARD_MATERIAL_FLAGS_ANISOTROPY_TEXTURE_BIT: u32 = 1u << 17u;
const STANDARD_MATERIAL_FLAGS_SPECULAR_TEXTURE_BIT: u32 = 1u << 18u;
const STANDARD_MATERIAL_FLAGS_SPECULAR_TINT_TEXTURE_BIT: u32 = 1u << 19u;
const STANDARD_MATERIAL_FLAGS_SPECULAR_ANTI_ALIASING_BIT: u32 = 1u << 20u;
const STANDARD_MATERIAL_FLAGS_ALPHA_MODE_RESERVED_BITS: u32 = 7u << 29u; // (0b111u << 29u)
const STANDARD_MATERIAL_FLAGS_ALPHA_MODE_OPAQUE: u32 = 0u << 29u;
const STANDARD_MATERIAL_FLAGS_ALPHA_MODE_MASK: u32 = 1u << 29u;
Expand Down Expand Up @@ -88,6 +91,8 @@ fn standard_material_new() -> StandardMaterial {
material.max_parallax_layer_count = 16.0;
material.max_relief_mapping_search_steps = 5u;
material.deferred_lighting_pass_id = 1u;
material.specular_anti_aliasing_screen_space_variance = 0.5;
material.specular_anti_aliasing_threshold = 0.18;
// scale 1, translation 0, rotation 0
material.uv_transform = mat3x3<f32>(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);

Expand Down
Loading
Loading