diff --git a/crates/bevy_pbr/src/entity.rs b/crates/bevy_pbr/src/entity.rs index fe7ea9d8f7bb6..9b1d3c6314743 100644 --- a/crates/bevy_pbr/src/entity.rs +++ b/crates/bevy_pbr/src/entity.rs @@ -1,4 +1,4 @@ -use crate::{light::Light, material::StandardMaterial, render_graph::PBR_PIPELINE_HANDLE}; +use crate::{light::PointLight, material::StandardMaterial, render_graph::PBR_PIPELINE_HANDLE}; use bevy_asset::Handle; use bevy_ecs::bundle::Bundle; use bevy_render::{ @@ -42,8 +42,8 @@ impl Default for PbrBundle { /// A component bundle for "light" entities #[derive(Debug, Bundle, Default)] -pub struct LightBundle { - pub light: Light, +pub struct PointLightBundle { + pub point_light: PointLight, pub transform: Transform, pub global_transform: GlobalTransform, } diff --git a/crates/bevy_pbr/src/lib.rs b/crates/bevy_pbr/src/lib.rs index cb130ab9d9a8f..c80a6f37f875d 100644 --- a/crates/bevy_pbr/src/lib.rs +++ b/crates/bevy_pbr/src/lib.rs @@ -9,7 +9,7 @@ pub use light::*; pub use material::*; pub mod prelude { - pub use crate::{entity::*, light::Light, material::StandardMaterial}; + pub use crate::{entity::*, light::PointLight, material::StandardMaterial}; } use bevy_app::prelude::*; @@ -26,7 +26,7 @@ pub struct PbrPlugin; impl Plugin for PbrPlugin { fn build(&self, app: &mut AppBuilder) { app.add_asset::() - .register_type::() + .register_type::() .add_system_to_stage( CoreStage::PostUpdate, shader::asset_shader_defs_system::.system(), diff --git a/crates/bevy_pbr/src/light.rs b/crates/bevy_pbr/src/light.rs index 149bfd92b2551..046ebd60c2a01 100644 --- a/crates/bevy_pbr/src/light.rs +++ b/crates/bevy_pbr/src/light.rs @@ -1,30 +1,22 @@ use bevy_core::Byteable; use bevy_ecs::reflect::ReflectComponent; use bevy_reflect::Reflect; -use bevy_render::{ - camera::{CameraProjection, PerspectiveProjection}, - color::Color, -}; +use bevy_render::color::Color; use bevy_transform::components::GlobalTransform; -use std::ops::Range; /// A point light #[derive(Debug, Reflect)] #[reflect(Component)] -pub struct Light { +pub struct PointLight { pub color: Color, - pub fov: f32, - pub depth: Range, pub intensity: f32, pub range: f32, } -impl Default for Light { +impl Default for PointLight { fn default() -> Self { - Light { + PointLight { color: Color::rgb(1.0, 1.0, 1.0), - depth: 0.1..50.0, - fov: f32::to_radians(60.0), intensity: 200.0, range: 20.0, } @@ -33,33 +25,25 @@ impl Default for Light { #[repr(C)] #[derive(Debug, Clone, Copy)] -pub(crate) struct LightRaw { - pub proj: [[f32; 4]; 4], +pub(crate) struct PointLightUniform { pub pos: [f32; 4], pub color: [f32; 4], + pub inverse_range_squared: f32, } -unsafe impl Byteable for LightRaw {} +unsafe impl Byteable for PointLightUniform {} -impl LightRaw { - pub fn from(light: &Light, global_transform: &GlobalTransform) -> LightRaw { - let perspective = PerspectiveProjection { - fov: light.fov, - aspect_ratio: 1.0, - near: light.depth.start, - far: light.depth.end, - }; - - let proj = perspective.get_projection_matrix() * global_transform.compute_matrix(); +impl PointLightUniform { + pub fn from(light: &PointLight, global_transform: &GlobalTransform) -> PointLightUniform { let (x, y, z) = global_transform.translation.into(); // premultiply color by intensity // we don't use the alpha at all, so no reason to multiply only [0..3] let color: [f32; 4] = (light.color * light.intensity).into(); - LightRaw { - proj: proj.to_cols_array_2d(), - pos: [x, y, z, 1.0 / (light.range * light.range)], // pos.w is the attenuation. + PointLightUniform { + pos: [x, y, z, 1.0], color, + inverse_range_squared: 1.0 / (light.range * light.range), } } } diff --git a/crates/bevy_pbr/src/render_graph/lights_node.rs b/crates/bevy_pbr/src/render_graph/lights_node.rs index 61bf5d04d3311..709a818286150 100644 --- a/crates/bevy_pbr/src/render_graph/lights_node.rs +++ b/crates/bevy_pbr/src/render_graph/lights_node.rs @@ -1,5 +1,5 @@ use crate::{ - light::{AmbientLight, Light, LightRaw}, + light::{AmbientLight, PointLight, PointLightUniform}, render_graph::uniform, }; use bevy_core::{AsBytes, Byteable}; @@ -20,13 +20,13 @@ use bevy_transform::prelude::*; #[derive(Debug, Default)] pub struct LightsNode { command_queue: CommandQueue, - max_lights: usize, + max_point_lights: usize, } impl LightsNode { pub fn new(max_lights: usize) -> Self { LightsNode { - max_lights, + max_point_lights: max_lights, command_queue: CommandQueue::default(), } } @@ -57,7 +57,7 @@ impl SystemNode for LightsNode { let system = lights_node_system.system().config(|config| { config.0 = Some(LightsNodeSystemState { command_queue: self.command_queue.clone(), - max_lights: self.max_lights, + max_point_lights: self.max_point_lights, light_buffer: None, staging_buffer: None, }) @@ -72,7 +72,7 @@ pub struct LightsNodeSystemState { light_buffer: Option, staging_buffer: Option, command_queue: CommandQueue, - max_lights: usize, + max_point_lights: usize, } pub fn lights_node_system( @@ -82,7 +82,7 @@ pub fn lights_node_system( // TODO: this write on RenderResourceBindings will prevent this system from running in parallel // with other systems that do the same mut render_resource_bindings: ResMut, - query: Query<(&Light, &GlobalTransform)>, + query: Query<(&PointLight, &GlobalTransform)>, ) { let state = &mut state; let render_resource_context = &**render_resource_context; @@ -91,16 +91,16 @@ pub fn lights_node_system( let ambient_light: [f32; 4] = (ambient_light_resource.color * ambient_light_resource.brightness).into(); let ambient_light_size = std::mem::size_of::<[f32; 4]>(); - let light_count = query.iter().count(); - let size = std::mem::size_of::(); + let point_light_count = query.iter().count(); + let size = std::mem::size_of::(); let light_count_size = ambient_light_size + std::mem::size_of::(); - let light_array_size = size * light_count; - let light_array_max_size = size * state.max_lights; - let current_light_uniform_size = light_count_size + light_array_size; - let max_light_uniform_size = light_count_size + light_array_max_size; + let point_light_array_size = size * point_light_count; + let point_light_array_max_size = size * state.max_point_lights; + let current_point_light_uniform_size = light_count_size + point_light_array_size; + let max_light_uniform_size = light_count_size + point_light_array_max_size; if let Some(staging_buffer) = state.staging_buffer { - if light_count == 0 { + if point_light_count == 0 { return; } @@ -132,21 +132,22 @@ pub fn lights_node_system( let staging_buffer = state.staging_buffer.unwrap(); render_resource_context.write_mapped_buffer( staging_buffer, - 0..current_light_uniform_size as u64, + 0..current_point_light_uniform_size as u64, &mut |data, _renderer| { // ambient light data[0..ambient_light_size].copy_from_slice(ambient_light.as_bytes()); // light count data[ambient_light_size..light_count_size] - .copy_from_slice([light_count as u32, 0, 0, 0].as_bytes()); + .copy_from_slice([point_light_count as u32, 0, 0, 0].as_bytes()); // light array - for ((light, global_transform), slot) in query - .iter() - .zip(data[light_count_size..current_light_uniform_size].chunks_exact_mut(size)) - { - slot.copy_from_slice(LightRaw::from(&light, &global_transform).as_bytes()); + for ((point_light, global_transform), slot) in query.iter().zip( + data[light_count_size..current_point_light_uniform_size].chunks_exact_mut(size), + ) { + slot.copy_from_slice( + PointLightUniform::from(&point_light, &global_transform).as_bytes(), + ); } }, ); diff --git a/crates/bevy_pbr/src/render_graph/mod.rs b/crates/bevy_pbr/src/render_graph/mod.rs index 507cd3fdb2347..55e3fbaa868d4 100644 --- a/crates/bevy_pbr/src/render_graph/mod.rs +++ b/crates/bevy_pbr/src/render_graph/mod.rs @@ -26,6 +26,7 @@ use bevy_render::{ }; use bevy_transform::prelude::GlobalTransform; +pub const MAX_POINT_LIGHTS: usize = 10; pub(crate) fn add_pbr_graph(world: &mut World) { { let mut graph = world.get_resource_mut::().unwrap(); @@ -37,7 +38,8 @@ pub(crate) fn add_pbr_graph(world: &mut World) { node::STANDARD_MATERIAL, AssetRenderResourcesNode::::new(true), ); - graph.add_system_node(node::LIGHTS, LightsNode::new(10)); + + graph.add_system_node(node::LIGHTS, LightsNode::new(MAX_POINT_LIGHTS)); // TODO: replace these with "autowire" groups graph diff --git a/crates/bevy_pbr/src/render_graph/pbr_pipeline/pbr.frag b/crates/bevy_pbr/src/render_graph/pbr_pipeline/pbr.frag index 790b50af2fd89..2730b5f2dafff 100644 --- a/crates/bevy_pbr/src/render_graph/pbr_pipeline/pbr.frag +++ b/crates/bevy_pbr/src/render_graph/pbr_pipeline/pbr.frag @@ -36,10 +36,10 @@ const int MAX_LIGHTS = 10; -struct Light { - mat4 proj; +struct PointLight { vec4 pos; vec4 color; + float inverseRangeSquared; }; layout(location = 0) in vec3 v_WorldPosition; @@ -62,7 +62,7 @@ layout(std140, set = 0, binding = 1) uniform CameraPosition { layout(std140, set = 1, binding = 0) uniform Lights { vec4 AmbientColor; uvec4 NumLights; - Light SceneLights[MAX_LIGHTS]; + PointLight PointLights[MAX_LIGHTS]; }; layout(set = 3, binding = 0) uniform StandardMaterial_base_color { @@ -130,9 +130,8 @@ float pow5(float x) { // // light radius is a non-physical construct for efficiency purposes, // because otherwise every light affects every fragment in the scene -float getDistanceAttenuation(const vec3 posToLight, float inverseRadiusSquared) { - float distanceSquare = dot(posToLight, posToLight); - float factor = distanceSquare * inverseRadiusSquared; +float getDistanceAttenuation(float distanceSquare, float inverseRangeSquared) { + float factor = distanceSquare * inverseRangeSquared; float smoothFactor = saturate(1.0 - factor * factor); float attenuation = smoothFactor * smoothFactor; return attenuation * 1.0 / max(distanceSquare, 1e-4); @@ -343,13 +342,14 @@ void main() { // accumulate color vec3 light_accum = vec3(0.0); for (int i = 0; i < int(NumLights.x) && i < MAX_LIGHTS; ++i) { - Light light = SceneLights[i]; + PointLight light = PointLights[i]; - vec3 lightDir = light.pos.xyz - v_WorldPosition.xyz; - vec3 L = normalize(lightDir); + vec3 light_to_frag = light.pos.xyz - v_WorldPosition.xyz; + vec3 L = normalize(light_to_frag); + float distance_square = dot(light_to_frag, light_to_frag); float rangeAttenuation = - getDistanceAttenuation(lightDir, light.pos.w); + getDistanceAttenuation(distance_square, light.inverseRangeSquared); vec3 H = normalize(L + V); float NoL = saturate(dot(N, L)); diff --git a/examples/3d/3d_scene.rs b/examples/3d/3d_scene.rs index d6aebe9f12498..3aec65a2e82b9 100644 --- a/examples/3d/3d_scene.rs +++ b/examples/3d/3d_scene.rs @@ -28,7 +28,7 @@ fn setup( ..Default::default() }); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 8.0, 4.0), ..Default::default() }); diff --git a/examples/3d/load_gltf.rs b/examples/3d/load_gltf.rs index 22409d7ffa9a4..b2eae05b26d2d 100644 --- a/examples/3d/load_gltf.rs +++ b/examples/3d/load_gltf.rs @@ -20,7 +20,7 @@ fn setup(mut commands: Commands, asset_server: Res) { ..Default::default() }); commands - .spawn_bundle(LightBundle { + .spawn_bundle(PointLightBundle { transform: Transform::from_xyz(3.0, 5.0, 3.0), ..Default::default() }) diff --git a/examples/3d/msaa.rs b/examples/3d/msaa.rs index c4ae861eaf2b5..6036600aa2d44 100644 --- a/examples/3d/msaa.rs +++ b/examples/3d/msaa.rs @@ -25,7 +25,7 @@ fn setup( ..Default::default() }); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 8.0, 4.0), ..Default::default() }); diff --git a/examples/3d/orthographic.rs b/examples/3d/orthographic.rs index cc258298894d8..6e0f7ac5fd9d8 100644 --- a/examples/3d/orthographic.rs +++ b/examples/3d/orthographic.rs @@ -54,7 +54,7 @@ fn setup( ..Default::default() }); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(3.0, 8.0, 5.0), ..Default::default() }); diff --git a/examples/3d/parenting.rs b/examples/3d/parenting.rs index 482e2e08267f0..9891b12dda118 100644 --- a/examples/3d/parenting.rs +++ b/examples/3d/parenting.rs @@ -52,7 +52,7 @@ fn setup( }); }); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 5.0, -4.0), ..Default::default() }); diff --git a/examples/3d/pbr.rs b/examples/3d/pbr.rs index ac0d19137a0b2..f4f99361d4ee3 100644 --- a/examples/3d/pbr.rs +++ b/examples/3d/pbr.rs @@ -39,7 +39,7 @@ fn setup( } } // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_translation(Vec3::new(0.0, 5.0, 5.0)), ..Default::default() }); diff --git a/examples/3d/spawner.rs b/examples/3d/spawner.rs index 16f8f1f8c4a9d..5b8433ae7ced3 100644 --- a/examples/3d/spawner.rs +++ b/examples/3d/spawner.rs @@ -41,7 +41,7 @@ fn setup( mut materials: ResMut>, ) { // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, -4.0, 5.0), ..Default::default() }); diff --git a/examples/3d/update_gltf_scene.rs b/examples/3d/update_gltf_scene.rs index 2e0c9b2b52596..c552b82727c30 100644 --- a/examples/3d/update_gltf_scene.rs +++ b/examples/3d/update_gltf_scene.rs @@ -24,7 +24,7 @@ fn setup( mut scene_spawner: ResMut, mut scene_instance: ResMut, ) { - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 5.0, 4.0), ..Default::default() }); diff --git a/examples/3d/wireframe.rs b/examples/3d/wireframe.rs index 0eba795731e28..1b861f91ff44d 100644 --- a/examples/3d/wireframe.rs +++ b/examples/3d/wireframe.rs @@ -46,7 +46,7 @@ fn setup( // This enables wireframe drawing on this entity .insert(Wireframe); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 8.0, 4.0), ..Default::default() }); diff --git a/examples/android/android.rs b/examples/android/android.rs index da256f7b0e622..2793b74b24fd8 100644 --- a/examples/android/android.rs +++ b/examples/android/android.rs @@ -30,7 +30,7 @@ fn setup( ..Default::default() }); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 8.0, 4.0), ..Default::default() }); diff --git a/examples/asset/asset_loading.rs b/examples/asset/asset_loading.rs index 7fe449601f293..5fe14b7272a74 100644 --- a/examples/asset/asset_loading.rs +++ b/examples/asset/asset_loading.rs @@ -65,7 +65,7 @@ fn setup( ..Default::default() }); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 5.0, 4.0), ..Default::default() }); diff --git a/examples/asset/hot_asset_reloading.rs b/examples/asset/hot_asset_reloading.rs index e8ec61cc928c0..ebe9a52e8acc0 100644 --- a/examples/asset/hot_asset_reloading.rs +++ b/examples/asset/hot_asset_reloading.rs @@ -23,7 +23,7 @@ fn setup(mut commands: Commands, asset_server: Res) { // mesh commands.spawn_scene(scene_handle); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 5.0, 4.0), ..Default::default() }); diff --git a/examples/game/alien_cake_addict.rs b/examples/game/alien_cake_addict.rs index e0f574713e547..3841552d7c106 100644 --- a/examples/game/alien_cake_addict.rs +++ b/examples/game/alien_cake_addict.rs @@ -104,7 +104,7 @@ fn setup(mut commands: Commands, asset_server: Res, mut game: ResMu game.player.i = BOARD_SIZE_I / 2; game.player.j = BOARD_SIZE_J / 2; - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 5.0, 4.0), ..Default::default() }); diff --git a/examples/ios/src/lib.rs b/examples/ios/src/lib.rs index 4509353c6b544..e01be8fddd2be 100644 --- a/examples/ios/src/lib.rs +++ b/examples/ios/src/lib.rs @@ -45,7 +45,7 @@ fn setup( ..Default::default() }); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 8.0, 4.0), ..Default::default() }); diff --git a/examples/window/multiple_windows.rs b/examples/window/multiple_windows.rs index e8b9d73bd11b3..14e33ea034d34 100644 --- a/examples/window/multiple_windows.rs +++ b/examples/window/multiple_windows.rs @@ -187,7 +187,7 @@ fn setup_pipeline( // add entities to the world commands.spawn_scene(asset_server.load("models/monkey/Monkey.gltf#Scene0")); // light - commands.spawn_bundle(LightBundle { + commands.spawn_bundle(PointLightBundle { transform: Transform::from_xyz(4.0, 5.0, 4.0), ..Default::default() });