Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4662,6 +4662,7 @@ name = "ssr"
path = "examples/3d/ssr.rs"
# Causes an ICE on docs.rs
doc-scrape-examples = false
required-features = ["bluenoise_texture"]

[package.metadata.example.ssr]
name = "Screen Space Reflections"
Expand Down
7 changes: 6 additions & 1 deletion crates/bevy_pbr/src/render/mesh_view_types.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,12 @@ struct LightProbes {
// For more information on these settings, see the documentation for
// `bevy_pbr::ssr::ScreenSpaceReflections`.
struct ScreenSpaceReflectionsSettings {
perceptual_roughness_threshold: f32,
min_perceptual_roughness: f32,
min_perceptual_roughness_fully_active: f32,
max_perceptual_roughness_starts_to_fade: f32,
max_perceptual_roughness: f32,
edge_fadeout_fully_active: f32,
edge_fadeout_no_longer_active: f32,
thickness: f32,
linear_steps: u32,
linear_march_exponent: f32,
Expand Down
31 changes: 16 additions & 15 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -607,32 +607,33 @@ fn apply_pbr_lighting(

// Environment map light (indirect)
#ifdef ENVIRONMENT_MAP
// If screen space reflections are going to be used for this material, don't
// accumulate environment map light yet. The SSR shader will do it.
// If screen space reflections are going to be used for this material, only
// accumulate the diffuse part of the environment map light. The SSR shader
// will accumulate the specular part.
#ifdef SCREEN_SPACE_REFLECTIONS
let use_ssr = perceptual_roughness <=
view_bindings::ssr_settings.perceptual_roughness_threshold;
view_bindings::ssr_settings.max_perceptual_roughness;
#else // SCREEN_SPACE_REFLECTIONS
let use_ssr = false;
#endif // SCREEN_SPACE_REFLECTIONS

if (!use_ssr) {
#ifdef STANDARD_MATERIAL_ANISOTROPY
var bent_normal_lighting_input = lighting_input;
bend_normal_for_anisotropy(&bent_normal_lighting_input);
let environment_map_lighting_input = &bent_normal_lighting_input;
var bent_normal_lighting_input = lighting_input;
bend_normal_for_anisotropy(&bent_normal_lighting_input);
let environment_map_lighting_input = &bent_normal_lighting_input;
#else // STANDARD_MATERIAL_ANISOTROPY
let environment_map_lighting_input = &lighting_input;
let environment_map_lighting_input = &lighting_input;
#endif // STANDARD_MATERIAL_ANISOTROPY

let environment_light = environment_map::environment_map_light(
environment_map_lighting_input,
&clusterable_object_index_ranges,
found_diffuse_indirect,
);
let environment_light = environment_map::environment_map_light(
environment_map_lighting_input,
&clusterable_object_index_ranges,
found_diffuse_indirect,
);

indirect_light += environment_light.diffuse * diffuse_occlusion +
environment_light.specular * specular_occlusion;
indirect_light += environment_light.diffuse * diffuse_occlusion;
if (!use_ssr) {
indirect_light += environment_light.specular * specular_occlusion;
}
#endif // ENVIRONMENT_MAP

Expand Down
77 changes: 62 additions & 15 deletions crates/bevy_pbr/src/ssr/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Screen space reflections implemented via raymarching.

use core::ops::Range;

use bevy_app::{App, Plugin};
use bevy_asset::{load_embedded_asset, AssetServer, Handle};
use bevy_core_pipeline::{
Expand Down Expand Up @@ -27,6 +29,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
diagnostic::RecordDiagnostics,
extract_component::{ExtractComponent, ExtractComponentPlugin},
render_asset::RenderAssets,
render_graph::{
NodeRunError, RenderGraph, RenderGraphContext, RenderGraphExt, ViewNode, ViewNodeRunner,
},
Expand All @@ -36,9 +39,11 @@ use bevy_render::{
DynamicUniformBuffer, FilterMode, FragmentState, Operations, PipelineCache,
RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, Sampler,
SamplerBindingType, SamplerDescriptor, ShaderStages, ShaderType, SpecializedRenderPipeline,
SpecializedRenderPipelines, TextureFormat, TextureSampleType,
SpecializedRenderPipelines, TextureFormat, TextureSampleType, TextureViewDescriptor,
TextureViewDimension,
},
renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue},
texture::GpuImage,
view::{ExtractedView, Msaa, ViewTarget, ViewUniformOffset},
Render, RenderApp, RenderStartup, RenderSystems,
};
Expand All @@ -47,8 +52,8 @@ use bevy_utils::{once, prelude::default};
use tracing::info;

use crate::{
binding_arrays_are_usable, graph::NodePbr, ExtractedAtmosphere, MeshPipelineViewLayoutKey,
MeshPipelineViewLayouts, MeshViewBindGroup, RenderViewLightProbes,
binding_arrays_are_usable, graph::NodePbr, Bluenoise, ExtractedAtmosphere,
MeshPipelineViewLayoutKey, MeshPipelineViewLayouts, MeshViewBindGroup, RenderViewLightProbes,
ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset,
ViewLightsUniformOffset,
};
Expand All @@ -65,10 +70,7 @@ pub struct ScreenSpaceReflectionsPlugin;
/// components, which are inserted automatically,
/// but deferred rendering itself is not automatically enabled.
///
/// SSR currently performs no roughness filtering for glossy reflections, so
/// only very smooth surfaces will reflect objects in screen space. You can
/// adjust the `perceptual_roughness_threshold` in order to tune the threshold
/// below which screen-space reflections will be traced.
/// Enable the `bluenoise_texture` feature to improve the quality of noise on rough reflections.
///
/// As with all screen-space techniques, SSR can only reflect objects on screen.
/// When objects leave the camera, they will disappear from reflections.
Expand All @@ -82,14 +84,22 @@ pub struct ScreenSpaceReflectionsPlugin;
/// Screen-space reflections are presently unsupported on WebGL 2 because of a
/// bug whereby Naga doesn't generate correct GLSL when sampling depth buffers,
/// which is required for screen-space raymarching.
#[derive(Clone, Copy, Component, Reflect)]
#[derive(Clone, Component, Reflect)]
#[reflect(Component, Default, Clone)]
#[require(DepthPrepass, DeferredPrepass)]
#[doc(alias = "Ssr")]
pub struct ScreenSpaceReflections {
/// The maximum PBR roughness level that will enable screen space
/// reflections.
pub perceptual_roughness_threshold: f32,
/// The perceptual roughness range over which SSR begins to fade in.
///
/// The first value is the roughness at which SSR begins to appear; the
/// second value is the roughness at which SSR is fully active.
pub min_perceptual_roughness: Range<f32>,

/// The perceptual roughness range over which SSR begins to fade out.
///
/// The first value is the roughness at which SSR begins to fade out; the
/// second value is the roughness at which SSR is no longer active.
pub max_perceptual_roughness: Range<f32>,

/// When marching the depth buffer, we only have 2.5D information and don't
/// know how thick surfaces are. We shall assume that the depth buffer
Expand All @@ -115,6 +125,14 @@ pub struct ScreenSpaceReflections {
/// as 1 or 2.
pub linear_march_exponent: f32,

/// The range over which SSR begins to fade out at the edges of the screen,
/// in terms of a percentage of the screen dimensions.
///
/// The first value is the percentage from the edge at which SSR is no
/// longer active; the second value is the percentage at which SSR is fully
/// active.
pub edge_fadeout: Range<f32>,

/// Number of steps in a bisection (binary search) to perform once the
/// linear search has found an intersection. Helps narrow down the hit,
/// increasing the chance of the secant method finding an accurate hit
Expand All @@ -133,13 +151,20 @@ pub struct ScreenSpaceReflections {
/// [`ScreenSpaceReflections`].
#[derive(Clone, Copy, Component, ShaderType)]
pub struct ScreenSpaceReflectionsUniform {
perceptual_roughness_threshold: f32,
min_perceptual_roughness: f32,
min_perceptual_roughness_fully_active: f32,
max_perceptual_roughness_starts_to_fade: f32,
max_perceptual_roughness: f32,
edge_fadeout_fully_active: f32,
edge_fadeout_no_longer_active: f32,
thickness: f32,
linear_steps: u32,
linear_march_exponent: f32,
bisection_steps: u32,
/// A boolean converted to a `u32`.
use_secant: u32,
pad_a: u32,
pad_b: u32,
}

/// The node in the render graph that traces screen space reflections.
Expand Down Expand Up @@ -240,12 +265,14 @@ impl Default for ScreenSpaceReflections {
// <https://gist.github.com/h3r2tic/9c8356bdaefbe80b1a22ae0aaee192db?permalink_comment_id=4552149#gistcomment-4552149>.
fn default() -> Self {
Self {
perceptual_roughness_threshold: 0.1,
min_perceptual_roughness: 0.08..0.12,
max_perceptual_roughness: 0.55..0.7,
linear_steps: 16,
bisection_steps: 4,
use_secant: true,
thickness: 0.25,
linear_march_exponent: 1.0,
edge_fadeout: 0.0..0.0,
}
}
}
Expand Down Expand Up @@ -293,6 +320,17 @@ impl ViewNode for ScreenSpaceReflectionsNode {

// Create the bind group for this view.
let ssr_pipeline = world.resource::<ScreenSpaceReflectionsPipeline>();
let bluenoise = world.resource::<Bluenoise>();
let render_images = world.resource::<RenderAssets<GpuImage>>();
let Some(stbn_texture) = render_images.get(&bluenoise.texture) else {
return Ok(());
};
let stbn_view = stbn_texture.texture.create_view(&TextureViewDescriptor {
label: Some("ssr_stbn_view"),
dimension: Some(TextureViewDimension::D2Array),
..default()
});

let ssr_bind_group = render_context.render_device().create_bind_group(
"SSR bind group",
&pipeline_cache.get_bind_group_layout(&ssr_pipeline.bind_group_layout),
Expand All @@ -301,6 +339,7 @@ impl ViewNode for ScreenSpaceReflectionsNode {
&ssr_pipeline.color_sampler,
&ssr_pipeline.depth_linear_sampler,
&ssr_pipeline.depth_nearest_sampler,
&stbn_view,
)),
);

Expand Down Expand Up @@ -363,6 +402,7 @@ pub fn init_screen_space_reflections_pipeline(
binding_types::sampler(SamplerBindingType::Filtering),
binding_types::sampler(SamplerBindingType::Filtering),
binding_types::sampler(SamplerBindingType::NonFiltering),
binding_types::texture_2d_array(TextureSampleType::Float { filterable: false }),
),
),
);
Expand Down Expand Up @@ -517,7 +557,7 @@ impl ExtractComponent for ScreenSpaceReflections {
return None;
}

Some((*settings).into())
Some(settings.clone().into())
}
}

Expand Down Expand Up @@ -581,12 +621,19 @@ impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline {
impl From<ScreenSpaceReflections> for ScreenSpaceReflectionsUniform {
fn from(settings: ScreenSpaceReflections) -> Self {
Self {
perceptual_roughness_threshold: settings.perceptual_roughness_threshold,
min_perceptual_roughness: settings.min_perceptual_roughness.start,
min_perceptual_roughness_fully_active: settings.min_perceptual_roughness.end,
max_perceptual_roughness_starts_to_fade: settings.max_perceptual_roughness.start,
max_perceptual_roughness: settings.max_perceptual_roughness.end,
edge_fadeout_no_longer_active: settings.edge_fadeout.start,
edge_fadeout_fully_active: settings.edge_fadeout.end,
thickness: settings.thickness,
linear_steps: settings.linear_steps,
linear_march_exponent: settings.linear_march_exponent,
bisection_steps: settings.bisection_steps,
use_secant: settings.use_secant as u32,
pad_a: 0,
pad_b: 0,
}
}
}
Loading