Skip to content
Open
Show file tree
Hide file tree
Changes from 16 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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4645,6 +4645,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
9 changes: 8 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,12 +155,19 @@ 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,
bisection_steps: u32,
use_secant: u32,
pad_a: u32,
pad_b: u32,
};

struct EnvironmentMapUniform {
Expand Down
33 changes: 17 additions & 16 deletions crates/bevy_pbr/src/render/pbr_functions.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -604,32 +604,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