bevy_light (#19991)

# Objective

- make lights usable without bevy_render

## Solution

- make a new crate for lights to live in

## Testing

- 3d_scene, lighting, volumetric_fog, ssr, transmission, pcss,
light_textures

Note: no breaking changes because of re-exports, except for light
textures, which were introduced this cycle so it doesn't matter anyways
This commit is contained in:
atlv 2025-07-06 20:07:38 -04:00 committed by GitHub
parent a1139c23c6
commit 537adcc3f7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 678 additions and 386 deletions

View File

@ -0,0 +1,44 @@
[package]
name = "bevy_light"
version = "0.17.0-dev"
edition = "2024"
description = "Keeps the lights on at Bevy Engine"
homepage = "https://bevy.org"
repository = "https://github.com/bevyengine/bevy"
license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.17.0-dev" }
bevy_asset = { path = "../bevy_asset", version = "0.17.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
bevy_mesh = { path = "../bevy_mesh", version = "0.17.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }
bevy_camera = { path = "../bevy_camera", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
bevy_transform = { path = "../bevy_transform", version = "0.17.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
bevy_utils = { path = "../bevy_utils", version = "0.17.0-dev" }
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev" }
bevy_color = { path = "../bevy_color", version = "0.17.0-dev", features = [
"serialize",
] }
# other
serde = { version = "1", default-features = false, features = ["derive"] }
tracing = { version = "0.1", default-features = false }
[features]
default = []
experimental_pbr_pcss = []
webgl = []
webgpu = []
[lints]
workspace = true
[package.metadata.docs.rs]
rustdoc-args = ["-Zunstable-options", "--generate-link-to-definition"]
all-features = true

View File

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

View File

@ -0,0 +1,19 @@
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -2,7 +2,6 @@ use bevy_camera::Camera;
use bevy_color::Color;
use bevy_ecs::prelude::*;
use bevy_reflect::prelude::*;
use bevy_render::{extract_component::ExtractComponent, extract_resource::ExtractResource};
/// An ambient light, which lights the entire scene equally.
///
@ -16,14 +15,14 @@ use bevy_render::{extract_component::ExtractComponent, extract_resource::Extract
///
/// ```
/// # use bevy_ecs::system::ResMut;
/// # use bevy_pbr::AmbientLight;
/// # use bevy_light::AmbientLight;
/// fn setup_ambient_light(mut ambient_light: ResMut<AmbientLight>) {
/// ambient_light.brightness = 100.0;
/// }
/// ```
///
/// [`LightPlugin`]: crate::LightPlugin
#[derive(Resource, Component, Clone, Debug, ExtractResource, ExtractComponent, Reflect)]
#[derive(Resource, Component, Clone, Debug, Reflect)]
#[reflect(Resource, Component, Debug, Default, Clone)]
#[require(Camera)]
pub struct AmbientLight {

View File

@ -11,8 +11,8 @@ use crate::{DirectionalLight, DirectionalLightShadowMap};
/// Prefer using [`CascadeShadowConfigBuilder`] to construct an instance.
///
/// ```
/// # use bevy_pbr::CascadeShadowConfig;
/// # use bevy_pbr::CascadeShadowConfigBuilder;
/// # use bevy_light::CascadeShadowConfig;
/// # use bevy_light::CascadeShadowConfigBuilder;
/// # use bevy_utils::default;
/// #
/// let config: CascadeShadowConfig = CascadeShadowConfigBuilder {

View File

@ -22,10 +22,7 @@ use super::{
ClusterConfig, ClusterFarZMode, ClusteredDecal, Clusters, GlobalClusterSettings,
GlobalVisibleClusterableObjects, VisibleClusterableObjects,
};
use crate::{
prelude::EnvironmentMapLight, ExtractedPointLight, LightProbe, PointLight, SpotLight,
VolumetricLight,
};
use crate::{EnvironmentMapLight, LightProbe, PointLight, SpotLight, VolumetricLight};
const NDC_MIN: Vec2 = Vec2::NEG_ONE;
const NDC_MAX: Vec2 = Vec2::ONE;
@ -57,7 +54,7 @@ impl ClusterableObjectAssignmentData {
/// Data needed to assign objects to clusters that's specific to the type of
/// clusterable object.
#[derive(Clone, Copy, Debug)]
pub(crate) enum ClusterableObjectType {
pub enum ClusterableObjectType {
/// Data needed to assign point lights to clusters.
PointLight {
/// Whether shadows are enabled for this point light.
@ -105,7 +102,7 @@ impl ClusterableObjectType {
/// Generally, we sort first by type, then, for lights, by whether shadows
/// are enabled (enabled before disabled), and then whether volumetrics are
/// enabled (enabled before disabled).
pub(crate) fn ordering(&self) -> (u8, bool, bool) {
pub fn ordering(&self) -> (u8, bool, bool) {
match *self {
ClusterableObjectType::PointLight {
shadows_enabled,
@ -121,23 +118,6 @@ impl ClusterableObjectType {
ClusterableObjectType::Decal => (4, false, false),
}
}
/// Creates the [`ClusterableObjectType`] data for a point or spot light.
pub(crate) fn from_point_or_spot_light(
point_light: &ExtractedPointLight,
) -> ClusterableObjectType {
match point_light.spot_light_angles {
Some((_, outer_angle)) => ClusterableObjectType::SpotLight {
outer_angle,
shadows_enabled: point_light.shadows_enabled,
volumetric: point_light.volumetric,
},
None => ClusterableObjectType::PointLight {
shadows_enabled: point_light.shadows_enabled,
volumetric: point_light.volumetric,
},
}
}
}
// NOTE: Run this before update_point_light_frusta!

View File

@ -17,15 +17,10 @@ use bevy_image::Image;
use bevy_math::{AspectRatio, UVec2, UVec3, Vec3Swizzles as _};
use bevy_platform::collections::HashSet;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::extract_component::ExtractComponent;
use bevy_transform::components::Transform;
use tracing::warn;
pub(crate) use crate::cluster::assign::assign_objects_to_clusters;
pub(crate) mod assign;
mod extract_and_prepare;
pub use extract_and_prepare::*;
pub mod assign;
#[cfg(test)]
mod test;
@ -106,14 +101,14 @@ pub enum ClusterConfig {
#[derive(Component, Debug, Default)]
pub struct Clusters {
/// Tile size
pub(crate) tile_size: UVec2,
pub tile_size: UVec2,
/// Number of clusters in `X` / `Y` / `Z` in the view frustum
pub(crate) dimensions: UVec3,
pub dimensions: UVec3,
/// Distance to the far plane of the first depth slice. The first depth slice is special
/// and explicitly-configured to avoid having unnecessarily many slices close to the camera.
pub(crate) near: f32,
pub(crate) far: f32,
pub(crate) clusterable_objects: Vec<VisibleClusterableObjects>,
pub near: f32,
pub far: f32,
pub clusterable_objects: Vec<VisibleClusterableObjects>,
}
/// The [`VisibilityClass`] used for clusterables (decals, point lights, directional lights, and spot lights).
@ -123,8 +118,8 @@ pub struct ClusterVisibilityClass;
#[derive(Clone, Component, Debug, Default)]
pub struct VisibleClusterableObjects {
pub(crate) entities: Vec<Entity>,
counts: ClusterableObjectCounts,
pub entities: Vec<Entity>,
pub counts: ClusterableObjectCounts,
}
#[derive(Resource, Default)]
@ -137,17 +132,17 @@ pub struct GlobalVisibleClusterableObjects {
/// Note that `reflection_probes` and `irradiance_volumes` won't be clustered if
/// fewer than 3 SSBOs are available, which usually means on WebGL 2.
#[derive(Clone, Copy, Default, Debug)]
struct ClusterableObjectCounts {
pub struct ClusterableObjectCounts {
/// The number of point lights in the cluster.
point_lights: u32,
pub point_lights: u32,
/// The number of spot lights in the cluster.
spot_lights: u32,
pub spot_lights: u32,
/// The number of reflection probes in the cluster.
reflection_probes: u32,
pub reflection_probes: u32,
/// The number of irradiance volumes in the cluster.
irradiance_volumes: u32,
pub irradiance_volumes: u32,
/// The number of decals in the cluster.
decals: u32,
pub decals: u32,
}
/// An object that projects a decal onto surfaces within its bounds.
@ -160,7 +155,7 @@ struct ClusterableObjectCounts {
/// but they require bindless textures. This means that they presently can't be
/// used on WebGL 2, WebGPU, macOS, or iOS. Bevy's clustered decals can be used
/// with forward or deferred rendering and don't require a prepass.
#[derive(Component, Debug, Clone, Reflect, ExtractComponent)]
#[derive(Component, Debug, Clone, Reflect)]
#[reflect(Component, Debug, Clone)]
#[require(Transform, Visibility, VisibilityClass)]
#[component(on_add = visibility::add_visibility_class::<ClusterVisibilityClass>)]

View File

@ -1,6 +1,6 @@
use bevy_math::UVec2;
use crate::{ClusterConfig, Clusters};
use super::{ClusterConfig, Clusters};
fn test_cluster_tiling(config: ClusterConfig, screen_size: UVec2) -> Clusters {
let dims = config.dimensions_for_screen_size(screen_size);

View File

@ -10,8 +10,9 @@ use bevy_image::Image;
use bevy_reflect::prelude::*;
use bevy_transform::components::Transform;
use super::{cascade::CascadeShadowConfig, light_consts, Cascades};
use crate::cluster::ClusterVisibilityClass;
use super::{
cascade::CascadeShadowConfig, cluster::ClusterVisibilityClass, light_consts, Cascades,
};
/// A Directional light.
///
@ -172,7 +173,7 @@ pub struct DirectionalLightTexture {
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_pbr::DirectionalLightShadowMap;
/// # use bevy_light::DirectionalLightShadowMap;
/// App::new()
/// .insert_resource(DirectionalLightShadowMap { size: 4096 });
/// ```

View File

@ -1,3 +1,5 @@
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
use bevy_app::{App, Plugin, PostUpdate};
use bevy_camera::{
primitives::{Aabb, CascadesFrusta, CubemapFrusta, Frustum, Sphere},
@ -10,21 +12,27 @@ use bevy_camera::{
};
use bevy_ecs::{entity::EntityHashSet, prelude::*};
use bevy_math::Vec3A;
use bevy_mesh::Mesh3d;
use bevy_reflect::prelude::*;
use bevy_render::{extract_component::ExtractComponent, mesh::Mesh3d};
use bevy_transform::{components::GlobalTransform, TransformSystems};
use bevy_utils::Parallel;
use core::ops::DerefMut;
use crate::cluster::{add_clusters, assign_objects_to_clusters, VisibleClusterableObjects};
pub mod cluster;
pub use cluster::ClusteredDecal;
use cluster::{
add_clusters, assign::assign_objects_to_clusters, ClusterConfig,
GlobalVisibleClusterableObjects, VisibleClusterableObjects,
};
mod ambient_light;
pub use ambient_light::AmbientLight;
mod probe;
pub use probe::{EnvironmentMapLight, LightProbe};
mod volumetric;
pub use volumetric::{FogVolume, VolumetricFog, VolumetricLight};
pub mod cascade;
use cascade::{
build_directional_light_cascades, clear_directional_light_cascades, CascadeShadowConfig,
Cascades,
};
use cascade::{build_directional_light_cascades, clear_directional_light_cascades};
pub use cascade::{CascadeShadowConfig, CascadeShadowConfigBuilder, Cascades};
mod point_light;
pub use point_light::{
update_point_light_frusta, PointLight, PointLightShadowMap, PointLightTexture,
@ -111,9 +119,15 @@ impl Plugin for LightPlugin {
.register_type::<NotShadowCaster>()
.register_type::<NotShadowReceiver>()
.register_type::<PointLight>()
.register_type::<LightProbe>()
.register_type::<EnvironmentMapLight>()
.register_type::<VolumetricFog>()
.register_type::<VolumetricLight>()
.register_type::<PointLightShadowMap>()
.register_type::<SpotLight>()
.register_type::<ShadowFilteringMethod>()
.register_type::<ClusterConfig>()
.init_resource::<GlobalVisibleClusterableObjects>()
.init_resource::<AmbientLight>()
.init_resource::<DirectionalLightShadowMap>()
.init_resource::<PointLightShadowMap>()
@ -182,7 +196,7 @@ impl Plugin for LightPlugin {
}
/// A convenient alias for `Or<(With<PointLight>, With<SpotLight>,
/// With<DirectionalLight>)>`, for use with [`bevy_render::view::VisibleEntities`].
/// With<DirectionalLight>)>`, for use with [`bevy_camera::visibility::VisibleEntities`].
pub type WithLight = Or<(With<PointLight>, With<SpotLight>, With<DirectionalLight>)>;
/// Add this component to make a [`Mesh3d`] not cast shadows.
@ -197,10 +211,10 @@ pub struct NotShadowCaster;
#[derive(Debug, Component, Reflect, Default)]
#[reflect(Component, Default, Debug)]
pub struct NotShadowReceiver;
/// Add this component to make a [`Mesh3d`] using a PBR material with [`diffuse_transmission`](crate::pbr_material::StandardMaterial::diffuse_transmission)`> 0.0`
/// Add this component to make a [`Mesh3d`] using a PBR material with `StandardMaterial::diffuse_transmission > 0.0`
/// receive shadows on its diffuse transmission lobe. (i.e. its “backside”)
///
/// Not enabled by default, as it requires carefully setting up [`thickness`](crate::pbr_material::StandardMaterial::thickness)
/// Not enabled by default, as it requires carefully setting up `StandardMaterial::thickness`
/// (and potentially even baking a thickness texture!) to match the geometry of the mesh, in order to avoid self-shadow artifacts.
///
/// **Note:** Using [`NotShadowReceiver`] overrides this component.
@ -208,12 +222,12 @@ pub struct NotShadowReceiver;
#[reflect(Component, Default, Debug)]
pub struct TransmittedShadowReceiver;
/// Add this component to a [`Camera3d`](bevy_core_pipeline::core_3d::Camera3d)
/// Add this component to a [`Camera3d`](bevy_camera::Camera3d)
/// to control how to anti-alias shadow edges.
///
/// The different modes use different approaches to
/// [Percentage Closer Filtering](https://developer.nvidia.com/gpugems/gpugems/part-ii-lighting-and-shadows/chapter-11-shadow-map-antialiasing).
#[derive(Debug, Component, ExtractComponent, Reflect, Clone, Copy, PartialEq, Eq, Default)]
#[derive(Debug, Component, Reflect, Clone, Copy, PartialEq, Eq, Default)]
#[reflect(Component, Default, Debug, PartialEq, Clone)]
pub enum ShadowFilteringMethod {
/// Hardware 2x2.

View File

@ -166,7 +166,7 @@ pub struct PointLightTexture {
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_pbr::PointLightShadowMap;
/// # use bevy_light::PointLightShadowMap;
/// App::new()
/// .insert_resource(PointLightShadowMap { size: 2048 });
/// ```

View File

@ -0,0 +1,109 @@
use bevy_asset::Handle;
use bevy_camera::visibility::Visibility;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_math::Quat;
use bevy_reflect::prelude::*;
use bevy_transform::components::Transform;
/// 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.
///
/// When multiple sources of indirect illumination can be applied to a fragment,
/// the highest-quality one is 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.
///
/// 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, Visibility)]
pub struct LightProbe;
impl LightProbe {
/// Creates a new light probe component.
#[inline]
pub fn new() -> Self {
Self
}
}
/// 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)]
#[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.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.
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 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,
}
}
}

View File

@ -1,14 +1,13 @@
use bevy_asset::Handle;
use bevy_camera::{
primitives::Frustum,
visibility::{self, Visibility, VisibilityClass},
visibility::{self, Visibility, VisibilityClass, VisibleMeshEntities},
};
use bevy_color::Color;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_math::{Mat4, Vec4};
use bevy_reflect::prelude::*;
use bevy_render::view::VisibleMeshEntities;
use bevy_transform::components::{GlobalTransform, Transform};
use crate::cluster::{ClusterVisibilityClass, GlobalVisibleClusterableObjects};

View File

@ -0,0 +1,157 @@
use bevy_asset::Handle;
use bevy_camera::visibility::Visibility;
use bevy_color::Color;
use bevy_ecs::prelude::*;
use bevy_image::Image;
use bevy_math::Vec3;
use bevy_reflect::prelude::*;
use bevy_transform::components::Transform;
/// Add this component to a [`DirectionalLight`](crate::DirectionalLight) with a shadow map
/// (`shadows_enabled: true`) to make volumetric fog interact with it.
///
/// This allows the light to generate light shafts/god rays.
#[derive(Clone, Copy, Component, Default, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct VolumetricLight;
/// When placed on a [`bevy_camera::Camera3d`], enables
/// volumetric fog and volumetric lighting, also known as light shafts or god
/// rays.
#[derive(Clone, Copy, Component, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct VolumetricFog {
/// Color of the ambient light.
///
/// This is separate from Bevy's [`AmbientLight`](crate::AmbientLight) because an
/// [`EnvironmentMapLight`](crate::EnvironmentMapLight) is
/// still considered an ambient light for the purposes of volumetric fog. If you're using a
/// [`EnvironmentMapLight`](crate::EnvironmentMapLight), for best results,
/// this should be a good approximation of the average color of the environment map.
///
/// Defaults to white.
pub ambient_color: Color,
/// The brightness of the ambient light.
///
/// If there's no [`EnvironmentMapLight`](crate::EnvironmentMapLight),
/// set this to 0.
///
/// Defaults to 0.1.
pub ambient_intensity: f32,
/// The maximum distance to offset the ray origin randomly by, in meters.
///
/// This is intended for use with temporal antialiasing. It helps fog look
/// less blocky by varying the start position of the ray, using interleaved
/// gradient noise.
pub jitter: f32,
/// The number of raymarching steps to perform.
///
/// Higher values produce higher-quality results with less banding, but
/// reduce performance.
///
/// The default value is 64.
pub step_count: u32,
}
impl Default for VolumetricFog {
fn default() -> Self {
Self {
step_count: 64,
// Matches `AmbientLight` defaults.
ambient_color: Color::WHITE,
ambient_intensity: 0.1,
jitter: 0.0,
}
}
}
#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
#[require(Transform, Visibility)]
pub struct FogVolume {
/// The color of the fog.
///
/// Note that the fog must be lit by a [`VolumetricLight`] or ambient light
/// in order for this color to appear.
///
/// Defaults to white.
pub fog_color: Color,
/// The density of fog, which measures how dark the fog is.
///
/// The default value is 0.1.
pub density_factor: f32,
/// Optional 3D voxel density texture for the fog.
pub density_texture: Option<Handle<Image>>,
/// Configurable offset of the density texture in UVW coordinates.
///
/// This can be used to scroll a repeating density texture in a direction over time
/// to create effects like fog moving in the wind. Make sure to configure the texture
/// to use `ImageAddressMode::Repeat` if this is your intention.
///
/// Has no effect when no density texture is present.
///
/// The default value is (0, 0, 0).
pub density_texture_offset: Vec3,
/// The absorption coefficient, which measures what fraction of light is
/// absorbed by the fog at each step.
///
/// Increasing this value makes the fog darker.
///
/// The default value is 0.3.
pub absorption: f32,
/// The scattering coefficient, which measures the fraction of light that's
/// scattered toward, and away from, the viewer.
///
/// The default value is 0.3.
pub scattering: f32,
/// Measures the fraction of light that's scattered *toward* the camera, as
/// opposed to *away* from the camera.
///
/// Increasing this value makes light shafts become more prominent when the
/// camera is facing toward their source and less prominent when the camera
/// is facing away. Essentially, a high value here means the light shafts
/// will fade into view as the camera focuses on them and fade away when the
/// camera is pointing away.
///
/// The default value is 0.8.
pub scattering_asymmetry: f32,
/// Applies a nonphysical color to the light.
///
/// This can be useful for artistic purposes but is nonphysical.
///
/// The default value is white.
pub light_tint: Color,
/// Scales the light by a fixed fraction.
///
/// This can be useful for artistic purposes but is nonphysical.
///
/// The default value is 1.0, which results in no adjustment.
pub light_intensity: f32,
}
impl Default for FogVolume {
fn default() -> Self {
Self {
absorption: 0.3,
scattering: 0.3,
density_factor: 0.1,
density_texture: None,
density_texture_offset: Vec3::ZERO,
scattering_asymmetry: 0.5,
fog_color: Color::WHITE,
light_tint: Color::WHITE,
light_intensity: 1.0,
}
}
}

View File

@ -9,12 +9,12 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[features]
webgl = []
webgpu = []
webgl = ["bevy_light/webgl"]
webgpu = ["bevy_light/webgpu"]
pbr_transmission_textures = []
pbr_multi_layer_material_textures = []
pbr_anisotropy_texture = []
experimental_pbr_pcss = []
experimental_pbr_pcss = ["bevy_light/experimental_pbr_pcss"]
pbr_specular_textures = []
pbr_clustered_decals = []
pbr_light_textures = []
@ -40,6 +40,7 @@ bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.17.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.17.0-dev" }
bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.17.0-dev" }
bevy_light = { path = "../bevy_light", version = "0.17.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
bevy_math = { path = "../bevy_math", version = "0.17.0-dev" }
bevy_reflect = { path = "../bevy_reflect", version = "0.17.0-dev" }

View File

@ -2,6 +2,7 @@ use core::num::NonZero;
use bevy_camera::Camera;
use bevy_ecs::{entity::EntityHashMap, prelude::*};
use bevy_light::cluster::{ClusterableObjectCounts, Clusters, GlobalClusterSettings};
use bevy_math::{uvec4, UVec3, UVec4, Vec4};
use bevy_render::{
render_resource::{
@ -13,7 +14,6 @@ use bevy_render::{
};
use tracing::warn;
use super::{ClusterableObjectCounts, Clusters, GlobalClusterSettings};
use crate::MeshPipeline;
// NOTE: this must be kept in sync with the same constants in

View File

@ -27,6 +27,8 @@ use bevy_ecs::{
system::{Query, Res, ResMut},
};
use bevy_image::Image;
pub use bevy_light::cluster::ClusteredDecal;
use bevy_light::{DirectionalLightTexture, PointLightTexture, SpotLightTexture};
use bevy_math::Mat4;
use bevy_platform::collections::HashMap;
pub use bevy_render::primitives::CubemapLayout;
@ -47,9 +49,7 @@ use bevy_render::{
use bevy_transform::components::GlobalTransform;
use bytemuck::{Pod, Zeroable};
pub use crate::ClusteredDecal;
use crate::{binding_arrays_are_usable, prepare_lights, GlobalClusterableObjectMeta};
pub use crate::{DirectionalLightTexture, PointLightTexture, SpotLightTexture};
/// The maximum number of decals that can be present in a view.
///

View File

@ -1,14 +1,11 @@
use crate::{
graph::NodePbr, irradiance_volume::IrradianceVolume, prelude::EnvironmentMapLight,
MeshPipeline, MeshViewBindGroup, RenderViewLightProbes, ScreenSpaceAmbientOcclusion,
ScreenSpaceReflectionsUniform, ViewEnvironmentMapUniformOffset, ViewLightProbesUniformOffset,
graph::NodePbr, irradiance_volume::IrradianceVolume, MeshPipeline, MeshViewBindGroup,
RenderViewLightProbes, ScreenSpaceAmbientOcclusion, ScreenSpaceReflectionsUniform,
ViewEnvironmentMapUniformOffset, ViewLightProbesUniformOffset,
ViewScreenSpaceReflectionsUniformOffset, TONEMAPPING_LUT_SAMPLER_BINDING_INDEX,
TONEMAPPING_LUT_TEXTURE_BINDING_INDEX,
};
use crate::{
DistanceFog, MeshPipelineKey, ShadowFilteringMethod, ViewFogUniformOffset,
ViewLightsUniformOffset,
};
use crate::{DistanceFog, MeshPipelineKey, ViewFogUniformOffset, ViewLightsUniformOffset};
use bevy_app::prelude::*;
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
use bevy_core_pipeline::{
@ -21,6 +18,7 @@ use bevy_core_pipeline::{
};
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_image::BevyDefault as _;
use bevy_light::{EnvironmentMapLight, ShadowFilteringMethod};
use bevy_render::{
extract_component::{
ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin,

View File

@ -31,7 +31,6 @@ pub mod decal;
pub mod deferred;
mod extended_material;
mod fog;
mod light;
mod light_probe;
mod lightmap;
mod material;
@ -48,12 +47,19 @@ mod volumetric_fog;
use bevy_color::{Color, LinearRgba};
pub use atmosphere::*;
use bevy_light::SimulationLightSystems;
pub use bevy_light::{
light_consts, AmbientLight, CascadeShadowConfig, CascadeShadowConfigBuilder, Cascades,
ClusteredDecal, DirectionalLight, DirectionalLightShadowMap, DirectionalLightTexture,
FogVolume, LightPlugin, LightProbe, NotShadowCaster, NotShadowReceiver, PointLight,
PointLightShadowMap, PointLightTexture, ShadowFilteringMethod, SpotLight, SpotLightTexture,
TransmittedShadowReceiver, VolumetricFog, VolumetricLight,
};
pub use cluster::*;
pub use components::*;
pub use decal::clustered::ClusteredDecalPlugin;
pub use extended_material::*;
pub use fog::*;
pub use light::*;
pub use light_probe::*;
pub use lightmap::*;
pub use material::*;
@ -65,7 +71,7 @@ pub use prepass::*;
pub use render::*;
pub use ssao::*;
pub use ssr::*;
pub use volumetric_fog::{FogVolume, VolumetricFog, VolumetricFogPlugin, VolumetricLight};
pub use volumetric_fog::VolumetricFogPlugin;
/// The PBR prelude.
///
@ -74,14 +80,17 @@ pub mod prelude {
#[doc(hidden)]
pub use crate::{
fog::{DistanceFog, FogFalloff},
light::{light_consts, AmbientLight, DirectionalLight, PointLight, SpotLight},
light_probe::{environment_map::EnvironmentMapLight, LightProbe},
material::{Material, MaterialPlugin},
mesh_material::MeshMaterial3d,
parallax::ParallaxMappingMethod,
pbr_material::StandardMaterial,
ssao::ScreenSpaceAmbientOcclusionPlugin,
};
#[doc(hidden)]
pub use bevy_light::{
light_consts, AmbientLight, DirectionalLight, EnvironmentMapLight, LightProbe, PointLight,
SpotLight,
};
}
pub mod graph {
@ -122,7 +131,6 @@ pub mod graph {
}
}
pub use crate::cascade::{CascadeShadowConfig, CascadeShadowConfigBuilder, Cascades};
use crate::{deferred::DeferredPbrLightingPlugin, graph::NodePbr};
use bevy_app::prelude::*;
use bevy_asset::{AssetApp, AssetPath, Assets, Handle};
@ -203,8 +211,6 @@ impl Plugin for PbrPlugin {
load_shader_library!(app, "meshlet/dummy_visibility_buffer_resolve.wgsl");
app.register_asset_reflect::<StandardMaterial>()
.register_type::<ClusterConfig>()
.init_resource::<GlobalVisibleClusterableObjects>()
.register_type::<DefaultOpaqueRendererMethod>()
.init_resource::<DefaultOpaqueRendererMethod>()
.add_plugins((

View File

@ -44,13 +44,10 @@
//!
//! [several pre-filtered environment maps]: https://github.com/KhronosGroup/glTF-Sample-Environments
use bevy_asset::{AssetId, Handle};
use bevy_ecs::{
component::Component, query::QueryItem, reflect::ReflectComponent, system::lifetimeless::Read,
};
use bevy_asset::AssetId;
use bevy_ecs::{query::QueryItem, system::lifetimeless::Read};
use bevy_image::Image;
use bevy_math::Quat;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_light::EnvironmentMapLight;
use bevy_render::{
extract_instances::ExtractInstance,
render_asset::RenderAssets,
@ -72,56 +69,6 @@ use crate::{
use super::{LightProbeComponent, RenderViewLightProbes};
/// A pair of cubemap textures that represent the surroundings of a specific
/// area in space.
///
/// See [`crate::environment_map`] for detailed information.
#[derive(Clone, Component, Reflect)]
#[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.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.
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 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,
}
}
}
/// Like [`EnvironmentMapLight`], but contains asset IDs instead of handles.
///
/// This is for use in the render app.

View File

@ -135,6 +135,7 @@
use bevy_ecs::{component::Component, reflect::ReflectComponent};
use bevy_image::Image;
use bevy_light::LightProbe;
use bevy_render::{
render_asset::RenderAssets,
render_resource::{
@ -155,7 +156,7 @@ use crate::{
MAX_VIEW_LIGHT_PROBES,
};
use super::{LightProbe, LightProbeComponent};
use super::LightProbeComponent;
/// On WebGL and WebGPU, we must disable irradiance volumes, as otherwise we can
/// overflow the number of texture bindings when deferred rendering is in use

View File

@ -8,15 +8,14 @@ use bevy_ecs::{
component::Component,
entity::Entity,
query::With,
reflect::ReflectComponent,
resource::Resource,
schedule::IntoScheduleConfigs,
system::{Commands, Local, Query, Res, ResMut},
};
use bevy_image::Image;
use bevy_light::{EnvironmentMapLight, LightProbe};
use bevy_math::{Affine3A, FloatOrd, Mat4, Vec3A, Vec4};
use bevy_platform::collections::HashMap;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
extract_instances::ExtractInstancesPlugin,
load_shader_library,
@ -27,7 +26,7 @@ use bevy_render::{
settings::WgpuFeatures,
sync_world::RenderEntity,
texture::{FallbackImage, GpuImage},
view::{ExtractedView, Visibility},
view::ExtractedView,
Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
};
use bevy_transform::{components::Transform, prelude::GlobalTransform};
@ -35,7 +34,7 @@ use tracing::error;
use core::{hash::Hash, ops::Deref};
use crate::light_probe::environment_map::{EnvironmentMapIds, EnvironmentMapLight};
use crate::light_probe::environment_map::EnvironmentMapIds;
use self::irradiance_volume::IrradianceVolume;
@ -59,50 +58,6 @@ const STANDARD_MATERIAL_FRAGMENT_SHADER_MIN_TEXTURE_BINDINGS: usize = 16;
/// cubemaps applied to all objects that a view renders.
pub struct LightProbePlugin;
/// 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.
///
/// When multiple sources of indirect illumination can be applied to a fragment,
/// the highest-quality one is 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.
///
/// 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, Visibility)]
pub struct LightProbe;
/// A GPU type that stores information about a light probe.
#[derive(Clone, Copy, ShaderType, Default)]
struct RenderLightProbe {
@ -302,14 +257,6 @@ pub trait LightProbeComponent: Send + Sync + Component + Sized {
) -> RenderViewLightProbes<Self>;
}
impl LightProbe {
/// Creates a new light probe component.
#[inline]
pub fn new() -> Self {
Self
}
}
/// The uniform struct extracted from [`EnvironmentMapLight`].
/// Will be available for use in the Environment Map shader.
#[derive(Component, ShaderType, Clone)]
@ -341,9 +288,7 @@ impl Plugin for LightProbePlugin {
load_shader_library!(app, "environment_map.wgsl");
load_shader_library!(app, "irradiance_volume.wgsl");
app.register_type::<LightProbe>()
.register_type::<EnvironmentMapLight>()
.register_type::<IrradianceVolume>()
app.register_type::<IrradianceVolume>()
.add_plugins(ExtractInstancesPlugin::<EnvironmentMapIds>::new());
}

View File

@ -2,13 +2,14 @@ use super::{
instance_manager::InstanceManager, pipelines::MeshletPipelines,
resource_manager::ResourceManager,
};
use crate::{environment_map::EnvironmentMapLight, irradiance_volume::IrradianceVolume, *};
use crate::{irradiance_volume::IrradianceVolume, *};
use bevy_core_pipeline::{
core_3d::Camera3d,
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
tonemapping::{DebandDither, Tonemapping},
};
use bevy_derive::{Deref, DerefMut};
use bevy_light::EnvironmentMapLight;
use bevy_platform::collections::{HashMap, HashSet};
use bevy_render::erased_render_asset::ErasedRenderAssets;
use bevy_render::{

View File

@ -1,6 +1,3 @@
use self::assign::ClusterableObjectType;
use crate::assign::calculate_cluster_factors;
use crate::cascade::{Cascade, CascadeShadowConfig, Cascades};
use crate::*;
use bevy_asset::UntypedAssetId;
pub use bevy_camera::primitives::{face_index_to_name, CubeMapFace, CUBE_MAP_FACES};
@ -14,6 +11,13 @@ use bevy_ecs::{
prelude::*,
system::lifetimeless::Read,
};
use bevy_light::cascade::Cascade;
use bevy_light::cluster::assign::{calculate_cluster_factors, ClusterableObjectType};
use bevy_light::cluster::GlobalVisibleClusterableObjects;
use bevy_light::{
spot_light_clip_from_view, spot_light_world_from_view, DirectionalLightShadowMap,
NotShadowCaster, PointLightShadowMap,
};
use bevy_math::{ops, Mat4, UVec4, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_platform::collections::{HashMap, HashSet};
use bevy_platform::hash::FixedHasher;
@ -798,7 +802,7 @@ pub fn prepare_lights(
// - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded.
point_lights.sort_by_cached_key(|(entity, _, light, _)| {
(
ClusterableObjectType::from_point_or_spot_light(light).ordering(),
point_or_spot_light_to_clusterable(light).ordering(),
*entity,
)
});
@ -2265,3 +2269,18 @@ impl ShadowPassNode {
Ok(())
}
}
/// Creates the [`ClusterableObjectType`] data for a point or spot light.
fn point_or_spot_light_to_clusterable(point_light: &ExtractedPointLight) -> ClusterableObjectType {
match point_light.spot_light_angles {
Some((_, outer_angle)) => ClusterableObjectType::SpotLight {
outer_angle,
shadows_enabled: point_light.shadows_enabled,
volumetric: point_light.volumetric,
},
None => ClusterableObjectType::PointLight {
shadows_enabled: point_light.shadows_enabled,
volumetric: point_light.volumetric,
},
}
}

View File

@ -15,6 +15,9 @@ use bevy_ecs::{
system::{lifetimeless::*, SystemParamItem, SystemState},
};
use bevy_image::{BevyDefault, ImageSampler, TextureFormatPixelInfo};
use bevy_light::{
EnvironmentMapLight, NotShadowCaster, NotShadowReceiver, TransmittedShadowReceiver,
};
use bevy_math::{Affine3, Rect, UVec2, Vec3, Vec4};
use bevy_platform::collections::{hash_map::Entry, HashMap};
use bevy_render::{
@ -52,7 +55,6 @@ use material_bind_groups::MaterialBindingId;
use tracing::{error, warn};
use self::irradiance_volume::IRRADIANCE_VOLUMES_ARE_USABLE;
use crate::environment_map::EnvironmentMapLight;
use crate::irradiance_volume::IrradianceVolume;
use crate::{
render::{

View File

@ -17,6 +17,7 @@ use bevy_ecs::{
world::{FromWorld, World},
};
use bevy_image::BevyDefault as _;
use bevy_light::EnvironmentMapLight;
use bevy_math::Vec4;
use bevy_render::{
globals::{GlobalsBuffer, GlobalsUniform},
@ -30,7 +31,6 @@ use bevy_render::{
},
};
use core::{array, num::NonZero};
use environment_map::EnvironmentMapLight;
use crate::{
decal::{

View File

@ -6,14 +6,14 @@
//! for light beams from directional lights to shine through, creating what is
//! known as *light shafts* or *god rays*.
//!
//! To add volumetric fog to a scene, add [`VolumetricFog`] to the
//! camera, and add [`VolumetricLight`] to directional lights that you wish to
//! be volumetric. [`VolumetricFog`] feature numerous settings that
//! To add volumetric fog to a scene, add [`crate::VolumetricFog`] to the
//! camera, and add [`crate::VolumetricLight`] to directional lights that you wish to
//! be volumetric. [`crate::VolumetricFog`] feature numerous settings that
//! allow you to define the accuracy of the simulation, as well as the look of
//! the fog. Currently, only interaction with directional lights that have
//! shadow maps is supported. Note that the overhead of the effect scales
//! directly with the number of directional lights in use, so apply
//! [`VolumetricLight`] sparingly for the best results.
//! [`crate::VolumetricLight`] sparingly for the best results.
//!
//! The overall algorithm, which is implemented as a postprocessing effect, is a
//! combination of the techniques described in [Scratchapixel] and [this blog
@ -30,30 +30,24 @@
//! [Henyey-Greenstein phase function]: https://www.pbr-book.org/4ed/Volume_Scattering/Phase_Functions#TheHenyeyndashGreensteinPhaseFunction
use bevy_app::{App, Plugin};
use bevy_asset::{embedded_asset, Assets, Handle};
use bevy_color::Color;
use bevy_asset::{embedded_asset, Assets};
use bevy_core_pipeline::core_3d::{
graph::{Core3d, Node3d},
prepare_core_3d_depth_textures,
};
use bevy_ecs::{
component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs as _,
};
use bevy_image::Image;
use bevy_ecs::schedule::IntoScheduleConfigs as _;
use bevy_light::FogVolume;
use bevy_math::{
primitives::{Cuboid, Plane3d},
Vec2, Vec3,
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
mesh::{Mesh, Meshable},
render_graph::{RenderGraphExt, ViewNodeRunner},
render_resource::SpecializedRenderPipelines,
sync_component::SyncComponentPlugin,
view::Visibility,
ExtractSchedule, Render, RenderApp, RenderSystems,
};
use bevy_transform::components::Transform;
use render::{
VolumetricFogNode, VolumetricFogPipeline, VolumetricFogUniformBuffer, CUBE_MESH, PLANE_MESH,
};
@ -65,127 +59,6 @@ pub mod render;
/// A plugin that implements volumetric fog.
pub struct VolumetricFogPlugin;
/// Add this component to a [`DirectionalLight`](crate::DirectionalLight) with a shadow map
/// (`shadows_enabled: true`) to make volumetric fog interact with it.
///
/// This allows the light to generate light shafts/god rays.
#[derive(Clone, Copy, Component, Default, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct VolumetricLight;
/// When placed on a [`bevy_core_pipeline::core_3d::Camera3d`], enables
/// volumetric fog and volumetric lighting, also known as light shafts or god
/// rays.
#[derive(Clone, Copy, Component, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
pub struct VolumetricFog {
/// Color of the ambient light.
///
/// This is separate from Bevy's [`AmbientLight`](crate::light::AmbientLight) because an
/// [`EnvironmentMapLight`](crate::environment_map::EnvironmentMapLight) is
/// still considered an ambient light for the purposes of volumetric fog. If you're using a
/// [`EnvironmentMapLight`](crate::environment_map::EnvironmentMapLight), for best results,
/// this should be a good approximation of the average color of the environment map.
///
/// Defaults to white.
pub ambient_color: Color,
/// The brightness of the ambient light.
///
/// If there's no [`EnvironmentMapLight`](crate::environment_map::EnvironmentMapLight),
/// set this to 0.
///
/// Defaults to 0.1.
pub ambient_intensity: f32,
/// The maximum distance to offset the ray origin randomly by, in meters.
///
/// This is intended for use with temporal antialiasing. It helps fog look
/// less blocky by varying the start position of the ray, using interleaved
/// gradient noise.
pub jitter: f32,
/// The number of raymarching steps to perform.
///
/// Higher values produce higher-quality results with less banding, but
/// reduce performance.
///
/// The default value is 64.
pub step_count: u32,
}
#[derive(Clone, Component, Debug, Reflect)]
#[reflect(Component, Default, Debug, Clone)]
#[require(Transform, Visibility)]
pub struct FogVolume {
/// The color of the fog.
///
/// Note that the fog must be lit by a [`VolumetricLight`] or ambient light
/// in order for this color to appear.
///
/// Defaults to white.
pub fog_color: Color,
/// The density of fog, which measures how dark the fog is.
///
/// The default value is 0.1.
pub density_factor: f32,
/// Optional 3D voxel density texture for the fog.
pub density_texture: Option<Handle<Image>>,
/// Configurable offset of the density texture in UVW coordinates.
///
/// This can be used to scroll a repeating density texture in a direction over time
/// to create effects like fog moving in the wind. Make sure to configure the texture
/// to use `ImageAddressMode::Repeat` if this is your intention.
///
/// Has no effect when no density texture is present.
///
/// The default value is (0, 0, 0).
pub density_texture_offset: Vec3,
/// The absorption coefficient, which measures what fraction of light is
/// absorbed by the fog at each step.
///
/// Increasing this value makes the fog darker.
///
/// The default value is 0.3.
pub absorption: f32,
/// The scattering coefficient, which measures the fraction of light that's
/// scattered toward, and away from, the viewer.
///
/// The default value is 0.3.
pub scattering: f32,
/// Measures the fraction of light that's scattered *toward* the camera, as
/// opposed to *away* from the camera.
///
/// Increasing this value makes light shafts become more prominent when the
/// camera is facing toward their source and less prominent when the camera
/// is facing away. Essentially, a high value here means the light shafts
/// will fade into view as the camera focuses on them and fade away when the
/// camera is pointing away.
///
/// The default value is 0.8.
pub scattering_asymmetry: f32,
/// Applies a nonphysical color to the light.
///
/// This can be useful for artistic purposes but is nonphysical.
///
/// The default value is white.
pub light_tint: Color,
/// Scales the light by a fixed fraction.
///
/// This can be useful for artistic purposes but is nonphysical.
///
/// The default value is 1.0, which results in no adjustment.
pub light_intensity: f32,
}
impl Plugin for VolumetricFogPlugin {
fn build(&self, app: &mut App) {
embedded_asset!(app, "volumetric_fog.wgsl");
@ -194,9 +67,6 @@ impl Plugin for VolumetricFogPlugin {
meshes.insert(&PLANE_MESH, Plane3d::new(Vec3::Z, Vec2::ONE).mesh().into());
meshes.insert(&CUBE_MESH, Cuboid::new(1.0, 1.0, 1.0).mesh().into());
app.register_type::<VolumetricFog>()
.register_type::<VolumetricLight>();
app.add_plugins(SyncComponentPlugin::<FogVolume>::default());
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
@ -238,31 +108,3 @@ impl Plugin for VolumetricFogPlugin {
);
}
}
impl Default for VolumetricFog {
fn default() -> Self {
Self {
step_count: 64,
// Matches `AmbientLight` defaults.
ambient_color: Color::WHITE,
ambient_intensity: 0.1,
jitter: 0.0,
}
}
}
impl Default for FogVolume {
fn default() -> Self {
Self {
absorption: 0.3,
scattering: 0.3,
density_factor: 0.1,
density_texture: None,
density_texture_offset: Vec3::ZERO,
scattering_asymmetry: 0.5,
fog_color: Color::WHITE,
light_tint: Color::WHITE,
light_intensity: 1.0,
}
}
}

View File

@ -77,6 +77,7 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.17.0-dev" }
bevy_image = { path = "../bevy_image", version = "0.17.0-dev" }
bevy_mesh = { path = "../bevy_mesh", version = "0.17.0-dev" }
bevy_camera = { path = "../bevy_camera", version = "0.17.0-dev" }
bevy_light = { path = "../bevy_light", version = "0.17.0-dev" }
bevy_platform = { path = "../bevy_platform", version = "0.17.0-dev", default-features = false, features = [
"std",
"serialize",

View File

@ -0,0 +1,41 @@
//! This module exists because of the orphan rule
use bevy_ecs::query::QueryItem;
use bevy_light::{cluster::ClusteredDecal, AmbientLight, ShadowFilteringMethod};
use crate::{extract_component::ExtractComponent, extract_resource::ExtractResource};
impl ExtractComponent for ClusteredDecal {
type QueryData = &'static Self;
type QueryFilter = ();
type Out = Self;
fn extract_component(item: QueryItem<Self::QueryData>) -> Option<Self::Out> {
Some(item.clone())
}
}
impl ExtractResource for AmbientLight {
type Source = Self;
fn extract_resource(source: &Self::Source) -> Self {
source.clone()
}
}
impl ExtractComponent for AmbientLight {
type QueryData = &'static Self;
type QueryFilter = ();
type Out = Self;
fn extract_component(item: QueryItem<Self::QueryData>) -> Option<Self::Out> {
Some(item.clone())
}
}
impl ExtractComponent for ShadowFilteringMethod {
type QueryData = &'static Self;
type QueryFilter = ();
type Out = Self;
fn extract_component(item: QueryItem<Self::QueryData>) -> Option<Self::Out> {
Some(*item)
}
}

View File

@ -50,6 +50,7 @@ pub mod sync_world;
pub mod texture;
pub mod view;
pub use bevy_camera::primitives;
mod extract_impls;
/// The render prelude.
///

View File

@ -6,13 +6,7 @@ use std::fmt::{self, Formatter};
use bevy::{
color::palettes::css::{SILVER, YELLOW},
input::mouse::AccumulatedMouseMotion,
pbr::{
decal::{
self,
clustered::{DirectionalLightTexture, PointLightTexture, SpotLightTexture},
},
NotShadowCaster,
},
pbr::{decal, DirectionalLightTexture, NotShadowCaster, PointLightTexture, SpotLightTexture},
prelude::*,
render::renderer::{RenderAdapter, RenderDevice},
window::SystemCursorIcon,

View File

@ -1,8 +1,8 @@
diff --git a/crates/bevy_pbr/src/cluster/mod.rs b/crates/bevy_pbr/src/cluster/mod.rs
index a8c218b44..13cc9f9c6 100644
--- a/crates/bevy_pbr/src/cluster/mod.rs
+++ b/crates/bevy_pbr/src/cluster/mod.rs
@@ -239,8 +239,8 @@ impl Default for ClusterConfig {
--- a/crates/bevy_light/src/cluster/mod.rs
+++ b/crates/bevy_light/src/cluster/mod.rs
@@ -185,8 +185,8 @@ impl Default for ClusterConfig {
// 24 depth slices, square clusters with at most 4096 total clusters
// use max light distance as clusters max `Z`-depth, first slice extends to 5.0
Self::FixedZ {