bevy_render: Provide a way to opt-out of the built-in frustum culling (#3711)

# Objective

- Allow opting-out of the built-in frustum culling for cases where its behaviour would be incorrect
- Make use of the this in the shader_instancing example that uses a custom instancing method. The built-in frustum culling breaks the custom instancing in the shader_instancing example if the camera is moved to:

```rust
    commands.spawn_bundle(PerspectiveCameraBundle {
        transform: Transform::from_xyz(12.0, 0.0, 15.0)
            .looking_at(Vec3::new(12.0, 0.0, 0.0), Vec3::Y),
        ..Default::default()
    });
```

...such that the Aabb of the cube Mesh that is at the origin goes completely out of view. This incorrectly (for the purpose of the custom instancing) culls the `Mesh` and so culls all instances even though some may be visible.


## Solution

- Add a `NoFrustumCulling` marker component
- Do not compute and add an `Aabb` to `Mesh` entities without an `Aabb` if they have a `NoFrustumCulling` marker component
- Do not apply frustum culling to entities with the `NoFrustumCulling` marker component
This commit is contained in:
Robert Swain 2022-01-17 22:55:44 +00:00
parent e88e394feb
commit 55da315432
2 changed files with 19 additions and 3 deletions

View File

@ -40,6 +40,10 @@ impl Default for ComputedVisibility {
} }
} }
/// Use this component to opt-out of built-in frustum culling for Mesh entities
#[derive(Component)]
pub struct NoFrustumCulling;
#[derive(Clone, Component, Default, Debug, Reflect)] #[derive(Clone, Component, Default, Debug, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct VisibleEntities { pub struct VisibleEntities {
@ -106,7 +110,7 @@ impl Plugin for VisibilityPlugin {
pub fn calculate_bounds( pub fn calculate_bounds(
mut commands: Commands, mut commands: Commands,
meshes: Res<Assets<Mesh>>, meshes: Res<Assets<Mesh>>,
without_aabb: Query<(Entity, &Handle<Mesh>), Without<Aabb>>, without_aabb: Query<(Entity, &Handle<Mesh>), (Without<Aabb>, Without<NoFrustumCulling>)>,
) { ) {
for (entity, mesh_handle) in without_aabb.iter() { for (entity, mesh_handle) in without_aabb.iter() {
if let Some(mesh) = meshes.get(mesh_handle) { if let Some(mesh) = meshes.get(mesh_handle) {
@ -142,6 +146,7 @@ pub fn check_visibility(
&mut ComputedVisibility, &mut ComputedVisibility,
Option<&RenderLayers>, Option<&RenderLayers>,
Option<&Aabb>, Option<&Aabb>,
Option<&NoFrustumCulling>,
Option<&GlobalTransform>, Option<&GlobalTransform>,
)>, )>,
)>, )>,
@ -161,6 +166,7 @@ pub fn check_visibility(
mut computed_visibility, mut computed_visibility,
maybe_entity_mask, maybe_entity_mask,
maybe_aabb, maybe_aabb,
maybe_no_frustum_culling,
maybe_transform, maybe_transform,
) in visible_entity_query.q1().iter_mut() ) in visible_entity_query.q1().iter_mut()
{ {
@ -174,7 +180,9 @@ pub fn check_visibility(
} }
// If we have an aabb and transform, do frustum culling // If we have an aabb and transform, do frustum culling
if let (Some(aabb), Some(transform)) = (maybe_aabb, maybe_transform) { if let (Some(aabb), None, Some(transform)) =
(maybe_aabb, maybe_no_frustum_culling, maybe_transform)
{
if !frustum.intersects_obb(aabb, &transform.compute_matrix()) { if !frustum.intersects_obb(aabb, &transform.compute_matrix()) {
continue; continue;
} }

View File

@ -14,7 +14,7 @@ use bevy::{
}, },
render_resource::*, render_resource::*,
renderer::RenderDevice, renderer::RenderDevice,
view::{ComputedVisibility, ExtractedView, Msaa, Visibility}, view::{ComputedVisibility, ExtractedView, Msaa, NoFrustumCulling, Visibility},
RenderApp, RenderStage, RenderApp, RenderStage,
}, },
}; };
@ -45,6 +45,14 @@ fn setup(mut commands: Commands, mut meshes: ResMut<Assets<Mesh>>) {
), ),
Visibility::default(), Visibility::default(),
ComputedVisibility::default(), ComputedVisibility::default(),
// NOTE: Frustum culling is done based on the Aabb of the Mesh and the GlobalTransform.
// As the cube is at the origin, if its Aabb moves outside the view frustum, all the
// instanced cubes will be culled.
// The InstanceMaterialData contains the 'GlobalTransform' information for this custom
// instancing, and that is not taken into account with the built-in frustum culling.
// We must disable the built-in frustum culling by adding the `NoFrustumCulling` marker
// component to avoid incorrect culling.
NoFrustumCulling,
)); ));
// camera // camera