Skip to main content

bevy_post_process/effect_stack/
vignette.rs

1use bevy_camera::Camera;
2use bevy_color::Color;
3use bevy_ecs::{
4    component::Component,
5    query::{QueryItem, With},
6    reflect::ReflectComponent,
7    system::lifetimeless::Read,
8};
9use bevy_math::{Vec2, Vec4};
10use bevy_reflect::{std_traits::ReflectDefault, Reflect};
11use bevy_render::{
12    extract_component::ExtractComponent, render_resource::ShaderType,
13    sync_component::SyncComponent, RenderApp,
14};
15
16/// Adds a gradual shading effect to the edges of the screen, drawing focus
17/// towards the center.
18///
19/// A [Vignette] darkens the corners of the image relative to the center,
20/// simulating the natural fall-off seen in camera lenses and human vision.
21/// This effect is widely used in cinematography and games to direct the
22/// player's attention or to evoke a specific mood (e.g., nostalgia or
23/// claustrophobia).
24///
25/// Bevy's implementation applies a radial mask to the screen, modifying
26/// the alpha channel or luminance of the final image. It supports adjusting
27/// the size, roundness, and softness of the falloff, allowing you to
28/// simulate various optical hardware or stylistic choices.
29#[derive(Reflect, Component, Clone)]
30#[reflect(Component, Default, Clone)]
31pub struct Vignette {
32    /// Controls the strength of the darkening effect.
33    ///
34    /// Range: `0.0` (No effect) to `1.0` (Fully black corners)
35    ///
36    /// The default value is 1.0
37    pub intensity: f32,
38    /// The size of the unvignetted center area.
39    ///
40    /// Range: `0.0` (Tiny center) to `2.0+` (Large center)
41    ///
42    /// The default value is 0.75
43    pub radius: f32,
44    /// The softness of the edge between the clear and dark areas.
45    ///
46    /// Range: `0.01` (Sharp edge) to `1.0+` (Very soft edge)
47    ///
48    /// The default value is 5.0
49    pub smoothness: f32,
50    /// The shape of the vignette.
51    ///
52    /// `1.0` represents a perfect circle.
53    ///
54    /// The default value is 1.0
55    pub roundness: f32,
56    /// The center of the vignette in UV coordinates (0.0 to 1.0).
57    ///
58    /// `(0.5, 0.5)` is the exact center of the screen.
59    /// Deviating from this allows for off-center or asymmetric vignette effects.
60    ///
61    /// The default value is `Vec2::new(0.5, 0.5)`
62    pub center: Vec2,
63    /// Used to make the vignette effect fit the screen better.
64    ///
65    /// Range: `0.0`(No fit) to `1.0` (Perfect fit)
66    ///
67    /// The default value is 1.0
68    pub edge_compensation: f32,
69    /// The color of the vignette.
70    ///
71    /// Typically black for standard darkening, but can be any color for creative effects.
72    ///
73    /// The default value is `Color::BLACK`
74    pub color: Color,
75}
76
77impl Default for Vignette {
78    fn default() -> Self {
79        Self {
80            intensity: 1.0,
81            radius: 0.75,
82            smoothness: 5.0,
83            roundness: 1.0,
84            color: Color::BLACK,
85            edge_compensation: 1.0,
86            center: Vec2::new(0.5, 0.5),
87        }
88    }
89}
90
91impl SyncComponent<RenderApp> for Vignette {
92    type Target = Self;
93}
94
95impl ExtractComponent<RenderApp> for Vignette {
96    type QueryData = Read<Vignette>;
97    type QueryFilter = With<Camera>;
98    type Out = Self;
99
100    fn extract_component(vignette: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
101        // Skip the postprocessing phase entirely if the intensity is negligible.
102        if vignette.intensity > 1e-4 {
103            Some(vignette.clone())
104        } else {
105            None
106        }
107    }
108}
109
110/// The on-GPU version of the [`Vignette`] settings.
111///
112/// See the documentation for [`Vignette`] for more information on
113/// each of these fields.
114#[derive(ShaderType, Default)]
115pub struct VignetteUniform {
116    pub(super) intensity: f32,
117    pub(super) radius: f32,
118    pub(super) smoothness: f32,
119    pub(super) roundness: f32,
120    pub(super) center: Vec2,
121    pub(super) edge_compensation: f32,
122    pub(super) unused: u32,
123    pub(super) color: Vec4,
124}