Rename Light => PointLight and remove unused properties (#1778)

After an inquiry on Reddit about support for Directional Lights and the unused properties on Light, I wanted to clean it up, to hopefully make it ever so slightly more clear for anyone wanting to add additional light types.

Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
Jonas Matser 2021-04-13 02:21:24 +00:00
parent 8f1eaa6db5
commit 5c4f3554f9
21 changed files with 66 additions and 79 deletions

View File

@ -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_asset::Handle;
use bevy_ecs::bundle::Bundle; use bevy_ecs::bundle::Bundle;
use bevy_render::{ use bevy_render::{
@ -42,8 +42,8 @@ impl Default for PbrBundle {
/// A component bundle for "light" entities /// A component bundle for "light" entities
#[derive(Debug, Bundle, Default)] #[derive(Debug, Bundle, Default)]
pub struct LightBundle { pub struct PointLightBundle {
pub light: Light, pub point_light: PointLight,
pub transform: Transform, pub transform: Transform,
pub global_transform: GlobalTransform, pub global_transform: GlobalTransform,
} }

View File

@ -9,7 +9,7 @@ pub use light::*;
pub use material::*; pub use material::*;
pub mod prelude { pub mod prelude {
pub use crate::{entity::*, light::Light, material::StandardMaterial}; pub use crate::{entity::*, light::PointLight, material::StandardMaterial};
} }
use bevy_app::prelude::*; use bevy_app::prelude::*;
@ -26,7 +26,7 @@ pub struct PbrPlugin;
impl Plugin for PbrPlugin { impl Plugin for PbrPlugin {
fn build(&self, app: &mut AppBuilder) { fn build(&self, app: &mut AppBuilder) {
app.add_asset::<StandardMaterial>() app.add_asset::<StandardMaterial>()
.register_type::<Light>() .register_type::<PointLight>()
.add_system_to_stage( .add_system_to_stage(
CoreStage::PostUpdate, CoreStage::PostUpdate,
shader::asset_shader_defs_system::<StandardMaterial>.system(), shader::asset_shader_defs_system::<StandardMaterial>.system(),

View File

@ -1,30 +1,22 @@
use bevy_core::Byteable; use bevy_core::Byteable;
use bevy_ecs::reflect::ReflectComponent; use bevy_ecs::reflect::ReflectComponent;
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
use bevy_render::{ use bevy_render::color::Color;
camera::{CameraProjection, PerspectiveProjection},
color::Color,
};
use bevy_transform::components::GlobalTransform; use bevy_transform::components::GlobalTransform;
use std::ops::Range;
/// A point light /// A point light
#[derive(Debug, Reflect)] #[derive(Debug, Reflect)]
#[reflect(Component)] #[reflect(Component)]
pub struct Light { pub struct PointLight {
pub color: Color, pub color: Color,
pub fov: f32,
pub depth: Range<f32>,
pub intensity: f32, pub intensity: f32,
pub range: f32, pub range: f32,
} }
impl Default for Light { impl Default for PointLight {
fn default() -> Self { fn default() -> Self {
Light { PointLight {
color: Color::rgb(1.0, 1.0, 1.0), color: Color::rgb(1.0, 1.0, 1.0),
depth: 0.1..50.0,
fov: f32::to_radians(60.0),
intensity: 200.0, intensity: 200.0,
range: 20.0, range: 20.0,
} }
@ -33,33 +25,25 @@ impl Default for Light {
#[repr(C)] #[repr(C)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct LightRaw { pub(crate) struct PointLightUniform {
pub proj: [[f32; 4]; 4],
pub pos: [f32; 4], pub pos: [f32; 4],
pub color: [f32; 4], pub color: [f32; 4],
pub inverse_range_squared: f32,
} }
unsafe impl Byteable for LightRaw {} unsafe impl Byteable for PointLightUniform {}
impl LightRaw { impl PointLightUniform {
pub fn from(light: &Light, global_transform: &GlobalTransform) -> LightRaw { pub fn from(light: &PointLight, global_transform: &GlobalTransform) -> PointLightUniform {
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();
let (x, y, z) = global_transform.translation.into(); let (x, y, z) = global_transform.translation.into();
// premultiply color by intensity // premultiply color by intensity
// we don't use the alpha at all, so no reason to multiply only [0..3] // 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(); let color: [f32; 4] = (light.color * light.intensity).into();
LightRaw { PointLightUniform {
proj: proj.to_cols_array_2d(), pos: [x, y, z, 1.0],
pos: [x, y, z, 1.0 / (light.range * light.range)], // pos.w is the attenuation.
color, color,
inverse_range_squared: 1.0 / (light.range * light.range),
} }
} }
} }

View File

@ -1,5 +1,5 @@
use crate::{ use crate::{
light::{AmbientLight, Light, LightRaw}, light::{AmbientLight, PointLight, PointLightUniform},
render_graph::uniform, render_graph::uniform,
}; };
use bevy_core::{AsBytes, Byteable}; use bevy_core::{AsBytes, Byteable};
@ -20,13 +20,13 @@ use bevy_transform::prelude::*;
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct LightsNode { pub struct LightsNode {
command_queue: CommandQueue, command_queue: CommandQueue,
max_lights: usize, max_point_lights: usize,
} }
impl LightsNode { impl LightsNode {
pub fn new(max_lights: usize) -> Self { pub fn new(max_lights: usize) -> Self {
LightsNode { LightsNode {
max_lights, max_point_lights: max_lights,
command_queue: CommandQueue::default(), command_queue: CommandQueue::default(),
} }
} }
@ -57,7 +57,7 @@ impl SystemNode for LightsNode {
let system = lights_node_system.system().config(|config| { let system = lights_node_system.system().config(|config| {
config.0 = Some(LightsNodeSystemState { config.0 = Some(LightsNodeSystemState {
command_queue: self.command_queue.clone(), command_queue: self.command_queue.clone(),
max_lights: self.max_lights, max_point_lights: self.max_point_lights,
light_buffer: None, light_buffer: None,
staging_buffer: None, staging_buffer: None,
}) })
@ -72,7 +72,7 @@ pub struct LightsNodeSystemState {
light_buffer: Option<BufferId>, light_buffer: Option<BufferId>,
staging_buffer: Option<BufferId>, staging_buffer: Option<BufferId>,
command_queue: CommandQueue, command_queue: CommandQueue,
max_lights: usize, max_point_lights: usize,
} }
pub fn lights_node_system( 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 // TODO: this write on RenderResourceBindings will prevent this system from running in parallel
// with other systems that do the same // with other systems that do the same
mut render_resource_bindings: ResMut<RenderResourceBindings>, mut render_resource_bindings: ResMut<RenderResourceBindings>,
query: Query<(&Light, &GlobalTransform)>, query: Query<(&PointLight, &GlobalTransform)>,
) { ) {
let state = &mut state; let state = &mut state;
let render_resource_context = &**render_resource_context; let render_resource_context = &**render_resource_context;
@ -91,16 +91,16 @@ pub fn lights_node_system(
let ambient_light: [f32; 4] = let ambient_light: [f32; 4] =
(ambient_light_resource.color * ambient_light_resource.brightness).into(); (ambient_light_resource.color * ambient_light_resource.brightness).into();
let ambient_light_size = std::mem::size_of::<[f32; 4]>(); let ambient_light_size = std::mem::size_of::<[f32; 4]>();
let light_count = query.iter().count(); let point_light_count = query.iter().count();
let size = std::mem::size_of::<LightRaw>(); let size = std::mem::size_of::<PointLightUniform>();
let light_count_size = ambient_light_size + std::mem::size_of::<LightCount>(); let light_count_size = ambient_light_size + std::mem::size_of::<LightCount>();
let light_array_size = size * light_count; let point_light_array_size = size * point_light_count;
let light_array_max_size = size * state.max_lights; let point_light_array_max_size = size * state.max_point_lights;
let current_light_uniform_size = light_count_size + light_array_size; let current_point_light_uniform_size = light_count_size + point_light_array_size;
let max_light_uniform_size = light_count_size + light_array_max_size; let max_light_uniform_size = light_count_size + point_light_array_max_size;
if let Some(staging_buffer) = state.staging_buffer { if let Some(staging_buffer) = state.staging_buffer {
if light_count == 0 { if point_light_count == 0 {
return; return;
} }
@ -132,21 +132,22 @@ pub fn lights_node_system(
let staging_buffer = state.staging_buffer.unwrap(); let staging_buffer = state.staging_buffer.unwrap();
render_resource_context.write_mapped_buffer( render_resource_context.write_mapped_buffer(
staging_buffer, staging_buffer,
0..current_light_uniform_size as u64, 0..current_point_light_uniform_size as u64,
&mut |data, _renderer| { &mut |data, _renderer| {
// ambient light // ambient light
data[0..ambient_light_size].copy_from_slice(ambient_light.as_bytes()); data[0..ambient_light_size].copy_from_slice(ambient_light.as_bytes());
// light count // light count
data[ambient_light_size..light_count_size] 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 // light array
for ((light, global_transform), slot) in query for ((point_light, global_transform), slot) in query.iter().zip(
.iter() data[light_count_size..current_point_light_uniform_size].chunks_exact_mut(size),
.zip(data[light_count_size..current_light_uniform_size].chunks_exact_mut(size)) ) {
{ slot.copy_from_slice(
slot.copy_from_slice(LightRaw::from(&light, &global_transform).as_bytes()); PointLightUniform::from(&point_light, &global_transform).as_bytes(),
);
} }
}, },
); );

View File

@ -26,6 +26,7 @@ use bevy_render::{
}; };
use bevy_transform::prelude::GlobalTransform; use bevy_transform::prelude::GlobalTransform;
pub const MAX_POINT_LIGHTS: usize = 10;
pub(crate) fn add_pbr_graph(world: &mut World) { pub(crate) fn add_pbr_graph(world: &mut World) {
{ {
let mut graph = world.get_resource_mut::<RenderGraph>().unwrap(); let mut graph = world.get_resource_mut::<RenderGraph>().unwrap();
@ -37,7 +38,8 @@ pub(crate) fn add_pbr_graph(world: &mut World) {
node::STANDARD_MATERIAL, node::STANDARD_MATERIAL,
AssetRenderResourcesNode::<StandardMaterial>::new(true), AssetRenderResourcesNode::<StandardMaterial>::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 // TODO: replace these with "autowire" groups
graph graph

View File

@ -36,10 +36,10 @@
const int MAX_LIGHTS = 10; const int MAX_LIGHTS = 10;
struct Light { struct PointLight {
mat4 proj;
vec4 pos; vec4 pos;
vec4 color; vec4 color;
float inverseRangeSquared;
}; };
layout(location = 0) in vec3 v_WorldPosition; 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 { layout(std140, set = 1, binding = 0) uniform Lights {
vec4 AmbientColor; vec4 AmbientColor;
uvec4 NumLights; uvec4 NumLights;
Light SceneLights[MAX_LIGHTS]; PointLight PointLights[MAX_LIGHTS];
}; };
layout(set = 3, binding = 0) uniform StandardMaterial_base_color { 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, // light radius is a non-physical construct for efficiency purposes,
// because otherwise every light affects every fragment in the scene // because otherwise every light affects every fragment in the scene
float getDistanceAttenuation(const vec3 posToLight, float inverseRadiusSquared) { float getDistanceAttenuation(float distanceSquare, float inverseRangeSquared) {
float distanceSquare = dot(posToLight, posToLight); float factor = distanceSquare * inverseRangeSquared;
float factor = distanceSquare * inverseRadiusSquared;
float smoothFactor = saturate(1.0 - factor * factor); float smoothFactor = saturate(1.0 - factor * factor);
float attenuation = smoothFactor * smoothFactor; float attenuation = smoothFactor * smoothFactor;
return attenuation * 1.0 / max(distanceSquare, 1e-4); return attenuation * 1.0 / max(distanceSquare, 1e-4);
@ -343,13 +342,14 @@ void main() {
// accumulate color // accumulate color
vec3 light_accum = vec3(0.0); vec3 light_accum = vec3(0.0);
for (int i = 0; i < int(NumLights.x) && i < MAX_LIGHTS; ++i) { 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 light_to_frag = light.pos.xyz - v_WorldPosition.xyz;
vec3 L = normalize(lightDir); vec3 L = normalize(light_to_frag);
float distance_square = dot(light_to_frag, light_to_frag);
float rangeAttenuation = float rangeAttenuation =
getDistanceAttenuation(lightDir, light.pos.w); getDistanceAttenuation(distance_square, light.inverseRangeSquared);
vec3 H = normalize(L + V); vec3 H = normalize(L + V);
float NoL = saturate(dot(N, L)); float NoL = saturate(dot(N, L));

View File

@ -28,7 +28,7 @@ fn setup(
..Default::default() ..Default::default()
}); });
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0), transform: Transform::from_xyz(4.0, 8.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -20,7 +20,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
..Default::default() ..Default::default()
}); });
commands commands
.spawn_bundle(LightBundle { .spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(3.0, 5.0, 3.0), transform: Transform::from_xyz(3.0, 5.0, 3.0),
..Default::default() ..Default::default()
}) })

View File

@ -25,7 +25,7 @@ fn setup(
..Default::default() ..Default::default()
}); });
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0), transform: Transform::from_xyz(4.0, 8.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -54,7 +54,7 @@ fn setup(
..Default::default() ..Default::default()
}); });
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(3.0, 8.0, 5.0), transform: Transform::from_xyz(3.0, 8.0, 5.0),
..Default::default() ..Default::default()
}); });

View File

@ -52,7 +52,7 @@ fn setup(
}); });
}); });
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, -4.0), transform: Transform::from_xyz(4.0, 5.0, -4.0),
..Default::default() ..Default::default()
}); });

View File

@ -39,7 +39,7 @@ fn setup(
} }
} }
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_translation(Vec3::new(0.0, 5.0, 5.0)), transform: Transform::from_translation(Vec3::new(0.0, 5.0, 5.0)),
..Default::default() ..Default::default()
}); });

