bevy_post_process/effect_stack/lens_distortion.rs
1use bevy_ecs::{
2 component::Component,
3 query::{QueryItem, With},
4 system::lifetimeless::Read,
5};
6use bevy_math::{ops::abs, Vec2};
7use bevy_reflect::Reflect;
8use bevy_render::{
9 extract_component::ExtractComponent, render_resource::ShaderType,
10 sync_component::SyncComponent, RenderApp,
11};
12
13/// Simulates the warping of the image caused by real-world camera lenses.
14///
15/// [Lens distortion] simulates the imperfections of optical systems, where
16/// straight lines in the real world appear curved in the image. This effect
17/// is commonly used to create a sense of unease or disorientation, to mimic
18/// specific camera equipment, or to enhance the scale and immersion of scenes.
19///
20/// Bevy's implementation is based on a simplified special case of the
21/// Brown-Conrady model, where p₁ = p₂ = 0 and control is retained only
22/// for k1 and k2.
23#[derive(Reflect, Component, Clone)]
24pub struct LensDistortion {
25 /// The overall strength of the distortion effect.
26 ///
27 /// Positive values typically produce **barrel distortion** (bulging outwards),
28 /// while negative values produce **pincushion distortion** (pinching inwards).
29 /// This corresponds roughly to the radial distortion coefficient `k1`
30 /// in the simplified model.
31 ///
32 /// The default value is 0.5.
33 pub intensity: f32,
34 /// A global scale factor applied to the final distorted image.
35 ///
36 /// Strong distortion pushes pixels away from the center or pulls them in,
37 /// resulting in visible **stretching artifacts** at the screen edges.
38 /// Increasing this value zooms in to **crop out** these extreme edge artifacts,
39 /// ensuring the screen remains fully covered at the cost of a tighter field of view.
40 ///
41 /// The default value is 1.0(No zoom).
42 pub scale: f32,
43 /// A multiplier that determines how the distortion scales along the X and Y axes.
44 ///
45 /// By default, this should be `Vec2::ONE` for uniform radial distortion.
46 /// Modifying these values allows for anamorphic-like effects where the distortion
47 /// is stronger on one axis than the other. When a component of multiplier is set to 0.0,
48 /// no distortion effect is applied.
49 ///
50 /// The default value is `Vec2::ONE`
51 pub multiplier: Vec2,
52 /// The center point of the distortion effect in UV space `[0.0, 1.0]`.
53 ///
54 /// Distortion radiates outward or inward from this point.
55 ///
56 /// The default value is `Vec2::splat(0.5)`
57 pub center: Vec2,
58 /// Controls the sharpness of the distortion curve near the screen edges.
59 ///
60 /// `edge_curvature` provides indirect control over the k2 parameter.
61 /// The reason for indirect control is that k1 and k2 are typically correlated.
62 /// If k2 did not vary with k1, it would easily cause visual jumping when intensity
63 /// transitions from positive to negative.
64 /// For a simple and natural look in most cases, we recommend setting `edge_curvature` to 0.0.
65 ///
66 /// The default value is 0.0.
67 pub edge_curvature: f32,
68}
69
70impl Default for LensDistortion {
71 fn default() -> Self {
72 Self {
73 intensity: 0.5,
74 scale: 1.0,
75 multiplier: Vec2::ONE,
76 center: Vec2::splat(0.5),
77 edge_curvature: 0.0,
78 }
79 }
80}
81
82impl SyncComponent<RenderApp> for LensDistortion {
83 type Target = Self;
84}
85
86impl ExtractComponent<RenderApp> for LensDistortion {
87 type QueryData = Read<LensDistortion>;
88 type QueryFilter = With<LensDistortion>;
89 type Out = Self;
90
91 fn extract_component(lens_distortion: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
92 // Skip the postprocessing phase entirely if the intensity is negligible.
93 if abs(lens_distortion.intensity) > 1e-4 {
94 Some(lens_distortion.clone())
95 } else {
96 None
97 }
98 }
99}
100
101/// The on-GPU version of the [`LensDistortion`] settings.
102///
103/// See the documentation for [`LensDistortion`] for more information on
104/// each of these fields.
105#[derive(ShaderType, Default)]
106pub struct LensDistortionUniform {
107 pub(super) intensity: f32,
108 pub(super) scale: f32,
109 pub(super) multiplier: Vec2,
110 pub(super) center: Vec2,
111 pub(super) edge_curvature: f32,
112 pub(super) unused: u32,
113}