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}