bevy_light 0.19.0-rc.2

Keeps the lights on at Bevy Engine
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
use bevy_asset::{Assets, Handle, HandleTemplate, RenderAssetUsages};
use bevy_camera::visibility::{self, ViewVisibility, Visibility, VisibilityClass};
use bevy_color::{Color, ColorToComponents, LinearRgba};
use bevy_ecs::prelude::*;
use bevy_ecs::template::{FromTemplate, OptionTemplate};
use bevy_image::Image;
use bevy_math::{Quat, UVec2, Vec3};
use bevy_reflect::prelude::*;
use bevy_transform::components::Transform;
use wgpu_types::{
    Extent3d, TextureDimension, TextureFormat, TextureViewDescriptor, TextureViewDimension,
};

use crate::cluster::ClusterVisibilityClass;

/// A marker component for a light probe, which is a cuboid region that provides
/// global illumination to all fragments inside it.
///
/// Note that a light probe will have no effect unless the entity contains some
/// kind of illumination, which can either be an [`EnvironmentMapLight`] or an
/// [`IrradianceVolume`].
///
/// The light probe range is conceptually a unit cube (1×1×1) centered on the
/// origin. The [`Transform`] applied to this entity can scale, rotate, or
/// translate that cube so that it contains all fragments that should take this
/// light probe into account.
///
/// Light probes may specify a *falloff* range over which their influence tapers
/// off. The falloff range is expressed as a range from 0, representing
/// infinitely-sharp falloff, to 1, representing the most gradual falloff,
/// *inside* the 1×1×1 cube. So, for example, if you set the falloff to 0.5 on
/// an axis, then any fragments with positions between 0.0 units to 0.25 units
/// on that axis will receive 100% influence from the light probe, while
/// fragments with positions between 0.25 units to 0.5 units on that axis will
/// receive gradually-diminished influence, and fragments more than 0.5 units
/// from the center of the light probe will receive no influence at all.
///
/// When multiple sources of indirect illumination can be applied to a fragment,
/// the highest-quality ones are chosen. Diffuse and specular illumination are
/// considered separately, so, for example, Bevy may decide to sample the
/// diffuse illumination from an irradiance volume and the specular illumination
/// from a reflection probe. From highest priority to lowest priority, the
/// ranking is as follows:
///
/// | Rank | Diffuse              | Specular             |
/// | ---- | -------------------- | -------------------- |
/// | 1    | Lightmap             | Lightmap             |
/// | 2    | Irradiance volume    | Reflection probe     |
/// | 3    | Reflection probe     | View environment map |
/// | 4    | View environment map |                      |
///
/// Note that ambient light is always added to the diffuse component and does
/// not participate in the ranking. That is, ambient light is applied in
/// addition to, not instead of, the light sources above.
///
/// Multiple light probes of the same type can apply to a single fragment. By
/// setting falloff regions appropriately, one can achieve a gradual blend from
/// one reflection probe and/or irradiance volume to another as objects move
/// between them.
///
/// A terminology note: Unfortunately, there is little agreement across game and
/// graphics engines as to what to call the various techniques that Bevy groups
/// under the term *light probe*. In Bevy, a *light probe* is the generic term
/// that encompasses both *reflection probes* and *irradiance volumes*. In
/// object-oriented terms, *light probe* is the superclass, and *reflection
/// probe* and *irradiance volume* are subclasses. In other engines, you may see
/// the term *light probe* refer to an irradiance volume with a single voxel, or
/// perhaps some other technique, while in Bevy *light probe* refers not to a
/// specific technique but rather to a class of techniques. Developers familiar
/// with other engines should be aware of this terminology difference.
#[derive(Component, Debug, Clone, Copy, Default, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
#[require(Transform, ViewVisibility, Visibility, VisibilityClass)]
#[component(on_add = visibility::add_visibility_class::<ClusterVisibilityClass>)]
pub struct LightProbe {
    /// The distance over which the effect of the light probe becomes weaker, on
    /// each axis.
    ///
    /// This is specified as a ratio of the total distance on each axis. So, for
    /// example, if you specify `Vec3::splat(0.25)` here, then the light probe
    /// will consist of a 0.75×0.75×0.75 unit cube within which fragments
    /// receive the maximum influence from the light probe, contained within a
    /// 1×1×1 cube which influences fragments inside it in a manner that
    /// diminishes as fragments get farther from its center.
    ///
    /// Falloff doesn't affect the influence range of the light probe itself;
    /// it's still conceptually a 1×1×1 cube, regardless of the falloff setting.
    /// In other words, falloff modifies the *interior* of the light probe cube
    /// instead of increasing the *exterior* boundaries of the cube.
    pub falloff: Vec3,
}

