From ffad06519047d86d10fdc4f1e48f7a7c01b815a7 Mon Sep 17 00:00:00 2001 From: Robert Swain Date: Wed, 23 Mar 2022 18:52:59 +0100 Subject: [PATCH] examples: Add many_lights example for testing many point lights --- Cargo.toml | 4 + examples/3d/many_lights.rs | 166 +++++++++++++++++++++++++++++++++++++ examples/README.md | 1 + 3 files changed, 171 insertions(+) create mode 100644 examples/3d/many_lights.rs diff --git a/Cargo.toml b/Cargo.toml index 6577c9317ea953..7e771ddc56d478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -191,6 +191,10 @@ path = "examples/3d/manual_gltf_animation_player.rs" name = "many_cubes" path = "examples/3d/many_cubes.rs" +[[example]] +name = "many_lights" +path = "examples/3d/many_lights.rs" + [[example]] name = "msaa" path = "examples/3d/msaa.rs" diff --git a/examples/3d/many_lights.rs b/examples/3d/many_lights.rs new file mode 100644 index 00000000000000..6134b459de082d --- /dev/null +++ b/examples/3d/many_lights.rs @@ -0,0 +1,166 @@ +use bevy::{ + diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, + math::{DVec2, DVec3}, + pbr::{ExtractedPointLight, GlobalLightMeta}, + prelude::*, + render::{RenderApp, RenderStage}, +}; + +fn main() { + App::new() + .insert_resource(WindowDescriptor { + width: 1024.0, + height: 768.0, + title: "many_lights".to_string(), + present_mode: bevy::window::PresentMode::Immediate, + ..default() + }) + .add_plugins(DefaultPlugins) + .add_plugin(FrameTimeDiagnosticsPlugin::default()) + .add_plugin(LogDiagnosticsPlugin::default()) + .add_startup_system(setup) + .add_system(move_camera) + .add_system(print_light_count) + .add_plugin(LogVisibleLights) + .run(); +} + +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, +) { + const LIGHT_RADIUS: f32 = 0.3; + const LIGHT_INTENSITY: f32 = 5.0; + const RADIUS: f32 = 50.0; + const N_LIGHTS: usize = 100_000; + + commands.spawn_bundle(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Icosphere { + radius: RADIUS, + subdivisions: 9, + })), + material: materials.add(StandardMaterial::from(Color::WHITE)), + transform: Transform::from_scale(Vec3::splat(-1.0)), + ..default() + }); + + let mesh = meshes.add(Mesh::from(shape::Cube { size: 1.0 })); + let material = materials.add(StandardMaterial { + base_color: Color::PINK, + ..default() + }); + + // NOTE: This pattern is good for testing performance of culling as it provides roughly + // the same number of visible meshes regardless of the viewing angle. + // NOTE: f64 is used to avoid precision issues that produce visual artifacts in the distribution + let golden_ratio = 0.5f64 * (1.0f64 + 5.0f64.sqrt()); + for i in 0..N_LIGHTS { + let spherical_polar_theta_phi = fibonacci_spiral_on_sphere(golden_ratio, i, N_LIGHTS); + let unit_sphere_p = spherical_polar_to_cartesian(spherical_polar_theta_phi); + commands.spawn_bundle(PointLightBundle { + point_light: PointLight { + range: LIGHT_RADIUS, + intensity: LIGHT_INTENSITY, + ..default() + }, + transform: Transform::from_translation((RADIUS as f64 * unit_sphere_p).as_vec3()), + ..default() + }); + } + + // camera + commands.spawn_bundle(PerspectiveCameraBundle::default()); + + // add one cube, the only one with strong handles + // also serves as a reference point during rotation + commands.spawn_bundle(PbrBundle { + mesh, + material, + transform: Transform { + translation: Vec3::new(0.0, RADIUS as f32, 0.0), + scale: Vec3::splat(5.0), + ..default() + }, + ..default() + }); +} + +// NOTE: This epsilon value is apparently optimal for optimizing for the average +// nearest-neighbor distance. See: +// http://extremelearning.com.au/how-to-evenly-distribute-points-on-a-sphere-more-effectively-than-the-canonical-fibonacci-lattice/ +// for details. +const EPSILON: f64 = 0.36; +fn fibonacci_spiral_on_sphere(golden_ratio: f64, i: usize, n: usize) -> DVec2 { + DVec2::new( + 2.0 * std::f64::consts::PI * (i as f64 / golden_ratio), + (1.0 - 2.0 * (i as f64 + EPSILON) / (n as f64 - 1.0 + 2.0 * EPSILON)).acos(), + ) +} + +fn spherical_polar_to_cartesian(p: DVec2) -> DVec3 { + let (sin_theta, cos_theta) = p.x.sin_cos(); + let (sin_phi, cos_phi) = p.y.sin_cos(); + DVec3::new(cos_theta * sin_phi, sin_theta * sin_phi, cos_phi) +} + +// System for rotating the camera +fn move_camera(time: Res