2021-12-14 03:58:23 +00:00
use std ::collections ::HashSet ;
use bevy_ecs ::prelude ::* ;
2022-07-16 00:51:12 +00:00
use bevy_math ::{ Mat4 , UVec2 , UVec3 , Vec2 , Vec3 , Vec3A , Vec3Swizzles , Vec4 , Vec4Swizzles } ;
2022-05-03 19:20:13 +00:00
use bevy_reflect ::prelude ::* ;
2021-12-14 03:58:23 +00:00
use bevy_render ::{
2023-01-25 12:35:39 +00:00
camera ::Camera ,
2021-12-14 03:58:23 +00:00
color ::Color ,
2022-05-30 18:36:03 +00:00
extract_resource ::ExtractResource ,
2023-01-25 12:35:39 +00:00
prelude ::Projection ,
primitives ::{ Aabb , CascadesFrusta , CubemapFrusta , Frustum , Plane , Sphere } ,
2022-04-07 16:16:35 +00:00
render_resource ::BufferBindingType ,
renderer ::RenderDevice ,
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
view ::{ ComputedVisibility , RenderLayers , VisibleEntities } ,
2021-12-14 03:58:23 +00:00
} ;
2022-07-16 00:51:12 +00:00
use bevy_transform ::{ components ::GlobalTransform , prelude ::Transform } ;
2023-01-25 12:35:39 +00:00
use bevy_utils ::{ tracing ::warn , HashMap } ;
2021-12-14 03:58:23 +00:00
use crate ::{
2023-01-25 12:35:39 +00:00
calculate_cluster_factors , spot_light_projection_matrix , spot_light_view_matrix ,
CascadesVisibleEntities , CubeMapFace , CubemapVisibleEntities , ViewClusterBindings ,
CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT , CUBE_MAP_FACES , MAX_UNIFORM_BUFFER_POINT_LIGHTS ,
POINT_LIGHT_NEAR_Z ,
2021-12-14 03:58:23 +00:00
} ;
2019-12-02 04:03:04 +00:00
2021-12-14 03:58:23 +00:00
/// A light that emits light in all directions from a central point.
///
/// Real-world values for `intensity` (luminous power in lumens) based on the electrical power
/// consumption of the type of real-world light are:
///
/// | Luminous Power (lumen) (i.e. the intensity member) | Incandescent non-halogen (Watts) | Incandescent halogen (Watts) | Compact fluorescent (Watts) | LED (Watts |
/// |------|-----|----|--------|-------|
/// | 200 | 25 | | 3-5 | 3 |
/// | 450 | 40 | 29 | 9-11 | 5-8 |
/// | 800 | 60 | | 13-15 | 8-12 |
/// | 1100 | 75 | 53 | 18-20 | 10-16 |
/// | 1600 | 100 | 72 | 24-28 | 14-17 |
/// | 2400 | 150 | | 30-52 | 24-30 |
/// | 3100 | 200 | | 49-75 | 32 |
/// | 4000 | 300 | | 75-100 | 40.5 |
///
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lumen_(unit)#Lighting)
2022-01-03 07:59:25 +00:00
#[ derive(Component, Debug, Clone, Copy, Reflect) ]
2022-05-03 19:20:13 +00:00
#[ reflect(Component, Default) ]
2021-04-13 02:21:24 +00:00
pub struct PointLight {
2020-03-10 06:43:40 +00:00
pub color : Color ,
2021-03-20 03:22:33 +00:00
pub intensity : f32 ,
pub range : f32 ,
2021-04-22 18:49:02 +00:00
pub radius : f32 ,
2021-12-14 03:58:23 +00:00
pub shadows_enabled : bool ,
pub shadow_depth_bias : f32 ,
/// A bias applied along the direction of the fragment's surface normal. It is scaled to the
/// shadow map's texel size so that it can be small close to the camera and gets larger further
/// away.
pub shadow_normal_bias : f32 ,
2020-02-18 03:06:12 +00:00
}
2021-04-13 02:21:24 +00:00
impl Default for PointLight {
2020-02-18 03:06:12 +00:00
fn default ( ) -> Self {
2021-04-13 02:21:24 +00:00
PointLight {
2020-03-10 06:43:40 +00:00
color : Color ::rgb ( 1.0 , 1.0 , 1.0 ) ,
2021-12-14 03:58:23 +00:00
/// Luminous power in lumens
intensity : 800.0 , // Roughly a 60W non-halogen incandescent bulb
2021-03-20 03:22:33 +00:00
range : 20.0 ,
2021-04-22 18:49:02 +00:00
radius : 0.0 ,
2021-12-14 03:58:23 +00:00
shadows_enabled : false ,
shadow_depth_bias : Self ::DEFAULT_SHADOW_DEPTH_BIAS ,
shadow_normal_bias : Self ::DEFAULT_SHADOW_NORMAL_BIAS ,
2020-02-18 03:06:12 +00:00
}
}
2019-12-02 04:03:04 +00:00
}
2021-12-14 03:58:23 +00:00
impl PointLight {
pub const DEFAULT_SHADOW_DEPTH_BIAS : f32 = 0.02 ;
pub const DEFAULT_SHADOW_NORMAL_BIAS : f32 = 0.6 ;
2019-12-02 04:03:04 +00:00
}
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
#[ derive(Resource, Clone, Debug, Reflect) ]
2022-07-04 13:04:20 +00:00
#[ reflect(Resource) ]
2021-12-14 03:58:23 +00:00
pub struct PointLightShadowMap {
pub size : usize ,
}
2021-04-22 18:49:02 +00:00
2021-12-14 03:58:23 +00:00
impl Default for PointLightShadowMap {
fn default ( ) -> Self {
Self { size : 1024 }
2019-12-02 04:03:04 +00:00
}
2020-01-11 10:11:27 +00:00
}
2020-11-15 19:34:55 +00:00
2022-07-08 19:57:43 +00:00
/// A light that emits light in a given direction from a central point.
/// Behaves like a point light in a perfectly absorbant housing that
/// shines light only in a given direction. The direction is taken from
/// the transform, and can be specified with [`Transform::looking_at`](bevy_transform::components::Transform::looking_at).
#[ derive(Component, Debug, Clone, Copy, Reflect) ]
#[ reflect(Component, Default) ]
pub struct SpotLight {
pub color : Color ,
pub intensity : f32 ,
pub range : f32 ,
pub radius : f32 ,
pub shadows_enabled : bool ,
pub shadow_depth_bias : f32 ,
/// A bias applied along the direction of the fragment's surface normal. It is scaled to the
/// shadow map's texel size so that it can be small close to the camera and gets larger further
/// away.
pub shadow_normal_bias : f32 ,
/// Angle defining the distance from the spot light direction to the outer limit
/// of the light's cone of effect.
/// `outer_angle` should be < `PI / 2.0`.
/// `PI / 2.0` defines a hemispherical spot light, but shadows become very blocky as the angle
/// approaches this limit.
pub outer_angle : f32 ,
/// Angle defining the distance from the spot light direction to the inner limit
/// of the light's cone of effect.
/// Light is attenuated from `inner_angle` to `outer_angle` to give a smooth falloff.
/// `inner_angle` should be <= `outer_angle`
pub inner_angle : f32 ,
}
impl SpotLight {
pub const DEFAULT_SHADOW_DEPTH_BIAS : f32 = 0.02 ;
pub const DEFAULT_SHADOW_NORMAL_BIAS : f32 = 0.6 ;
}
impl Default for SpotLight {
fn default ( ) -> Self {
// a quarter arc attenuating from the centre
Self {
color : Color ::rgb ( 1.0 , 1.0 , 1.0 ) ,
/// Luminous power in lumens
intensity : 800.0 , // Roughly a 60W non-halogen incandescent bulb
range : 20.0 ,
radius : 0.0 ,
shadows_enabled : false ,
shadow_depth_bias : Self ::DEFAULT_SHADOW_DEPTH_BIAS ,
shadow_normal_bias : Self ::DEFAULT_SHADOW_NORMAL_BIAS ,
inner_angle : 0.0 ,
outer_angle : std ::f32 ::consts ::FRAC_PI_4 ,
}
}
}
2021-05-14 20:37:34 +00:00
/// A Directional light.
///
/// Directional lights don't exist in reality but they are a good
/// approximation for light sources VERY far away, like the sun or
/// the moon.
///
2022-08-02 18:13:21 +00:00
/// The light shines along the forward direction of the entity's transform. With a default transform
/// this would be along the negative-Z axis.
///
2021-05-14 20:37:34 +00:00
/// Valid values for `illuminance` are:
///
/// | Illuminance (lux) | Surfaces illuminated by |
/// |-------------------|------------------------------------------------|
/// | 0.0001 | Moonless, overcast night sky (starlight) |
/// | 0.002 | Moonless clear night sky with airglow |
/// | 0.05– 0.3 | Full moon on a clear night |
/// | 3.4 | Dark limit of civil twilight under a clear sky |
/// | 20– 50 | Public areas with dark surroundings |
/// | 50 | Family living room lights |
/// | 80 | Office building hallway/toilet lighting |
/// | 100 | Very dark overcast day |
/// | 150 | Train station platforms |
/// | 320– 500 | Office lighting |
/// | 400 | Sunrise or sunset on a clear day. |
/// | 1000 | Overcast day; typical TV studio lighting |
/// | 10,000– 25,000 | Full daylight (not direct sun) |
/// | 32,000– 100,000 | Direct sunlight |
///
/// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux)
Take DirectionalLight's GlobalTransform into account when calculating shadow map volume (not just direction) (#6384)
# Objective
This PR fixes #5789, by enabling movable (and scalable) directional light shadow volumes.
## Solution
This PR changes `ExtractedDirectionalLight` to hold a copy of the `DirectionalLight` entity's `GlobalTransform`, instead of just a `direction` vector. This allows the shadow map volume (as defined by the light's `shadow_projection` field) to be transformed honoring translation _and_ scale transforms, and not just rotation.
It also augments the texel size calculation (used to determine the `shadow_normal_bias`) so that it now takes into account the upper bound of the x/y/z scale of the `GlobalTransform`.
This change makes the directional light extraction code more consistent with point and spot lights (that already use `transform`), and allows easily moving and scaling the shadow volume along with a player entity based on camera distance/angle, immediately enabling more real world use cases until we have a more sophisticated adaptive implementation, such as the one described in #3629.
**Note:** While it was previously possible to update the projection achieving a similar effect, depending on the light direction and distance to the origin, the fact that the shadow map camera was always positioned at the origin with a hardcoded `Vec3::Y` up value meant you would get sub-optimal or inconsistent/incorrect results.
---
## Changelog
### Changed
- `DirectionalLight` shadow volumes now honor translation and scale transforms
## Migration Guide
- If your directional lights were positioned at the origin and not scaled (the default, most common scenario) no changes are needed on your part; it just works as before;
- If you previously had a system for dynamically updating directional light shadow projections, you might now be able to simplify your code by updating the directional light entity's transform instead;
- In the unlikely scenario that a scene with directional lights that previously rendered shadows correctly has missing shadows, make sure your directional lights are positioned at (0, 0, 0) and are not scaled to a size that's too large or too small.
2022-11-04 20:12:26 +00:00
///
/// ## Shadows
///
/// To enable shadows, set the `shadows_enabled` property to `true`.
///
2023-01-25 12:35:39 +00:00
/// Shadows are produced via [cascaded shadow maps](https://developer.download.nvidia.com/SDK/10.5/opengl/src/cascaded_shadow_maps/doc/cascaded_shadow_maps.pdf).
Take DirectionalLight's GlobalTransform into account when calculating shadow map volume (not just direction) (#6384)
# Objective
This PR fixes #5789, by enabling movable (and scalable) directional light shadow volumes.
## Solution
This PR changes `ExtractedDirectionalLight` to hold a copy of the `DirectionalLight` entity's `GlobalTransform`, instead of just a `direction` vector. This allows the shadow map volume (as defined by the light's `shadow_projection` field) to be transformed honoring translation _and_ scale transforms, and not just rotation.
It also augments the texel size calculation (used to determine the `shadow_normal_bias`) so that it now takes into account the upper bound of the x/y/z scale of the `GlobalTransform`.
This change makes the directional light extraction code more consistent with point and spot lights (that already use `transform`), and allows easily moving and scaling the shadow volume along with a player entity based on camera distance/angle, immediately enabling more real world use cases until we have a more sophisticated adaptive implementation, such as the one described in #3629.
**Note:** While it was previously possible to update the projection achieving a similar effect, depending on the light direction and distance to the origin, the fact that the shadow map camera was always positioned at the origin with a hardcoded `Vec3::Y` up value meant you would get sub-optimal or inconsistent/incorrect results.
---
## Changelog
### Changed
- `DirectionalLight` shadow volumes now honor translation and scale transforms
## Migration Guide
- If your directional lights were positioned at the origin and not scaled (the default, most common scenario) no changes are needed on your part; it just works as before;
- If you previously had a system for dynamically updating directional light shadow projections, you might now be able to simplify your code by updating the directional light entity's transform instead;
- In the unlikely scenario that a scene with directional lights that previously rendered shadows correctly has missing shadows, make sure your directional lights are positioned at (0, 0, 0) and are not scaled to a size that's too large or too small.
2022-11-04 20:12:26 +00:00
///
2023-01-25 12:35:39 +00:00
/// To modify the cascade set up, such as the number of cascades or the maximum shadow distance,
/// change the [`CascadeShadowConfig`] component of the [`crate::bundle::DirectionalLightBundle`].
Take DirectionalLight's GlobalTransform into account when calculating shadow map volume (not just direction) (#6384)
# Objective
This PR fixes #5789, by enabling movable (and scalable) directional light shadow volumes.
## Solution
This PR changes `ExtractedDirectionalLight` to hold a copy of the `DirectionalLight` entity's `GlobalTransform`, instead of just a `direction` vector. This allows the shadow map volume (as defined by the light's `shadow_projection` field) to be transformed honoring translation _and_ scale transforms, and not just rotation.
It also augments the texel size calculation (used to determine the `shadow_normal_bias`) so that it now takes into account the upper bound of the x/y/z scale of the `GlobalTransform`.
This change makes the directional light extraction code more consistent with point and spot lights (that already use `transform`), and allows easily moving and scaling the shadow volume along with a player entity based on camera distance/angle, immediately enabling more real world use cases until we have a more sophisticated adaptive implementation, such as the one described in #3629.
**Note:** While it was previously possible to update the projection achieving a similar effect, depending on the light direction and distance to the origin, the fact that the shadow map camera was always positioned at the origin with a hardcoded `Vec3::Y` up value meant you would get sub-optimal or inconsistent/incorrect results.
---
## Changelog
### Changed
- `DirectionalLight` shadow volumes now honor translation and scale transforms
## Migration Guide
- If your directional lights were positioned at the origin and not scaled (the default, most common scenario) no changes are needed on your part; it just works as before;
- If you previously had a system for dynamically updating directional light shadow projections, you might now be able to simplify your code by updating the directional light entity's transform instead;
- In the unlikely scenario that a scene with directional lights that previously rendered shadows correctly has missing shadows, make sure your directional lights are positioned at (0, 0, 0) and are not scaled to a size that's too large or too small.
2022-11-04 20:12:26 +00:00
///
/// To control the resolution of the shadow maps, use the [`DirectionalLightShadowMap`] resource:
///
/// ```
/// # use bevy_app::prelude::*;
/// # use bevy_pbr::DirectionalLightShadowMap;
/// App::new()
/// .insert_resource(DirectionalLightShadowMap { size: 2048 });
/// ```
2022-01-03 07:59:25 +00:00
#[ derive(Component, Debug, Clone, Reflect) ]
2022-05-03 19:20:13 +00:00
#[ reflect(Component, Default) ]
2021-05-14 20:37:34 +00:00
pub struct DirectionalLight {
pub color : Color ,
2021-12-14 03:58:23 +00:00
/// Illuminance in lux
2021-05-14 20:37:34 +00:00
pub illuminance : f32 ,
2021-12-14 03:58:23 +00:00
pub shadows_enabled : bool ,
pub shadow_depth_bias : f32 ,
/// A bias applied along the direction of the fragment's surface normal. It is scaled to the
/// shadow map's texel size so that it is automatically adjusted to the orthographic projection.
pub shadow_normal_bias : f32 ,
2021-05-14 20:37:34 +00:00
}
impl Default for DirectionalLight {
fn default ( ) -> Self {
DirectionalLight {
color : Color ::rgb ( 1.0 , 1.0 , 1.0 ) ,
illuminance : 100000.0 ,
2021-12-14 03:58:23 +00:00
shadows_enabled : false ,
shadow_depth_bias : Self ::DEFAULT_SHADOW_DEPTH_BIAS ,
shadow_normal_bias : Self ::DEFAULT_SHADOW_NORMAL_BIAS ,
2021-05-14 20:37:34 +00:00
}
}
}
2021-12-14 03:58:23 +00:00
impl DirectionalLight {
pub const DEFAULT_SHADOW_DEPTH_BIAS : f32 = 0.02 ;
pub const DEFAULT_SHADOW_NORMAL_BIAS : f32 = 0.6 ;
2021-05-14 20:37:34 +00:00
}
Take DirectionalLight's GlobalTransform into account when calculating shadow map volume (not just direction) (#6384)
# Objective
This PR fixes #5789, by enabling movable (and scalable) directional light shadow volumes.
## Solution
This PR changes `ExtractedDirectionalLight` to hold a copy of the `DirectionalLight` entity's `GlobalTransform`, instead of just a `direction` vector. This allows the shadow map volume (as defined by the light's `shadow_projection` field) to be transformed honoring translation _and_ scale transforms, and not just rotation.
It also augments the texel size calculation (used to determine the `shadow_normal_bias`) so that it now takes into account the upper bound of the x/y/z scale of the `GlobalTransform`.
This change makes the directional light extraction code more consistent with point and spot lights (that already use `transform`), and allows easily moving and scaling the shadow volume along with a player entity based on camera distance/angle, immediately enabling more real world use cases until we have a more sophisticated adaptive implementation, such as the one described in #3629.
**Note:** While it was previously possible to update the projection achieving a similar effect, depending on the light direction and distance to the origin, the fact that the shadow map camera was always positioned at the origin with a hardcoded `Vec3::Y` up value meant you would get sub-optimal or inconsistent/incorrect results.
---
## Changelog
### Changed
- `DirectionalLight` shadow volumes now honor translation and scale transforms
## Migration Guide
- If your directional lights were positioned at the origin and not scaled (the default, most common scenario) no changes are needed on your part; it just works as before;
- If you previously had a system for dynamically updating directional light shadow projections, you might now be able to simplify your code by updating the directional light entity's transform instead;
- In the unlikely scenario that a scene with directional lights that previously rendered shadows correctly has missing shadows, make sure your directional lights are positioned at (0, 0, 0) and are not scaled to a size that's too large or too small.
2022-11-04 20:12:26 +00:00
/// Controls the resolution of [`DirectionalLight`] shadow maps.
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
#[ derive(Resource, Clone, Debug, Reflect) ]
2022-07-04 13:04:20 +00:00
#[ reflect(Resource) ]
2021-12-14 03:58:23 +00:00
pub struct DirectionalLightShadowMap {
pub size : usize ,
}
2021-05-14 20:37:34 +00:00
2021-12-14 03:58:23 +00:00
impl Default for DirectionalLightShadowMap {
fn default ( ) -> Self {
2021-12-22 20:59:48 +00:00
#[ cfg(feature = " webgl " ) ]
2023-01-25 12:35:39 +00:00
return Self { size : 1024 } ;
2021-12-22 20:59:48 +00:00
#[ cfg(not(feature = " webgl " )) ]
2023-01-25 12:35:39 +00:00
return Self { size : 2048 } ;
}
}
/// Controls how cascaded shadow mapping works.
#[ derive(Component, Clone, Debug, Reflect) ]
#[ reflect(Component) ]
pub struct CascadeShadowConfig {
/// The (positive) distance to the far boundary of each cascade.
pub bounds : Vec < f32 > ,
/// The proportion of overlap each cascade has with the previous cascade.
pub overlap_proportion : f32 ,
}
impl Default for CascadeShadowConfig {
fn default ( ) -> Self {
if cfg! ( feature = " webgl " ) {
// Currently only support one cascade in webgl.
Self ::new ( 1 , 5.0 , 100.0 , 0.2 )
} else {
Self ::new ( 4 , 5.0 , 1000.0 , 0.2 )
}
}
}
fn calculate_cascade_bounds (
num_cascades : usize ,
nearest_bound : f32 ,
shadow_maximum_distance : f32 ,
) -> Vec < f32 > {
if num_cascades = = 1 {
return vec! [ shadow_maximum_distance ] ;
}
let base = ( shadow_maximum_distance / nearest_bound ) . powf ( 1.0 / ( num_cascades - 1 ) as f32 ) ;
( 0 .. num_cascades )
. map ( | i | nearest_bound * base . powf ( i as f32 ) )
. collect ( )
}
impl CascadeShadowConfig {
/// Returns a cascade config for `num_cascades` cascades, with the first cascade
/// having far bound `nearest_bound` and the last cascade having far bound `shadow_maximum_distance`.
/// In-between cascades will be exponentially spaced.
pub fn new (
num_cascades : usize ,
nearest_bound : f32 ,
shadow_maximum_distance : f32 ,
overlap_proportion : f32 ,
) -> Self {
assert! (
num_cascades > 0 ,
2023-01-26 17:34:52 +00:00
" num_cascades must be positive, but was {num_cascades} " ,
2023-01-25 12:35:39 +00:00
) ;
assert! (
( 0. 0 .. 1.0 ) . contains ( & overlap_proportion ) ,
2023-01-26 17:34:52 +00:00
" overlap_proportion must be in [0.0, 1.0) but was {overlap_proportion} " ,
2023-01-25 12:35:39 +00:00
) ;
Self {
bounds : calculate_cascade_bounds ( num_cascades , nearest_bound , shadow_maximum_distance ) ,
overlap_proportion ,
}
}
}
#[ derive(Component, Clone, Debug, Default, Reflect) ]
#[ reflect(Component) ]
pub struct Cascades {
/// Map from a view to the configuration of each of its [`Cascade`]s.
pub ( crate ) cascades : HashMap < Entity , Vec < Cascade > > ,
}
#[ derive(Clone, Debug, Default, Reflect, FromReflect) ]
pub struct Cascade {
/// The transform of the light, i.e. the view to world matrix.
pub ( crate ) view_transform : Mat4 ,
/// The orthographic projection for this cascade.
pub ( crate ) projection : Mat4 ,
/// The view-projection matrix for this cacade, converting world space into light clip space.
/// Importantly, this is derived and stored separately from `view_transform` and `projection` to
/// ensure shadow stability.
pub ( crate ) view_projection : Mat4 ,
/// Size of each shadow map texel in world units.
pub ( crate ) texel_size : f32 ,
}
pub fn update_directional_light_cascades (
directional_light_shadow_map : Res < DirectionalLightShadowMap > ,
views : Query < ( Entity , & GlobalTransform , & Projection , & Camera ) > ,
mut lights : Query < (
& GlobalTransform ,
& DirectionalLight ,
& CascadeShadowConfig ,
& mut Cascades ,
) > ,
) {
let views = views
. iter ( )
. filter_map ( | view | match view {
// TODO: orthographic camera projection support.
( entity , transform , Projection ::Perspective ( projection ) , camera )
if camera . is_active = >
{
Some ( (
entity ,
projection . aspect_ratio ,
( 0.5 * projection . fov ) . tan ( ) ,
transform . compute_matrix ( ) ,
) )
}
_ = > None ,
} )
. collect ::< Vec < _ > > ( ) ;
for ( transform , directional_light , cascades_config , mut cascades ) in lights . iter_mut ( ) {
if ! directional_light . shadows_enabled {
continue ;
}
// It is very important to the numerical and thus visual stability of shadows that
// light_to_world has orthogonal upper-left 3x3 and zero translation.
// Even though only the direction (i.e. rotation) of the light matters, we don't constrain
// users to not change any other aspects of the transform - there's no guarantee
// `transform.compute_matrix()` will give us a matrix with our desired properties.
// Instead, we directly create a good matrix from just the rotation.
let light_to_world = Mat4 ::from_quat ( transform . compute_transform ( ) . rotation ) ;
let light_to_world_inverse = light_to_world . inverse ( ) ;
cascades . cascades . clear ( ) ;
for ( view_entity , aspect_ratio , tan_half_fov , view_to_world ) in views . iter ( ) . copied ( ) {
let camera_to_light_view = light_to_world_inverse * view_to_world ;
let view_cascades = cascades_config
. bounds
. iter ( )
. enumerate ( )
. map ( | ( idx , far_bound ) | {
calculate_cascade (
aspect_ratio ,
tan_half_fov ,
directional_light_shadow_map . size as f32 ,
light_to_world ,
camera_to_light_view ,
// Negate bounds as -z is camera forward direction.
if idx > 0 {
( 1.0 - cascades_config . overlap_proportion )
* - cascades_config . bounds [ idx - 1 ]
} else {
0.0
} ,
- far_bound ,
)
} )
. collect ( ) ;
cascades . cascades . insert ( view_entity , view_cascades ) ;
}
}
}
fn calculate_cascade (
aspect_ratio : f32 ,
tan_half_fov : f32 ,
cascade_texture_size : f32 ,
light_to_world : Mat4 ,
camera_to_light : Mat4 ,
z_near : f32 ,
z_far : f32 ,
) -> Cascade {
2023-01-26 17:34:52 +00:00
debug_assert! ( z_near < = 0.0 , " z_near {z_near} must be <= 0.0 " ) ;
debug_assert! ( z_far < = 0.0 , " z_far {z_far} must be <= 0.0 " ) ;
2023-01-25 12:35:39 +00:00
// NOTE: This whole function is very sensitive to floating point precision and instability and
// has followed instructions to avoid view dependence from the section on cascade shadow maps in
// Eric Lengyel's Foundations of Game Engine Development 2: Rendering. Be very careful when
// modifying this code!
let a = z_near . abs ( ) * tan_half_fov ;
let b = z_far . abs ( ) * tan_half_fov ;
// NOTE: These vertices are in a specific order: bottom right, top right, top left, bottom left
// for near then for far
let frustum_corners = [
Vec3A ::new ( a * aspect_ratio , - a , z_near ) ,
Vec3A ::new ( a * aspect_ratio , a , z_near ) ,
Vec3A ::new ( - a * aspect_ratio , a , z_near ) ,
Vec3A ::new ( - a * aspect_ratio , - a , z_near ) ,
Vec3A ::new ( b * aspect_ratio , - b , z_far ) ,
Vec3A ::new ( b * aspect_ratio , b , z_far ) ,
Vec3A ::new ( - b * aspect_ratio , b , z_far ) ,
Vec3A ::new ( - b * aspect_ratio , - b , z_far ) ,
] ;
let mut min = Vec3A ::splat ( f32 ::MAX ) ;
let mut max = Vec3A ::splat ( f32 ::MIN ) ;
for corner_camera_view in frustum_corners {
let corner_light_view = camera_to_light . transform_point3a ( corner_camera_view ) ;
min = min . min ( corner_light_view ) ;
max = max . max ( corner_light_view ) ;
}
// NOTE: Use the larger of the frustum slice far plane diagonal and body diagonal lengths as this
// will be the maximum possible projection size. Use the ceiling to get an integer which is
// very important for floating point stability later. It is also important that these are
// calculated using the original camera space corner positions for floating point precision
// as even though the lengths using corner_light_view above should be the same, precision can
// introduce small but significant differences.
// NOTE: The size remains the same unless the view frustum or cascade configuration is modified.
let cascade_diameter = ( frustum_corners [ 0 ] - frustum_corners [ 6 ] )
. length ( )
. max ( ( frustum_corners [ 4 ] - frustum_corners [ 6 ] ) . length ( ) )
. ceil ( ) ;
// NOTE: If we ensure that cascade_texture_size is a power of 2, then as we made cascade_diameter an
// integer, cascade_texel_size is then an integer multiple of a power of 2 and can be
// exactly represented in a floating point value.
let cascade_texel_size = cascade_diameter / cascade_texture_size ;
// NOTE: For shadow stability it is very important that the near_plane_center is at integer
// multiples of the texel size to be exactly representable in a floating point value.
let near_plane_center = Vec3A ::new (
( 0.5 * ( min . x + max . x ) / cascade_texel_size ) . floor ( ) * cascade_texel_size ,
( 0.5 * ( min . y + max . y ) / cascade_texel_size ) . floor ( ) * cascade_texel_size ,
// NOTE: max.z is the near plane for right-handed y-up
max . z ,
) ;
// It is critical for `world_to_cascade` to be stable. So rather than forming `cascade_to_world`
// and inverting it, which risks instability due to numerical precision, we directly form
// `world_to_cascde` as the reference material suggests.
let light_to_world_transpose = light_to_world . transpose ( ) ;
let world_to_cascade = Mat4 ::from_cols (
light_to_world_transpose . x_axis ,
light_to_world_transpose . y_axis ,
light_to_world_transpose . z_axis ,
( - near_plane_center ) . extend ( 1.0 ) ,
) ;
// Right-handed orthographic projection, centered at `near_plane_center`.
// NOTE: This is different from the reference material, as we use reverse Z.
let r = ( max . z - min . z ) . recip ( ) ;
let cascade_projection = Mat4 ::from_cols (
Vec4 ::new ( 2.0 / cascade_diameter , 0.0 , 0.0 , 0.0 ) ,
Vec4 ::new ( 0.0 , 2.0 / cascade_diameter , 0.0 , 0.0 ) ,
Vec4 ::new ( 0.0 , 0.0 , r , 0.0 ) ,
Vec4 ::new ( 0.0 , 0.0 , 1.0 , 1.0 ) ,
) ;
let cascade_view_projection = cascade_projection * world_to_cascade ;
Cascade {
view_transform : world_to_cascade . inverse ( ) ,
projection : cascade_projection ,
view_projection : cascade_view_projection ,
texel_size : cascade_texel_size ,
2021-05-14 20:37:34 +00:00
}
}
2021-12-14 03:58:23 +00:00
/// An ambient light, which lights the entire scene equally.
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
#[ derive(Resource, Clone, Debug, ExtractResource, Reflect) ]
2022-07-04 13:04:20 +00:00
#[ reflect(Resource) ]
2020-11-15 19:34:55 +00:00
pub struct AmbientLight {
pub color : Color ,
2021-12-14 03:58:23 +00:00
/// A direct scale factor multiplied with `color` before being passed to the shader.
2021-03-12 18:59:24 +00:00
pub brightness : f32 ,
2020-11-15 19:34:55 +00:00
}
impl Default for AmbientLight {
fn default ( ) -> Self {
Self {
2021-03-12 18:59:24 +00:00
color : Color ::rgb ( 1.0 , 1.0 , 1.0 ) ,
brightness : 0.05 ,
2020-11-15 19:34:55 +00:00
}
}
}
2021-12-14 03:58:23 +00:00
/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not cast shadows.
2022-05-03 19:20:13 +00:00
#[ derive(Component, Reflect, Default) ]
#[ reflect(Component, Default) ]
2021-12-14 03:58:23 +00:00
pub struct NotShadowCaster ;
/// Add this component to make a [`Mesh`](bevy_render::mesh::Mesh) not receive shadows.
2022-05-03 19:20:13 +00:00
#[ derive(Component, Reflect, Default) ]
#[ reflect(Component, Default) ]
2021-12-14 03:58:23 +00:00
pub struct NotShadowReceiver ;
#[ derive(Debug, Hash, PartialEq, Eq, Clone, SystemLabel) ]
pub enum SimulationLightSystems {
AddClusters ,
AssignLightsToClusters ,
2023-01-25 12:35:39 +00:00
UpdateDirectionalLightCascades ,
2022-07-08 19:57:43 +00:00
UpdateLightFrusta ,
2021-12-14 03:58:23 +00:00
CheckLightVisibility ,
}
// Clustered-forward rendering notes
// The main initial reference material used was this rather accessible article:
// http://www.aortiz.me/2018/12/21/CG.html
// Some inspiration was taken from “Practical Clustered Shading” which is part 2 of:
// https://efficientshading.com/2015/01/01/real-time-many-light-management-and-shadows-with-clustered-shading/
// (Also note that Part 3 of the above shows how we could support the shadow mapping for many lights.)
2023-01-06 00:43:30 +00:00
// The z-slicing method mentioned in the aortiz article is originally from Tiago Sousa's Siggraph 2016 talk about Doom 2016:
2021-12-14 03:58:23 +00:00
// http://advances.realtimerendering.com/s2016/Siggraph2016_idTech6.pdf
2022-03-08 04:56:42 +00:00
/// Configure the far z-plane mode used for the furthest depth slice for clustered forward
/// rendering
2022-11-07 19:44:17 +00:00
#[ derive(Debug, Copy, Clone, Reflect, FromReflect) ]
2022-03-08 04:56:42 +00:00
pub enum ClusterFarZMode {
/// Calculate the required maximum z-depth based on currently visible lights.
/// Makes better use of available clusters, speeding up GPU lighting operations
/// at the expense of some CPU time and using more indices in the cluster light
/// index lists.
MaxLightRange ,
/// Constant max z-depth
Constant ( f32 ) ,
}
/// Configure the depth-slicing strategy for clustered forward rendering
2022-11-07 19:44:17 +00:00
#[ derive(Debug, Copy, Clone, Reflect, FromReflect) ]
#[ reflect(Default) ]
2022-03-08 04:56:42 +00:00
pub struct ClusterZConfig {
2022-07-12 13:06:16 +00:00
/// Far `Z` plane of the first depth slice
2022-03-08 04:56:42 +00:00
pub first_slice_depth : f32 ,
2022-07-12 13:06:16 +00:00
/// Strategy for how to evaluate the far `Z` plane of the furthest depth slice
2022-03-08 04:56:42 +00:00
pub far_z_mode : ClusterFarZMode ,
}
impl Default for ClusterZConfig {
fn default ( ) -> Self {
Self {
first_slice_depth : 5.0 ,
far_z_mode : ClusterFarZMode ::MaxLightRange ,
}
}
}
/// Configuration of the clustering strategy for clustered forward rendering
2022-11-07 19:44:17 +00:00
#[ derive(Debug, Copy, Clone, Component, Reflect) ]
#[ reflect(Component) ]
2022-03-08 04:56:42 +00:00
pub enum ClusterConfig {
/// Disable light cluster calculations for this view
None ,
/// One single cluster. Optimal for low-light complexity scenes or scenes where
/// most lights affect the entire scene.
Single ,
2022-07-12 13:06:16 +00:00
/// Explicit `X`, `Y` and `Z` counts (may yield non-square `X/Y` clusters depending on the aspect ratio)
2022-03-08 04:56:42 +00:00
XYZ {
dimensions : UVec3 ,
z_config : ClusterZConfig ,
2022-07-12 13:06:16 +00:00
/// Specify if clusters should automatically resize in `X/Y` if there is a risk of exceeding
2022-03-08 04:56:42 +00:00
/// the available cluster-light index limit
dynamic_resizing : bool ,
} ,
2022-07-12 13:06:16 +00:00
/// Fixed number of `Z` slices, `X` and `Y` calculated to give square clusters
2022-03-08 04:56:42 +00:00
/// with at most total clusters. For top-down games where lights will generally always be within a
2022-07-12 13:06:16 +00:00
/// short depth range, it may be useful to use this configuration with 1 or few `Z` slices. This
2022-03-08 04:56:42 +00:00
/// would reduce the number of lights per cluster by distributing more clusters in screen space
2022-07-12 13:06:16 +00:00
/// `X/Y` which matches how lights are distributed in the scene.
2022-03-08 04:56:42 +00:00
FixedZ {
total : u32 ,
z_slices : u32 ,
z_config : ClusterZConfig ,
2022-07-12 13:06:16 +00:00
/// Specify if clusters should automatically resize in `X/Y` if there is a risk of exceeding
2022-03-08 04:56:42 +00:00
/// the available cluster-light index limit
dynamic_resizing : bool ,
} ,
}
impl Default for ClusterConfig {
fn default ( ) -> Self {
// 24 depth slices, square clusters with at most 4096 total clusters
2022-07-12 13:06:16 +00:00
// use max light distance as clusters max `Z`-depth, first slice extends to 5.0
2022-03-08 04:56:42 +00:00
Self ::FixedZ {
total : 4096 ,
z_slices : 24 ,
z_config : ClusterZConfig ::default ( ) ,
dynamic_resizing : true ,
}
}
}
impl ClusterConfig {
fn dimensions_for_screen_size ( & self , screen_size : UVec2 ) -> UVec3 {
match & self {
ClusterConfig ::None = > UVec3 ::ZERO ,
ClusterConfig ::Single = > UVec3 ::ONE ,
ClusterConfig ::XYZ { dimensions , .. } = > * dimensions ,
ClusterConfig ::FixedZ {
total , z_slices , ..
} = > {
let aspect_ratio = screen_size . x as f32 / screen_size . y as f32 ;
2022-03-10 01:14:21 +00:00
let mut z_slices = * z_slices ;
if * total < z_slices {
warn! ( " ClusterConfig has more z-slices than total clusters! " ) ;
z_slices = * total ;
}
let per_layer = * total as f32 / z_slices as f32 ;
2022-03-08 04:56:42 +00:00
let y = f32 ::sqrt ( per_layer / aspect_ratio ) ;
2022-03-10 01:14:21 +00:00
let mut x = ( y * aspect_ratio ) as u32 ;
let mut y = y as u32 ;
// check extremes
if x = = 0 {
x = 1 ;
y = per_layer as u32 ;
}
if y = = 0 {
x = per_layer as u32 ;
y = 1 ;
}
UVec3 ::new ( x , y , z_slices )
2022-03-08 04:56:42 +00:00
}
}
}
fn first_slice_depth ( & self ) -> f32 {
match self {
2022-05-31 01:38:07 +00:00
ClusterConfig ::None | ClusterConfig ::Single = > 0.0 ,
2022-03-08 04:56:42 +00:00
ClusterConfig ::XYZ { z_config , .. } | ClusterConfig ::FixedZ { z_config , .. } = > {
z_config . first_slice_depth
}
}
}
fn far_z_mode ( & self ) -> ClusterFarZMode {
match self {
ClusterConfig ::None = > ClusterFarZMode ::Constant ( 0.0 ) ,
2022-03-24 00:20:27 +00:00
ClusterConfig ::Single = > ClusterFarZMode ::MaxLightRange ,
2022-03-08 04:56:42 +00:00
ClusterConfig ::XYZ { z_config , .. } | ClusterConfig ::FixedZ { z_config , .. } = > {
z_config . far_z_mode
}
}
}
fn dynamic_resizing ( & self ) -> bool {
match self {
ClusterConfig ::None | ClusterConfig ::Single = > false ,
ClusterConfig ::XYZ {
dynamic_resizing , ..
}
| ClusterConfig ::FixedZ {
dynamic_resizing , ..
} = > * dynamic_resizing ,
}
}
}
2022-03-24 00:20:27 +00:00
#[ derive(Component, Debug, Default) ]
2021-12-14 03:58:23 +00:00
pub struct Clusters {
/// Tile size
pub ( crate ) tile_size : UVec2 ,
2022-07-12 13:06:16 +00:00
/// Number of clusters in `X` / `Y` / `Z` in the view frustum
2022-03-24 00:20:27 +00:00
pub ( crate ) dimensions : UVec3 ,
2022-01-07 21:25:59 +00:00
/// 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 ,
2022-03-08 04:56:42 +00:00
pub ( crate ) far : f32 ,
2021-12-14 03:58:23 +00:00
pub ( crate ) lights : Vec < VisiblePointLights > ,
}
impl Clusters {
2022-03-24 00:20:27 +00:00
fn update ( & mut self , screen_size : UVec2 , requested_dimensions : UVec3 ) {
debug_assert! (
requested_dimensions . x > 0 & & requested_dimensions . y > 0 & & requested_dimensions . z > 0
) ;
2021-12-14 03:58:23 +00:00
2022-03-24 00:20:27 +00:00
let tile_size = ( screen_size . as_vec2 ( ) / requested_dimensions . xy ( ) . as_vec2 ( ) )
. ceil ( )
. as_uvec2 ( )
. max ( UVec2 ::ONE ) ;
2021-12-14 03:58:23 +00:00
self . tile_size = tile_size ;
2022-03-24 00:20:27 +00:00
self . dimensions = ( screen_size . as_vec2 ( ) / tile_size . as_vec2 ( ) )
2022-03-10 01:14:21 +00:00
. ceil ( )
. as_uvec2 ( )
2022-03-24 00:20:27 +00:00
. extend ( requested_dimensions . z )
. max ( UVec3 ::ONE ) ;
2022-01-07 21:25:59 +00:00
// NOTE: Maximum 4096 clusters due to uniform buffer size constraints
2022-03-24 00:20:27 +00:00
debug_assert! ( self . dimensions . x * self . dimensions . y * self . dimensions . z < = 4096 ) ;
2021-12-14 03:58:23 +00:00
}
2022-04-15 07:32:21 +00:00
fn clear ( & mut self ) {
self . tile_size = UVec2 ::ONE ;
2022-04-15 11:22:48 +00:00
self . dimensions = UVec3 ::ZERO ;
2022-04-15 07:32:21 +00:00
self . near = 0.0 ;
self . far = 0.0 ;
self . lights . clear ( ) ;
}
2021-12-14 03:58:23 +00:00
}
fn clip_to_view ( inverse_projection : Mat4 , clip : Vec4 ) -> Vec4 {
let view = inverse_projection * clip ;
view / view . w
}
pub fn add_clusters (
mut commands : Commands ,
2022-03-08 04:56:42 +00:00
cameras : Query < ( Entity , Option < & ClusterConfig > ) , ( With < Camera > , Without < Clusters > ) > ,
2021-12-14 03:58:23 +00:00
) {
2022-07-11 15:28:50 +00:00
for ( entity , config ) in & cameras {
2022-03-08 04:56:42 +00:00
let config = config . copied ( ) . unwrap_or_default ( ) ;
// actual settings here don't matter - they will be overwritten in assign_lights_to_clusters
2022-03-24 00:20:27 +00:00
commands
. entity ( entity )
2022-09-21 21:47:53 +00:00
. insert ( ( Clusters ::default ( ) , config ) ) ;
2021-12-14 03:58:23 +00:00
}
}
#[ derive(Clone, Component, Debug, Default) ]
pub struct VisiblePointLights {
2022-03-24 00:20:27 +00:00
pub ( crate ) entities : Vec < Entity > ,
2022-07-08 19:57:43 +00:00
pub point_light_count : usize ,
pub spot_light_count : usize ,
2021-12-14 03:58:23 +00:00
}
impl VisiblePointLights {
2022-03-24 00:20:27 +00:00
#[ inline ]
2021-12-14 03:58:23 +00:00
pub fn iter ( & self ) -> impl DoubleEndedIterator < Item = & Entity > {
self . entities . iter ( )
}
2022-03-24 00:20:27 +00:00
#[ inline ]
2021-12-14 03:58:23 +00:00
pub fn len ( & self ) -> usize {
self . entities . len ( )
}
2022-03-24 00:20:27 +00:00
#[ inline ]
2021-12-14 03:58:23 +00:00
pub fn is_empty ( & self ) -> bool {
self . entities . is_empty ( )
}
}
2022-04-15 02:53:20 +00:00
// NOTE: Keep in sync with bevy_pbr/src/render/pbr.wgsl
2022-01-07 21:25:59 +00:00
fn view_z_to_z_slice (
cluster_factors : Vec2 ,
2022-04-15 02:53:20 +00:00
z_slices : u32 ,
2022-01-07 21:25:59 +00:00
view_z : f32 ,
is_orthographic : bool ,
) -> u32 {
2022-04-15 02:53:20 +00:00
let z_slice = if is_orthographic {
2021-12-14 23:42:35 +00:00
// NOTE: view_z is correct in the orthographic case
( ( view_z - cluster_factors . x ) * cluster_factors . y ) . floor ( ) as u32
} else {
// NOTE: had to use -view_z to make it positive else log(negative) is nan
2022-04-15 02:53:20 +00:00
( ( - view_z ) . ln ( ) * cluster_factors . x - cluster_factors . y + 1.0 ) as u32
} ;
2023-01-06 00:43:30 +00:00
// NOTE: We use min as we may limit the far z plane used for clustering to be closer than
2022-04-15 02:53:20 +00:00
// the furthest thing being drawn. This means that we need to limit to the maximum cluster.
z_slice . min ( z_slices - 1 )
}
// NOTE: Keep in sync as the inverse of view_z_to_z_slice above
fn z_slice_to_view_z (
near : f32 ,
far : f32 ,
z_slices : u32 ,
z_slice : u32 ,
is_orthographic : bool ,
) -> f32 {
if is_orthographic {
return - near - ( far - near ) * z_slice as f32 / z_slices as f32 ;
}
// Perspective
if z_slice = = 0 {
0.0
} else {
- near * ( far / near ) . powf ( ( z_slice - 1 ) as f32 / ( z_slices - 1 ) as f32 )
2021-12-14 23:42:35 +00:00
}
2021-12-14 03:58:23 +00:00
}
fn ndc_position_to_cluster (
cluster_dimensions : UVec3 ,
cluster_factors : Vec2 ,
2021-12-14 23:42:35 +00:00
is_orthographic : bool ,
2021-12-14 03:58:23 +00:00
ndc_p : Vec3 ,
view_z : f32 ,
) -> UVec3 {
let cluster_dimensions_f32 = cluster_dimensions . as_vec3 ( ) ;
2022-05-06 22:05:45 +00:00
let frag_coord = ( ndc_p . xy ( ) * VEC2_HALF_NEGATIVE_Y + VEC2_HALF ) . clamp ( Vec2 ::ZERO , Vec2 ::ONE ) ;
2021-12-14 03:58:23 +00:00
let xy = ( frag_coord * cluster_dimensions_f32 . xy ( ) ) . floor ( ) ;
2022-01-07 21:25:59 +00:00
let z_slice = view_z_to_z_slice (
cluster_factors ,
2022-04-15 02:53:20 +00:00
cluster_dimensions . z ,
2022-01-07 21:25:59 +00:00
view_z ,
is_orthographic ,
) ;
2021-12-14 03:58:23 +00:00
xy . as_uvec2 ( )
. extend ( z_slice )
. clamp ( UVec3 ::ZERO , cluster_dimensions - UVec3 ::ONE )
}
2022-07-03 19:55:33 +00:00
const VEC2_HALF : Vec2 = Vec2 ::splat ( 0.5 ) ;
const VEC2_HALF_NEGATIVE_Y : Vec2 = Vec2 ::new ( 0.5 , - 0.5 ) ;
2022-05-06 22:05:45 +00:00
2022-07-12 13:06:16 +00:00
/// Calculate bounds for the light using a view space aabb.
/// Returns a `(Vec3, Vec3)` containing minimum and maximum with
/// `X` and `Y` in normalized device coordinates with range `[-1, 1]`
/// `Z` in view space, with range `[-inf, -f32::MIN_POSITIVE]`
2022-03-08 04:56:42 +00:00
fn cluster_space_light_aabb (
inverse_view_transform : Mat4 ,
projection_matrix : Mat4 ,
light_sphere : & Sphere ,
) -> ( Vec3 , Vec3 ) {
let light_aabb_view = Aabb {
2022-03-19 04:41:28 +00:00
center : Vec3A ::from ( inverse_view_transform * light_sphere . center . extend ( 1.0 ) ) ,
half_extents : Vec3A ::splat ( light_sphere . radius ) ,
2022-03-08 04:56:42 +00:00
} ;
let ( mut light_aabb_view_min , mut light_aabb_view_max ) =
( light_aabb_view . min ( ) , light_aabb_view . max ( ) ) ;
// Constrain view z to be negative - i.e. in front of the camera
// When view z is >= 0.0 and we're using a perspective projection, bad things happen.
// At view z == 0.0, ndc x,y are mathematically undefined. At view z > 0.0, i.e. behind the camera,
// the perspective projection flips the directions of the axes. This breaks assumptions about
// use of min/max operations as something that was to the left in view space is now returning a
// coordinate that for view z in front of the camera would be on the right, but at view z behind the
// camera is on the left. So, we just constrain view z to be < 0.0 and necessarily in front of the camera.
light_aabb_view_min . z = light_aabb_view_min . z . min ( - f32 ::MIN_POSITIVE ) ;
light_aabb_view_max . z = light_aabb_view_max . z . min ( - f32 ::MIN_POSITIVE ) ;
// Is there a cheaper way to do this? The problem is that because of perspective
// the point at max z but min xy may be less xy in screenspace, and similar. As
// such, projecting the min and max xy at both the closer and further z and taking
// the min and max of those projected points addresses this.
let (
light_aabb_view_xymin_near ,
light_aabb_view_xymin_far ,
light_aabb_view_xymax_near ,
light_aabb_view_xymax_far ,
) = (
light_aabb_view_min ,
light_aabb_view_min . xy ( ) . extend ( light_aabb_view_max . z ) ,
light_aabb_view_max . xy ( ) . extend ( light_aabb_view_min . z ) ,
light_aabb_view_max ,
) ;
let (
light_aabb_clip_xymin_near ,
light_aabb_clip_xymin_far ,
light_aabb_clip_xymax_near ,
light_aabb_clip_xymax_far ,
) = (
projection_matrix * light_aabb_view_xymin_near . extend ( 1.0 ) ,
projection_matrix * light_aabb_view_xymin_far . extend ( 1.0 ) ,
projection_matrix * light_aabb_view_xymax_near . extend ( 1.0 ) ,
projection_matrix * light_aabb_view_xymax_far . extend ( 1.0 ) ,
) ;
let (
light_aabb_ndc_xymin_near ,
light_aabb_ndc_xymin_far ,
light_aabb_ndc_xymax_near ,
light_aabb_ndc_xymax_far ,
) = (
light_aabb_clip_xymin_near . xyz ( ) / light_aabb_clip_xymin_near . w ,
light_aabb_clip_xymin_far . xyz ( ) / light_aabb_clip_xymin_far . w ,
light_aabb_clip_xymax_near . xyz ( ) / light_aabb_clip_xymax_near . w ,
light_aabb_clip_xymax_far . xyz ( ) / light_aabb_clip_xymax_far . w ,
) ;
let ( light_aabb_ndc_min , light_aabb_ndc_max ) = (
light_aabb_ndc_xymin_near
. min ( light_aabb_ndc_xymin_far )
. min ( light_aabb_ndc_xymax_near )
. min ( light_aabb_ndc_xymax_far ) ,
light_aabb_ndc_xymin_near
. max ( light_aabb_ndc_xymin_far )
. max ( light_aabb_ndc_xymax_near )
. max ( light_aabb_ndc_xymax_far ) ,
) ;
2022-05-06 22:05:45 +00:00
// clamp to ndc coords without depth
let ( aabb_min_ndc , aabb_max_ndc ) = (
light_aabb_ndc_min . xy ( ) . clamp ( NDC_MIN , NDC_MAX ) ,
light_aabb_ndc_max . xy ( ) . clamp ( NDC_MIN , NDC_MAX ) ,
2022-03-08 04:56:42 +00:00
) ;
2022-05-06 22:05:45 +00:00
// pack unadjusted z depth into the vecs
2022-03-08 04:56:42 +00:00
(
2022-05-06 22:05:45 +00:00
aabb_min_ndc . extend ( light_aabb_view_min . z ) ,
aabb_max_ndc . extend ( light_aabb_view_max . z ) ,
2022-03-08 04:56:42 +00:00
)
}
2022-07-08 19:57:43 +00:00
fn screen_to_view ( screen_size : Vec2 , inverse_projection : Mat4 , screen : Vec2 , ndc_z : f32 ) -> Vec4 {
let tex_coord = screen / screen_size ;
let clip = Vec4 ::new (
tex_coord . x * 2.0 - 1.0 ,
( 1.0 - tex_coord . y ) * 2.0 - 1.0 ,
ndc_z ,
1.0 ,
) ;
clip_to_view ( inverse_projection , clip )
}
2022-07-03 19:55:33 +00:00
const NDC_MIN : Vec2 = Vec2 ::NEG_ONE ;
const NDC_MAX : Vec2 = Vec2 ::ONE ;
2022-05-06 22:05:45 +00:00
2022-07-08 19:57:43 +00:00
// Calculate the intersection of a ray from the eye through the view space position to a z plane
fn line_intersection_to_z_plane ( origin : Vec3 , p : Vec3 , z : f32 ) -> Vec3 {
let v = p - origin ;
let t = ( z - Vec3 ::Z . dot ( origin ) ) / Vec3 ::Z . dot ( v ) ;
origin + t * v
}
#[ allow(clippy::too_many_arguments) ]
fn compute_aabb_for_cluster (
z_near : f32 ,
z_far : f32 ,
tile_size : Vec2 ,
screen_size : Vec2 ,
inverse_projection : Mat4 ,
is_orthographic : bool ,
cluster_dimensions : UVec3 ,
ijk : UVec3 ,
) -> Aabb {
let ijk = ijk . as_vec3 ( ) ;
// Calculate the minimum and maximum points in screen space
let p_min = ijk . xy ( ) * tile_size ;
let p_max = p_min + tile_size ;
let cluster_min ;
let cluster_max ;
if is_orthographic {
// Use linear depth slicing for orthographic
// Convert to view space at the cluster near and far planes
// NOTE: 1.0 is the near plane due to using reverse z projections
let p_min = screen_to_view (
screen_size ,
inverse_projection ,
p_min ,
1.0 - ( ijk . z / cluster_dimensions . z as f32 ) ,
)
. xyz ( ) ;
let p_max = screen_to_view (
screen_size ,
inverse_projection ,
p_max ,
1.0 - ( ( ijk . z + 1.0 ) / cluster_dimensions . z as f32 ) ,
)
. xyz ( ) ;
cluster_min = p_min . min ( p_max ) ;
cluster_max = p_min . max ( p_max ) ;
} else {
// Convert to view space at the near plane
// NOTE: 1.0 is the near plane due to using reverse z projections
let p_min = screen_to_view ( screen_size , inverse_projection , p_min , 1.0 ) ;
let p_max = screen_to_view ( screen_size , inverse_projection , p_max , 1.0 ) ;
let z_far_over_z_near = - z_far / - z_near ;
let cluster_near = if ijk . z = = 0.0 {
0.0
} else {
- z_near * z_far_over_z_near . powf ( ( ijk . z - 1.0 ) / ( cluster_dimensions . z - 1 ) as f32 )
} ;
// NOTE: This could be simplified to:
// cluster_far = cluster_near * z_far_over_z_near;
let cluster_far = if cluster_dimensions . z = = 1 {
- z_far
} else {
- z_near * z_far_over_z_near . powf ( ijk . z / ( cluster_dimensions . z - 1 ) as f32 )
} ;
// Calculate the four intersection points of the min and max points with the cluster near and far planes
let p_min_near = line_intersection_to_z_plane ( Vec3 ::ZERO , p_min . xyz ( ) , cluster_near ) ;
let p_min_far = line_intersection_to_z_plane ( Vec3 ::ZERO , p_min . xyz ( ) , cluster_far ) ;
let p_max_near = line_intersection_to_z_plane ( Vec3 ::ZERO , p_max . xyz ( ) , cluster_near ) ;
let p_max_far = line_intersection_to_z_plane ( Vec3 ::ZERO , p_max . xyz ( ) , cluster_far ) ;
cluster_min = p_min_near . min ( p_min_far ) . min ( p_max_near . min ( p_max_far ) ) ;
cluster_max = p_min_near . max ( p_min_far ) . max ( p_max_near . max ( p_max_far ) ) ;
}
Aabb ::from_min_max ( cluster_min , cluster_max )
}
// Sort lights by
// - point-light vs spot-light, so that we can iterate point lights and spot lights in contiguous blocks in the fragment shader,
// - then those with shadows enabled first, so that the index can be used to render at most `point_light_shadow_maps_count`
// point light shadows and `spot_light_shadow_maps_count` spot light shadow maps,
// - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded.
2022-03-01 10:17:41 +00:00
pub ( crate ) fn point_light_order (
2022-07-08 19:57:43 +00:00
( entity_1 , shadows_enabled_1 , is_spot_light_1 ) : ( & Entity , & bool , & bool ) ,
( entity_2 , shadows_enabled_2 , is_spot_light_2 ) : ( & Entity , & bool , & bool ) ,
2022-03-01 10:17:41 +00:00
) -> std ::cmp ::Ordering {
2022-07-08 19:57:43 +00:00
is_spot_light_1
. cmp ( is_spot_light_2 ) // pointlights before spot lights
. then_with ( | | shadows_enabled_2 . cmp ( shadows_enabled_1 ) ) // shadow casters before non-casters
. then_with ( | | entity_1 . cmp ( entity_2 ) ) // stable
2022-03-01 10:17:41 +00:00
}
2022-11-03 07:09:51 +00:00
// Sort lights by
// - those with shadows enabled first, so that the index can be used to render at most `directional_light_shadow_maps_count`
// directional light shadows
// - then by entity as a stable key to ensure that a consistent set of lights are chosen if the light count limit is exceeded.
pub ( crate ) fn directional_light_order (
( entity_1 , shadows_enabled_1 ) : ( & Entity , & bool ) ,
( entity_2 , shadows_enabled_2 ) : ( & Entity , & bool ) ,
) -> std ::cmp ::Ordering {
shadows_enabled_2
. cmp ( shadows_enabled_1 ) // shadow casters before non-casters
. then_with ( | | entity_1 . cmp ( entity_2 ) ) // stable
}
2022-03-01 10:17:41 +00:00
#[ derive(Clone, Copy) ]
// data required for assigning lights to clusters
pub ( crate ) struct PointLightAssignmentData {
entity : Entity ,
2022-07-16 00:51:12 +00:00
transform : GlobalTransform ,
2022-03-01 10:17:41 +00:00
range : f32 ,
shadows_enabled : bool ,
2022-07-08 19:57:43 +00:00
spot_light_angle : Option < f32 > ,
2022-03-01 10:17:41 +00:00
}
2022-07-16 00:51:12 +00:00
impl PointLightAssignmentData {
pub fn sphere ( & self ) -> Sphere {
Sphere {
center : self . transform . translation_vec3a ( ) ,
radius : self . range ,
}
}
}
Make `Resource` trait opt-in, requiring `#[derive(Resource)]` V2 (#5577)
*This PR description is an edited copy of #5007, written by @alice-i-cecile.*
# Objective
Follow-up to https://github.com/bevyengine/bevy/pull/2254. The `Resource` trait currently has a blanket implementation for all types that meet its bounds.
While ergonomic, this results in several drawbacks:
* it is possible to make confusing, silent mistakes such as inserting a function pointer (Foo) rather than a value (Foo::Bar) as a resource
* it is challenging to discover if a type is intended to be used as a resource
* we cannot later add customization options (see the [RFC](https://github.com/bevyengine/rfcs/blob/main/rfcs/27-derive-component.md) for the equivalent choice for Component).
* dependencies can use the same Rust type as a resource in invisibly conflicting ways
* raw Rust types used as resources cannot preserve privacy appropriately, as anyone able to access that type can read and write to internal values
* we cannot capture a definitive list of possible resources to display to users in an editor
## Notes to reviewers
* Review this commit-by-commit; there's effectively no back-tracking and there's a lot of churn in some of these commits.
*ira: My commits are not as well organized :')*
* I've relaxed the bound on Local to Send + Sync + 'static: I don't think these concerns apply there, so this can keep things simple. Storing e.g. a u32 in a Local is fine, because there's a variable name attached explaining what it does.
* I think this is a bad place for the Resource trait to live, but I've left it in place to make reviewing easier. IMO that's best tackled with https://github.com/bevyengine/bevy/issues/4981.
## Changelog
`Resource` is no longer automatically implemented for all matching types. Instead, use the new `#[derive(Resource)]` macro.
## Migration Guide
Add `#[derive(Resource)]` to all types you are using as a resource.
If you are using a third party type as a resource, wrap it in a tuple struct to bypass orphan rules. Consider deriving `Deref` and `DerefMut` to improve ergonomics.
`ClearColor` no longer implements `Component`. Using `ClearColor` as a component in 0.8 did nothing.
Use the `ClearColorConfig` in the `Camera3d` and `Camera2d` components instead.
Co-authored-by: Alice <alice.i.cecile@gmail.com>
Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: devil-ira <justthecooldude@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-08-08 21:36:35 +00:00
#[ derive(Resource, Default) ]
2022-03-24 00:20:27 +00:00
pub struct GlobalVisiblePointLights {
entities : HashSet < Entity > ,
}
impl GlobalVisiblePointLights {
#[ inline ]
pub fn iter ( & self ) -> impl Iterator < Item = & Entity > {
self . entities . iter ( )
}
#[ inline ]
pub fn contains ( & self , entity : Entity ) -> bool {
self . entities . contains ( & entity )
}
}
2021-12-14 03:58:23 +00:00
// NOTE: Run this before update_point_light_frusta!
2022-03-08 04:56:42 +00:00
#[ allow(clippy::too_many_arguments) ]
2022-03-01 10:17:41 +00:00
pub ( crate ) fn assign_lights_to_clusters (
2021-12-14 03:58:23 +00:00
mut commands : Commands ,
2022-03-24 00:20:27 +00:00
mut global_lights : ResMut < GlobalVisiblePointLights > ,
2022-03-08 04:56:42 +00:00
mut views : Query < (
Entity ,
& GlobalTransform ,
& Camera ,
& Frustum ,
& ClusterConfig ,
& mut Clusters ,
2022-03-24 00:20:27 +00:00
Option < & mut VisiblePointLights > ,
2022-03-08 04:56:42 +00:00
) > ,
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
point_lights_query : Query < ( Entity , & GlobalTransform , & PointLight , & ComputedVisibility ) > ,
spot_lights_query : Query < ( Entity , & GlobalTransform , & SpotLight , & ComputedVisibility ) > ,
2022-03-01 10:17:41 +00:00
mut lights : Local < Vec < PointLightAssignmentData > > ,
2022-07-08 19:57:43 +00:00
mut cluster_aabb_spheres : Local < Vec < Option < Sphere > > > ,
2022-03-01 10:17:41 +00:00
mut max_point_lights_warning_emitted : Local < bool > ,
2022-04-15 07:13:37 +00:00
render_device : Option < Res < RenderDevice > > ,
2021-12-14 03:58:23 +00:00
) {
2022-04-15 07:13:37 +00:00
let render_device = match render_device {
Some ( render_device ) = > render_device ,
None = > return ,
} ;
2022-03-24 00:20:27 +00:00
global_lights . entities . clear ( ) ;
lights . clear ( ) ;
2022-03-01 10:17:41 +00:00
// collect just the relevant light query data into a persisted vec to avoid reallocating each frame
lights . extend (
2022-07-08 19:57:43 +00:00
point_lights_query
. iter ( )
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
. filter ( | ( .. , visibility ) | visibility . is_visible ( ) )
2022-07-08 19:57:43 +00:00
. map (
| ( entity , transform , point_light , _visibility ) | PointLightAssignmentData {
entity ,
2022-07-16 00:51:12 +00:00
transform : GlobalTransform ::from_translation ( transform . translation ( ) ) ,
2022-07-08 19:57:43 +00:00
shadows_enabled : point_light . shadows_enabled ,
range : point_light . range ,
spot_light_angle : None ,
} ,
) ,
) ;
lights . extend (
spot_lights_query
2022-03-01 10:17:41 +00:00
. iter ( )
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
. filter ( | ( .. , visibility ) | visibility . is_visible ( ) )
2022-03-05 03:23:01 +00:00
. map (
2022-07-08 19:57:43 +00:00
| ( entity , transform , spot_light , _visibility ) | PointLightAssignmentData {
2022-03-05 03:23:01 +00:00
entity ,
2022-07-16 00:51:12 +00:00
transform : * transform ,
2022-07-08 19:57:43 +00:00
shadows_enabled : spot_light . shadows_enabled ,
range : spot_light . range ,
spot_light_angle : Some ( spot_light . outer_angle ) ,
2022-03-05 03:23:01 +00:00
} ,
) ,
2022-03-01 10:17:41 +00:00
) ;
2022-04-07 16:16:35 +00:00
let clustered_forward_buffer_binding_type =
render_device . get_supported_read_only_binding_type ( CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT ) ;
let supports_storage_buffers = matches! (
clustered_forward_buffer_binding_type ,
BufferBindingType ::Storage { .. }
) ;
if lights . len ( ) > MAX_UNIFORM_BUFFER_POINT_LIGHTS & & ! supports_storage_buffers {
2022-03-01 10:17:41 +00:00
lights . sort_by ( | light_1 , light_2 | {
point_light_order (
2022-07-08 19:57:43 +00:00
(
& light_1 . entity ,
& light_1 . shadows_enabled ,
& light_1 . spot_light_angle . is_some ( ) ,
) ,
(
& light_2 . entity ,
& light_2 . shadows_enabled ,
& light_2 . spot_light_angle . is_some ( ) ,
) ,
2022-03-01 10:17:41 +00:00
)
} ) ;
// check each light against each view's frustum, keep only those that affect at least one of our views
2022-03-08 04:56:42 +00:00
let frusta : Vec < _ > = views
. iter ( )
2022-03-24 00:20:27 +00:00
. map ( | ( _ , _ , _ , frustum , _ , _ , _ ) | * frustum )
2022-03-08 04:56:42 +00:00
. collect ( ) ;
2022-03-01 10:17:41 +00:00
let mut lights_in_view_count = 0 ;
lights . retain ( | light | {
// take one extra light to check if we should emit the warning
2022-04-07 16:16:35 +00:00
if lights_in_view_count = = MAX_UNIFORM_BUFFER_POINT_LIGHTS + 1 {
2022-03-01 10:17:41 +00:00
false
} else {
2022-07-16 00:51:12 +00:00
let light_sphere = light . sphere ( ) ;
2022-03-01 10:17:41 +00:00
let light_in_view = frusta
. iter ( )
2022-03-19 04:41:28 +00:00
. any ( | frustum | frustum . intersects_sphere ( & light_sphere , true ) ) ;
2022-03-01 10:17:41 +00:00
if light_in_view {
lights_in_view_count + = 1 ;
}
light_in_view
}
} ) ;
2022-04-07 16:16:35 +00:00
if lights . len ( ) > MAX_UNIFORM_BUFFER_POINT_LIGHTS & & ! * max_point_lights_warning_emitted {
warn! (
" MAX_UNIFORM_BUFFER_POINT_LIGHTS ({}) exceeded " ,
MAX_UNIFORM_BUFFER_POINT_LIGHTS
) ;
2022-03-01 10:17:41 +00:00
* max_point_lights_warning_emitted = true ;
}
2022-04-07 16:16:35 +00:00
lights . truncate ( MAX_UNIFORM_BUFFER_POINT_LIGHTS ) ;
2022-03-01 10:17:41 +00:00
}
2022-03-24 00:20:27 +00:00
for ( view_entity , camera_transform , camera , frustum , config , clusters , mut visible_lights ) in
2022-07-11 15:28:50 +00:00
& mut views
2022-03-24 00:20:27 +00:00
{
2022-04-15 07:32:21 +00:00
let clusters = clusters . into_inner ( ) ;
2022-04-15 16:05:59 +00:00
if matches! ( config , ClusterConfig ::None ) {
if visible_lights . is_some ( ) {
commands . entity ( view_entity ) . remove ::< VisiblePointLights > ( ) ;
}
2022-04-15 07:32:21 +00:00
clusters . clear ( ) ;
2022-03-08 04:56:42 +00:00
continue ;
}
2022-11-04 21:32:09 +00:00
let Some ( screen_size ) = camera . physical_viewport_size ( ) else {
Camera Driven Rendering (#4745)
This adds "high level camera driven rendering" to Bevy. The goal is to give users more control over what gets rendered (and where) without needing to deal with render logic. This will make scenarios like "render to texture", "multiple windows", "split screen", "2d on 3d", "3d on 2d", "pass layering", and more significantly easier.
Here is an [example of a 2d render sandwiched between two 3d renders (each from a different perspective)](https://gist.github.com/cart/4fe56874b2e53bc5594a182fc76f4915):
![image](https://user-images.githubusercontent.com/2694663/168411086-af13dec8-0093-4a84-bdd4-d4362d850ffa.png)
Users can now spawn a camera, point it at a RenderTarget (a texture or a window), and it will "just work".
Rendering to a second window is as simple as spawning a second camera and assigning it to a specific window id:
```rust
// main camera (main window)
commands.spawn_bundle(Camera2dBundle::default());
// second camera (other window)
commands.spawn_bundle(Camera2dBundle {
camera: Camera {
target: RenderTarget::Window(window_id),
..default()
},
..default()
});
```
Rendering to a texture is as simple as pointing the camera at a texture:
```rust
commands.spawn_bundle(Camera2dBundle {
camera: Camera {
target: RenderTarget::Texture(image_handle),
..default()
},
..default()
});
```
Cameras now have a "render priority", which controls the order they are drawn in. If you want to use a camera's output texture as a texture in the main pass, just set the priority to a number lower than the main pass camera (which defaults to `0`).
```rust
// main pass camera with a default priority of 0
commands.spawn_bundle(Camera2dBundle::default());
commands.spawn_bundle(Camera2dBundle {
camera: Camera {
target: RenderTarget::Texture(image_handle.clone()),
priority: -1,
..default()
},
..default()
});
commands.spawn_bundle(SpriteBundle {
texture: image_handle,
..default()
})
```
Priority can also be used to layer to cameras on top of each other for the same RenderTarget. This is what "2d on top of 3d" looks like in the new system:
```rust
commands.spawn_bundle(Camera3dBundle::default());
commands.spawn_bundle(Camera2dBundle {
camera: Camera {
// this will render 2d entities "on top" of the default 3d camera's render
priority: 1,
..default()
},
..default()
});
```
There is no longer the concept of a global "active camera". Resources like `ActiveCamera<Camera2d>` and `ActiveCamera<Camera3d>` have been replaced with the camera-specific `Camera::is_active` field. This does put the onus on users to manage which cameras should be active.
Cameras are now assigned a single render graph as an "entry point", which is configured on each camera entity using the new `CameraRenderGraph` component. The old `PerspectiveCameraBundle` and `OrthographicCameraBundle` (generic on camera marker components like Camera2d and Camera3d) have been replaced by `Camera3dBundle` and `Camera2dBundle`, which set 3d and 2d default values for the `CameraRenderGraph` and projections.
```rust
// old 3d perspective camera
commands.spawn_bundle(PerspectiveCameraBundle::default())
// new 3d perspective camera
commands.spawn_bundle(Camera3dBundle::default())
```
```rust
// old 2d orthographic camera
commands.spawn_bundle(OrthographicCameraBundle::new_2d())
// new 2d orthographic camera
commands.spawn_bundle(Camera2dBundle::default())
```
```rust
// old 3d orthographic camera
commands.spawn_bundle(OrthographicCameraBundle::new_3d())
// new 3d orthographic camera
commands.spawn_bundle(Camera3dBundle {
projection: OrthographicProjection {
scale: 3.0,
scaling_mode: ScalingMode::FixedVertical,
..default()
}.into(),
..default()
})
```
Note that `Camera3dBundle` now uses a new `Projection` enum instead of hard coding the projection into the type. There are a number of motivators for this change: the render graph is now a part of the bundle, the way "generic bundles" work in the rust type system prevents nice `..default()` syntax, and changing projections at runtime is much easier with an enum (ex for editor scenarios). I'm open to discussing this choice, but I'm relatively certain we will all come to the same conclusion here. Camera2dBundle and Camera3dBundle are much clearer than being generic on marker components / using non-default constructors.
If you want to run a custom render graph on a camera, just set the `CameraRenderGraph` component:
```rust
commands.spawn_bundle(Camera3dBundle {
camera_render_graph: CameraRenderGraph::new(some_render_graph_name),
..default()
})
```
Just note that if the graph requires data from specific components to work (such as `Camera3d` config, which is provided in the `Camera3dBundle`), make sure the relevant components have been added.
Speaking of using components to configure graphs / passes, there are a number of new configuration options:
```rust
commands.spawn_bundle(Camera3dBundle {
camera_3d: Camera3d {
// overrides the default global clear color
clear_color: ClearColorConfig::Custom(Color::RED),
..default()
},
..default()
})
commands.spawn_bundle(Camera3dBundle {
camera_3d: Camera3d {
// disables clearing
clear_color: ClearColorConfig::None,
..default()
},
..default()
})
```
Expect to see more of the "graph configuration Components on Cameras" pattern in the future.
By popular demand, UI no longer requires a dedicated camera. `UiCameraBundle` has been removed. `Camera2dBundle` and `Camera3dBundle` now both default to rendering UI as part of their own render graphs. To disable UI rendering for a camera, disable it using the CameraUi component:
```rust
commands
.spawn_bundle(Camera3dBundle::default())
.insert(CameraUi {
is_enabled: false,
..default()
})
```
## Other Changes
* The separate clear pass has been removed. We should revisit this for things like sky rendering, but I think this PR should "keep it simple" until we're ready to properly support that (for code complexity and performance reasons). We can come up with the right design for a modular clear pass in a followup pr.
* I reorganized bevy_core_pipeline into Core2dPlugin and Core3dPlugin (and core_2d / core_3d modules). Everything is pretty much the same as before, just logically separate. I've moved relevant types (like Camera2d, Camera3d, Camera3dBundle, Camera2dBundle) into their relevant modules, which is what motivated this reorganization.
* I adapted the `scene_viewer` example (which relied on the ActiveCameras behavior) to the new system. I also refactored bits and pieces to be a bit simpler.
* All of the examples have been ported to the new camera approach. `render_to_texture` and `multiple_windows` are now _much_ simpler. I removed `two_passes` because it is less relevant with the new approach. If someone wants to add a new "layered custom pass with CameraRenderGraph" example, that might fill a similar niche. But I don't feel much pressure to add that in this pr.
* Cameras now have `target_logical_size` and `target_physical_size` fields, which makes finding the size of a camera's render target _much_ simpler. As a result, the `Assets<Image>` and `Windows` parameters were removed from `Camera::world_to_screen`, making that operation much more ergonomic.
* Render order ambiguities between cameras with the same target and the same priority now produce a warning. This accomplishes two goals:
1. Now that there is no "global" active camera, by default spawning two cameras will result in two renders (one covering the other). This would be a silent performance killer that would be hard to detect after the fact. By detecting ambiguities, we can provide a helpful warning when this occurs.
2. Render order ambiguities could result in unexpected / unpredictable render results. Resolving them makes sense.
## Follow Up Work
* Per-Camera viewports, which will make it possible to render to a smaller area inside of a RenderTarget (great for something like splitscreen)
* Camera-specific MSAA config (should use the same "overriding" pattern used for ClearColor)
* Graph Based Camera Ordering: priorities are simple, but they make complicated ordering constraints harder to express. We should consider adopting a "graph based" camera ordering model with "before" and "after" relationships to other cameras (or build it "on top" of the priority system).
* Consider allowing graphs to run subgraphs from any nest level (aka a global namespace for graphs). Right now the 2d and 3d graphs each need their own UI subgraph, which feels "fine" in the short term. But being able to share subgraphs between other subgraphs seems valuable.
* Consider splitting `bevy_core_pipeline` into `bevy_core_2d` and `bevy_core_3d` packages. Theres a shared "clear color" dependency here, which would need a new home.
2022-06-02 00:12:17 +00:00
clusters . clear ( ) ;
continue ;
} ;
2022-03-24 00:20:27 +00:00
let mut requested_cluster_dimensions = config . dimensions_for_screen_size ( screen_size ) ;
let view_transform = camera_transform . compute_matrix ( ) ;
2021-12-14 03:58:23 +00:00
let inverse_view_transform = view_transform . inverse ( ) ;
Camera Driven Viewports (#4898)
# Objective
Users should be able to render cameras to specific areas of a render target, which enables scenarios like split screen, minimaps, etc.
Builds on the new Camera Driven Rendering added here: #4745
Fixes: #202
Alternative to #1389 and #3626 (which are incompatible with the new Camera Driven Rendering)
## Solution
![image](https://user-images.githubusercontent.com/2694663/171560044-f0694f67-0cd9-4598-83e2-a9658c4fed57.png)
Cameras can now configure an optional "viewport", which defines a rectangle within their render target to draw to. If a `Viewport` is defined, the camera's `CameraProjection`, `View`, and visibility calculations will use the viewport configuration instead of the full render target.
```rust
// This camera will render to the first half of the primary window (on the left side).
commands.spawn_bundle(Camera3dBundle {
camera: Camera {
viewport: Some(Viewport {
physical_position: UVec2::new(0, 0),
physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()),
depth: 0.0..1.0,
}),
..default()
},
..default()
});
```
To account for this, the `Camera` component has received a few adjustments:
* `Camera` now has some new getter functions:
* `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, `projection_matrix`
* All computed camera values are now private and live on the `ComputedCameraValues` field (logical/physical width/height, the projection matrix). They are now exposed on `Camera` via getters/setters This wasn't _needed_ for viewports, but it was long overdue.
---
## Changelog
### Added
* `Camera` components now have a `viewport` field, which can be set to draw to a portion of a render target instead of the full target.
* `Camera` component has some new functions: `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, and `projection_matrix`
* Added a new split_screen example illustrating how to render two cameras to the same scene
## Migration Guide
`Camera::projection_matrix` is no longer a public field. Use the new `Camera::projection_matrix()` method instead:
```rust
// Bevy 0.7
let projection = camera.projection_matrix;
// Bevy 0.8
let projection = camera.projection_matrix();
```
2022-06-05 00:27:49 +00:00
let is_orthographic = camera . projection_matrix ( ) . w_axis . w = = 1.0 ;
2022-03-08 04:56:42 +00:00
let far_z = match config . far_z_mode ( ) {
ClusterFarZMode ::MaxLightRange = > {
let inverse_view_row_2 = inverse_view_transform . row ( 2 ) ;
lights
. iter ( )
. map ( | light | {
2022-07-16 00:51:12 +00:00
- inverse_view_row_2 . dot ( light . transform . translation ( ) . extend ( 1.0 ) )
+ light . range
2022-03-08 04:56:42 +00:00
} )
. reduce ( f32 ::max )
. unwrap_or ( 0.0 )
}
ClusterFarZMode ::Constant ( far ) = > far ,
} ;
2022-04-15 02:53:20 +00:00
let first_slice_depth = match ( is_orthographic , requested_cluster_dimensions . z ) {
2022-05-16 16:37:33 +00:00
( true , _ ) = > {
// NOTE: Based on glam's Mat4::orthographic_rh(), as used to calculate the orthographic projection
// matrix, we can calculate the projection's view-space near plane as follows:
// component 3,2 = r * near and 2,2 = r where r = 1.0 / (near - far)
// There is a caveat here that when calculating the projection matrix, near and far were swapped to give
// reversed z, consistent with the perspective projection. So,
// 3,2 = r * far and 2,2 = r where r = 1.0 / (far - near)
// rearranging r = 1.0 / (far - near), r * (far - near) = 1.0, r * far - 1.0 = r * near, near = (r * far - 1.0) / r
// = (3,2 - 1.0) / 2,2
Camera Driven Viewports (#4898)
# Objective
Users should be able to render cameras to specific areas of a render target, which enables scenarios like split screen, minimaps, etc.
Builds on the new Camera Driven Rendering added here: #4745
Fixes: #202
Alternative to #1389 and #3626 (which are incompatible with the new Camera Driven Rendering)
## Solution
![image](https://user-images.githubusercontent.com/2694663/171560044-f0694f67-0cd9-4598-83e2-a9658c4fed57.png)
Cameras can now configure an optional "viewport", which defines a rectangle within their render target to draw to. If a `Viewport` is defined, the camera's `CameraProjection`, `View`, and visibility calculations will use the viewport configuration instead of the full render target.
```rust
// This camera will render to the first half of the primary window (on the left side).
commands.spawn_bundle(Camera3dBundle {
camera: Camera {
viewport: Some(Viewport {
physical_position: UVec2::new(0, 0),
physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()),
depth: 0.0..1.0,
}),
..default()
},
..default()
});
```
To account for this, the `Camera` component has received a few adjustments:
* `Camera` now has some new getter functions:
* `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, `projection_matrix`
* All computed camera values are now private and live on the `ComputedCameraValues` field (logical/physical width/height, the projection matrix). They are now exposed on `Camera` via getters/setters This wasn't _needed_ for viewports, but it was long overdue.
---
## Changelog
### Added
* `Camera` components now have a `viewport` field, which can be set to draw to a portion of a render target instead of the full target.
* `Camera` component has some new functions: `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, and `projection_matrix`
* Added a new split_screen example illustrating how to render two cameras to the same scene
## Migration Guide
`Camera::projection_matrix` is no longer a public field. Use the new `Camera::projection_matrix()` method instead:
```rust
// Bevy 0.7
let projection = camera.projection_matrix;
// Bevy 0.8
let projection = camera.projection_matrix();
```
2022-06-05 00:27:49 +00:00
( camera . projection_matrix ( ) . w_axis . z - 1.0 ) / camera . projection_matrix ( ) . z_axis . z
2022-05-16 16:37:33 +00:00
}
2022-04-15 02:53:20 +00:00
( false , 1 ) = > config . first_slice_depth ( ) . max ( far_z ) ,
2022-03-08 04:56:42 +00:00
_ = > config . first_slice_depth ( ) ,
} ;
// NOTE: Ensure the far_z is at least as far as the first_depth_slice to avoid clustering problems.
let far_z = far_z . max ( first_slice_depth ) ;
2021-12-14 23:42:35 +00:00
let cluster_factors = calculate_cluster_factors (
2022-03-08 04:56:42 +00:00
first_slice_depth ,
far_z ,
2022-03-24 00:20:27 +00:00
requested_cluster_dimensions . z as f32 ,
2021-12-14 23:42:35 +00:00
is_orthographic ,
2021-12-14 03:58:23 +00:00
) ;
2022-03-08 04:56:42 +00:00
if config . dynamic_resizing ( ) {
let mut cluster_index_estimate = 0.0 ;
2022-10-24 21:01:08 +00:00
for light in & lights {
2022-07-16 00:51:12 +00:00
let light_sphere = light . sphere ( ) ;
2022-03-08 04:56:42 +00:00
// Check if the light is within the view frustum
2022-03-19 04:41:28 +00:00
if ! frustum . intersects_sphere ( & light_sphere , true ) {
2022-03-08 04:56:42 +00:00
continue ;
}
// calculate a conservative aabb estimate of number of clusters affected by this light
// this overestimates index counts by at most 50% (and typically much less) when the whole light range is in view
// it can overestimate more significantly when light ranges are only partially in view
let ( light_aabb_min , light_aabb_max ) = cluster_space_light_aabb (
inverse_view_transform ,
Camera Driven Viewports (#4898)
# Objective
Users should be able to render cameras to specific areas of a render target, which enables scenarios like split screen, minimaps, etc.
Builds on the new Camera Driven Rendering added here: #4745
Fixes: #202
Alternative to #1389 and #3626 (which are incompatible with the new Camera Driven Rendering)
## Solution
![image](https://user-images.githubusercontent.com/2694663/171560044-f0694f67-0cd9-4598-83e2-a9658c4fed57.png)
Cameras can now configure an optional "viewport", which defines a rectangle within their render target to draw to. If a `Viewport` is defined, the camera's `CameraProjection`, `View`, and visibility calculations will use the viewport configuration instead of the full render target.
```rust
// This camera will render to the first half of the primary window (on the left side).
commands.spawn_bundle(Camera3dBundle {
camera: Camera {
viewport: Some(Viewport {
physical_position: UVec2::new(0, 0),
physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()),
depth: 0.0..1.0,
}),
..default()
},
..default()
});
```
To account for this, the `Camera` component has received a few adjustments:
* `Camera` now has some new getter functions:
* `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, `projection_matrix`
* All computed camera values are now private and live on the `ComputedCameraValues` field (logical/physical width/height, the projection matrix). They are now exposed on `Camera` via getters/setters This wasn't _needed_ for viewports, but it was long overdue.
---
## Changelog
### Added
* `Camera` components now have a `viewport` field, which can be set to draw to a portion of a render target instead of the full target.
* `Camera` component has some new functions: `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, and `projection_matrix`
* Added a new split_screen example illustrating how to render two cameras to the same scene
## Migration Guide
`Camera::projection_matrix` is no longer a public field. Use the new `Camera::projection_matrix()` method instead:
```rust
// Bevy 0.7
let projection = camera.projection_matrix;
// Bevy 0.8
let projection = camera.projection_matrix();
```
2022-06-05 00:27:49 +00:00
camera . projection_matrix ( ) ,
2022-03-08 04:56:42 +00:00
& light_sphere ,
) ;
// since we won't adjust z slices we can calculate exact number of slices required in z dimension
let z_cluster_min = view_z_to_z_slice (
cluster_factors ,
2022-04-15 02:53:20 +00:00
requested_cluster_dimensions . z ,
2022-03-08 04:56:42 +00:00
light_aabb_min . z ,
is_orthographic ,
) ;
let z_cluster_max = view_z_to_z_slice (
cluster_factors ,
2022-04-15 02:53:20 +00:00
requested_cluster_dimensions . z ,
2022-03-08 04:56:42 +00:00
light_aabb_max . z ,
is_orthographic ,
) ;
let z_count =
z_cluster_min . max ( z_cluster_max ) - z_cluster_min . min ( z_cluster_max ) + 1 ;
// calculate x/y count using floats to avoid overestimating counts due to large initial tile sizes
let xy_min = light_aabb_min . xy ( ) ;
let xy_max = light_aabb_max . xy ( ) ;
// multiply by 0.5 to move from [-1,1] to [-0.5, 0.5], max extent of 1 in each dimension
let xy_count = ( xy_max - xy_min )
* 0.5
2022-03-24 00:20:27 +00:00
* Vec2 ::new (
requested_cluster_dimensions . x as f32 ,
requested_cluster_dimensions . y as f32 ,
) ;
2022-03-08 04:56:42 +00:00
// add up to 2 to each axis to account for overlap
let x_overlap = if xy_min . x < = - 1.0 { 0.0 } else { 1.0 }
+ if xy_max . x > = 1.0 { 0.0 } else { 1.0 } ;
let y_overlap = if xy_min . y < = - 1.0 { 0.0 } else { 1.0 }
+ if xy_max . y > = 1.0 { 0.0 } else { 1.0 } ;
cluster_index_estimate + =
( xy_count . x + x_overlap ) * ( xy_count . y + y_overlap ) * z_count as f32 ;
}
2022-03-24 00:20:27 +00:00
if cluster_index_estimate > ViewClusterBindings ::MAX_INDICES as f32 {
2022-03-08 04:56:42 +00:00
// scale x and y cluster count to be able to fit all our indices
// we take the ratio of the actual indices over the index estimate.
// this not not guaranteed to be small enough due to overlapped tiles, but
// the conservative estimate is more than sufficient to cover the
// difference
2022-10-28 21:03:01 +00:00
let index_ratio = ViewClusterBindings ::MAX_INDICES as f32 / cluster_index_estimate ;
2022-03-08 04:56:42 +00:00
let xy_ratio = index_ratio . sqrt ( ) ;
2022-03-24 00:20:27 +00:00
requested_cluster_dimensions . x =
( ( requested_cluster_dimensions . x as f32 * xy_ratio ) . floor ( ) as u32 ) . max ( 1 ) ;
requested_cluster_dimensions . y =
( ( requested_cluster_dimensions . y as f32 * xy_ratio ) . floor ( ) as u32 ) . max ( 1 ) ;
2022-03-08 04:56:42 +00:00
}
}
2022-03-24 00:20:27 +00:00
clusters . update ( screen_size , requested_cluster_dimensions ) ;
clusters . near = first_slice_depth ;
clusters . far = far_z ;
// NOTE: Maximum 4096 clusters due to uniform buffer size constraints
debug_assert! (
clusters . dimensions . x * clusters . dimensions . y * clusters . dimensions . z < = 4096
2022-03-08 04:56:42 +00:00
) ;
2022-03-24 00:20:27 +00:00
Camera Driven Viewports (#4898)
# Objective
Users should be able to render cameras to specific areas of a render target, which enables scenarios like split screen, minimaps, etc.
Builds on the new Camera Driven Rendering added here: #4745
Fixes: #202
Alternative to #1389 and #3626 (which are incompatible with the new Camera Driven Rendering)
## Solution
![image](https://user-images.githubusercontent.com/2694663/171560044-f0694f67-0cd9-4598-83e2-a9658c4fed57.png)
Cameras can now configure an optional "viewport", which defines a rectangle within their render target to draw to. If a `Viewport` is defined, the camera's `CameraProjection`, `View`, and visibility calculations will use the viewport configuration instead of the full render target.
```rust
// This camera will render to the first half of the primary window (on the left side).
commands.spawn_bundle(Camera3dBundle {
camera: Camera {
viewport: Some(Viewport {
physical_position: UVec2::new(0, 0),
physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()),
depth: 0.0..1.0,
}),
..default()
},
..default()
});
```
To account for this, the `Camera` component has received a few adjustments:
* `Camera` now has some new getter functions:
* `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, `projection_matrix`
* All computed camera values are now private and live on the `ComputedCameraValues` field (logical/physical width/height, the projection matrix). They are now exposed on `Camera` via getters/setters This wasn't _needed_ for viewports, but it was long overdue.
---
## Changelog
### Added
* `Camera` components now have a `viewport` field, which can be set to draw to a portion of a render target instead of the full target.
* `Camera` component has some new functions: `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, and `projection_matrix`
* Added a new split_screen example illustrating how to render two cameras to the same scene
## Migration Guide
`Camera::projection_matrix` is no longer a public field. Use the new `Camera::projection_matrix()` method instead:
```rust
// Bevy 0.7
let projection = camera.projection_matrix;
// Bevy 0.8
let projection = camera.projection_matrix();
```
2022-06-05 00:27:49 +00:00
let inverse_projection = camera . projection_matrix ( ) . inverse ( ) ;
2022-03-24 00:20:27 +00:00
2022-05-31 01:38:07 +00:00
for lights in & mut clusters . lights {
2022-03-24 00:20:27 +00:00
lights . entities . clear ( ) ;
2022-07-08 19:57:43 +00:00
lights . point_light_count = 0 ;
lights . spot_light_count = 0 ;
2022-03-24 00:20:27 +00:00
}
2022-07-08 19:57:43 +00:00
let cluster_count =
( clusters . dimensions . x * clusters . dimensions . y * clusters . dimensions . z ) as usize ;
clusters
. lights
. resize_with ( cluster_count , VisiblePointLights ::default ) ;
// initialize empty cluster bounding spheres
cluster_aabb_spheres . clear ( ) ;
cluster_aabb_spheres . extend ( std ::iter ::repeat ( None ) . take ( cluster_count ) ) ;
2022-03-24 00:20:27 +00:00
2022-04-15 02:53:20 +00:00
// Calculate the x/y/z cluster frustum planes in view space
let mut x_planes = Vec ::with_capacity ( clusters . dimensions . x as usize + 1 ) ;
let mut y_planes = Vec ::with_capacity ( clusters . dimensions . y as usize + 1 ) ;
let mut z_planes = Vec ::with_capacity ( clusters . dimensions . z as usize + 1 ) ;
if is_orthographic {
let x_slices = clusters . dimensions . x as f32 ;
for x in 0 ..= clusters . dimensions . x {
let x_proportion = x as f32 / x_slices ;
let x_pos = x_proportion * 2.0 - 1.0 ;
let view_x = clip_to_view ( inverse_projection , Vec4 ::new ( x_pos , 0.0 , 1.0 , 1.0 ) ) . x ;
let normal = Vec3 ::X ;
let d = view_x * normal . x ;
x_planes . push ( Plane ::new ( normal . extend ( d ) ) ) ;
}
let y_slices = clusters . dimensions . y as f32 ;
for y in 0 ..= clusters . dimensions . y {
let y_proportion = 1.0 - y as f32 / y_slices ;
let y_pos = y_proportion * 2.0 - 1.0 ;
let view_y = clip_to_view ( inverse_projection , Vec4 ::new ( 0.0 , y_pos , 1.0 , 1.0 ) ) . y ;
let normal = Vec3 ::Y ;
let d = view_y * normal . y ;
y_planes . push ( Plane ::new ( normal . extend ( d ) ) ) ;
}
} else {
let x_slices = clusters . dimensions . x as f32 ;
for x in 0 ..= clusters . dimensions . x {
let x_proportion = x as f32 / x_slices ;
let x_pos = x_proportion * 2.0 - 1.0 ;
let nb = clip_to_view ( inverse_projection , Vec4 ::new ( x_pos , - 1.0 , 1.0 , 1.0 ) ) . xyz ( ) ;
let nt = clip_to_view ( inverse_projection , Vec4 ::new ( x_pos , 1.0 , 1.0 , 1.0 ) ) . xyz ( ) ;
let normal = nb . cross ( nt ) ;
let d = nb . dot ( normal ) ;
x_planes . push ( Plane ::new ( normal . extend ( d ) ) ) ;
}
let y_slices = clusters . dimensions . y as f32 ;
for y in 0 ..= clusters . dimensions . y {
let y_proportion = 1.0 - y as f32 / y_slices ;
let y_pos = y_proportion * 2.0 - 1.0 ;
let nl = clip_to_view ( inverse_projection , Vec4 ::new ( - 1.0 , y_pos , 1.0 , 1.0 ) ) . xyz ( ) ;
let nr = clip_to_view ( inverse_projection , Vec4 ::new ( 1.0 , y_pos , 1.0 , 1.0 ) ) . xyz ( ) ;
let normal = nr . cross ( nl ) ;
let d = nr . dot ( normal ) ;
y_planes . push ( Plane ::new ( normal . extend ( d ) ) ) ;
}
}
let z_slices = clusters . dimensions . z ;
for z in 0 ..= z_slices {
let view_z = z_slice_to_view_z ( first_slice_depth , far_z , z_slices , z , is_orthographic ) ;
let normal = - Vec3 ::Z ;
let d = view_z * normal . z ;
z_planes . push ( Plane ::new ( normal . extend ( d ) ) ) ;
}
2022-04-15 07:32:21 +00:00
let mut update_from_light_intersections = | visible_lights : & mut Vec < Entity > | {
2022-10-24 21:01:08 +00:00
for light in & lights {
2022-07-16 00:51:12 +00:00
let light_sphere = light . sphere ( ) ;
2021-12-14 03:58:23 +00:00
2022-03-24 00:20:27 +00:00
// Check if the light is within the view frustum
if ! frustum . intersects_sphere ( & light_sphere , true ) {
continue ;
}
2021-12-14 03:58:23 +00:00
2022-03-24 00:20:27 +00:00
// NOTE: The light intersects the frustum so it must be visible and part of the global set
global_lights . entities . insert ( light . entity ) ;
visible_lights . push ( light . entity ) ;
2022-02-28 22:02:06 +00:00
2022-03-24 00:20:27 +00:00
// note: caching seems to be slower than calling twice for this aabb calculation
let ( light_aabb_xy_ndc_z_view_min , light_aabb_xy_ndc_z_view_max ) =
cluster_space_light_aabb (
inverse_view_transform ,
Camera Driven Viewports (#4898)
# Objective
Users should be able to render cameras to specific areas of a render target, which enables scenarios like split screen, minimaps, etc.
Builds on the new Camera Driven Rendering added here: #4745
Fixes: #202
Alternative to #1389 and #3626 (which are incompatible with the new Camera Driven Rendering)
## Solution
![image](https://user-images.githubusercontent.com/2694663/171560044-f0694f67-0cd9-4598-83e2-a9658c4fed57.png)
Cameras can now configure an optional "viewport", which defines a rectangle within their render target to draw to. If a `Viewport` is defined, the camera's `CameraProjection`, `View`, and visibility calculations will use the viewport configuration instead of the full render target.
```rust
// This camera will render to the first half of the primary window (on the left side).
commands.spawn_bundle(Camera3dBundle {
camera: Camera {
viewport: Some(Viewport {
physical_position: UVec2::new(0, 0),
physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()),
depth: 0.0..1.0,
}),
..default()
},
..default()
});
```
To account for this, the `Camera` component has received a few adjustments:
* `Camera` now has some new getter functions:
* `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, `projection_matrix`
* All computed camera values are now private and live on the `ComputedCameraValues` field (logical/physical width/height, the projection matrix). They are now exposed on `Camera` via getters/setters This wasn't _needed_ for viewports, but it was long overdue.
---
## Changelog
### Added
* `Camera` components now have a `viewport` field, which can be set to draw to a portion of a render target instead of the full target.
* `Camera` component has some new functions: `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, and `projection_matrix`
* Added a new split_screen example illustrating how to render two cameras to the same scene
## Migration Guide
`Camera::projection_matrix` is no longer a public field. Use the new `Camera::projection_matrix()` method instead:
```rust
// Bevy 0.7
let projection = camera.projection_matrix;
// Bevy 0.8
let projection = camera.projection_matrix();
```
2022-06-05 00:27:49 +00:00
camera . projection_matrix ( ) ,
2022-03-24 00:20:27 +00:00
& light_sphere ,
) ;
2022-03-08 04:56:42 +00:00
2022-03-24 00:20:27 +00:00
let min_cluster = ndc_position_to_cluster (
clusters . dimensions ,
cluster_factors ,
is_orthographic ,
light_aabb_xy_ndc_z_view_min ,
light_aabb_xy_ndc_z_view_min . z ,
) ;
let max_cluster = ndc_position_to_cluster (
clusters . dimensions ,
cluster_factors ,
is_orthographic ,
light_aabb_xy_ndc_z_view_max ,
light_aabb_xy_ndc_z_view_max . z ,
) ;
let ( min_cluster , max_cluster ) =
( min_cluster . min ( max_cluster ) , min_cluster . max ( max_cluster ) ) ;
2022-04-15 02:53:20 +00:00
// What follows is the Iterative Sphere Refinement algorithm from Just Cause 3
// Persson et al, Practical Clustered Shading
// http://newq.net/dl/pub/s2015_practical.pdf
// NOTE: A sphere under perspective projection is no longer a sphere. It gets
// stretched and warped, which prevents simpler algorithms from being correct
// as they often assume that the widest part of the sphere under projection is the
// center point on the axis of interest plus the radius, and that is not true!
let view_light_sphere = Sphere {
center : Vec3A ::from ( inverse_view_transform * light_sphere . center . extend ( 1.0 ) ) ,
radius : light_sphere . radius ,
} ;
2022-07-08 19:57:43 +00:00
let spot_light_dir_sin_cos = light . spot_light_angle . map ( | angle | {
let ( angle_sin , angle_cos ) = angle . sin_cos ( ) ;
(
2022-07-16 00:51:12 +00:00
( inverse_view_transform * light . transform . back ( ) . extend ( 0.0 ) ) . truncate ( ) ,
2022-07-08 19:57:43 +00:00
angle_sin ,
angle_cos ,
)
} ) ;
2022-04-15 02:53:20 +00:00
let light_center_clip =
Camera Driven Viewports (#4898)
# Objective
Users should be able to render cameras to specific areas of a render target, which enables scenarios like split screen, minimaps, etc.
Builds on the new Camera Driven Rendering added here: #4745
Fixes: #202
Alternative to #1389 and #3626 (which are incompatible with the new Camera Driven Rendering)
## Solution
![image](https://user-images.githubusercontent.com/2694663/171560044-f0694f67-0cd9-4598-83e2-a9658c4fed57.png)
Cameras can now configure an optional "viewport", which defines a rectangle within their render target to draw to. If a `Viewport` is defined, the camera's `CameraProjection`, `View`, and visibility calculations will use the viewport configuration instead of the full render target.
```rust
// This camera will render to the first half of the primary window (on the left side).
commands.spawn_bundle(Camera3dBundle {
camera: Camera {
viewport: Some(Viewport {
physical_position: UVec2::new(0, 0),
physical_size: UVec2::new(window.physical_width() / 2, window.physical_height()),
depth: 0.0..1.0,
}),
..default()
},
..default()
});
```
To account for this, the `Camera` component has received a few adjustments:
* `Camera` now has some new getter functions:
* `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, `projection_matrix`
* All computed camera values are now private and live on the `ComputedCameraValues` field (logical/physical width/height, the projection matrix). They are now exposed on `Camera` via getters/setters This wasn't _needed_ for viewports, but it was long overdue.
---
## Changelog
### Added
* `Camera` components now have a `viewport` field, which can be set to draw to a portion of a render target instead of the full target.
* `Camera` component has some new functions: `logical_viewport_size`, `physical_viewport_size`, `logical_target_size`, `physical_target_size`, and `projection_matrix`
* Added a new split_screen example illustrating how to render two cameras to the same scene
## Migration Guide
`Camera::projection_matrix` is no longer a public field. Use the new `Camera::projection_matrix()` method instead:
```rust
// Bevy 0.7
let projection = camera.projection_matrix;
// Bevy 0.8
let projection = camera.projection_matrix();
```
2022-06-05 00:27:49 +00:00
camera . projection_matrix ( ) * view_light_sphere . center . extend ( 1.0 ) ;
2022-04-15 02:53:20 +00:00
let light_center_ndc = light_center_clip . xyz ( ) / light_center_clip . w ;
let cluster_coordinates = ndc_position_to_cluster (
clusters . dimensions ,
cluster_factors ,
is_orthographic ,
light_center_ndc ,
view_light_sphere . center . z ,
) ;
let z_center = if light_center_ndc . z < = 1.0 {
Some ( cluster_coordinates . z )
} else {
None
} ;
let y_center = if light_center_ndc . y > 1.0 {
None
} else if light_center_ndc . y < - 1.0 {
Some ( clusters . dimensions . y + 1 )
} else {
Some ( cluster_coordinates . y )
} ;
for z in min_cluster . z ..= max_cluster . z {
let mut z_light = view_light_sphere . clone ( ) ;
if z_center . is_none ( ) | | z ! = z_center . unwrap ( ) {
// The z plane closer to the light has the larger radius circle where the
// light sphere intersects the z plane.
let z_plane = if z_center . is_some ( ) & & z < z_center . unwrap ( ) {
z_planes [ ( z + 1 ) as usize ]
} else {
z_planes [ z as usize ]
} ;
// Project the sphere to this z plane and use its radius as the radius of a
// new, refined sphere.
if let Some ( projected ) = project_to_plane_z ( z_light , z_plane ) {
z_light = projected ;
} else {
continue ;
}
}
for y in min_cluster . y ..= max_cluster . y {
let mut y_light = z_light . clone ( ) ;
if y_center . is_none ( ) | | y ! = y_center . unwrap ( ) {
// The y plane closer to the light has the larger radius circle where the
// light sphere intersects the y plane.
let y_plane = if y_center . is_some ( ) & & y < y_center . unwrap ( ) {
y_planes [ ( y + 1 ) as usize ]
} else {
y_planes [ y as usize ]
} ;
// Project the refined sphere to this y plane and use its radius as the
// radius of a new, even more refined sphere.
if let Some ( projected ) =
project_to_plane_y ( y_light , y_plane , is_orthographic )
{
y_light = projected ;
} else {
continue ;
2022-03-24 00:20:27 +00:00
}
2021-12-14 03:58:23 +00:00
}
2022-04-15 02:53:20 +00:00
// Loop from the left to find the first affected cluster
let mut min_x = min_cluster . x ;
loop {
if min_x > = max_cluster . x
| | - get_distance_x (
x_planes [ ( min_x + 1 ) as usize ] ,
y_light . center ,
is_orthographic ,
) + y_light . radius
> 0.0
{
break ;
}
min_x + = 1 ;
}
// Loop from the right to find the last affected cluster
let mut max_x = max_cluster . x ;
loop {
if max_x < = min_x
| | get_distance_x (
x_planes [ max_x as usize ] ,
y_light . center ,
is_orthographic ,
) + y_light . radius
> 0.0
{
break ;
}
max_x - = 1 ;
}
let mut cluster_index = ( ( y * clusters . dimensions . x + min_x )
* clusters . dimensions . z
+ z ) as usize ;
2022-07-08 19:57:43 +00:00
if let Some ( ( view_light_direction , angle_sin , angle_cos ) ) =
spot_light_dir_sin_cos
{
for x in min_x ..= max_x {
// further culling for spot lights
// get or initialize cluster bounding sphere
let cluster_aabb_sphere = & mut cluster_aabb_spheres [ cluster_index ] ;
let cluster_aabb_sphere = if let Some ( sphere ) = cluster_aabb_sphere
{
& * sphere
} else {
let aabb = compute_aabb_for_cluster (
first_slice_depth ,
far_z ,
clusters . tile_size . as_vec2 ( ) ,
screen_size . as_vec2 ( ) ,
inverse_projection ,
is_orthographic ,
clusters . dimensions ,
UVec3 ::new ( x , y , z ) ,
) ;
let sphere = Sphere {
center : aabb . center ,
radius : aabb . half_extents . length ( ) ,
} ;
* cluster_aabb_sphere = Some ( sphere ) ;
cluster_aabb_sphere . as_ref ( ) . unwrap ( )
} ;
// test -- based on https://bartwronski.com/2017/04/13/cull-that-cone/
let spot_light_offset = Vec3 ::from (
view_light_sphere . center - cluster_aabb_sphere . center ,
) ;
let spot_light_dist_sq = spot_light_offset . length_squared ( ) ;
let v1_len = spot_light_offset . dot ( view_light_direction ) ;
let distance_closest_point = ( angle_cos
* ( spot_light_dist_sq - v1_len * v1_len ) . sqrt ( ) )
- v1_len * angle_sin ;
let angle_cull =
distance_closest_point > cluster_aabb_sphere . radius ;
let front_cull = v1_len > cluster_aabb_sphere . radius + light . range ;
let back_cull = v1_len < - cluster_aabb_sphere . radius ;
if ! angle_cull & & ! front_cull & & ! back_cull {
// this cluster is affected by the spot light
clusters . lights [ cluster_index ] . entities . push ( light . entity ) ;
clusters . lights [ cluster_index ] . spot_light_count + = 1 ;
}
cluster_index + = clusters . dimensions . z as usize ;
}
} else {
for _ in min_x ..= max_x {
// all clusters within range are affected by point lights
clusters . lights [ cluster_index ] . entities . push ( light . entity ) ;
clusters . lights [ cluster_index ] . point_light_count + = 1 ;
cluster_index + = clusters . dimensions . z as usize ;
}
2022-04-15 02:53:20 +00:00
}
2021-12-14 03:58:23 +00:00
}
}
}
2022-04-15 07:32:21 +00:00
} ;
2021-12-14 03:58:23 +00:00
2022-04-15 07:32:21 +00:00
// reuse existing visible lights Vec, if it exists
if let Some ( visible_lights ) = visible_lights . as_mut ( ) {
visible_lights . entities . clear ( ) ;
update_from_light_intersections ( & mut visible_lights . entities ) ;
} else {
let mut entities = Vec ::new ( ) ;
update_from_light_intersections ( & mut entities ) ;
2022-07-08 19:57:43 +00:00
commands . entity ( view_entity ) . insert ( VisiblePointLights {
entities ,
.. Default ::default ( )
} ) ;
2021-12-14 03:58:23 +00:00
}
}
}
2022-04-15 02:53:20 +00:00
// NOTE: This exploits the fact that a x-plane normal has only x and z components
fn get_distance_x ( plane : Plane , point : Vec3A , is_orthographic : bool ) -> f32 {
if is_orthographic {
point . x - plane . d ( )
} else {
// Distance from a point to a plane:
// signed distance to plane = (nx * px + ny * py + nz * pz + d) / n.length()
// NOTE: For a x-plane, ny and d are 0 and we have a unit normal
// = nx * px + nz * pz
plane . normal_d ( ) . xz ( ) . dot ( point . xz ( ) )
}
}
// NOTE: This exploits the fact that a z-plane normal has only a z component
fn project_to_plane_z ( z_light : Sphere , z_plane : Plane ) -> Option < Sphere > {
// p = sphere center
// n = plane normal
// d = n.p if p is in the plane
// NOTE: For a z-plane, nx and ny are both 0
// d = px * nx + py * ny + pz * nz
// = pz * nz
// => pz = d / nz
let z = z_plane . d ( ) / z_plane . normal_d ( ) . z ;
let distance_to_plane = z - z_light . center . z ;
if distance_to_plane . abs ( ) > z_light . radius {
return None ;
}
Some ( Sphere {
center : Vec3A ::from ( z_light . center . xy ( ) . extend ( z ) ) ,
// hypotenuse length = radius
2023-01-06 00:43:30 +00:00
// pythagoras = (distance to plane)^2 + b^2 = radius^2
2022-04-15 02:53:20 +00:00
radius : ( z_light . radius * z_light . radius - distance_to_plane * distance_to_plane ) . sqrt ( ) ,
} )
}
// NOTE: This exploits the fact that a y-plane normal has only y and z components
fn project_to_plane_y ( y_light : Sphere , y_plane : Plane , is_orthographic : bool ) -> Option < Sphere > {
let distance_to_plane = if is_orthographic {
y_plane . d ( ) - y_light . center . y
} else {
- y_light . center . yz ( ) . dot ( y_plane . normal_d ( ) . yz ( ) )
} ;
if distance_to_plane . abs ( ) > y_light . radius {
return None ;
}
Some ( Sphere {
center : y_light . center + distance_to_plane * y_plane . normal ( ) ,
radius : ( y_light . radius * y_light . radius - distance_to_plane * distance_to_plane ) . sqrt ( ) ,
} )
}
2021-12-14 03:58:23 +00:00
pub fn update_directional_light_frusta (
2022-03-08 01:00:22 +00:00
mut views : Query <
(
2023-01-25 12:35:39 +00:00
& Cascades ,
2022-03-08 01:00:22 +00:00
& DirectionalLight ,
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
& ComputedVisibility ,
2023-01-25 12:35:39 +00:00
& mut CascadesFrusta ,
2022-03-08 01:00:22 +00:00
) ,
2022-10-27 12:56:03 +00:00
(
// Prevents this query from conflicting with camera queries.
Without < Camera > ,
) ,
2022-03-08 01:00:22 +00:00
> ,
2021-12-14 03:58:23 +00:00
) {
2023-01-25 12:35:39 +00:00
for ( cascades , directional_light , visibility , mut frusta ) in & mut views {
2021-12-14 03:58:23 +00:00
// The frustum is used for culling meshes to the light for shadow mapping
// so if shadow mapping is disabled for this light, then the frustum is
// not needed.
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
if ! directional_light . shadows_enabled | | ! visibility . is_visible ( ) {
2021-12-14 03:58:23 +00:00
continue ;
}
2023-01-25 12:35:39 +00:00
frusta . frusta = cascades
. cascades
. iter ( )
. map ( | ( view , cascades ) | {
(
* view ,
cascades
. iter ( )
. map ( | c | Frustum ::from_view_projection ( & c . view_projection ) )
. collect ::< Vec < _ > > ( ) ,
)
} )
. collect ( ) ;
2021-12-14 03:58:23 +00:00
}
}
// NOTE: Run this after assign_lights_to_clusters!
pub fn update_point_light_frusta (
2022-03-24 00:20:27 +00:00
global_lights : Res < GlobalVisiblePointLights > ,
2022-03-08 01:00:22 +00:00
mut views : Query <
( Entity , & GlobalTransform , & PointLight , & mut CubemapFrusta ) ,
Or < ( Changed < GlobalTransform > , Changed < PointLight > ) > ,
> ,
2021-12-14 03:58:23 +00:00
) {
let projection =
Mat4 ::perspective_infinite_reverse_rh ( std ::f32 ::consts ::FRAC_PI_2 , 1.0 , POINT_LIGHT_NEAR_Z ) ;
let view_rotations = CUBE_MAP_FACES
. iter ( )
2022-08-30 22:10:24 +00:00
. map ( | CubeMapFace { target , up } | Transform ::IDENTITY . looking_at ( * target , * up ) )
2021-12-14 03:58:23 +00:00
. collect ::< Vec < _ > > ( ) ;
2022-07-11 15:28:50 +00:00
for ( entity , transform , point_light , mut cubemap_frusta ) in & mut views {
2021-12-14 03:58:23 +00:00
// The frusta are used for culling meshes to the light for shadow mapping
// so if shadow mapping is disabled for this light, then the frusta are
// not needed.
// Also, if the light is not relevant for any cluster, it will not be in the
// global lights set and so there is no need to update its frusta.
2022-03-24 00:20:27 +00:00
if ! point_light . shadows_enabled | | ! global_lights . entities . contains ( & entity ) {
2021-12-14 03:58:23 +00:00
continue ;
}
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
// and ignore rotation because we want the shadow map projections to align with the axes
2022-07-16 00:51:12 +00:00
let view_translation = Transform ::from_translation ( transform . translation ( ) ) ;
2021-12-14 03:58:23 +00:00
let view_backward = transform . back ( ) ;
for ( view_rotation , frustum ) in view_rotations . iter ( ) . zip ( cubemap_frusta . iter_mut ( ) ) {
let view = view_translation * * view_rotation ;
let view_projection = projection * view . compute_matrix ( ) . inverse ( ) ;
2023-01-25 12:35:39 +00:00
* frustum = Frustum ::from_view_projection_custom_far (
2021-12-14 03:58:23 +00:00
& view_projection ,
2022-07-16 00:51:12 +00:00
& transform . translation ( ) ,
2021-12-14 03:58:23 +00:00
& view_backward ,
point_light . range ,
) ;
}
}
}
2022-07-08 19:57:43 +00:00
pub fn update_spot_light_frusta (
global_lights : Res < GlobalVisiblePointLights > ,
mut views : Query <
( Entity , & GlobalTransform , & SpotLight , & mut Frustum ) ,
Or < ( Changed < GlobalTransform > , Changed < SpotLight > ) > ,
> ,
) {
for ( entity , transform , spot_light , mut frustum ) in views . iter_mut ( ) {
// The frusta are used for culling meshes to the light for shadow mapping
// so if shadow mapping is disabled for this light, then the frusta are
// not needed.
// Also, if the light is not relevant for any cluster, it will not be in the
// global lights set and so there is no need to update its frusta.
if ! spot_light . shadows_enabled | | ! global_lights . entities . contains ( & entity ) {
continue ;
}
// ignore scale because we don't want to effectively scale light radius and range
// by applying those as a view transform to shadow map rendering of objects
let view_backward = transform . back ( ) ;
let spot_view = spot_light_view_matrix ( transform ) ;
let spot_projection = spot_light_projection_matrix ( spot_light . outer_angle ) ;
let view_projection = spot_projection * spot_view . inverse ( ) ;
2023-01-25 12:35:39 +00:00
* frustum = Frustum ::from_view_projection_custom_far (
2022-07-08 19:57:43 +00:00
& view_projection ,
2022-07-16 00:51:12 +00:00
& transform . translation ( ) ,
2022-07-08 19:57:43 +00:00
& view_backward ,
spot_light . range ,
) ;
}
}
2021-12-14 03:58:23 +00:00
pub fn check_light_mesh_visibility (
2022-02-04 03:07:22 +00:00
visible_point_lights : Query < & VisiblePointLights > ,
2021-12-14 03:58:23 +00:00
mut point_lights : Query < (
& PointLight ,
& GlobalTransform ,
& CubemapFrusta ,
& mut CubemapVisibleEntities ,
Option < & RenderLayers > ,
) > ,
2022-07-08 19:57:43 +00:00
mut spot_lights : Query < (
& SpotLight ,
& GlobalTransform ,
2021-12-14 03:58:23 +00:00
& Frustum ,
& mut VisibleEntities ,
Option < & RenderLayers > ,
) > ,
2022-07-08 19:57:43 +00:00
mut directional_lights : Query <
(
& DirectionalLight ,
2023-01-25 12:35:39 +00:00
& CascadesFrusta ,
& mut CascadesVisibleEntities ,
2022-07-08 19:57:43 +00:00
Option < & RenderLayers > ,
2023-01-25 12:35:39 +00:00
& mut ComputedVisibility ,
2022-07-08 19:57:43 +00:00
) ,
Without < SpotLight > ,
> ,
2021-12-14 03:58:23 +00:00
mut visible_entity_query : Query <
(
Entity ,
& mut ComputedVisibility ,
Option < & RenderLayers > ,
Option < & Aabb > ,
Option < & GlobalTransform > ,
) ,
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
( Without < NotShadowCaster > , Without < DirectionalLight > ) ,
2021-12-14 03:58:23 +00:00
> ,
) {
2022-10-31 15:36:08 +00:00
fn shrink_entities ( visible_entities : & mut VisibleEntities ) {
// Check that visible entities capacity() is no more than two times greater than len()
let capacity = visible_entities . entities . capacity ( ) ;
let reserved = capacity
. checked_div ( visible_entities . entities . len ( ) )
. map_or ( 0 , | reserve | {
if reserve > 2 {
capacity / ( reserve / 2 )
} else {
capacity
}
} ) ;
visible_entities . entities . shrink_to ( reserved ) ;
}
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
// Directional lights
for (
directional_light ,
2023-01-25 12:35:39 +00:00
frusta ,
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
mut visible_entities ,
maybe_view_mask ,
light_computed_visibility ,
) in & mut directional_lights
2021-12-14 03:58:23 +00:00
{
2023-01-25 12:35:39 +00:00
// Re-use already allocated entries where possible.
let mut views_to_remove = Vec ::new ( ) ;
for ( view , cascade_view_entities ) in visible_entities . entities . iter_mut ( ) {
match frusta . frusta . get ( view ) {
Some ( view_frusta ) = > {
cascade_view_entities . resize ( view_frusta . len ( ) , Default ::default ( ) ) ;
cascade_view_entities
. iter_mut ( )
. for_each ( | x | x . entities . clear ( ) ) ;
}
None = > views_to_remove . push ( * view ) ,
} ;
}
for ( view , frusta ) in frusta . frusta . iter ( ) {
visible_entities
. entities
. entry ( * view )
. or_insert_with ( | | vec! [ VisibleEntities ::default ( ) ; frusta . len ( ) ] ) ;
}
for v in views_to_remove {
visible_entities . entities . remove ( & v ) ;
}
2021-12-14 03:58:23 +00:00
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
if ! directional_light . shadows_enabled | | ! light_computed_visibility . is_visible ( ) {
2021-12-14 03:58:23 +00:00
continue ;
}
let view_mask = maybe_view_mask . copied ( ) . unwrap_or_default ( ) ;
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
for ( entity , mut computed_visibility , maybe_entity_mask , maybe_aabb , maybe_transform ) in
& mut visible_entity_query
2021-12-14 03:58:23 +00:00
{
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
if ! computed_visibility . is_visible_in_hierarchy ( ) {
2021-12-14 03:58:23 +00:00
continue ;
}
let entity_mask = maybe_entity_mask . copied ( ) . unwrap_or_default ( ) ;
if ! view_mask . intersects ( & entity_mask ) {
continue ;
}
// If we have an aabb and transform, do frustum culling
if let ( Some ( aabb ) , Some ( transform ) ) = ( maybe_aabb , maybe_transform ) {
2023-01-25 12:35:39 +00:00
for ( view , view_frusta ) in frusta . frusta . iter ( ) {
let view_visible_entities = visible_entities
. entities
. get_mut ( view )
. expect ( " Per-view visible entities should have been inserted already " ) ;
for ( frustum , frustum_visible_entities ) in
view_frusta . iter ( ) . zip ( view_visible_entities )
{
// Disable near-plane culling, as a shadow caster could lie before the near plane.
if ! frustum . intersects_obb ( aabb , & transform . compute_matrix ( ) , false , true ) {
continue ;
}
computed_visibility . set_visible_in_view ( ) ;
frustum_visible_entities . entities . push ( entity ) ;
}
2021-12-14 03:58:23 +00:00
}
}
}
2023-01-25 12:35:39 +00:00
for ( _ , cascade_view_entities ) in visible_entities . entities . iter_mut ( ) {
cascade_view_entities . iter_mut ( ) . for_each ( shrink_entities ) ;
}
2021-12-14 03:58:23 +00:00
}
2022-07-11 15:28:50 +00:00
for visible_lights in & visible_point_lights {
2021-12-14 03:58:23 +00:00
for light_entity in visible_lights . entities . iter ( ) . copied ( ) {
2022-07-08 19:57:43 +00:00
// Point lights
2021-12-14 03:58:23 +00:00
if let Ok ( (
point_light ,
transform ,
cubemap_frusta ,
mut cubemap_visible_entities ,
maybe_view_mask ,
) ) = point_lights . get_mut ( light_entity )
{
for visible_entities in cubemap_visible_entities . iter_mut ( ) {
visible_entities . entities . clear ( ) ;
}
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
if ! point_light . shadows_enabled {
continue ;
}
let view_mask = maybe_view_mask . copied ( ) . unwrap_or_default ( ) ;
let light_sphere = Sphere {
2022-07-16 00:51:12 +00:00
center : Vec3A ::from ( transform . translation ( ) ) ,
2021-12-14 03:58:23 +00:00
radius : point_light . range ,
} ;
for (
entity ,
mut computed_visibility ,
maybe_entity_mask ,
maybe_aabb ,
maybe_transform ,
2022-07-11 15:28:50 +00:00
) in & mut visible_entity_query
2021-12-14 03:58:23 +00:00
{
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
if ! computed_visibility . is_visible_in_hierarchy ( ) {
2021-12-14 03:58:23 +00:00
continue ;
}
let entity_mask = maybe_entity_mask . copied ( ) . unwrap_or_default ( ) ;
if ! view_mask . intersects ( & entity_mask ) {
continue ;
}
// If we have an aabb and transform, do frustum culling
if let ( Some ( aabb ) , Some ( transform ) ) = ( maybe_aabb , maybe_transform ) {
let model_to_world = transform . compute_matrix ( ) ;
// Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light
if ! light_sphere . intersects_obb ( aabb , & model_to_world ) {
continue ;
}
2022-07-08 19:57:43 +00:00
2021-12-14 03:58:23 +00:00
for ( frustum , visible_entities ) in cubemap_frusta
. iter ( )
. zip ( cubemap_visible_entities . iter_mut ( ) )
{
2023-01-25 12:35:39 +00:00
if frustum . intersects_obb ( aabb , & model_to_world , true , true ) {
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
computed_visibility . set_visible_in_view ( ) ;
2021-12-14 03:58:23 +00:00
visible_entities . entities . push ( entity ) ;
}
}
} else {
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
computed_visibility . set_visible_in_view ( ) ;
2021-12-14 03:58:23 +00:00
for visible_entities in cubemap_visible_entities . iter_mut ( ) {
2022-02-13 22:33:55 +00:00
visible_entities . entities . push ( entity ) ;
2021-12-14 03:58:23 +00:00
}
}
}
2022-10-31 15:36:08 +00:00
for visible_entities in cubemap_visible_entities . iter_mut ( ) {
shrink_entities ( visible_entities ) ;
}
2021-12-14 03:58:23 +00:00
}
2022-07-08 19:57:43 +00:00
2022-10-31 15:36:08 +00:00
// Spot lights
2022-07-08 19:57:43 +00:00
if let Ok ( ( point_light , transform , frustum , mut visible_entities , maybe_view_mask ) ) =
spot_lights . get_mut ( light_entity )
{
visible_entities . entities . clear ( ) ;
// NOTE: If shadow mapping is disabled for the light then it must have no visible entities
if ! point_light . shadows_enabled {
continue ;
}
let view_mask = maybe_view_mask . copied ( ) . unwrap_or_default ( ) ;
let light_sphere = Sphere {
2022-07-16 00:51:12 +00:00
center : Vec3A ::from ( transform . translation ( ) ) ,
2022-07-08 19:57:43 +00:00
radius : point_light . range ,
} ;
for (
entity ,
mut computed_visibility ,
maybe_entity_mask ,
maybe_aabb ,
maybe_transform ,
) in visible_entity_query . iter_mut ( )
{
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
if ! computed_visibility . is_visible_in_hierarchy ( ) {
2022-07-08 19:57:43 +00:00
continue ;
}
let entity_mask = maybe_entity_mask . copied ( ) . unwrap_or_default ( ) ;
if ! view_mask . intersects ( & entity_mask ) {
continue ;
}
// If we have an aabb and transform, do frustum culling
if let ( Some ( aabb ) , Some ( transform ) ) = ( maybe_aabb , maybe_transform ) {
let model_to_world = transform . compute_matrix ( ) ;
// Do a cheap sphere vs obb test to prune out most meshes outside the sphere of the light
if ! light_sphere . intersects_obb ( aabb , & model_to_world ) {
continue ;
}
2023-01-25 12:35:39 +00:00
if frustum . intersects_obb ( aabb , & model_to_world , true , true ) {
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
computed_visibility . set_visible_in_view ( ) ;
2022-07-08 19:57:43 +00:00
visible_entities . entities . push ( entity ) ;
}
} else {
Visibilty Inheritance, universal ComputedVisibility and RenderLayers support (#5310)
# Objective
Fixes #4907. Fixes #838. Fixes #5089.
Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114
Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.
Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information.
## Solution
Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function.
Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate.
This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views.
Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice.
Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite.
![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png)
Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](https://github.com/bevyengine/bevy/pull/5146#issuecomment-1182783452) for rationale.
## Follow up work
* Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?.
* Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset.
* Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc).
---
## Changelog
* ComputedVisibility now takes hierarchy visibility into account.
* 2D, UI and Light entities now use the ComputedVisibility component.
## Migration Guide
If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead:
```rust
// before (0.7)
fn system(query: Query<&Visibility>) {
for visibility in query.iter() {
if visibility.is_visible {
log!("found visible entity");
}
}
}
// after (0.8)
fn system(query: Query<&ComputedVisibility>) {
for visibility in query.iter() {
if visibility.is_visible() {
log!("found visible entity");
}
}
}
```
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2022-07-15 23:24:42 +00:00
computed_visibility . set_visible_in_view ( ) ;
2022-07-08 19:57:43 +00:00
visible_entities . entities . push ( entity ) ;
}
}
2022-10-31 15:36:08 +00:00
shrink_entities ( & mut visible_entities ) ;
2022-07-08 19:57:43 +00:00
}
2021-12-14 03:58:23 +00:00
}
}
}
2022-03-10 01:14:21 +00:00
#[ cfg(test) ]
mod test {
use super ::* ;
fn test_cluster_tiling ( config : ClusterConfig , screen_size : UVec2 ) -> Clusters {
let dims = config . dimensions_for_screen_size ( screen_size ) ;
// note: near & far do not affect tiling
2022-03-24 00:20:27 +00:00
let mut clusters = Clusters ::default ( ) ;
clusters . update ( screen_size , dims ) ;
2022-03-10 01:14:21 +00:00
// check we cover the screen
2022-03-24 00:20:27 +00:00
assert! ( clusters . tile_size . x * clusters . dimensions . x > = screen_size . x ) ;
assert! ( clusters . tile_size . y * clusters . dimensions . y > = screen_size . y ) ;
2022-03-10 01:14:21 +00:00
// check a smaller number of clusters would not cover the screen
2022-03-24 00:20:27 +00:00
assert! ( clusters . tile_size . x * ( clusters . dimensions . x - 1 ) < screen_size . x ) ;
assert! ( clusters . tile_size . y * ( clusters . dimensions . y - 1 ) < screen_size . y ) ;
2022-03-10 01:14:21 +00:00
// check a smaller tilesize would not cover the screen
2022-03-24 00:20:27 +00:00
assert! ( ( clusters . tile_size . x - 1 ) * clusters . dimensions . x < screen_size . x ) ;
assert! ( ( clusters . tile_size . y - 1 ) * clusters . dimensions . y < screen_size . y ) ;
2022-03-10 01:14:21 +00:00
// check we don't have more clusters than pixels
2022-03-24 00:20:27 +00:00
assert! ( clusters . dimensions . x < = screen_size . x ) ;
assert! ( clusters . dimensions . y < = screen_size . y ) ;
2022-03-10 01:14:21 +00:00
clusters
}
#[ test ]
// check tiling for small screen sizes
fn test_default_cluster_setup_small_screensizes ( ) {
for x in 1 .. 100 {
for y in 1 .. 100 {
let screen_size = UVec2 ::new ( x , y ) ;
let clusters = test_cluster_tiling ( ClusterConfig ::default ( ) , screen_size ) ;
assert! (
2022-03-24 00:20:27 +00:00
clusters . dimensions . x * clusters . dimensions . y * clusters . dimensions . z < = 4096
2022-03-10 01:14:21 +00:00
) ;
}
}
}
#[ test ]
// check tiling for long thin screen sizes
fn test_default_cluster_setup_small_x ( ) {
for x in 1 .. 10 {
for y in 1 .. 5000 {
let screen_size = UVec2 ::new ( x , y ) ;
let clusters = test_cluster_tiling ( ClusterConfig ::default ( ) , screen_size ) ;
assert! (
2022-03-24 00:20:27 +00:00
clusters . dimensions . x * clusters . dimensions . y * clusters . dimensions . z < = 4096
2022-03-10 01:14:21 +00:00
) ;
let screen_size = UVec2 ::new ( y , x ) ;
let clusters = test_cluster_tiling ( ClusterConfig ::default ( ) , screen_size ) ;
assert! (
2022-03-24 00:20:27 +00:00
clusters . dimensions . x * clusters . dimensions . y * clusters . dimensions . z < = 4096
2022-03-10 01:14:21 +00:00
) ;
}
}
}
}