impl LightProbe {
    /// Creates a new light probe component.
    #[inline]
    pub fn new() -> Self {
        Self::default()
    }
}

/// A pair of cubemap textures that represent the surroundings of a specific
/// area in space.
///
/// See `bevy_pbr::environment_map` for detailed information.
#[derive(Clone, Component, Reflect, FromTemplate)]
#[reflect(Component, Default, Clone)]
pub struct EnvironmentMapLight {
    /// The blurry image that represents diffuse radiance surrounding a region.
    pub diffuse_map: Handle<Image>,

    /// The typically-sharper, mipmapped image that represents specular radiance
    /// surrounding a region.
    pub specular_map: Handle<Image>,

    /// Scale factor applied to the diffuse and specular light generated by this component.
    ///
    /// After applying this multiplier, the resulting values should
    /// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre).
    ///
    /// See also <https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights/iblunit>.
    pub intensity: f32,

    /// World space rotation applied to the environment light cubemaps.
    /// This is useful for users who require a different axis, such as the Z-axis, to serve
    /// as the vertical axis.
    ///
    /// Note: This only has an effect if attached to a view.
    pub rotation: Quat,

    /// Whether the light from this environment map contributes diffuse lighting
    /// to meshes with lightmaps.
    ///
    /// Set this to false if your lightmap baking tool bakes the diffuse light
    /// from this environment light into the lightmaps in order to avoid
    /// counting the radiance from this environment map twice.
    ///
    /// By default, this is set to true.
    pub affects_lightmapped_mesh_diffuse: bool,
}

impl EnvironmentMapLight {
    /// An environment map with a uniform color, useful for uniform ambient lighting.
    pub fn solid_color(assets: &mut Assets<Image>, color: impl Into<Color>) -> Self {
        let color = color.into();
        Self::hemispherical_gradient(assets, color, color, color)
    }

    /// An environment map with a hemispherical gradient, fading between the sky and ground colors
    /// at the horizon. Useful as a very simple 'sky'.
    pub fn hemispherical_gradient(
        assets: &mut Assets<Image>,
        top_color: impl Into<Color>,
        mid_color: impl Into<Color>,
        bottom_color: impl Into<Color>,
    ) -> Self {
        let handle = assets.add(Self::hemispherical_gradient_cubemap(
            top_color.into(),
            mid_color.into(),
            bottom_color.into(),
        ));

        Self {
            diffuse_map: handle.clone(),
            specular_map: handle,
            intensity: 1.0,
            ..Default::default()
        }
    }

    pub(crate) fn hemispherical_gradient_cubemap(
        top_color: Color,
        mid_color: Color,
        bottom_color: Color,
    ) -> Image {
        let top_color: LinearRgba = top_color.into();
        let mid_color: LinearRgba = mid_color.into();
        let bottom_color: LinearRgba = bottom_color.into();
        Image {
            texture_view_descriptor: Some(TextureViewDescriptor {
                dimension: Some(TextureViewDimension::Cube),
                ..Default::default()
            }),
            ..Image::new(
                Extent3d {
                    width: 1,
                    height: 1,
                    depth_or_array_layers: 6,
                },
                TextureDimension::D2,
                [
                    mid_color,
                    mid_color,
                    top_color,
                    bottom_color,
                    mid_color,
                    mid_color,
                ]
                .into_iter()
                .flat_map(|c| c.to_f32_array().map(half::f16::from_f32))
                .flat_map(half::f16::to_le_bytes)
                .collect(),
                TextureFormat::Rgba16Float,
                RenderAssetUsages::RENDER_WORLD,
            )
        }
    }
}

impl Default for EnvironmentMapLight {
    fn default() -> Self {
        EnvironmentMapLight {
            diffuse_map: Handle::default(),
            specular_map: Handle::default(),
            intensity: 0.0,
            rotation: Quat::IDENTITY,
            affects_lightmapped_mesh_diffuse: true,
        }
    }
}

/// Adds a skybox to a 3D camera, based on a cubemap texture.
///
/// Note that this component does not (currently) affect the scene's lighting.
/// To do so, use [`EnvironmentMapLight`] alongside this component.
///
/// See also <https://en.wikipedia.org/wiki/Skybox_(video_games)>.
#[derive(Component, Clone, Reflect, FromTemplate)]
#[reflect(Component, Default, Clone)]
pub struct Skybox {
    /// The cubemap to use.
    ///
    /// If this is [`None`], the skybox will not be rendered, as if it does not exist.
    /// This allows `Skybox` to implement [`Default`].
    #[template(OptionTemplate<HandleTemplate<Image>>)]
    pub image: Option<Handle<Image>>,

