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