bevyengine / bevyengine/bevy

VisibilityRange dithering feature is broken

Open
#19,783 0 comments 0 reactions 0 assignees View on GitHub
A-Rendering C-Bug S-Needs-Investigation
Dominant language
Rust
Stars
48.2k
Forks
4.8k
Avg merge
3d 22h
Merged PRs (30d)
161

Description

## Bevy version

The problem occurs in both `0.16` release and current `main`. I did not test older versions.

System/Adapter Info:
```ignore
SystemInfo { os: "Windows 11 Pro", kernel: "26100", cpu: "Intel(R) Core(TM) i7-8700K CPU @ 3.70GHz", core_count: "6", memory: "15.9 GiB" }
AdapterInfo { name: "NVIDIA GeForce GTX 1080 Ti", vendor: 4318, device: 6918, device_type: DiscreteGpu, driver: "NVIDIA", driver_info: "576.02", backend: Vulkan }
```

## What you did

Spawn more than one gltf model with dithering enabled (via inserting a VisibilityRange component).

## What went wrong

Only the first model receives dithering.

## Additional information

Interestingly, if you spawn a bunch of models very quickly, all that started spawning before the first one finished spawning will have working dithering, and all after that will not.

## Video

![Image](https://github.com/user-attachments/assets/77fcc480-f5cd-4abf-9abd-3281e6761349)

## Code Example

I put together a reduced version of `examples/3d/visibility_range.rs` to demonstrate the problem.

It differs from the original example in that it will only spawn the gltf model when you press the F key.

Press F to spawn the first model, then wait until its visible and press F again to spawn a second copy. Only the first will receive dithering.

Restart the app and then press F key several times very quickly. All those models will receive dithering, but any more you spawn after they are finished will not.

visibility_range.rs

```rs
use std::f32::consts::PI;

use bevy::prelude::*;

use bevy_render::view::VisibilityRange;

const CAMERA_FOCAL_POINT: Vec3 = vec3(0.0, 0.3, 0.0);
const CAMERA_KEYBOARD_ZOOM_SPEED: f32 = 0.05;
const CAMERA_KEYBOARD_PAN_SPEED: f32 = 0.01;
const MIN_ZOOM_DISTANCE: f32 = 0.5;

static VISIBILITY_RANGE: VisibilityRange = VisibilityRange {
start_margin: 0.0..0.0,
end_margin: 0.5..4.0,
use_aabb: false,
};

#[derive(Component, Clone, Copy)]
struct ModelRoot;

fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_systems(Startup, setup)
.add_systems(
Update,
(move_camera, spawn_new_instance, set_visibility_ranges),
)
.run();
}

fn setup(
mut commands: Commands,
mut meshes: ResMut>,
mut materials: ResMut>,
) {
// Spawn a plane.

commands.spawn((
Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0))),
MeshMaterial3d(materials.add(Color::srgb(0.1, 0.2, 0.1))),
));

// Spawn a light.

commands.spawn((
DirectionalLight::default(),
Transform::from_rotation(Quat::from_euler(EulerRot::ZYX, 0.0, PI * -0.15, PI * -0.15)),
));

// Spawn a camera.

commands.spawn((
Camera3d::default(),
Transform::from_xyz(0.7, 0.7, 1.0).looking_at(CAMERA_FOCAL_POINT, Vec3::Y),
));
}

fn move_camera(
keyboard_input: Res>,
cameras: Query<&mut Transform, With>,
) {
let (mut zoom_delta, mut theta_delta) = (0.0, 0.0);

if keyboard_input.pressed(KeyCode::KeyW) {
zoom_delta -= CAMERA_KEYBOARD_ZOOM_SPEED;
} else if keyboard_input.pressed(KeyCode::KeyS) {
zoom_delta += CAMERA_KEYBOARD_ZOOM_SPEED;
}

if keyboard_input.pressed(KeyCode::KeyA) {
theta_delta -= CAMERA_KEYBOARD_PAN_SPEED;
} else if keyboard_input.pressed(KeyCode::KeyD) {
theta_delta += CAMERA_KEYBOARD_PAN_SPEED;
}

// Update the camera transform.
for transform in cameras {
let transform = transform.into_inner();

let direction = transform.translation.normalize_or_zero();
let magnitude = transform.translation.length();

let new_direction = Mat3::from_rotation_y(theta_delta) * direction;
let new_magnitude = (magnitude + zoom_delta).max(MIN_ZOOM_DISTANCE);

transform.translation = new_direction * new_magnitude;
transform.look_at(CAMERA_FOCAL_POINT, Vec3::Y);
}
}

fn spawn_new_instance(
mut commands: Commands,
keyboard_input: Res>,
asset_server: Res,
mut offset: Local,
) {
if keyboard_input.just_pressed(KeyCode::KeyF) {
commands.spawn((
SceneRoot(asset_server.load(
GltfAssetLabel::Scene(0).from_asset("models/FlightHelmet/FlightHelmet.gltf"),
)),
ModelRoot,
Transform::from_translation(vec3(*offset, 0.0, 0.0)),
));
*offset += 0.5;
}
}

// We need to add the `VisibilityRange` components manually, as glTF currently
// has no way to specify visibility ranges. This system watches for new meshes,
// determines which `Scene` they're under, and adds the `VisibilityRange`
// component as appropriate.
fn set_visibility_ranges(
mut commands: Commands,
meshes: Query>,
roots: Query<&ModelRoot>,
children: Query<&ChildOf>,
) {
for mesh in meshes {
for ancestor in children.iter_ancestors(mesh) {
if roots.contains(ancestor) {
info!("Adding visibility range to mesh: {mesh}");
commands.entity(mesh).insert(VISIBILITY_RANGE.clone());
break;
}
}
}
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.