View File

@ -41,7 +41,7 @@ fn setup(
mut materials: ResMut<Assets<StandardMaterial>>, mut materials: ResMut<Assets<StandardMaterial>>,
) { ) {
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, -4.0, 5.0), transform: Transform::from_xyz(4.0, -4.0, 5.0),
..Default::default() ..Default::default()
}); });

View File

@ -24,7 +24,7 @@ fn setup(
mut scene_spawner: ResMut<SceneSpawner>, mut scene_spawner: ResMut<SceneSpawner>,
mut scene_instance: ResMut<SceneInstance>, mut scene_instance: ResMut<SceneInstance>,
) { ) {
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0), transform: Transform::from_xyz(4.0, 5.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -46,7 +46,7 @@ fn setup(
// This enables wireframe drawing on this entity // This enables wireframe drawing on this entity
.insert(Wireframe); .insert(Wireframe);
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0), transform: Transform::from_xyz(4.0, 8.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -30,7 +30,7 @@ fn setup(
..Default::default() ..Default::default()
}); });
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0), transform: Transform::from_xyz(4.0, 8.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -65,7 +65,7 @@ fn setup(
..Default::default() ..Default::default()
}); });
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0), transform: Transform::from_xyz(4.0, 5.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -23,7 +23,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
// mesh // mesh
commands.spawn_scene(scene_handle); commands.spawn_scene(scene_handle);
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0), transform: Transform::from_xyz(4.0, 5.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -104,7 +104,7 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>, mut game: ResMu
game.player.i = BOARD_SIZE_I / 2; game.player.i = BOARD_SIZE_I / 2;
game.player.j = BOARD_SIZE_J / 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), transform: Transform::from_xyz(4.0, 5.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -45,7 +45,7 @@ fn setup(
..Default::default() ..Default::default()
}); });
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 8.0, 4.0), transform: Transform::from_xyz(4.0, 8.0, 4.0),
..Default::default() ..Default::default()
}); });

View File

@ -187,7 +187,7 @@ fn setup_pipeline(
// add entities to the world // add entities to the world
commands.spawn_scene(asset_server.load("models/monkey/Monkey.gltf#Scene0")); commands.spawn_scene(asset_server.load("models/monkey/Monkey.gltf#Scene0"));
// light // light
commands.spawn_bundle(LightBundle { commands.spawn_bundle(PointLightBundle {
transform: Transform::from_xyz(4.0, 5.0, 4.0), transform: Transform::from_xyz(4.0, 5.0, 4.0),
..Default::default() ..Default::default()
}); });