    /// Scale factor applied to the skybox image.
    /// After applying this multiplier to the image samples, the resulting values should
    /// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre).
    pub brightness: f32,

    /// View space rotation applied to the skybox cubemap.
    /// This is useful for users who require a different axis, such as the Z-axis, to serve
    /// as the vertical axis.
    pub rotation: Quat,
}

impl Default for Skybox {
    fn default() -> Self {
        Skybox {
            image: None,
            brightness: 0.0,
            rotation: Quat::IDENTITY,
        }
    }
}

/// A generated environment map that is filtered at runtime.
///
/// See `bevy_pbr::light_probe::generate` for detailed information.
#[derive(Clone, Component, Reflect, FromTemplate)]
#[reflect(Component, Default, Clone)]
pub struct GeneratedEnvironmentMapLight {
    /// Source cubemap to be filtered on the GPU, size must be a power of two.
    pub environment_map: Handle<Image>,

    /// Scale factor applied to the diffuse and specular light generated by this
    /// component. Expressed in cd/m² (candela per square meter).
    pub intensity: f32,

    /// World-space rotation applied to the cubemap.
    pub rotation: Quat,

    /// Whether this light contributes diffuse lighting to meshes that already
    /// have baked lightmaps.
    pub affects_lightmapped_mesh_diffuse: bool,
}

impl Default for GeneratedEnvironmentMapLight {
    fn default() -> Self {
        GeneratedEnvironmentMapLight {
            environment_map: Handle::default(),
            intensity: 0.0,
            rotation: Quat::IDENTITY,
            affects_lightmapped_mesh_diffuse: true,
        }
    }
}

/// Lets the atmosphere contribute environment lighting (reflections and ambient diffuse) to your scene.
///
/// Attach this to a [`Camera3d`](bevy_camera::Camera3d) to light the entire view, or to a
/// [`LightProbe`] to light only a specific region.
/// Behind the scenes, this generates an environment map from the atmosphere for image-based lighting
/// and inserts a corresponding [`GeneratedEnvironmentMapLight`].
///
/// For HDRI-based lighting, use a preauthored [`EnvironmentMapLight`] or filter one at runtime with
/// [`GeneratedEnvironmentMapLight`].
#[derive(Component, Clone)]
pub struct AtmosphereEnvironmentMapLight {
    /// Controls how bright the atmosphere's environment lighting is.
    /// Increase this value to brighten reflections and ambient diffuse lighting.
    ///
    /// The default is `1.0` so that the generated environment lighting matches
    /// the light intensity of the atmosphere in the scene.
    pub intensity: f32,
    /// Whether the diffuse contribution should affect meshes that already have lightmaps.
    pub affects_lightmapped_mesh_diffuse: bool,
    /// Cubemap resolution in pixels (must be a power-of-two).
    pub size: UVec2,
}

impl Default for AtmosphereEnvironmentMapLight {
    fn default() -> Self {
        Self {
            intensity: 1.0,
            affects_lightmapped_mesh_diffuse: true,
            size: UVec2::new(512, 512),
        }
    }
}

/// The component that defines an irradiance volume.
///
/// See `bevy_pbr::irradiance_volume` for detailed information.
///
/// This component requires the [`LightProbe`] component, and is typically used with
/// [`bevy_transform::components::Transform`] to place the volume appropriately.
#[derive(Clone, Reflect, Component, Debug, FromTemplate)]
#[reflect(Component, Default, Debug, Clone)]
#[require(LightProbe)]
pub struct IrradianceVolume {
    /// The 3D texture that represents the ambient cubes, encoded in the format
    /// described in `bevy_pbr::irradiance_volume`.
    pub voxels: Handle<Image>,

    /// Scale factor applied to the diffuse and specular light generated by this component.
    ///
    /// After applying this multiplier, the resulting values should
    /// be in units of [cd/m^2](https://en.wikipedia.org/wiki/Candela_per_square_metre).
    ///
    /// See also <https://google.github.io/filament/Filament.md.html#lighting/imagebasedlights/iblunit>.
    pub intensity: f32,

    /// Whether the light from this irradiance volume has an effect on meshes
    /// with lightmaps.
    ///
    /// Set this to false if your lightmap baking tool bakes the light from this
    /// irradiance volume into the lightmaps in order to avoid counting the
    /// irradiance twice. Frequently, applications use irradiance volumes as a
    /// lower-quality alternative to lightmaps for capturing indirect
    /// illumination on dynamic objects, and such applications will want to set
    /// this value to false.
    ///
    /// By default, this is set to true.
    pub affects_lightmapped_meshes: bool,
}

impl Default for IrradianceVolume {
    #[inline]
    fn default() -> Self {
        IrradianceVolume {
            voxels: Handle::default(),
            intensity: 0.0,
            affects_lightmapped_meshes: true,
        }
    }
}

/// Add this component to a reflection probe to customize *parallax correction*.
///
/// For environment maps added directly to a camera, Bevy renders the reflected
/// scene that a cubemap captures as though it were infinitely far away. This is
/// acceptable if the cubemap captures very distant objects, such as distant
/// mountains in outdoor scenes. It's less ideal, however, if the cubemap
/// reflects near objects, such as the interior of a room. Therefore, by default
/// for reflection probes Bevy uses *parallax-corrected cubemaps* (PCCM), which
/// causes Bevy to treat the reflected scene as though it coincided with the
/// boundaries of the light probe.
///
/// As an example, for indoor scenes, it's common to place reflection probes
/// inside each room and to make the boundaries of the reflection probe (as
/// determined by the light probe's [`bevy_transform::components::Transform`])
/// coincide with the walls of the room. That way, the reflection probes will
/// (1) apply to the objects inside the room and (2) take the positions of those
/// objects into account in order to create a realistic reflection.
///
/// Instead of having the simulated boundaries of the reflected area coincide
/// with the boundaries of the light probe, it's also possible to specify
/// *custom* parallax correction boundaries, so that the region of influence of
/// the light probe doesn't correspond with the simulated boundaries used for
/// parallax correction. This is commonly used when the boundaries of the light
/// probe are slightly larger than the room that the light probe contains, for
/// instance in order to avoid artifacts along the edges of the room that occur
/// due to rounding error, or else when the *falloff* feature is used that
/// blends reflection probes into adjacent ones.
///
/// Place this component on an entity that has a [`LightProbe`] and
/// [`EnvironmentMapLight`] component in order to either (1) opt out of parallax
/// correction via [`ParallaxCorrection::None`] or (2) specify custom parallax
/// correction boundaries via [`ParallaxCorrection::Custom`]. If you don't
/// manually place this component on a reflection probe, Bevy will automatically
/// add a [`ParallaxCorrection::Auto`] component so that the boundaries of the
/// light probe will coincide with the simulated boundaries used for parallax
/// correction.
///
/// See the `pccm` example for an example of usage of parallax-corrected
/// cubemaps and the `light_probe_blending` example for an example of use of
/// custom parallax correction boundaries.
#[derive(Clone, Copy, Default, Component, Reflect)]
#[reflect(Clone, Default, Component)]
pub enum ParallaxCorrection {
    /// No parallax correction is used.
    ///
    /// This component causes Bevy to render the reflection as though the
    /// reflected surface were infinitely distant.
    None,

    /// The parallax correction boundaries correspond with the boundaries of the
    /// light probe.
    ///
    /// This is the default value. Bevy automatically adds this component value
    /// to reflection probes that don't have a [`ParallaxCorrection`] component.
    /// It's equivalent to `ParallaxCorrection::Custom(Vec3::splat(0.5))`.
    #[default]
    Auto,

    /// The parallax correction boundaries are specified manually.
    ///
    /// The simulated reflection boundaries are specified as an axis-aligned
    /// cube *in light probe space* with the given *half* extents. Thus, for
    /// example, if you set the parallax correction boundaries to `vec3(0.5,
    /// 1.0, 2.0)` and the scale of the light probe is `vec3(3.0, 3.0, 3.0)`,
    /// then the simulated boundaries of the reflected area used for parallax
    /// correction will be centered on the reflection probe with a width of 3.0
    /// m, a height of 6.0 m, and a depth of 12.0 m.
    Custom(Vec3),
}

/// A system that automatically adds a [`ParallaxCorrection::Auto`] component to
/// any reflection probe that doesn't already have a [`ParallaxCorrection`]
/// component.
///
/// A reflection probe is any entity with both an [`EnvironmentMapLight`] and a
/// [`LightProbe`] component.
pub fn automatically_add_parallax_correction_components(
    mut commands: Commands,
    query: Query<
        Entity,
        (
            With<EnvironmentMapLight>,
            With<LightProbe>,
            Without<ParallaxCorrection>,
        ),
    >,
) {
    for entity in &query {
        commands
            .entity(entity)
            .insert(ParallaxCorrection::default());
    }
}