mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Start a built-in postprocessing stack, and implement chromatic aberration in it. (#13695)
This commit creates a new built-in postprocessing shader that's designed to hold miscellaneous postprocessing effects, and starts it off with chromatic aberration. Possible future effects include vignette, film grain, and lens distortion. [Chromatic aberration] is a common postprocessing effect that simulates lenses that fail to focus all colors of light to a single point. It's often used for impact effects and/or horror games. This patch uses the technique from *Inside* ([Gjøl & Svendsen 2016]), which allows the developer to customize the particular color pattern to achieve different effects. Unity HDRP uses the same technique, while Unreal has a hard-wired fixed color pattern. A new example, `post_processing`, has been added, in order to demonstrate the technique. The existing `post_processing` shader has been renamed to `custom_post_processing`, for clarity. [Chromatic aberration]: https://en.wikipedia.org/wiki/Chromatic_aberration [Gjøl & Svendsen 2016]: https://github.com/playdeadgames/publications/blob/master/INSIDE/rendering_inside_gdc2016.pdf ![Screenshot 2024-06-04 180304](https://github.com/bevyengine/bevy/assets/157897/3631c64f-a615-44fe-91ca-7f04df0a54b2) ![Screenshot 2024-06-04 180743](https://github.com/bevyengine/bevy/assets/157897/ee055cbf-4314-49c5-8bfa-8d8a17bd52bb) ## Changelog ### Added * Chromatic aberration is now available as a built-in postprocessing effect. To use it, add `ChromaticAberration` to your camera.
This commit is contained in:
parent
ed2b8e0f35
commit
fcda67e894
10 changed files with 843 additions and 4 deletions
17
Cargo.toml
17
Cargo.toml
|
@ -2237,11 +2237,11 @@ category = "Shaders"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "post_processing"
|
name = "custom_post_processing"
|
||||||
path = "examples/shader/post_processing.rs"
|
path = "examples/shader/custom_post_processing.rs"
|
||||||
doc-scrape-examples = true
|
doc-scrape-examples = true
|
||||||
|
|
||||||
[package.metadata.example.post_processing]
|
[package.metadata.example.custom_post_processing]
|
||||||
name = "Post Processing - Custom Render Pass"
|
name = "Post Processing - Custom Render Pass"
|
||||||
description = "A custom post processing effect, using a custom render pass that runs after the main pass"
|
description = "A custom post processing effect, using a custom render pass that runs after the main pass"
|
||||||
category = "Shaders"
|
category = "Shaders"
|
||||||
|
@ -3284,6 +3284,17 @@ description = "Handles input, physics, and rendering in an industry-standard way
|
||||||
category = "Movement"
|
category = "Movement"
|
||||||
wasm = true
|
wasm = true
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "post_processing"
|
||||||
|
path = "examples/3d/post_processing.rs"
|
||||||
|
doc-scrape-examples = true
|
||||||
|
|
||||||
|
[package.metadata.example.post_processing]
|
||||||
|
name = "Built-in postprocessing"
|
||||||
|
description = "Demonstrates the built-in postprocessing features"
|
||||||
|
category = "3D Rendering"
|
||||||
|
wasm = true
|
||||||
|
|
||||||
[profile.wasm-release]
|
[profile.wasm-release]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub mod graph {
|
||||||
MainTransparentPass,
|
MainTransparentPass,
|
||||||
EndMainPass,
|
EndMainPass,
|
||||||
Bloom,
|
Bloom,
|
||||||
|
PostProcessing,
|
||||||
Tonemapping,
|
Tonemapping,
|
||||||
Fxaa,
|
Fxaa,
|
||||||
Smaa,
|
Smaa,
|
||||||
|
|
|
@ -30,6 +30,7 @@ pub mod graph {
|
||||||
Bloom,
|
Bloom,
|
||||||
AutoExposure,
|
AutoExposure,
|
||||||
DepthOfField,
|
DepthOfField,
|
||||||
|
PostProcessing,
|
||||||
Tonemapping,
|
Tonemapping,
|
||||||
Fxaa,
|
Fxaa,
|
||||||
Smaa,
|
Smaa,
|
||||||
|
|
|
@ -19,6 +19,7 @@ pub mod fullscreen_vertex_shader;
|
||||||
pub mod fxaa;
|
pub mod fxaa;
|
||||||
pub mod motion_blur;
|
pub mod motion_blur;
|
||||||
pub mod msaa_writeback;
|
pub mod msaa_writeback;
|
||||||
|
pub mod post_process;
|
||||||
pub mod prepass;
|
pub mod prepass;
|
||||||
mod skybox;
|
mod skybox;
|
||||||
pub mod smaa;
|
pub mod smaa;
|
||||||
|
@ -60,6 +61,7 @@ use crate::{
|
||||||
fxaa::FxaaPlugin,
|
fxaa::FxaaPlugin,
|
||||||
motion_blur::MotionBlurPlugin,
|
motion_blur::MotionBlurPlugin,
|
||||||
msaa_writeback::MsaaWritebackPlugin,
|
msaa_writeback::MsaaWritebackPlugin,
|
||||||
|
post_process::PostProcessingPlugin,
|
||||||
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
|
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
|
||||||
smaa::SmaaPlugin,
|
smaa::SmaaPlugin,
|
||||||
tonemapping::TonemappingPlugin,
|
tonemapping::TonemappingPlugin,
|
||||||
|
@ -99,6 +101,7 @@ impl Plugin for CorePipelinePlugin {
|
||||||
MotionBlurPlugin,
|
MotionBlurPlugin,
|
||||||
DepthOfFieldPlugin,
|
DepthOfFieldPlugin,
|
||||||
SmaaPlugin,
|
SmaaPlugin,
|
||||||
|
PostProcessingPlugin,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
// The chromatic aberration postprocessing effect.
|
||||||
|
//
|
||||||
|
// This makes edges of objects turn into multicolored streaks.
|
||||||
|
|
||||||
|
#define_import_path bevy_core_pipeline::post_processing::chromatic_aberration
|
||||||
|
|
||||||
|
// See `bevy_core_pipeline::post_process::ChromaticAberration` for more
|
||||||
|
// information on these fields.
|
||||||
|
struct ChromaticAberrationSettings {
|
||||||
|
intensity: f32,
|
||||||
|
max_samples: u32,
|
||||||
|
unused_a: u32,
|
||||||
|
unused_b: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
// The source framebuffer texture.
|
||||||
|
@group(0) @binding(0) var chromatic_aberration_source_texture: texture_2d<f32>;
|
||||||
|
// The sampler used to sample the source framebuffer texture.
|
||||||
|
@group(0) @binding(1) var chromatic_aberration_source_sampler: sampler;
|
||||||
|
// The 1D lookup table for chromatic aberration.
|
||||||
|
@group(0) @binding(2) var chromatic_aberration_lut_texture: texture_2d<f32>;
|
||||||
|
// The sampler used to sample that lookup table.
|
||||||
|
@group(0) @binding(3) var chromatic_aberration_lut_sampler: sampler;
|
||||||
|
// The settings supplied by the developer.
|
||||||
|
@group(0) @binding(4) var<uniform> chromatic_aberration_settings: ChromaticAberrationSettings;
|
||||||
|
|
||||||
|
fn chromatic_aberration(start_pos: vec2<f32>) -> vec3<f32> {
|
||||||
|
// Radial chromatic aberration implemented using the *Inside* technique:
|
||||||
|
//
|
||||||
|
// <https://github.com/playdeadgames/publications/blob/master/INSIDE/rendering_inside_gdc2016.pdf>
|
||||||
|
|
||||||
|
let end_pos = mix(start_pos, vec2(0.5), chromatic_aberration_settings.intensity);
|
||||||
|
|
||||||
|
// Determine the number of samples. We aim for one sample per texel, unless
|
||||||
|
// that's higher than the developer-specified maximum number of samples, in
|
||||||
|
// which case we choose the maximum number of samples.
|
||||||
|
let texel_length = length((end_pos - start_pos) *
|
||||||
|
vec2<f32>(textureDimensions(chromatic_aberration_source_texture)));
|
||||||
|
let sample_count = min(u32(ceil(texel_length)), chromatic_aberration_settings.max_samples);
|
||||||
|
|
||||||
|
var color: vec3<f32>;
|
||||||
|
if (sample_count > 1u) {
|
||||||
|
// The LUT texture is in clamp-to-edge mode, so we start at 0.5 texels
|
||||||
|
// from the sides so that we have a nice gradient over the entire LUT
|
||||||
|
// range.
|
||||||
|
let lut_u_offset = 0.5 / f32(textureDimensions(chromatic_aberration_lut_texture).x);
|
||||||
|
|
||||||
|
var sample_sum = vec3(0.0);
|
||||||
|
var modulate_sum = vec3(0.0);
|
||||||
|
|
||||||
|
// Start accumulating samples.
|
||||||
|
for (var sample_index = 0u; sample_index < sample_count; sample_index += 1u) {
|
||||||
|
let t = (f32(sample_index) + 0.5) / f32(sample_count);
|
||||||
|
|
||||||
|
// Sample the framebuffer.
|
||||||
|
let sample_uv = mix(start_pos, end_pos, t);
|
||||||
|
let sample = textureSampleLevel(
|
||||||
|
chromatic_aberration_source_texture,
|
||||||
|
chromatic_aberration_source_sampler,
|
||||||
|
sample_uv,
|
||||||
|
0.0,
|
||||||
|
).rgb;
|
||||||
|
|
||||||
|
// Sample the LUT.
|
||||||
|
let lut_u = mix(lut_u_offset, 1.0 - lut_u_offset, t);
|
||||||
|
let modulate = textureSampleLevel(
|
||||||
|
chromatic_aberration_lut_texture,
|
||||||
|
chromatic_aberration_lut_sampler,
|
||||||
|
vec2(lut_u, 0.5),
|
||||||
|
0.0,
|
||||||
|
).rgb;
|
||||||
|
|
||||||
|
// Modulate the sample by the LUT value.
|
||||||
|
sample_sum += sample * modulate;
|
||||||
|
modulate_sum += modulate;
|
||||||
|
}
|
||||||
|
|
||||||
|
color = sample_sum / modulate_sum;
|
||||||
|
} else {
|
||||||
|
// If there's only one sample, don't do anything. If we don't do this,
|
||||||
|
// then this shader will apply whatever tint is in the center of the LUT
|
||||||
|
// texture to such pixels, which is wrong.
|
||||||
|
color = textureSampleLevel(
|
||||||
|
chromatic_aberration_source_texture,
|
||||||
|
chromatic_aberration_source_sampler,
|
||||||
|
start_pos,
|
||||||
|
0.0,
|
||||||
|
).rgb;
|
||||||
|
}
|
||||||
|
|
||||||
|
return color;
|
||||||
|
}
|
505
crates/bevy_core_pipeline/src/post_process/mod.rs
Normal file
505
crates/bevy_core_pipeline/src/post_process/mod.rs
Normal file
|
@ -0,0 +1,505 @@
|
||||||
|
//! Miscellaneous built-in postprocessing effects.
|
||||||
|
//!
|
||||||
|
//! Currently, this consists only of chromatic aberration.
|
||||||
|
|
||||||
|
use bevy_app::{App, Plugin};
|
||||||
|
use bevy_asset::{load_internal_asset, Assets, Handle};
|
||||||
|
use bevy_derive::{Deref, DerefMut};
|
||||||
|
use bevy_ecs::{
|
||||||
|
component::Component,
|
||||||
|
entity::Entity,
|
||||||
|
query::{QueryItem, With},
|
||||||
|
reflect::ReflectComponent,
|
||||||
|
schedule::IntoSystemConfigs as _,
|
||||||
|
system::{lifetimeless::Read, Commands, Query, Res, ResMut, Resource},
|
||||||
|
world::{FromWorld, World},
|
||||||
|
};
|
||||||
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
||||||
|
use bevy_render::{
|
||||||
|
camera::Camera,
|
||||||
|
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
||||||
|
render_asset::{RenderAssetUsages, RenderAssets},
|
||||||
|
render_graph::{
|
||||||
|
NodeRunError, RenderGraphApp as _, RenderGraphContext, ViewNode, ViewNodeRunner,
|
||||||
|
},
|
||||||
|
render_resource::{
|
||||||
|
binding_types::{sampler, texture_2d, uniform_buffer},
|
||||||
|
BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries, CachedRenderPipelineId,
|
||||||
|
ColorTargetState, ColorWrites, DynamicUniformBuffer, Extent3d, FilterMode, FragmentState,
|
||||||
|
Operations, PipelineCache, RenderPassColorAttachment, RenderPassDescriptor,
|
||||||
|
RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, Shader,
|
||||||
|
ShaderStages, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
|
||||||
|
TextureDimension, TextureFormat, TextureSampleType,
|
||||||
|
},
|
||||||
|
renderer::{RenderContext, RenderDevice, RenderQueue},
|
||||||
|
texture::{BevyDefault, GpuImage, Image},
|
||||||
|
view::{ExtractedView, ViewTarget},
|
||||||
|
Render, RenderApp, RenderSet,
|
||||||
|
};
|
||||||
|
use bevy_utils::prelude::default;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
core_2d::graph::{Core2d, Node2d},
|
||||||
|
core_3d::graph::{Core3d, Node3d},
|
||||||
|
fullscreen_vertex_shader,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The handle to the built-in postprocessing shader `post_process.wgsl`.
|
||||||
|
const POST_PROCESSING_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(14675654334038973533);
|
||||||
|
/// The handle to the chromatic aberration shader `chromatic_aberration.wgsl`.
|
||||||
|
const CHROMATIC_ABERRATION_SHADER_HANDLE: Handle<Shader> =
|
||||||
|
Handle::weak_from_u128(10969893303667163833);
|
||||||
|
|
||||||
|
/// The handle to the default chromatic aberration lookup texture.
|
||||||
|
///
|
||||||
|
/// This is just a 3x1 image consisting of one red pixel, one green pixel, and
|
||||||
|
/// one blue pixel, in that order.
|
||||||
|
const DEFAULT_CHROMATIC_ABERRATION_LUT_HANDLE: Handle<Image> =
|
||||||
|
Handle::weak_from_u128(2199972955136579180);
|
||||||
|
|
||||||
|
/// The default chromatic aberration intensity amount, in a fraction of the
|
||||||
|
/// window size.
|
||||||
|
const DEFAULT_CHROMATIC_ABERRATION_INTENSITY: f32 = 0.02;
|
||||||
|
|
||||||
|
/// The default maximum number of samples for chromatic aberration.
|
||||||
|
const DEFAULT_CHROMATIC_ABERRATION_MAX_SAMPLES: u32 = 8;
|
||||||
|
|
||||||
|
/// The raw RGBA data for the default chromatic aberration gradient.
|
||||||
|
///
|
||||||
|
/// This consists of one red pixel, one green pixel, and one blue pixel, in that
|
||||||
|
/// order.
|
||||||
|
static DEFAULT_CHROMATIC_ABERRATION_LUT_DATA: [u8; 12] =
|
||||||
|
[255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255];
|
||||||
|
|
||||||
|
/// A plugin that implements a built-in postprocessing stack with some common
|
||||||
|
/// effects.
|
||||||
|
///
|
||||||
|
/// Currently, this only consists of chromatic aberration.
|
||||||
|
pub struct PostProcessingPlugin;
|
||||||
|
|
||||||
|
/// Adds colored fringes to the edges of objects in the scene.
|
||||||
|
///
|
||||||
|
/// [Chromatic aberration] simulates the effect when lenses fail to focus all
|
||||||
|
/// colors of light toward a single point. It causes rainbow-colored streaks to
|
||||||
|
/// appear, which are especially apparent on the edges of objects. Chromatic
|
||||||
|
/// aberration is commonly used for collision effects, especially in horror
|
||||||
|
/// games.
|
||||||
|
///
|
||||||
|
/// Bevy's implementation is based on that of *Inside* ([Gjøl & Svendsen 2016]).
|
||||||
|
/// It's based on a customizable lookup texture, which allows for changing the
|
||||||
|
/// color pattern. By default, the color pattern is simply a 3×1 pixel texture
|
||||||
|
/// consisting of red, green, and blue, in that order, but you can change it to
|
||||||
|
/// any image in order to achieve different effects.
|
||||||
|
///
|
||||||
|
/// [Chromatic aberration]: https://en.wikipedia.org/wiki/Chromatic_aberration
|
||||||
|
///
|
||||||
|
/// [Gjøl & Svendsen 2016]: https://github.com/playdeadgames/publications/blob/master/INSIDE/rendering_inside_gdc2016.pdf
|
||||||
|
#[derive(Reflect, Component, Clone)]
|
||||||
|
#[reflect(Component, Default)]
|
||||||
|
pub struct ChromaticAberration {
|
||||||
|
/// The lookup texture that determines the color gradient.
|
||||||
|
///
|
||||||
|
/// By default, this is a 3×1 texel texture consisting of one red pixel, one
|
||||||
|
/// green pixel, and one blue texel, in that order. This recreates the most
|
||||||
|
/// typical chromatic aberration pattern. However, you can change it to
|
||||||
|
/// achieve different artistic effects.
|
||||||
|
///
|
||||||
|
/// The texture is always sampled in its vertical center, so it should
|
||||||
|
/// ordinarily have a height of 1 texel.
|
||||||
|
pub color_lut: Handle<Image>,
|
||||||
|
|
||||||
|
/// The size of the streaks around the edges of objects, as a fraction of
|
||||||
|
/// the window size.
|
||||||
|
///
|
||||||
|
/// The default value is 0.2.
|
||||||
|
pub intensity: f32,
|
||||||
|
|
||||||
|
/// A cap on the number of texture samples that will be performed.
|
||||||
|
///
|
||||||
|
/// Higher values result in smoother-looking streaks but are slower.
|
||||||
|
///
|
||||||
|
/// The default value is 8.
|
||||||
|
pub max_samples: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GPU pipeline data for the built-in postprocessing stack.
|
||||||
|
///
|
||||||
|
/// This is stored in the render world.
|
||||||
|
#[derive(Resource)]
|
||||||
|
pub struct PostProcessingPipeline {
|
||||||
|
/// The layout of bind group 0, containing the source, LUT, and settings.
|
||||||
|
bind_group_layout: BindGroupLayout,
|
||||||
|
/// Specifies how to sample the source framebuffer texture.
|
||||||
|
source_sampler: Sampler,
|
||||||
|
/// Specifies how to sample the chromatic aberration gradient.
|
||||||
|
chromatic_aberration_lut_sampler: Sampler,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A key that uniquely identifies a built-in postprocessing pipeline.
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct PostProcessingPipelineKey {
|
||||||
|
/// The format of the source and destination textures.
|
||||||
|
texture_format: TextureFormat,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A component attached to cameras in the render world that stores the
|
||||||
|
/// specialized pipeline ID for the built-in postprocessing stack.
|
||||||
|
#[derive(Component, Deref, DerefMut)]
|
||||||
|
pub struct PostProcessingPipelineId(CachedRenderPipelineId);
|
||||||
|
|
||||||
|
/// The on-GPU version of the [`ChromaticAberration`] settings.
|
||||||
|
///
|
||||||
|
/// See the documentation for [`ChromaticAberration`] for more information on
|
||||||
|
/// each of these fields.
|
||||||
|
#[derive(ShaderType)]
|
||||||
|
pub struct ChromaticAberrationUniform {
|
||||||
|
/// The intensity of the effect, in a fraction of the screen.
|
||||||
|
intensity: f32,
|
||||||
|
/// A cap on the number of samples of the source texture that the shader
|
||||||
|
/// will perform.
|
||||||
|
max_samples: u32,
|
||||||
|
/// Padding data.
|
||||||
|
unused_1: u32,
|
||||||
|
/// Padding data.
|
||||||
|
unused_2: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A resource, part of the render world, that stores the
|
||||||
|
/// [`ChromaticAberrationUniform`]s for each view.
|
||||||
|
#[derive(Resource, Deref, DerefMut, Default)]
|
||||||
|
pub struct PostProcessingUniformBuffers {
|
||||||
|
chromatic_aberration: DynamicUniformBuffer<ChromaticAberrationUniform>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A component, part of the render world, that stores the appropriate byte
|
||||||
|
/// offset within the [`PostProcessingUniformBuffers`] for the camera it's
|
||||||
|
/// attached to.
|
||||||
|
#[derive(Component, Deref, DerefMut)]
|
||||||
|
pub struct PostProcessingUniformBufferOffsets {
|
||||||
|
chromatic_aberration: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The render node that runs the built-in postprocessing stack.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct PostProcessingNode;
|
||||||
|
|
||||||
|
impl Plugin for PostProcessingPlugin {
|
||||||
|
fn build(&self, app: &mut App) {
|
||||||
|
load_internal_asset!(
|
||||||
|
app,
|
||||||
|
POST_PROCESSING_SHADER_HANDLE,
|
||||||
|
"post_process.wgsl",
|
||||||
|
Shader::from_wgsl
|
||||||
|
);
|
||||||
|
load_internal_asset!(
|
||||||
|
app,
|
||||||
|
CHROMATIC_ABERRATION_SHADER_HANDLE,
|
||||||
|
"chromatic_aberration.wgsl",
|
||||||
|
Shader::from_wgsl
|
||||||
|
);
|
||||||
|
|
||||||
|
// Load the default chromatic aberration LUT.
|
||||||
|
let mut assets = app.world_mut().resource_mut::<Assets<_>>();
|
||||||
|
assets.insert(
|
||||||
|
DEFAULT_CHROMATIC_ABERRATION_LUT_HANDLE.id(),
|
||||||
|
Image::new(
|
||||||
|
Extent3d {
|
||||||
|
width: 3,
|
||||||
|
height: 1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
TextureDimension::D2,
|
||||||
|
DEFAULT_CHROMATIC_ABERRATION_LUT_DATA.to_vec(),
|
||||||
|
TextureFormat::Rgba8UnormSrgb,
|
||||||
|
RenderAssetUsages::RENDER_WORLD,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
app.register_type::<ChromaticAberration>();
|
||||||
|
app.add_plugins(ExtractComponentPlugin::<ChromaticAberration>::default());
|
||||||
|
|
||||||
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
render_app
|
||||||
|
.init_resource::<SpecializedRenderPipelines<PostProcessingPipeline>>()
|
||||||
|
.init_resource::<PostProcessingUniformBuffers>()
|
||||||
|
.add_systems(
|
||||||
|
Render,
|
||||||
|
(
|
||||||
|
prepare_post_processing_pipelines,
|
||||||
|
prepare_post_processing_uniforms,
|
||||||
|
)
|
||||||
|
.in_set(RenderSet::Prepare),
|
||||||
|
)
|
||||||
|
.add_render_graph_node::<ViewNodeRunner<PostProcessingNode>>(
|
||||||
|
Core3d,
|
||||||
|
Node3d::PostProcessing,
|
||||||
|
)
|
||||||
|
.add_render_graph_edges(
|
||||||
|
Core3d,
|
||||||
|
(
|
||||||
|
Node3d::DepthOfField,
|
||||||
|
Node3d::PostProcessing,
|
||||||
|
Node3d::Tonemapping,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.add_render_graph_node::<ViewNodeRunner<PostProcessingNode>>(
|
||||||
|
Core2d,
|
||||||
|
Node2d::PostProcessing,
|
||||||
|
)
|
||||||
|
.add_render_graph_edges(
|
||||||
|
Core2d,
|
||||||
|
(Node2d::Bloom, Node2d::PostProcessing, Node2d::Tonemapping),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(&self, app: &mut App) {
|
||||||
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
render_app.init_resource::<PostProcessingPipeline>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ChromaticAberration {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
color_lut: DEFAULT_CHROMATIC_ABERRATION_LUT_HANDLE,
|
||||||
|
intensity: DEFAULT_CHROMATIC_ABERRATION_INTENSITY,
|
||||||
|
max_samples: DEFAULT_CHROMATIC_ABERRATION_MAX_SAMPLES,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromWorld for PostProcessingPipeline {
|
||||||
|
fn from_world(world: &mut World) -> Self {
|
||||||
|
let render_device = world.resource::<RenderDevice>();
|
||||||
|
|
||||||
|
// Create our single bind group layout.
|
||||||
|
let bind_group_layout = render_device.create_bind_group_layout(
|
||||||
|
Some("postprocessing bind group layout"),
|
||||||
|
&BindGroupLayoutEntries::sequential(
|
||||||
|
ShaderStages::FRAGMENT,
|
||||||
|
(
|
||||||
|
// Chromatic aberration source:
|
||||||
|
texture_2d(TextureSampleType::Float { filterable: true }),
|
||||||
|
// Chromatic aberration source sampler:
|
||||||
|
sampler(SamplerBindingType::Filtering),
|
||||||
|
// Chromatic aberration LUT:
|
||||||
|
texture_2d(TextureSampleType::Float { filterable: true }),
|
||||||
|
// Chromatic aberration LUT sampler:
|
||||||
|
sampler(SamplerBindingType::Filtering),
|
||||||
|
// Chromatic aberration settings:
|
||||||
|
uniform_buffer::<ChromaticAberrationUniform>(true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Both source and chromatic aberration LUTs should be sampled
|
||||||
|
// bilinearly.
|
||||||
|
|
||||||
|
let source_sampler = render_device.create_sampler(&SamplerDescriptor {
|
||||||
|
mipmap_filter: FilterMode::Linear,
|
||||||
|
min_filter: FilterMode::Linear,
|
||||||
|
mag_filter: FilterMode::Linear,
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let chromatic_aberration_lut_sampler = render_device.create_sampler(&SamplerDescriptor {
|
||||||
|
mipmap_filter: FilterMode::Linear,
|
||||||
|
min_filter: FilterMode::Linear,
|
||||||
|
mag_filter: FilterMode::Linear,
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
PostProcessingPipeline {
|
||||||
|
bind_group_layout,
|
||||||
|
source_sampler,
|
||||||
|
chromatic_aberration_lut_sampler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpecializedRenderPipeline for PostProcessingPipeline {
|
||||||
|
type Key = PostProcessingPipelineKey;
|
||||||
|
|
||||||
|
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
||||||
|
RenderPipelineDescriptor {
|
||||||
|
label: Some("postprocessing".into()),
|
||||||
|
layout: vec![self.bind_group_layout.clone()],
|
||||||
|
vertex: fullscreen_vertex_shader::fullscreen_shader_vertex_state(),
|
||||||
|
fragment: Some(FragmentState {
|
||||||
|
shader: POST_PROCESSING_SHADER_HANDLE,
|
||||||
|
shader_defs: vec![],
|
||||||
|
entry_point: "fragment_main".into(),
|
||||||
|
targets: vec![Some(ColorTargetState {
|
||||||
|
format: key.texture_format,
|
||||||
|
blend: None,
|
||||||
|
write_mask: ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: default(),
|
||||||
|
depth_stencil: None,
|
||||||
|
multisample: default(),
|
||||||
|
push_constant_ranges: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ViewNode for PostProcessingNode {
|
||||||
|
type ViewQuery = (
|
||||||
|
Read<ViewTarget>,
|
||||||
|
Read<PostProcessingPipelineId>,
|
||||||
|
Read<ChromaticAberration>,
|
||||||
|
Read<PostProcessingUniformBufferOffsets>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn run<'w>(
|
||||||
|
&self,
|
||||||
|
_: &mut RenderGraphContext,
|
||||||
|
render_context: &mut RenderContext<'w>,
|
||||||
|
(view_target, pipeline_id, chromatic_aberration, post_processing_uniform_buffer_offsets): QueryItem<'w, Self::ViewQuery>,
|
||||||
|
world: &'w World,
|
||||||
|
) -> Result<(), NodeRunError> {
|
||||||
|
let pipeline_cache = world.resource::<PipelineCache>();
|
||||||
|
let post_processing_pipeline = world.resource::<PostProcessingPipeline>();
|
||||||
|
let post_processing_uniform_buffers = world.resource::<PostProcessingUniformBuffers>();
|
||||||
|
let gpu_image_assets = world.resource::<RenderAssets<GpuImage>>();
|
||||||
|
|
||||||
|
// We need a render pipeline to be prepared.
|
||||||
|
let Some(pipeline) = pipeline_cache.get_render_pipeline(**pipeline_id) else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// We need the chromatic aberration LUT to be present.
|
||||||
|
let Some(chromatic_aberration_lut) = gpu_image_assets.get(&chromatic_aberration.color_lut)
|
||||||
|
else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// We need the postprocessing settings to be uploaded to the GPU.
|
||||||
|
let Some(chromatic_aberration_uniform_buffer_binding) = post_processing_uniform_buffers
|
||||||
|
.chromatic_aberration
|
||||||
|
.binding()
|
||||||
|
else {
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Use the [`PostProcessWrite`] infrastructure, since this is a
|
||||||
|
// full-screen pass.
|
||||||
|
let post_process = view_target.post_process_write();
|
||||||
|
|
||||||
|
let pass_descriptor = RenderPassDescriptor {
|
||||||
|
label: Some("postprocessing pass"),
|
||||||
|
color_attachments: &[Some(RenderPassColorAttachment {
|
||||||
|
view: post_process.destination,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: Operations::default(),
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bind_group = render_context.render_device().create_bind_group(
|
||||||
|
Some("postprocessing bind group"),
|
||||||
|
&post_processing_pipeline.bind_group_layout,
|
||||||
|
&BindGroupEntries::sequential((
|
||||||
|
post_process.source,
|
||||||
|
&post_processing_pipeline.source_sampler,
|
||||||
|
&chromatic_aberration_lut.texture_view,
|
||||||
|
&post_processing_pipeline.chromatic_aberration_lut_sampler,
|
||||||
|
chromatic_aberration_uniform_buffer_binding,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut render_pass = render_context
|
||||||
|
.command_encoder()
|
||||||
|
.begin_render_pass(&pass_descriptor);
|
||||||
|
|
||||||
|
render_pass.set_pipeline(pipeline);
|
||||||
|
render_pass.set_bind_group(0, &bind_group, &[**post_processing_uniform_buffer_offsets]);
|
||||||
|
render_pass.draw(0..3, 0..1);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specializes the built-in postprocessing pipeline for each applicable view.
|
||||||
|
pub fn prepare_post_processing_pipelines(
|
||||||
|
mut commands: Commands,
|
||||||
|
pipeline_cache: Res<PipelineCache>,
|
||||||
|
mut pipelines: ResMut<SpecializedRenderPipelines<PostProcessingPipeline>>,
|
||||||
|
post_processing_pipeline: Res<PostProcessingPipeline>,
|
||||||
|
views: Query<(Entity, &ExtractedView), With<ChromaticAberration>>,
|
||||||
|
) {
|
||||||
|
for (entity, view) in views.iter() {
|
||||||
|
let pipeline_id = pipelines.specialize(
|
||||||
|
&pipeline_cache,
|
||||||
|
&post_processing_pipeline,
|
||||||
|
PostProcessingPipelineKey {
|
||||||
|
texture_format: if view.hdr {
|
||||||
|
ViewTarget::TEXTURE_FORMAT_HDR
|
||||||
|
} else {
|
||||||
|
TextureFormat::bevy_default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
commands
|
||||||
|
.entity(entity)
|
||||||
|
.insert(PostProcessingPipelineId(pipeline_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gathers the built-in postprocessing settings for every view and uploads them
|
||||||
|
/// to the GPU.
|
||||||
|
pub fn prepare_post_processing_uniforms(
|
||||||
|
mut commands: Commands,
|
||||||
|
mut post_processing_uniform_buffers: ResMut<PostProcessingUniformBuffers>,
|
||||||
|
render_device: Res<RenderDevice>,
|
||||||
|
render_queue: Res<RenderQueue>,
|
||||||
|
mut views: Query<(Entity, &ChromaticAberration)>,
|
||||||
|
) {
|
||||||
|
post_processing_uniform_buffers.clear();
|
||||||
|
|
||||||
|
// Gather up all the postprocessing settings.
|
||||||
|
for (view_entity, chromatic_aberration) in views.iter_mut() {
|
||||||
|
let chromatic_aberration_uniform_buffer_offset =
|
||||||
|
post_processing_uniform_buffers.push(&ChromaticAberrationUniform {
|
||||||
|
intensity: chromatic_aberration.intensity,
|
||||||
|
max_samples: chromatic_aberration.max_samples,
|
||||||
|
unused_1: 0,
|
||||||
|
unused_2: 0,
|
||||||
|
});
|
||||||
|
commands
|
||||||
|
.entity(view_entity)
|
||||||
|
.insert(PostProcessingUniformBufferOffsets {
|
||||||
|
chromatic_aberration: chromatic_aberration_uniform_buffer_offset,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload to the GPU.
|
||||||
|
post_processing_uniform_buffers.write_buffer(&render_device, &render_queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExtractComponent for ChromaticAberration {
|
||||||
|
type QueryData = Read<ChromaticAberration>;
|
||||||
|
|
||||||
|
type QueryFilter = With<Camera>;
|
||||||
|
|
||||||
|
type Out = ChromaticAberration;
|
||||||
|
|
||||||
|
fn extract_component(
|
||||||
|
chromatic_aberration: QueryItem<'_, Self::QueryData>,
|
||||||
|
) -> Option<Self::Out> {
|
||||||
|
// Skip the postprocessing phase entirely if the intensity is zero.
|
||||||
|
if chromatic_aberration.intensity > 0.0 {
|
||||||
|
Some(chromatic_aberration.clone())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
// Miscellaneous postprocessing effects, currently just chromatic aberration.
|
||||||
|
|
||||||
|
#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput
|
||||||
|
#import bevy_core_pipeline::post_processing::chromatic_aberration::chromatic_aberration
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fragment_main(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return vec4(chromatic_aberration(in.uv), 1.0);
|
||||||
|
}
|
216
examples/3d/post_processing.rs
Normal file
216
examples/3d/post_processing.rs
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
//! Demonstrates Bevy's built-in postprocessing features.
|
||||||
|
//!
|
||||||
|
//! Currently, this simply consists of chromatic aberration.
|
||||||
|
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
|
use bevy::{
|
||||||
|
core_pipeline::post_process::ChromaticAberration, pbr::CascadeShadowConfigBuilder, prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// The number of units per frame to add to or subtract from intensity when the
|
||||||
|
/// arrow keys are held.
|
||||||
|
const CHROMATIC_ABERRATION_INTENSITY_ADJUSTMENT_SPEED: f32 = 0.002;
|
||||||
|
|
||||||
|
/// The maximum supported chromatic aberration intensity level.
|
||||||
|
const MAX_CHROMATIC_ABERRATION_INTENSITY: f32 = 0.4;
|
||||||
|
|
||||||
|
/// The settings that the user can control.
|
||||||
|
#[derive(Resource)]
|
||||||
|
struct AppSettings {
|
||||||
|
/// The intensity of the chromatic aberration effect.
|
||||||
|
chromatic_aberration_intensity: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The entry point.
|
||||||
|
fn main() {
|
||||||
|
App::new()
|
||||||
|
.init_resource::<AppSettings>()
|
||||||
|
.add_plugins(DefaultPlugins.set(WindowPlugin {
|
||||||
|
primary_window: Some(Window {
|
||||||
|
title: "Bevy Chromatic Aberration Example".into(),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
..default()
|
||||||
|
}))
|
||||||
|
.add_systems(Startup, setup)
|
||||||
|
.add_systems(Update, handle_keyboard_input)
|
||||||
|
.add_systems(
|
||||||
|
Update,
|
||||||
|
(update_chromatic_aberration_settings, update_help_text)
|
||||||
|
.run_if(resource_changed::<AppSettings>)
|
||||||
|
.after(handle_keyboard_input),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the example scene and spawns the UI.
|
||||||
|
fn setup(mut commands: Commands, asset_server: Res<AssetServer>, app_settings: Res<AppSettings>) {
|
||||||
|
// Spawn the camera.
|
||||||
|
spawn_camera(&mut commands, &asset_server);
|
||||||
|
|
||||||
|
// Create the scene.
|
||||||
|
spawn_scene(&mut commands, &asset_server);
|
||||||
|
|
||||||
|
// Spawn the help text.
|
||||||
|
spawn_text(&mut commands, &app_settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawns the camera, including the [`ChromaticAberration`] component.
|
||||||
|
fn spawn_camera(commands: &mut Commands, asset_server: &AssetServer) {
|
||||||
|
commands.spawn((
|
||||||
|
Camera3dBundle {
|
||||||
|
camera: Camera {
|
||||||
|
hdr: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
transform: Transform::from_xyz(0.7, 0.7, 1.0)
|
||||||
|
.looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y),
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
FogSettings {
|
||||||
|
color: Color::srgb_u8(43, 44, 47),
|
||||||
|
falloff: FogFalloff::Linear {
|
||||||
|
start: 1.0,
|
||||||
|
end: 8.0,
|
||||||
|
},
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
EnvironmentMapLight {
|
||||||
|
diffuse_map: asset_server.load("environment_maps/pisa_diffuse_rgb9e5_zstd.ktx2"),
|
||||||
|
specular_map: asset_server.load("environment_maps/pisa_specular_rgb9e5_zstd.ktx2"),
|
||||||
|
intensity: 2000.0,
|
||||||
|
},
|
||||||
|
// Include the `ChromaticAberration` component.
|
||||||
|
ChromaticAberration::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawns the scene.
|
||||||
|
///
|
||||||
|
/// This is just the tonemapping test scene, chosen for the fact that it uses a
|
||||||
|
/// variety of colors.
|
||||||
|
fn spawn_scene(commands: &mut Commands, asset_server: &AssetServer) {
|
||||||
|
// Spawn the main scene.
|
||||||
|
commands.spawn(SceneBundle {
|
||||||
|
scene: asset_server.load(
|
||||||
|
GltfAssetLabel::Scene(0).from_asset("models/TonemappingTest/TonemappingTest.gltf"),
|
||||||
|
),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn the flight helmet.
|
||||||
|
commands.spawn(SceneBundle {
|
||||||
|
scene: asset_server
|
||||||
|
.load(GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf")),
|
||||||
|
transform: Transform::from_xyz(0.5, 0.0, -0.5)
|
||||||
|
.with_rotation(Quat::from_rotation_y(-0.15 * PI)),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spawn the light.
|
||||||
|
commands.spawn(DirectionalLightBundle {
|
||||||
|
directional_light: DirectionalLight {
|
||||||
|
illuminance: 15000.0,
|
||||||
|
shadows_enabled: true,
|
||||||
|
..default()
|
||||||
|
},
|
||||||
|
transform: Transform::from_rotation(Quat::from_euler(
|
||||||
|
EulerRot::ZYX,
|
||||||
|
0.0,
|
||||||
|
PI * -0.15,
|
||||||
|
PI * -0.15,
|
||||||
|
)),
|
||||||
|
cascade_shadow_config: CascadeShadowConfigBuilder {
|
||||||
|
maximum_distance: 3.0,
|
||||||
|
first_cascade_far_bound: 0.9,
|
||||||
|
..default()
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
..default()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spawns the help text at the bottom of the screen.
|
||||||
|
fn spawn_text(commands: &mut Commands, app_settings: &AppSettings) {
|
||||||
|
commands.spawn(
|
||||||
|
TextBundle {
|
||||||
|
text: create_help_text(app_settings),
|
||||||
|
..default()
|
||||||
|
}
|
||||||
|
.with_style(Style {
|
||||||
|
position_type: PositionType::Absolute,
|
||||||
|
bottom: Val::Px(12.0),
|
||||||
|
left: Val::Px(12.0),
|
||||||
|
..default()
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AppSettings {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
chromatic_aberration_intensity: ChromaticAberration::default().intensity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates help text at the bottom of the screen.
|
||||||
|
fn create_help_text(app_settings: &AppSettings) -> Text {
|
||||||
|
Text::from_section(
|
||||||
|
format!(
|
||||||
|
"Chromatic aberration intensity: {} (Press Left or Right to change)",
|
||||||
|
app_settings.chromatic_aberration_intensity
|
||||||
|
),
|
||||||
|
TextStyle::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Handles requests from the user to change the chromatic aberration intensity.
|
||||||
|
fn handle_keyboard_input(mut app_settings: ResMut<AppSettings>, input: Res<ButtonInput<KeyCode>>) {
|
||||||
|
let mut delta = 0.0;
|
||||||
|
if input.pressed(KeyCode::ArrowLeft) {
|
||||||
|
delta -= CHROMATIC_ABERRATION_INTENSITY_ADJUSTMENT_SPEED;
|
||||||
|
} else if input.pressed(KeyCode::ArrowRight) {
|
||||||
|
delta += CHROMATIC_ABERRATION_INTENSITY_ADJUSTMENT_SPEED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no arrow key was pressed, just bail out.
|
||||||
|
if delta == 0.0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app_settings.chromatic_aberration_intensity = (app_settings.chromatic_aberration_intensity
|
||||||
|
+ delta)
|
||||||
|
.clamp(0.0, MAX_CHROMATIC_ABERRATION_INTENSITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the [`ChromaticAberration`] settings per the [`AppSettings`].
|
||||||
|
fn update_chromatic_aberration_settings(
|
||||||
|
mut chromatic_aberration_settings: Query<&mut ChromaticAberration>,
|
||||||
|
app_settings: Res<AppSettings>,
|
||||||
|
) {
|
||||||
|
let intensity = app_settings.chromatic_aberration_intensity;
|
||||||
|
|
||||||
|
// Pick a reasonable maximum sample size for the intensity to avoid an
|
||||||
|
// artifact whereby the individual samples appear instead of producing
|
||||||
|
// smooth streaks of color.
|
||||||
|
//
|
||||||
|
// Don't take this formula too seriously; it hasn't been heavily tuned.
|
||||||
|
let max_samples = ((intensity - 0.02) / (0.20 - 0.02) * 56.0 + 8.0)
|
||||||
|
.clamp(8.0, 64.0)
|
||||||
|
.round() as u32;
|
||||||
|
|
||||||
|
for mut chromatic_aberration_settings in &mut chromatic_aberration_settings {
|
||||||
|
chromatic_aberration_settings.intensity = intensity;
|
||||||
|
chromatic_aberration_settings.max_samples = max_samples;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Updates the help text at the bottom of the screen to reflect the current
|
||||||
|
/// [`AppSettings`].
|
||||||
|
fn update_help_text(mut text: Query<&mut Text>, app_settings: Res<AppSettings>) {
|
||||||
|
for mut text in text.iter_mut() {
|
||||||
|
*text = create_help_text(&app_settings);
|
||||||
|
}
|
||||||
|
}
|
|
@ -135,6 +135,7 @@ Example | Description
|
||||||
[Atmospheric Fog](../examples/3d/atmospheric_fog.rs) | A scene showcasing the atmospheric fog effect
|
[Atmospheric Fog](../examples/3d/atmospheric_fog.rs) | A scene showcasing the atmospheric fog effect
|
||||||
[Auto Exposure](../examples/3d/auto_exposure.rs) | A scene showcasing auto exposure
|
[Auto Exposure](../examples/3d/auto_exposure.rs) | A scene showcasing auto exposure
|
||||||
[Blend Modes](../examples/3d/blend_modes.rs) | Showcases different blend modes
|
[Blend Modes](../examples/3d/blend_modes.rs) | Showcases different blend modes
|
||||||
|
[Built-in postprocessing](../examples/3d/post_processing.rs) | Demonstrates the built-in postprocessing features
|
||||||
[Clearcoat](../examples/3d/clearcoat.rs) | Demonstrates the clearcoat PBR feature
|
[Clearcoat](../examples/3d/clearcoat.rs) | Demonstrates the clearcoat PBR feature
|
||||||
[Color grading](../examples/3d/color_grading.rs) | Demonstrates color grading
|
[Color grading](../examples/3d/color_grading.rs) | Demonstrates color grading
|
||||||
[Deferred Rendering](../examples/3d/deferred_rendering.rs) | Renders meshes with both forward and deferred pipelines
|
[Deferred Rendering](../examples/3d/deferred_rendering.rs) | Renders meshes with both forward and deferred pipelines
|
||||||
|
@ -386,7 +387,7 @@ Example | Description
|
||||||
[Material - GLSL](../examples/shader/shader_material_glsl.rs) | A shader that uses the GLSL shading language
|
[Material - GLSL](../examples/shader/shader_material_glsl.rs) | A shader that uses the GLSL shading language
|
||||||
[Material - Screenspace Texture](../examples/shader/shader_material_screenspace_texture.rs) | A shader that samples a texture with view-independent UV coordinates
|
[Material - Screenspace Texture](../examples/shader/shader_material_screenspace_texture.rs) | A shader that samples a texture with view-independent UV coordinates
|
||||||
[Material Prepass](../examples/shader/shader_prepass.rs) | A shader that uses the various textures generated by the prepass
|
[Material Prepass](../examples/shader/shader_prepass.rs) | A shader that uses the various textures generated by the prepass
|
||||||
[Post Processing - Custom Render Pass](../examples/shader/post_processing.rs) | A custom post processing effect, using a custom render pass that runs after the main pass
|
[Post Processing - Custom Render Pass](../examples/shader/custom_post_processing.rs) | A custom post processing effect, using a custom render pass that runs after the main pass
|
||||||
[Shader Defs](../examples/shader/shader_defs.rs) | A shader that uses "shaders defs" (a bevy tool to selectively toggle parts of a shader)
|
[Shader Defs](../examples/shader/shader_defs.rs) | A shader that uses "shaders defs" (a bevy tool to selectively toggle parts of a shader)
|
||||||
[Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how to bind and sample multiple textures as a binding array (a.k.a. bindless textures).
|
[Texture Binding Array (Bindless Textures)](../examples/shader/texture_binding_array.rs) | A shader that shows how to bind and sample multiple textures as a binding array (a.k.a. bindless textures).
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue