Directional light (#2112)
This PR adds a `DirectionalLight` component to bevy_pbr.
This commit is contained in:
		
							parent
							
								
									d1f40148fd
								
							
						
					
					
						commit
						73f4a9d18f
					
				| @ -10,7 +10,11 @@ pub use material::*; | |||||||
| 
 | 
 | ||||||
| pub mod prelude { | pub mod prelude { | ||||||
|     #[doc(hidden)] |     #[doc(hidden)] | ||||||
|     pub use crate::{entity::*, light::PointLight, material::StandardMaterial}; |     pub use crate::{ | ||||||
|  |         entity::*, | ||||||
|  |         light::{DirectionalLight, PointLight}, | ||||||
|  |         material::StandardMaterial, | ||||||
|  |     }; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| use bevy_app::prelude::*; | use bevy_app::prelude::*; | ||||||
|  | |||||||
| @ -1,11 +1,12 @@ | |||||||
| use bevy_core::Byteable; | use bevy_core::Byteable; | ||||||
| use bevy_ecs::reflect::ReflectComponent; | use bevy_ecs::reflect::ReflectComponent; | ||||||
|  | use bevy_math::Vec3; | ||||||
| use bevy_reflect::Reflect; | use bevy_reflect::Reflect; | ||||||
| use bevy_render::color::Color; | use bevy_render::color::Color; | ||||||
| use bevy_transform::components::GlobalTransform; | use bevy_transform::components::GlobalTransform; | ||||||
| 
 | 
 | ||||||
| /// A point light
 | /// A point light
 | ||||||
| #[derive(Debug, Reflect)] | #[derive(Debug, Clone, Copy, Reflect)] | ||||||
| #[reflect(Component)] | #[reflect(Component)] | ||||||
| pub struct PointLight { | pub struct PointLight { | ||||||
|     pub color: Color, |     pub color: Color, | ||||||
| @ -37,7 +38,7 @@ pub(crate) struct PointLightUniform { | |||||||
| unsafe impl Byteable for PointLightUniform {} | unsafe impl Byteable for PointLightUniform {} | ||||||
| 
 | 
 | ||||||
| impl PointLightUniform { | impl PointLightUniform { | ||||||
|     pub fn from(light: &PointLight, global_transform: &GlobalTransform) -> PointLightUniform { |     pub fn new(light: &PointLight, global_transform: &GlobalTransform) -> PointLightUniform { | ||||||
|         let (x, y, z) = global_transform.translation.into(); |         let (x, y, z) = global_transform.translation.into(); | ||||||
| 
 | 
 | ||||||
|         // premultiply color by intensity
 |         // premultiply color by intensity
 | ||||||
| @ -52,6 +53,109 @@ impl PointLightUniform { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// A Directional light.
 | ||||||
|  | ///
 | ||||||
|  | /// Directional lights don't exist in reality but they are a good
 | ||||||
|  | /// approximation for light sources VERY far away, like the sun or
 | ||||||
|  | /// the moon.
 | ||||||
|  | ///
 | ||||||
|  | /// Valid values for `illuminance` are:
 | ||||||
|  | ///
 | ||||||
|  | /// | Illuminance (lux) | Surfaces illuminated by                        |
 | ||||||
|  | /// |-------------------|------------------------------------------------|
 | ||||||
|  | /// | 0.0001            | Moonless, overcast night sky (starlight)       |
 | ||||||
|  | /// | 0.002             | Moonless clear night sky with airglow          |
 | ||||||
|  | /// | 0.05–0.3          | Full moon on a clear night                     |
 | ||||||
|  | /// | 3.4               | Dark limit of civil twilight under a clear sky |
 | ||||||
|  | /// | 20–50             | Public areas with dark surroundings            |
 | ||||||
|  | /// | 50                | Family living room lights                      |
 | ||||||
|  | /// | 80                | Office building hallway/toilet lighting        |
 | ||||||
|  | /// | 100               | Very dark overcast day                         |
 | ||||||
|  | /// | 150               | Train station platforms                        |
 | ||||||
|  | /// | 320–500           | Office lighting                                |
 | ||||||
|  | /// | 400               | Sunrise or sunset on a clear day.              |
 | ||||||
|  | /// | 1000              | Overcast day; typical TV studio lighting       |
 | ||||||
|  | /// | 10,000–25,000     | Full daylight (not direct sun)                 |
 | ||||||
|  | /// | 32,000–100,000    | Direct sunlight                                |
 | ||||||
|  | ///
 | ||||||
|  | /// Source: [Wikipedia](https://en.wikipedia.org/wiki/Lux)
 | ||||||
|  | #[derive(Debug, Clone, Copy, Reflect)] | ||||||
|  | #[reflect(Component)] | ||||||
|  | pub struct DirectionalLight { | ||||||
|  |     pub color: Color, | ||||||
|  |     pub illuminance: f32, | ||||||
|  |     direction: Vec3, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl DirectionalLight { | ||||||
|  |     /// Create a new directional light component.
 | ||||||
|  |     pub fn new(color: Color, illuminance: f32, direction: Vec3) -> Self { | ||||||
|  |         DirectionalLight { | ||||||
|  |             color, | ||||||
|  |             illuminance, | ||||||
|  |             direction: direction.normalize(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Set direction of light.
 | ||||||
|  |     pub fn set_direction(&mut self, direction: Vec3) { | ||||||
|  |         self.direction = direction.normalize(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     pub fn get_direction(&self) -> Vec3 { | ||||||
|  |         self.direction | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for DirectionalLight { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         DirectionalLight { | ||||||
|  |             color: Color::rgb(1.0, 1.0, 1.0), | ||||||
|  |             illuminance: 100000.0, | ||||||
|  |             direction: Vec3::new(0.0, -1.0, 0.0), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #[repr(C)] | ||||||
|  | #[derive(Debug, Clone, Copy)] | ||||||
|  | pub(crate) struct DirectionalLightUniform { | ||||||
|  |     pub dir: [f32; 4], | ||||||
|  |     pub color: [f32; 4], | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | unsafe impl Byteable for DirectionalLightUniform {} | ||||||
|  | 
 | ||||||
|  | impl DirectionalLightUniform { | ||||||
|  |     pub fn new(light: &DirectionalLight) -> DirectionalLightUniform { | ||||||
|  |         // direction is negated to be ready for N.L
 | ||||||
|  |         let dir: [f32; 4] = [ | ||||||
|  |             -light.direction.x, | ||||||
|  |             -light.direction.y, | ||||||
|  |             -light.direction.z, | ||||||
|  |             0.0, | ||||||
|  |         ]; | ||||||
|  | 
 | ||||||
|  |         // convert from illuminance (lux) to candelas
 | ||||||
|  |         //
 | ||||||
|  |         // exposure is hard coded at the moment but should be replaced
 | ||||||
|  |         // by values coming from the camera
 | ||||||
|  |         // see: https://google.github.io/filament/Filament.html#imagingpipeline/physicallybasedcamera/exposuresettings
 | ||||||
|  |         const APERTURE: f32 = 4.0; | ||||||
|  |         const SHUTTER_SPEED: f32 = 1.0 / 250.0; | ||||||
|  |         const SENSITIVITY: f32 = 100.0; | ||||||
|  |         let ev100 = f32::log2(APERTURE * APERTURE / SHUTTER_SPEED) - f32::log2(SENSITIVITY / 100.0); | ||||||
|  |         let exposure = 1.0 / (f32::powf(2.0, ev100) * 1.2); | ||||||
|  |         let intensity = light.illuminance * exposure; | ||||||
|  | 
 | ||||||
|  |         // 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 * intensity).into(); | ||||||
|  | 
 | ||||||
|  |         DirectionalLightUniform { dir, color } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // Ambient light color.
 | // Ambient light color.
 | ||||||
| #[derive(Debug)] | #[derive(Debug)] | ||||||
| pub struct AmbientLight { | pub struct AmbientLight { | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|     light::{AmbientLight, PointLight, PointLightUniform}, |     light::{ | ||||||
|  |         AmbientLight, DirectionalLight, DirectionalLightUniform, PointLight, PointLightUniform, | ||||||
|  |     }, | ||||||
|     render_graph::uniform, |     render_graph::uniform, | ||||||
| }; | }; | ||||||
| use bevy_core::{AsBytes, Byteable}; | use bevy_core::{AsBytes, Byteable}; | ||||||
| @ -21,12 +23,14 @@ use bevy_transform::prelude::*; | |||||||
| pub struct LightsNode { | pub struct LightsNode { | ||||||
|     command_queue: CommandQueue, |     command_queue: CommandQueue, | ||||||
|     max_point_lights: usize, |     max_point_lights: usize, | ||||||
|  |     max_dir_lights: usize, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl LightsNode { | impl LightsNode { | ||||||
|     pub fn new(max_lights: usize) -> Self { |     pub fn new(max_point_lights: usize, max_dir_lights: usize) -> Self { | ||||||
|         LightsNode { |         LightsNode { | ||||||
|             max_point_lights: max_lights, |             max_point_lights, | ||||||
|  |             max_dir_lights, | ||||||
|             command_queue: CommandQueue::default(), |             command_queue: CommandQueue::default(), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @ -48,6 +52,8 @@ impl Node for LightsNode { | |||||||
| #[derive(Debug, Clone, Copy)] | #[derive(Debug, Clone, Copy)] | ||||||
| struct LightCount { | struct LightCount { | ||||||
|     // storing as a `[u32; 4]` for memory alignement
 |     // storing as a `[u32; 4]` for memory alignement
 | ||||||
|  |     // Index 0 is for point lights,
 | ||||||
|  |     // Index 1 is for directional lights
 | ||||||
|     pub num_lights: [u32; 4], |     pub num_lights: [u32; 4], | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -59,6 +65,7 @@ impl SystemNode for LightsNode { | |||||||
|             config.0 = Some(LightsNodeSystemState { |             config.0 = Some(LightsNodeSystemState { | ||||||
|                 command_queue: self.command_queue.clone(), |                 command_queue: self.command_queue.clone(), | ||||||
|                 max_point_lights: self.max_point_lights, |                 max_point_lights: self.max_point_lights, | ||||||
|  |                 max_dir_lights: self.max_dir_lights, | ||||||
|                 light_buffer: None, |                 light_buffer: None, | ||||||
|                 staging_buffer: None, |                 staging_buffer: None, | ||||||
|             }) |             }) | ||||||
| @ -74,6 +81,7 @@ pub struct LightsNodeSystemState { | |||||||
|     staging_buffer: Option<BufferId>, |     staging_buffer: Option<BufferId>, | ||||||
|     command_queue: CommandQueue, |     command_queue: CommandQueue, | ||||||
|     max_point_lights: usize, |     max_point_lights: usize, | ||||||
|  |     max_dir_lights: usize, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn lights_node_system( | pub fn lights_node_system( | ||||||
| @ -83,7 +91,8 @@ 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<(&PointLight, &GlobalTransform)>, |     point_lights: Query<(&PointLight, &GlobalTransform)>, | ||||||
|  |     dir_lights: Query<&DirectionalLight>, | ||||||
| ) { | ) { | ||||||
|     let state = &mut state; |     let state = &mut state; | ||||||
|     let render_resource_context = &**render_resource_context; |     let render_resource_context = &**render_resource_context; | ||||||
| @ -92,16 +101,31 @@ 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 point_light_count = query.iter().len().min(state.max_point_lights); | 
 | ||||||
|     let size = std::mem::size_of::<PointLightUniform>(); |     let point_light_count = point_lights.iter().len().min(state.max_point_lights); | ||||||
|  |     let point_light_size = std::mem::size_of::<PointLightUniform>(); | ||||||
|  |     let point_light_array_size = point_light_size * point_light_count; | ||||||
|  |     let point_light_array_max_size = point_light_size * state.max_point_lights; | ||||||
|  | 
 | ||||||
|  |     let dir_light_count = dir_lights.iter().len().min(state.max_dir_lights); | ||||||
|  |     let dir_light_size = std::mem::size_of::<DirectionalLightUniform>(); | ||||||
|  |     let dir_light_array_size = dir_light_size * dir_light_count; | ||||||
|  |     let dir_light_array_max_size = dir_light_size * state.max_dir_lights; | ||||||
|  | 
 | ||||||
|     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 point_light_array_size = size * point_light_count; | 
 | ||||||
|     let point_light_array_max_size = size * state.max_point_lights; |     let point_light_uniform_start = light_count_size; | ||||||
|     let current_point_light_uniform_size = light_count_size + point_light_array_size; |     let point_light_uniform_end = light_count_size + point_light_array_size; | ||||||
|     let max_light_uniform_size = light_count_size + point_light_array_max_size; | 
 | ||||||
|  |     let dir_light_uniform_start = light_count_size + point_light_array_max_size; | ||||||
|  |     let dir_light_uniform_end = | ||||||
|  |         light_count_size + point_light_array_max_size + dir_light_array_size; | ||||||
|  | 
 | ||||||
|  |     let max_light_uniform_size = | ||||||
|  |         light_count_size + point_light_array_max_size + dir_light_array_max_size; | ||||||
| 
 | 
 | ||||||
|     if let Some(staging_buffer) = state.staging_buffer { |     if let Some(staging_buffer) = state.staging_buffer { | ||||||
|         if point_light_count == 0 { |         if point_light_count == 0 && dir_light_count == 0 { | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -133,23 +157,33 @@ 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_point_light_uniform_size as u64, |         0..max_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( | ||||||
|                 .copy_from_slice([point_light_count as u32, 0, 0, 0].as_bytes()); |                 [point_light_count as u32, dir_light_count as u32, 0, 0].as_bytes(), | ||||||
|  |             ); | ||||||
| 
 | 
 | ||||||
|             // light array
 |             // point light array
 | ||||||
|             for ((point_light, global_transform), slot) in query.iter().zip( |             for ((point_light, global_transform), slot) in point_lights.iter().zip( | ||||||
|                 data[light_count_size..current_point_light_uniform_size].chunks_exact_mut(size), |                 data[point_light_uniform_start..point_light_uniform_end] | ||||||
|  |                     .chunks_exact_mut(point_light_size), | ||||||
|             ) { |             ) { | ||||||
|                 slot.copy_from_slice( |                 slot.copy_from_slice( | ||||||
|                     PointLightUniform::from(&point_light, &global_transform).as_bytes(), |                     PointLightUniform::new(&point_light, &global_transform).as_bytes(), | ||||||
|                 ); |                 ); | ||||||
|             } |             } | ||||||
|  | 
 | ||||||
|  |             // directional light array
 | ||||||
|  |             for (dir_light, slot) in dir_lights.iter().zip( | ||||||
|  |                 data[dir_light_uniform_start..dir_light_uniform_end] | ||||||
|  |                     .chunks_exact_mut(dir_light_size), | ||||||
|  |             ) { | ||||||
|  |                 slot.copy_from_slice(DirectionalLightUniform::new(&dir_light).as_bytes()); | ||||||
|  |             } | ||||||
|         }, |         }, | ||||||
|     ); |     ); | ||||||
|     render_resource_context.unmap_buffer(staging_buffer); |     render_resource_context.unmap_buffer(staging_buffer); | ||||||
|  | |||||||
| @ -27,6 +27,7 @@ use bevy_render::{ | |||||||
| use bevy_transform::prelude::GlobalTransform; | use bevy_transform::prelude::GlobalTransform; | ||||||
| 
 | 
 | ||||||
| pub const MAX_POINT_LIGHTS: usize = 10; | pub const MAX_POINT_LIGHTS: usize = 10; | ||||||
|  | pub const MAX_DIRECTIONAL_LIGHTS: usize = 1; | ||||||
| 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(); | ||||||
| @ -39,7 +40,10 @@ pub(crate) fn add_pbr_graph(world: &mut World) { | |||||||
|             AssetRenderResourcesNode::<StandardMaterial>::new(true), |             AssetRenderResourcesNode::<StandardMaterial>::new(true), | ||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         graph.add_system_node(node::LIGHTS, LightsNode::new(MAX_POINT_LIGHTS)); |         graph.add_system_node( | ||||||
|  |             node::LIGHTS, | ||||||
|  |             LightsNode::new(MAX_POINT_LIGHTS, MAX_DIRECTIONAL_LIGHTS), | ||||||
|  |         ); | ||||||
| 
 | 
 | ||||||
|         // TODO: replace these with "autowire" groups
 |         // TODO: replace these with "autowire" groups
 | ||||||
|         graph |         graph | ||||||
|  | |||||||
| @ -34,7 +34,9 @@ | |||||||
| // | // | ||||||
| // The above integration needs to be approximated. | // The above integration needs to be approximated. | ||||||
| 
 | 
 | ||||||
| const int MAX_LIGHTS = 10; | // reflects the constants defined bevy_pbr/src/render_graph/mod.rs | ||||||
|  | const int MAX_POINT_LIGHTS = 10; | ||||||
|  | const int MAX_DIRECTIONAL_LIGHTS = 1; | ||||||
| 
 | 
 | ||||||
| struct PointLight { | struct PointLight { | ||||||
|     vec4 pos; |     vec4 pos; | ||||||
| @ -42,6 +44,11 @@ struct PointLight { | |||||||
|     vec4 lightParams; |     vec4 lightParams; | ||||||
| }; | }; | ||||||
|   |   | ||||||
|  | struct DirectionalLight { | ||||||
|  |     vec4 direction; | ||||||
|  |     vec4 color; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| layout(location = 0) in vec3 v_WorldPosition; | layout(location = 0) in vec3 v_WorldPosition; | ||||||
| layout(location = 1) in vec3 v_WorldNormal; | layout(location = 1) in vec3 v_WorldNormal; | ||||||
| layout(location = 2) in vec2 v_Uv; | layout(location = 2) in vec2 v_Uv; | ||||||
| @ -61,8 +68,9 @@ 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; // x = point lights, y = directional lights | ||||||
|     PointLight PointLights[MAX_LIGHTS]; |     PointLight PointLights[MAX_POINT_LIGHTS]; | ||||||
|  |     DirectionalLight DirectionalLights[MAX_DIRECTIONAL_LIGHTS]; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| layout(set = 3, binding = 0) uniform StandardMaterial_base_color { | layout(set = 3, binding = 0) uniform StandardMaterial_base_color { | ||||||
| @ -277,6 +285,70 @@ vec3 reinhard_extended_luminance(vec3 color, float max_white_l) { | |||||||
| 
 | 
 | ||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
|  | vec3 point_light(PointLight light, float roughness, float NdotV, vec3 N, vec3 V, vec3 R, vec3 F0, vec3 diffuseColor) { | ||||||
|  |     vec3 light_to_frag = light.pos.xyz - v_WorldPosition.xyz; | ||||||
|  |     float distance_square = dot(light_to_frag, light_to_frag); | ||||||
|  |     float rangeAttenuation = | ||||||
|  |         getDistanceAttenuation(distance_square, light.lightParams.r); | ||||||
|  | 
 | ||||||
|  |     // Specular. | ||||||
|  |     // Representative Point Area Lights. | ||||||
|  |     // see http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p14-16 | ||||||
|  |     float a = roughness; | ||||||
|  |     float radius = light.lightParams.g; | ||||||
|  |     vec3 centerToRay = dot(light_to_frag, R) * R - light_to_frag; | ||||||
|  |     vec3 closestPoint = light_to_frag + centerToRay * saturate(radius * inversesqrt(dot(centerToRay, centerToRay))); | ||||||
|  |     float LspecLengthInverse = inversesqrt(dot(closestPoint, closestPoint)); | ||||||
|  |     float normalizationFactor = a / saturate(a + (radius * 0.5 * LspecLengthInverse)); | ||||||
|  |     float specularIntensity = normalizationFactor * normalizationFactor; | ||||||
|  | 
 | ||||||
|  |     vec3 L = closestPoint * LspecLengthInverse; // normalize() equivalent? | ||||||
|  |     vec3 H = normalize(L + V); | ||||||
|  |     float NoL = saturate(dot(N, L)); | ||||||
|  |     float NoH = saturate(dot(N, H)); | ||||||
|  |     float LoH = saturate(dot(L, H)); | ||||||
|  | 
 | ||||||
|  |     vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH, specularIntensity); | ||||||
|  | 
 | ||||||
|  |     // Diffuse. | ||||||
|  |     // Comes after specular since its NoL is used in the lighting equation. | ||||||
|  |     L = normalize(light_to_frag); | ||||||
|  |     H = normalize(L + V); | ||||||
|  |     NoL = saturate(dot(N, L)); | ||||||
|  |     NoH = saturate(dot(N, H)); | ||||||
|  |     LoH = saturate(dot(L, H)); | ||||||
|  | 
 | ||||||
|  |     vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH); | ||||||
|  | 
 | ||||||
|  |     // Lout = f(v,l) Φ / { 4 π d^2 }⟨n⋅l⟩ | ||||||
|  |     // where | ||||||
|  |     // f(v,l) = (f_d(v,l) + f_r(v,l)) * light_color | ||||||
|  |     // Φ is light intensity | ||||||
|  | 
 | ||||||
|  |     // our rangeAttentuation = 1 / d^2 multiplied with an attenuation factor for smoothing at the edge of the non-physical maximum light radius | ||||||
|  |     // It's not 100% clear where the 1/4π goes in the derivation, but we follow the filament shader and leave it out | ||||||
|  | 
 | ||||||
|  |     // See https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminanceEquation | ||||||
|  |     // TODO compensate for energy loss https://google.github.io/filament/Filament.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance | ||||||
|  |     // light.color.rgb is premultiplied with light.intensity on the CPU | ||||||
|  |     return ((diffuse + specular) * light.color.rgb) * (rangeAttenuation * NoL); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | vec3 dir_light(DirectionalLight light, float roughness, float NdotV, vec3 normal, vec3 view, vec3 R, vec3 F0, vec3 diffuseColor) { | ||||||
|  |     vec3 incident_light = light.direction.xyz; | ||||||
|  | 
 | ||||||
|  |     vec3 half_vector = normalize(incident_light + view); | ||||||
|  |     float NoL = saturate(dot(normal, incident_light)); | ||||||
|  |     float NoH = saturate(dot(normal, half_vector)); | ||||||
|  |     float LoH = saturate(dot(incident_light, half_vector)); | ||||||
|  | 
 | ||||||
|  |     vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH); | ||||||
|  |     float specularIntensity = 1.0; | ||||||
|  |     vec3 specular = specular(F0, roughness, half_vector, NdotV, NoL, NoH, LoH, specularIntensity); | ||||||
|  | 
 | ||||||
|  |     return (specular + diffuse) * light.color.rgb * NoL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void main() { | void main() { | ||||||
|     vec4 output_color = base_color; |     vec4 output_color = base_color; | ||||||
| #ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE | #ifdef STANDARDMATERIAL_BASE_COLOR_TEXTURE | ||||||
| @ -343,55 +415,11 @@ 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_POINT_LIGHTS; ++i) { | ||||||
|         PointLight light = PointLights[i]; |         light_accum += point_light(PointLights[i], roughness, NdotV, N, V, R, F0, diffuseColor); | ||||||
|         vec3 light_to_frag = light.pos.xyz - v_WorldPosition.xyz; |     } | ||||||
|         float distance_square = dot(light_to_frag, light_to_frag); |     for (int i = 0; i < int(NumLights.y) && i < MAX_DIRECTIONAL_LIGHTS; ++i) { | ||||||
|         float rangeAttenuation = |         light_accum += dir_light(DirectionalLights[i], roughness, NdotV, N, V, R, F0, diffuseColor); | ||||||
|             getDistanceAttenuation(distance_square, light.lightParams.r); |  | ||||||
| 
 |  | ||||||
|         // Specular. |  | ||||||
|         // Representative Point Area Lights. |  | ||||||
|         // see http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf p14-16 |  | ||||||
|         float a = roughness; |  | ||||||
|         float radius = light.lightParams.g; |  | ||||||
|         vec3 centerToRay = dot(light_to_frag, R) * R - light_to_frag; |  | ||||||
|         vec3 closestPoint = light_to_frag + centerToRay * saturate(radius * inversesqrt(dot(centerToRay, centerToRay))); |  | ||||||
|         float LspecLengthInverse = inversesqrt(dot(closestPoint, closestPoint)); |  | ||||||
|         float normalizationFactor = a / saturate(a + (radius * 0.5 * LspecLengthInverse)); |  | ||||||
|         float specularIntensity = normalizationFactor * normalizationFactor; |  | ||||||
| 
 |  | ||||||
|         vec3 L = closestPoint * LspecLengthInverse; // normalize() equivalent? |  | ||||||
|         vec3 H = normalize(L + V); |  | ||||||
|         float NoL = saturate(dot(N, L)); |  | ||||||
|         float NoH = saturate(dot(N, H)); |  | ||||||
|         float LoH = saturate(dot(L, H)); |  | ||||||
| 
 |  | ||||||
|         vec3 specular = specular(F0, roughness, H, NdotV, NoL, NoH, LoH, specularIntensity); |  | ||||||
| 
 |  | ||||||
|         // Diffuse. |  | ||||||
|         // Comes after specular since its NoL is used in the lighting equation. |  | ||||||
|         L = normalize(light_to_frag); |  | ||||||
|         H = normalize(L + V); |  | ||||||
|         NoL = saturate(dot(N, L)); |  | ||||||
|         NoH = saturate(dot(N, H)); |  | ||||||
|         LoH = saturate(dot(L, H)); |  | ||||||
| 
 |  | ||||||
|         vec3 diffuse = diffuseColor * Fd_Burley(roughness, NdotV, NoL, LoH); |  | ||||||
| 
 |  | ||||||
|         // Lout = f(v,l) Φ / { 4 π d^2 }⟨n⋅l⟩ |  | ||||||
|         // where |  | ||||||
|         // f(v,l) = (f_d(v,l) + f_r(v,l)) * light_color |  | ||||||
|         // Φ is light intensity |  | ||||||
| 
 |  | ||||||
|         // our rangeAttentuation = 1 / d^2 multiplied with an attenuation factor for smoothing at the edge of the non-physical maximum light radius |  | ||||||
|         // It's not 100% clear where the 1/4π goes in the derivation, but we follow the filament shader and leave it out |  | ||||||
| 
 |  | ||||||
|         // See https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminanceEquation |  | ||||||
|         // TODO compensate for energy loss https://google.github.io/filament/Filament.html#materialsystem/improvingthebrdfs/energylossinspecularreflectance |  | ||||||
|         // light.color.rgb is premultiplied with light.intensity on the CPU |  | ||||||
|         light_accum += |  | ||||||
|             ((diffuse + specular) * light.color.rgb) * (rangeAttenuation * NoL); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     vec3 diffuse_ambient = EnvBRDFApprox(diffuseColor, 1.0, NdotV); |     vec3 diffuse_ambient = EnvBRDFApprox(diffuseColor, 1.0, NdotV); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 MsK`
						MsK`