2021-08-25 05:57:57 +00:00
|
|
|
use crate::{
|
|
|
|
AmbientLight, DirectionalLight, DirectionalLightShadowMap, ExtractedMeshes, MeshMeta,
|
|
|
|
PbrShaders, PointLight, PointLightShadowMap,
|
|
|
|
};
|
2021-07-28 21:29:32 +00:00
|
|
|
use bevy_core_pipeline::Transparent3dPhase;
|
2021-06-02 02:59:17 +00:00
|
|
|
use bevy_ecs::{prelude::*, system::SystemState};
|
2021-07-01 23:48:55 +00:00
|
|
|
use bevy_math::{const_vec3, Mat4, Vec3, Vec4};
|
2021-06-02 02:59:17 +00:00
|
|
|
use bevy_render2::{
|
2021-07-08 02:49:33 +00:00
|
|
|
camera::CameraProjection,
|
2021-06-02 02:59:17 +00:00
|
|
|
color::Color,
|
2021-06-26 22:35:07 +00:00
|
|
|
mesh::Mesh,
|
|
|
|
render_asset::RenderAssets,
|
2021-06-02 02:59:17 +00:00
|
|
|
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
|
|
|
render_phase::{Draw, DrawFunctions, RenderPhase, TrackedRenderPass},
|
2021-06-21 23:28:52 +00:00
|
|
|
render_resource::*,
|
|
|
|
renderer::{RenderContext, RenderDevice},
|
2021-06-02 02:59:17 +00:00
|
|
|
texture::*,
|
2021-07-01 23:54:58 +00:00
|
|
|
view::{ExtractedView, ViewUniformOffset},
|
2021-06-02 02:59:17 +00:00
|
|
|
};
|
|
|
|
use bevy_transform::components::GlobalTransform;
|
|
|
|
use crevice::std140::AsStd140;
|
|
|
|
use std::num::NonZeroU32;
|
|
|
|
|
2021-06-27 23:10:23 +00:00
|
|
|
pub struct ExtractedAmbientLight {
|
|
|
|
color: Color,
|
|
|
|
brightness: f32,
|
|
|
|
}
|
|
|
|
|
2021-06-02 02:59:17 +00:00
|
|
|
pub struct ExtractedPointLight {
|
|
|
|
color: Color,
|
bevy_pbr2: Improve lighting units and documentation (#2704)
# Objective
A question was raised on Discord about the units of the `PointLight` `intensity` member.
After digging around in the bevy_pbr2 source code and [Google Filament documentation](https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminousPower) I discovered that the intention by Filament was that the 'intensity' value for point lights would be in lumens. This makes a lot of sense as these are quite relatable units given basically all light bulbs I've seen sold over the past years are rated in lumens as people move away from thinking about how bright a bulb is relative to a non-halogen incandescent bulb.
However, it seems that the derivation of the conversion between luminous power (lumens, denoted `Φ` in the Filament formulae) and luminous intensity (lumens per steradian, `I` in the Filament formulae) was missed and I can see why as it is tucked right under equation 58 at the link above. As such, while the formula states that for a point light, `I = Φ / 4 π` we have been using `intensity` as if it were luminous intensity `I`.
Before this PR, the intensity field is luminous intensity in lumens per steradian. After this PR, the intensity field is luminous power in lumens, [as suggested by Filament](https://google.github.io/filament/Filament.html#table_lighttypesunits) (unfortunately the link jumps to the table's caption so scroll up to see the actual table).
I appreciate that it may be confusing to call this an intensity, but I think this is intended as more of a non-scientific, human-relatable general term with a bit of hand waving so that most light types can just have an intensity field and for most of them it works in the same way or at least with some relatable value. I'm inclined to think this is reasonable rather than throwing terms like luminous power, luminous intensity, blah at users.
## Solution
- Documented the `PointLight` `intensity` member as 'luminous power' in units of lumens.
- Added a table of examples relating from various types of household lighting to lumen values.
- Added in the mapping from luminous power to luminous intensity when premultiplying the intensity into the colour before it is made into a graphics uniform.
- Updated the documentation in `pbr.wgsl` to clarify the earlier confusion about the missing `/ 4 π`.
- Bumped the intensity of the point lights in `3d_scene_pipelined` to 1600 lumens.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-08-23 23:48:11 +00:00
|
|
|
/// luminous intensity in lumens per steradian
|
2021-06-02 02:59:17 +00:00
|
|
|
intensity: f32,
|
|
|
|
range: f32,
|
|
|
|
radius: f32,
|
|
|
|
transform: GlobalTransform,
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: f32,
|
|
|
|
shadow_normal_bias: f32,
|
2021-07-08 02:49:33 +00:00
|
|
|
}
|
|
|
|
|
2021-08-25 05:57:57 +00:00
|
|
|
pub type ExtractedPointLightShadowMap = PointLightShadowMap;
|
|
|
|
|
2021-07-08 02:49:33 +00:00
|
|
|
pub struct ExtractedDirectionalLight {
|
|
|
|
color: Color,
|
|
|
|
illuminance: f32,
|
|
|
|
direction: Vec3,
|
|
|
|
projection: Mat4,
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: f32,
|
|
|
|
shadow_normal_bias: f32,
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
2021-08-25 05:57:57 +00:00
|
|
|
pub type ExtractedDirectionalLightShadowMap = DirectionalLightShadowMap;
|
|
|
|
|
2021-06-02 02:59:17 +00:00
|
|
|
#[repr(C)]
|
|
|
|
#[derive(Copy, Clone, AsStd140, Default, Debug)]
|
2021-07-08 02:49:33 +00:00
|
|
|
pub struct GpuPointLight {
|
2021-06-02 02:59:17 +00:00
|
|
|
color: Vec4,
|
2021-07-01 23:48:55 +00:00
|
|
|
// proj: Mat4,
|
2021-06-02 02:59:17 +00:00
|
|
|
position: Vec3,
|
2021-07-01 23:48:55 +00:00
|
|
|
inverse_square_range: f32,
|
|
|
|
radius: f32,
|
|
|
|
near: f32,
|
|
|
|
far: f32,
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: f32,
|
|
|
|
shadow_normal_bias: f32,
|
2021-07-08 02:49:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[repr(C)]
|
|
|
|
#[derive(Copy, Clone, AsStd140, Default, Debug)]
|
|
|
|
pub struct GpuDirectionalLight {
|
|
|
|
view_projection: Mat4,
|
|
|
|
color: Vec4,
|
|
|
|
dir_to_light: Vec3,
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: f32,
|
|
|
|
shadow_normal_bias: f32,
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[repr(C)]
|
|
|
|
#[derive(Copy, Clone, Debug, AsStd140)]
|
|
|
|
pub struct GpuLights {
|
2021-06-29 23:56:45 +00:00
|
|
|
// TODO: this comes first to work around a WGSL alignment issue. We need to solve this issue before releasing the renderer rework
|
2021-07-08 02:49:33 +00:00
|
|
|
point_lights: [GpuPointLight; MAX_POINT_LIGHTS],
|
|
|
|
directional_lights: [GpuDirectionalLight; MAX_DIRECTIONAL_LIGHTS],
|
2021-06-27 23:10:23 +00:00
|
|
|
ambient_color: Vec4,
|
2021-07-08 02:49:33 +00:00
|
|
|
n_point_lights: u32,
|
|
|
|
n_directional_lights: u32,
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
2021-07-08 02:49:33 +00:00
|
|
|
// NOTE: this must be kept in sync with the same constants in pbr.frag
|
2021-07-01 23:54:58 +00:00
|
|
|
pub const MAX_POINT_LIGHTS: usize = 10;
|
2021-07-08 02:49:33 +00:00
|
|
|
pub const MAX_DIRECTIONAL_LIGHTS: usize = 1;
|
2021-08-25 05:57:57 +00:00
|
|
|
pub const POINT_SHADOW_LAYERS: u32 = (6 * MAX_POINT_LIGHTS) as u32;
|
|
|
|
pub const DIRECTIONAL_SHADOW_LAYERS: u32 = MAX_DIRECTIONAL_LIGHTS as u32;
|
2021-06-02 02:59:17 +00:00
|
|
|
pub const SHADOW_FORMAT: TextureFormat = TextureFormat::Depth32Float;
|
|
|
|
|
|
|
|
pub struct ShadowShaders {
|
2021-06-21 23:28:52 +00:00
|
|
|
pub pipeline: RenderPipeline,
|
|
|
|
pub view_layout: BindGroupLayout,
|
2021-07-08 02:49:33 +00:00
|
|
|
pub point_light_sampler: Sampler,
|
|
|
|
pub directional_light_sampler: Sampler,
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: this pattern for initializing the shaders / pipeline isn't ideal. this should be handled by the asset system
|
|
|
|
impl FromWorld for ShadowShaders {
|
|
|
|
fn from_world(world: &mut World) -> Self {
|
2021-06-21 23:28:52 +00:00
|
|
|
let render_device = world.get_resource::<RenderDevice>().unwrap();
|
|
|
|
let pbr_shaders = world.get_resource::<PbrShaders>().unwrap();
|
|
|
|
|
|
|
|
let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
|
|
|
|
entries: &[
|
|
|
|
// View
|
|
|
|
BindGroupLayoutEntry {
|
|
|
|
binding: 0,
|
|
|
|
visibility: ShaderStage::VERTEX | ShaderStage::FRAGMENT,
|
|
|
|
ty: BindingType::Buffer {
|
|
|
|
ty: BufferBindingType::Uniform,
|
|
|
|
has_dynamic_offset: true,
|
2021-06-28 22:36:50 +00:00
|
|
|
// TODO: change this to ViewUniform::std140_size_static once crevice fixes this!
|
|
|
|
// Context: https://github.com/LPGhatguy/crevice/issues/29
|
|
|
|
min_binding_size: BufferSize::new(80),
|
2021-06-21 23:28:52 +00:00
|
|
|
},
|
|
|
|
count: None,
|
2021-06-02 02:59:17 +00:00
|
|
|
},
|
|
|
|
],
|
2021-06-21 23:28:52 +00:00
|
|
|
label: None,
|
|
|
|
});
|
2021-06-02 02:59:17 +00:00
|
|
|
|
2021-06-21 23:28:52 +00:00
|
|
|
let pipeline_layout = render_device.create_pipeline_layout(&PipelineLayoutDescriptor {
|
|
|
|
label: None,
|
|
|
|
push_constant_ranges: &[],
|
2021-06-26 22:35:07 +00:00
|
|
|
bind_group_layouts: &[&view_layout, &pbr_shaders.mesh_layout],
|
2021-06-21 23:28:52 +00:00
|
|
|
});
|
2021-06-02 02:59:17 +00:00
|
|
|
|
2021-06-21 23:28:52 +00:00
|
|
|
let pipeline = render_device.create_render_pipeline(&RenderPipelineDescriptor {
|
|
|
|
label: None,
|
|
|
|
vertex: VertexState {
|
|
|
|
buffers: &[VertexBufferLayout {
|
|
|
|
array_stride: 32,
|
|
|
|
step_mode: InputStepMode::Vertex,
|
|
|
|
attributes: &[
|
|
|
|
// Position (GOTCHA! Vertex_Position isn't first in the buffer due to how Mesh sorts attributes (alphabetically))
|
|
|
|
VertexAttribute {
|
|
|
|
format: VertexFormat::Float32x3,
|
|
|
|
offset: 12,
|
|
|
|
shader_location: 0,
|
|
|
|
},
|
|
|
|
// Normal
|
|
|
|
VertexAttribute {
|
|
|
|
format: VertexFormat::Float32x3,
|
|
|
|
offset: 0,
|
|
|
|
shader_location: 1,
|
|
|
|
},
|
|
|
|
// Uv
|
|
|
|
VertexAttribute {
|
|
|
|
format: VertexFormat::Float32x2,
|
|
|
|
offset: 24,
|
|
|
|
shader_location: 2,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
}],
|
2021-06-28 22:36:50 +00:00
|
|
|
module: &pbr_shaders.shader_module,
|
|
|
|
entry_point: "vertex",
|
2021-06-21 23:28:52 +00:00
|
|
|
},
|
|
|
|
fragment: None,
|
2021-06-02 02:59:17 +00:00
|
|
|
depth_stencil: Some(DepthStencilState {
|
|
|
|
format: SHADOW_FORMAT,
|
|
|
|
depth_write_enabled: true,
|
|
|
|
depth_compare: CompareFunction::LessEqual,
|
|
|
|
stencil: StencilState {
|
|
|
|
front: StencilFaceState::IGNORE,
|
|
|
|
back: StencilFaceState::IGNORE,
|
|
|
|
read_mask: 0,
|
|
|
|
write_mask: 0,
|
|
|
|
},
|
|
|
|
bias: DepthBiasState {
|
2021-07-19 19:20:59 +00:00
|
|
|
constant: 0,
|
|
|
|
slope_scale: 0.0,
|
2021-06-02 02:59:17 +00:00
|
|
|
clamp: 0.0,
|
|
|
|
},
|
|
|
|
}),
|
2021-06-21 23:28:52 +00:00
|
|
|
layout: Some(&pipeline_layout),
|
|
|
|
multisample: MultisampleState::default(),
|
2021-06-02 02:59:17 +00:00
|
|
|
primitive: PrimitiveState {
|
|
|
|
topology: PrimitiveTopology::TriangleList,
|
2021-06-21 23:28:52 +00:00
|
|
|
strip_index_format: None,
|
|
|
|
front_face: FrontFace::Ccw,
|
2021-07-01 23:48:55 +00:00
|
|
|
cull_mode: None,
|
2021-06-21 23:28:52 +00:00
|
|
|
polygon_mode: PolygonMode::Fill,
|
2021-06-02 02:59:17 +00:00
|
|
|
clamp_depth: false,
|
2021-06-21 23:28:52 +00:00
|
|
|
conservative: false,
|
2021-06-02 02:59:17 +00:00
|
|
|
},
|
2021-06-21 23:28:52 +00:00
|
|
|
});
|
2021-06-02 02:59:17 +00:00
|
|
|
|
|
|
|
ShadowShaders {
|
|
|
|
pipeline,
|
2021-06-21 23:28:52 +00:00
|
|
|
view_layout,
|
2021-07-08 02:49:33 +00:00
|
|
|
point_light_sampler: render_device.create_sampler(&SamplerDescriptor {
|
|
|
|
address_mode_u: AddressMode::ClampToEdge,
|
|
|
|
address_mode_v: AddressMode::ClampToEdge,
|
|
|
|
address_mode_w: AddressMode::ClampToEdge,
|
|
|
|
mag_filter: FilterMode::Linear,
|
|
|
|
min_filter: FilterMode::Linear,
|
|
|
|
mipmap_filter: FilterMode::Nearest,
|
|
|
|
compare: Some(CompareFunction::LessEqual),
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
directional_light_sampler: render_device.create_sampler(&SamplerDescriptor {
|
2021-06-02 02:59:17 +00:00
|
|
|
address_mode_u: AddressMode::ClampToEdge,
|
|
|
|
address_mode_v: AddressMode::ClampToEdge,
|
|
|
|
address_mode_w: AddressMode::ClampToEdge,
|
|
|
|
mag_filter: FilterMode::Linear,
|
|
|
|
min_filter: FilterMode::Linear,
|
|
|
|
mipmap_filter: FilterMode::Nearest,
|
2021-06-21 23:28:52 +00:00
|
|
|
compare: Some(CompareFunction::LessEqual),
|
2021-06-02 02:59:17 +00:00
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: ultimately these could be filtered down to lights relevant to actual views
|
|
|
|
pub fn extract_lights(
|
|
|
|
mut commands: Commands,
|
2021-06-27 23:10:23 +00:00
|
|
|
ambient_light: Res<AmbientLight>,
|
2021-08-25 05:57:57 +00:00
|
|
|
point_light_shadow_map: Res<PointLightShadowMap>,
|
|
|
|
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
|
2021-07-08 02:49:33 +00:00
|
|
|
point_lights: Query<(Entity, &PointLight, &GlobalTransform)>,
|
|
|
|
directional_lights: Query<(Entity, &DirectionalLight, &GlobalTransform)>,
|
2021-06-02 02:59:17 +00:00
|
|
|
) {
|
2021-06-27 23:10:23 +00:00
|
|
|
commands.insert_resource(ExtractedAmbientLight {
|
|
|
|
color: ambient_light.color,
|
|
|
|
brightness: ambient_light.brightness,
|
|
|
|
});
|
2021-08-25 05:57:57 +00:00
|
|
|
commands.insert_resource::<ExtractedPointLightShadowMap>(point_light_shadow_map.clone());
|
|
|
|
commands.insert_resource::<ExtractedDirectionalLightShadowMap>(
|
|
|
|
directional_light_shadow_map.clone(),
|
|
|
|
);
|
2021-07-19 19:20:59 +00:00
|
|
|
// This is the point light shadow map texel size for one face of the cube as a distance of 1.0
|
|
|
|
// world unit from the light.
|
|
|
|
// point_light_texel_size = 2.0 * 1.0 * tan(PI / 4.0) / cube face width in texels
|
|
|
|
// PI / 4.0 is half the cube face fov, tan(PI / 4.0) = 1.0, so this simplifies to:
|
|
|
|
// point_light_texel_size = 2.0 / cube face width in texels
|
|
|
|
// NOTE: When using various PCF kernel sizes, this will need to be adjusted, according to:
|
|
|
|
// https://catlikecoding.com/unity/tutorials/custom-srp/point-and-spot-shadows/
|
2021-08-25 05:57:57 +00:00
|
|
|
let point_light_texel_size = 2.0 / point_light_shadow_map.size as f32;
|
2021-07-08 02:49:33 +00:00
|
|
|
for (entity, point_light, transform) in point_lights.iter() {
|
2021-06-02 02:59:17 +00:00
|
|
|
commands.get_or_spawn(entity).insert(ExtractedPointLight {
|
2021-07-08 02:49:33 +00:00
|
|
|
color: point_light.color,
|
bevy_pbr2: Improve lighting units and documentation (#2704)
# Objective
A question was raised on Discord about the units of the `PointLight` `intensity` member.
After digging around in the bevy_pbr2 source code and [Google Filament documentation](https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminousPower) I discovered that the intention by Filament was that the 'intensity' value for point lights would be in lumens. This makes a lot of sense as these are quite relatable units given basically all light bulbs I've seen sold over the past years are rated in lumens as people move away from thinking about how bright a bulb is relative to a non-halogen incandescent bulb.
However, it seems that the derivation of the conversion between luminous power (lumens, denoted `Φ` in the Filament formulae) and luminous intensity (lumens per steradian, `I` in the Filament formulae) was missed and I can see why as it is tucked right under equation 58 at the link above. As such, while the formula states that for a point light, `I = Φ / 4 π` we have been using `intensity` as if it were luminous intensity `I`.
Before this PR, the intensity field is luminous intensity in lumens per steradian. After this PR, the intensity field is luminous power in lumens, [as suggested by Filament](https://google.github.io/filament/Filament.html#table_lighttypesunits) (unfortunately the link jumps to the table's caption so scroll up to see the actual table).
I appreciate that it may be confusing to call this an intensity, but I think this is intended as more of a non-scientific, human-relatable general term with a bit of hand waving so that most light types can just have an intensity field and for most of them it works in the same way or at least with some relatable value. I'm inclined to think this is reasonable rather than throwing terms like luminous power, luminous intensity, blah at users.
## Solution
- Documented the `PointLight` `intensity` member as 'luminous power' in units of lumens.
- Added a table of examples relating from various types of household lighting to lumen values.
- Added in the mapping from luminous power to luminous intensity when premultiplying the intensity into the colour before it is made into a graphics uniform.
- Updated the documentation in `pbr.wgsl` to clarify the earlier confusion about the missing `/ 4 π`.
- Bumped the intensity of the point lights in `3d_scene_pipelined` to 1600 lumens.
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2021-08-23 23:48:11 +00:00
|
|
|
// NOTE: Map from luminous power in lumens to luminous intensity in lumens per steradian
|
|
|
|
// for a point light. See https://google.github.io/filament/Filament.html#mjx-eqn-pointLightLuminousPower
|
|
|
|
// for details.
|
|
|
|
intensity: point_light.intensity / (4.0 * std::f32::consts::PI),
|
2021-07-08 02:49:33 +00:00
|
|
|
range: point_light.range,
|
|
|
|
radius: point_light.radius,
|
2021-07-02 01:05:20 +00:00
|
|
|
transform: *transform,
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: point_light.shadow_depth_bias,
|
2021-07-19 19:20:59 +00:00
|
|
|
// The factor of SQRT_2 is for the worst-case diagonal offset
|
|
|
|
shadow_normal_bias: point_light.shadow_normal_bias
|
|
|
|
* point_light_texel_size
|
|
|
|
* std::f32::consts::SQRT_2,
|
2021-06-02 02:59:17 +00:00
|
|
|
});
|
|
|
|
}
|
2021-07-08 02:49:33 +00:00
|
|
|
for (entity, directional_light, transform) in directional_lights.iter() {
|
2021-07-19 19:20:59 +00:00
|
|
|
// Calulate the directional light shadow map texel size using the largest x,y dimension of
|
|
|
|
// the orthographic projection divided by the shadow map resolution
|
|
|
|
// NOTE: When using various PCF kernel sizes, this will need to be adjusted, according to:
|
|
|
|
// https://catlikecoding.com/unity/tutorials/custom-srp/directional-shadows/
|
|
|
|
let largest_dimension = (directional_light.shadow_projection.right
|
|
|
|
- directional_light.shadow_projection.left)
|
|
|
|
.max(
|
|
|
|
directional_light.shadow_projection.top
|
|
|
|
- directional_light.shadow_projection.bottom,
|
|
|
|
);
|
2021-08-25 05:57:57 +00:00
|
|
|
let directional_light_texel_size =
|
|
|
|
largest_dimension / directional_light_shadow_map.size as f32;
|
2021-07-08 02:49:33 +00:00
|
|
|
commands
|
|
|
|
.get_or_spawn(entity)
|
|
|
|
.insert(ExtractedDirectionalLight {
|
|
|
|
color: directional_light.color,
|
|
|
|
illuminance: directional_light.illuminance,
|
|
|
|
direction: transform.forward(),
|
|
|
|
projection: directional_light.shadow_projection.get_projection_matrix(),
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: directional_light.shadow_depth_bias,
|
2021-07-19 19:20:59 +00:00
|
|
|
// The factor of SQRT_2 is for the worst-case diagonal offset
|
|
|
|
shadow_normal_bias: directional_light.shadow_normal_bias
|
|
|
|
* directional_light_texel_size
|
|
|
|
* std::f32::consts::SQRT_2,
|
2021-07-08 02:49:33 +00:00
|
|
|
});
|
|
|
|
}
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
2021-07-01 23:48:55 +00:00
|
|
|
// Can't do `Vec3::Y * -1.0` because mul isn't const
|
|
|
|
const NEGATIVE_X: Vec3 = const_vec3!([-1.0, 0.0, 0.0]);
|
|
|
|
const NEGATIVE_Y: Vec3 = const_vec3!([0.0, -1.0, 0.0]);
|
|
|
|
const NEGATIVE_Z: Vec3 = const_vec3!([0.0, 0.0, -1.0]);
|
|
|
|
|
|
|
|
struct CubeMapFace {
|
|
|
|
target: Vec3,
|
|
|
|
up: Vec3,
|
|
|
|
}
|
|
|
|
|
|
|
|
// see https://www.khronos.org/opengl/wiki/Cubemap_Texture
|
|
|
|
const CUBE_MAP_FACES: [CubeMapFace; 6] = [
|
|
|
|
// 0 GL_TEXTURE_CUBE_MAP_POSITIVE_X
|
|
|
|
CubeMapFace {
|
|
|
|
target: NEGATIVE_X,
|
|
|
|
up: NEGATIVE_Y,
|
|
|
|
},
|
|
|
|
// 1 GL_TEXTURE_CUBE_MAP_NEGATIVE_X
|
|
|
|
CubeMapFace {
|
|
|
|
target: Vec3::X,
|
|
|
|
up: NEGATIVE_Y,
|
|
|
|
},
|
|
|
|
// 2 GL_TEXTURE_CUBE_MAP_POSITIVE_Y
|
|
|
|
CubeMapFace {
|
|
|
|
target: NEGATIVE_Y,
|
|
|
|
up: Vec3::Z,
|
|
|
|
},
|
|
|
|
// 3 GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
|
|
|
|
CubeMapFace {
|
|
|
|
target: Vec3::Y,
|
|
|
|
up: NEGATIVE_Z,
|
|
|
|
},
|
|
|
|
// 4 GL_TEXTURE_CUBE_MAP_POSITIVE_Z
|
|
|
|
CubeMapFace {
|
|
|
|
target: NEGATIVE_Z,
|
|
|
|
up: NEGATIVE_Y,
|
|
|
|
},
|
|
|
|
// 5 GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
|
|
|
|
CubeMapFace {
|
|
|
|
target: Vec3::Z,
|
|
|
|
up: NEGATIVE_Y,
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
2021-07-08 02:49:33 +00:00
|
|
|
fn face_index_to_name(face_index: usize) -> &'static str {
|
|
|
|
match face_index {
|
|
|
|
0 => "+x",
|
|
|
|
1 => "-x",
|
|
|
|
2 => "+y",
|
|
|
|
3 => "-y",
|
|
|
|
4 => "+z",
|
|
|
|
5 => "-z",
|
|
|
|
_ => "invalid",
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 02:59:17 +00:00
|
|
|
pub struct ViewLight {
|
2021-07-01 23:48:55 +00:00
|
|
|
pub depth_texture_view: TextureView,
|
2021-07-08 02:49:33 +00:00
|
|
|
pub pass_name: String,
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ViewLights {
|
2021-07-08 02:49:33 +00:00
|
|
|
pub point_light_depth_texture: Texture,
|
|
|
|
pub point_light_depth_texture_view: TextureView,
|
|
|
|
pub directional_light_depth_texture: Texture,
|
|
|
|
pub directional_light_depth_texture_view: TextureView,
|
2021-06-02 02:59:17 +00:00
|
|
|
pub lights: Vec<Entity>,
|
|
|
|
pub gpu_light_binding_index: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
pub struct LightMeta {
|
|
|
|
pub view_gpu_lights: DynamicUniformVec<GpuLights>,
|
2021-06-21 23:28:52 +00:00
|
|
|
pub shadow_view_bind_group: Option<BindGroup>,
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
2021-07-08 03:01:41 +00:00
|
|
|
#[allow(clippy::too_many_arguments)]
|
2021-06-02 02:59:17 +00:00
|
|
|
pub fn prepare_lights(
|
|
|
|
mut commands: Commands,
|
|
|
|
mut texture_cache: ResMut<TextureCache>,
|
2021-06-21 23:28:52 +00:00
|
|
|
render_device: Res<RenderDevice>,
|
2021-06-02 02:59:17 +00:00
|
|
|
mut light_meta: ResMut<LightMeta>,
|
|
|
|
views: Query<Entity, With<RenderPhase<Transparent3dPhase>>>,
|
2021-06-27 23:10:23 +00:00
|
|
|
ambient_light: Res<ExtractedAmbientLight>,
|
2021-08-25 05:57:57 +00:00
|
|
|
point_light_shadow_map: Res<ExtractedPointLightShadowMap>,
|
|
|
|
directional_light_shadow_map: Res<ExtractedDirectionalLightShadowMap>,
|
2021-07-08 02:49:33 +00:00
|
|
|
point_lights: Query<&ExtractedPointLight>,
|
|
|
|
directional_lights: Query<&ExtractedDirectionalLight>,
|
2021-06-02 02:59:17 +00:00
|
|
|
) {
|
|
|
|
// PERF: view.iter().count() could be views.iter().len() if we implemented ExactSizeIterator for archetype-only filters
|
|
|
|
light_meta
|
|
|
|
.view_gpu_lights
|
2021-06-21 23:28:52 +00:00
|
|
|
.reserve_and_clear(views.iter().count(), &render_device);
|
2021-06-02 02:59:17 +00:00
|
|
|
|
2021-06-27 23:10:23 +00:00
|
|
|
let ambient_color = ambient_light.color.as_rgba_linear() * ambient_light.brightness;
|
2021-06-02 02:59:17 +00:00
|
|
|
// set up light data for each view
|
|
|
|
for entity in views.iter() {
|
2021-07-08 02:49:33 +00:00
|
|
|
let point_light_depth_texture = texture_cache.get(
|
|
|
|
&render_device,
|
|
|
|
TextureDescriptor {
|
2021-08-25 05:57:57 +00:00
|
|
|
size: Extent3d {
|
|
|
|
width: point_light_shadow_map.size as u32,
|
|
|
|
height: point_light_shadow_map.size as u32,
|
|
|
|
depth_or_array_layers: POINT_SHADOW_LAYERS,
|
|
|
|
},
|
2021-07-08 02:49:33 +00:00
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: 1,
|
|
|
|
dimension: TextureDimension::D2,
|
|
|
|
format: SHADOW_FORMAT,
|
|
|
|
usage: TextureUsage::RENDER_ATTACHMENT | TextureUsage::SAMPLED,
|
|
|
|
label: None,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
let directional_light_depth_texture = texture_cache.get(
|
2021-06-21 23:28:52 +00:00
|
|
|
&render_device,
|
2021-06-02 02:59:17 +00:00
|
|
|
TextureDescriptor {
|
2021-08-25 05:57:57 +00:00
|
|
|
size: Extent3d {
|
|
|
|
width: directional_light_shadow_map.size as u32,
|
|
|
|
height: directional_light_shadow_map.size as u32,
|
|
|
|
depth_or_array_layers: DIRECTIONAL_SHADOW_LAYERS,
|
|
|
|
},
|
2021-06-02 02:59:17 +00:00
|
|
|
mip_level_count: 1,
|
|
|
|
sample_count: 1,
|
|
|
|
dimension: TextureDimension::D2,
|
|
|
|
format: SHADOW_FORMAT,
|
|
|
|
usage: TextureUsage::RENDER_ATTACHMENT | TextureUsage::SAMPLED,
|
2021-06-21 23:28:52 +00:00
|
|
|
label: None,
|
2021-06-02 02:59:17 +00:00
|
|
|
},
|
|
|
|
);
|
|
|
|
let mut view_lights = Vec::new();
|
|
|
|
|
|
|
|
let mut gpu_lights = GpuLights {
|
2021-06-27 23:10:23 +00:00
|
|
|
ambient_color: ambient_color.into(),
|
2021-07-08 02:49:33 +00:00
|
|
|
n_point_lights: point_lights.iter().len() as u32,
|
|
|
|
n_directional_lights: directional_lights.iter().len() as u32,
|
|
|
|
point_lights: [GpuPointLight::default(); MAX_POINT_LIGHTS],
|
|
|
|
directional_lights: [GpuDirectionalLight::default(); MAX_DIRECTIONAL_LIGHTS],
|
2021-06-02 02:59:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// TODO: this should select lights based on relevance to the view instead of the first ones that show up in a query
|
2021-07-08 02:49:33 +00:00
|
|
|
for (light_index, light) in point_lights.iter().enumerate().take(MAX_POINT_LIGHTS) {
|
2021-06-27 23:10:23 +00:00
|
|
|
let projection =
|
|
|
|
Mat4::perspective_rh(std::f32::consts::FRAC_PI_2, 1.0, 0.1, light.range);
|
2021-06-02 02:59:17 +00:00
|
|
|
|
2021-07-01 23:48:55 +00:00
|
|
|
// ignore scale because we don't want to effectively scale light radius and range
|
|
|
|
// by applying those as a view transform to shadow map rendering of objects
|
|
|
|
// and ignore rotation because we want the shadow map projections to align with the axes
|
|
|
|
let view_translation = GlobalTransform::from_translation(light.transform.translation);
|
|
|
|
|
|
|
|
for (face_index, CubeMapFace { target, up }) in CUBE_MAP_FACES.iter().enumerate() {
|
|
|
|
// use the cubemap projection direction
|
|
|
|
let view_rotation = GlobalTransform::identity().looking_at(*target, *up);
|
|
|
|
|
|
|
|
let depth_texture_view =
|
2021-07-08 02:49:33 +00:00
|
|
|
point_light_depth_texture
|
2021-07-01 23:48:55 +00:00
|
|
|
.texture
|
|
|
|
.create_view(&TextureViewDescriptor {
|
|
|
|
label: None,
|
|
|
|
format: None,
|
|
|
|
dimension: Some(TextureViewDimension::D2),
|
|
|
|
aspect: TextureAspect::All,
|
|
|
|
base_mip_level: 0,
|
|
|
|
mip_level_count: None,
|
|
|
|
base_array_layer: (light_index * 6 + face_index) as u32,
|
|
|
|
array_layer_count: NonZeroU32::new(1),
|
|
|
|
});
|
|
|
|
|
|
|
|
let view_light_entity = commands
|
|
|
|
.spawn()
|
|
|
|
.insert_bundle((
|
2021-07-08 02:49:33 +00:00
|
|
|
ViewLight {
|
|
|
|
depth_texture_view,
|
|
|
|
pass_name: format!(
|
|
|
|
"shadow pass point light {} {}",
|
|
|
|
light_index,
|
|
|
|
face_index_to_name(face_index)
|
|
|
|
),
|
|
|
|
},
|
2021-07-01 23:48:55 +00:00
|
|
|
ExtractedView {
|
2021-08-25 05:57:57 +00:00
|
|
|
width: point_light_shadow_map.size as u32,
|
|
|
|
height: point_light_shadow_map.size as u32,
|
2021-07-01 23:48:55 +00:00
|
|
|
transform: view_translation * view_rotation,
|
|
|
|
projection,
|
|
|
|
},
|
|
|
|
RenderPhase::<ShadowPhase>::default(),
|
|
|
|
))
|
|
|
|
.id();
|
|
|
|
view_lights.push(view_light_entity);
|
|
|
|
}
|
|
|
|
|
2021-07-08 02:49:33 +00:00
|
|
|
gpu_lights.point_lights[light_index] = GpuPointLight {
|
2021-06-02 02:59:17 +00:00
|
|
|
// premultiply color by intensity
|
|
|
|
// we don't use the alpha at all, so no reason to multiply only [0..3]
|
2021-06-27 23:10:23 +00:00
|
|
|
color: (light.color.as_rgba_linear() * light.intensity).into(),
|
2021-07-02 01:05:20 +00:00
|
|
|
radius: light.radius,
|
|
|
|
position: light.transform.translation,
|
2021-07-01 23:48:55 +00:00
|
|
|
inverse_square_range: 1.0 / (light.range * light.range),
|
|
|
|
near: 0.1,
|
|
|
|
far: light.range,
|
|
|
|
// proj: projection,
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: light.shadow_depth_bias,
|
|
|
|
shadow_normal_bias: light.shadow_normal_bias,
|
2021-06-02 02:59:17 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-07-08 02:49:33 +00:00
|
|
|
for (i, light) in directional_lights
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.take(MAX_DIRECTIONAL_LIGHTS)
|
|
|
|
{
|
|
|
|
// direction is negated to be ready for N.L
|
|
|
|
let dir_to_light = -light.direction;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
// NOTE: A directional light seems to have to have an eye position on the line along the direction of the light
|
|
|
|
// through the world origin. I (Rob Swain) do not yet understand why it cannot be translated away from this.
|
|
|
|
let view = Mat4::look_at_rh(Vec3::ZERO, light.direction, Vec3::Y);
|
|
|
|
// NOTE: This orthographic projection defines the volume within which shadows from a directional light can be cast
|
|
|
|
let projection = light.projection;
|
|
|
|
|
|
|
|
gpu_lights.directional_lights[i] = GpuDirectionalLight {
|
|
|
|
// premultiply color by intensity
|
|
|
|
// we don't use the alpha at all, so no reason to multiply only [0..3]
|
|
|
|
color: (light.color.as_rgba_linear() * intensity).into(),
|
|
|
|
dir_to_light,
|
|
|
|
// NOTE: * view is correct, it should not be view.inverse() here
|
|
|
|
view_projection: projection * view,
|
2021-07-16 22:41:56 +00:00
|
|
|
shadow_depth_bias: light.shadow_depth_bias,
|
|
|
|
shadow_normal_bias: light.shadow_normal_bias,
|
2021-07-08 02:49:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let depth_texture_view =
|
|
|
|
directional_light_depth_texture
|
|
|
|
.texture
|
|
|
|
.create_view(&TextureViewDescriptor {
|
|
|
|
label: None,
|
|
|
|
format: None,
|
|
|
|
dimension: Some(TextureViewDimension::D2),
|
|
|
|
aspect: TextureAspect::All,
|
|
|
|
base_mip_level: 0,
|
|
|
|
mip_level_count: None,
|
|
|
|
base_array_layer: i as u32,
|
|
|
|
array_layer_count: NonZeroU32::new(1),
|
|
|
|
});
|
|
|
|
|
|
|
|
let view_light_entity = commands
|
|
|
|
.spawn()
|
|
|
|
.insert_bundle((
|
|
|
|
ViewLight {
|
|
|
|
depth_texture_view,
|
|
|
|
pass_name: format!("shadow pass directional light {}", i),
|
|
|
|
},
|
|
|
|
ExtractedView {
|
2021-08-25 05:57:57 +00:00
|
|
|
width: directional_light_shadow_map.size as u32,
|
|
|
|
height: directional_light_shadow_map.size as u32,
|
2021-07-08 02:49:33 +00:00
|
|
|
transform: GlobalTransform::from_matrix(view.inverse()),
|
|
|
|
projection,
|
|
|
|
},
|
|
|
|
RenderPhase::<ShadowPhase>::default(),
|
|
|
|
))
|
|
|
|
.id();
|
|
|
|
view_lights.push(view_light_entity);
|
|
|
|
}
|
|
|
|
let point_light_depth_texture_view =
|
|
|
|
point_light_depth_texture
|
2021-07-01 23:48:55 +00:00
|
|
|
.texture
|
|
|
|
.create_view(&TextureViewDescriptor {
|
|
|
|
label: None,
|
|
|
|
format: None,
|
|
|
|
dimension: Some(TextureViewDimension::CubeArray),
|
|
|
|
aspect: TextureAspect::All,
|
|
|
|
base_mip_level: 0,
|
|
|
|
mip_level_count: None,
|
2021-07-02 01:05:20 +00:00
|
|
|
base_array_layer: 0,
|
2021-07-01 23:48:55 +00:00
|
|
|
array_layer_count: None,
|
|
|
|
});
|
2021-07-08 02:49:33 +00:00
|
|
|
let directional_light_depth_texture_view = directional_light_depth_texture
|
|
|
|
.texture
|
|
|
|
.create_view(&TextureViewDescriptor {
|
|
|
|
label: None,
|
|
|
|
format: None,
|
|
|
|
dimension: Some(TextureViewDimension::D2Array),
|
|
|
|
aspect: TextureAspect::All,
|
|
|
|
base_mip_level: 0,
|
|
|
|
mip_level_count: None,
|
|
|
|
base_array_layer: 0,
|
|
|
|
array_layer_count: None,
|
|
|
|
});
|
2021-07-01 23:48:55 +00:00
|
|
|
|
2021-06-02 02:59:17 +00:00
|
|
|
commands.entity(entity).insert(ViewLights {
|
2021-07-08 02:49:33 +00:00
|
|
|
point_light_depth_texture: point_light_depth_texture.texture,
|
|
|
|
point_light_depth_texture_view,
|
|
|
|
directional_light_depth_texture: directional_light_depth_texture.texture,
|
|
|
|
directional_light_depth_texture_view,
|
2021-06-02 02:59:17 +00:00
|
|
|
lights: view_lights,
|
|
|
|
gpu_light_binding_index: light_meta.view_gpu_lights.push(gpu_lights),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
light_meta
|
|
|
|
.view_gpu_lights
|
2021-06-21 23:28:52 +00:00
|
|
|
.write_to_staging_buffer(&render_device);
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub struct ShadowPhase;
|
|
|
|
|
|
|
|
pub struct ShadowPassNode {
|
|
|
|
main_view_query: QueryState<&'static ViewLights>,
|
|
|
|
view_light_query: QueryState<(&'static ViewLight, &'static RenderPhase<ShadowPhase>)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ShadowPassNode {
|
|
|
|
pub const IN_VIEW: &'static str = "view";
|
|
|
|
|
|
|
|
pub fn new(world: &mut World) -> Self {
|
|
|
|
Self {
|
|
|
|
main_view_query: QueryState::new(world),
|
|
|
|
view_light_query: QueryState::new(world),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Node for ShadowPassNode {
|
|
|
|
fn input(&self) -> Vec<SlotInfo> {
|
|
|
|
vec![SlotInfo::new(ShadowPassNode::IN_VIEW, SlotType::Entity)]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn update(&mut self, world: &mut World) {
|
|
|
|
self.main_view_query.update_archetypes(world);
|
|
|
|
self.view_light_query.update_archetypes(world);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn run(
|
|
|
|
&self,
|
|
|
|
graph: &mut RenderGraphContext,
|
2021-06-21 23:28:52 +00:00
|
|
|
render_context: &mut RenderContext,
|
2021-06-02 02:59:17 +00:00
|
|
|
world: &World,
|
|
|
|
) -> Result<(), NodeRunError> {
|
|
|
|
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
2021-07-02 01:05:20 +00:00
|
|
|
if let Ok(view_lights) = self.main_view_query.get_manual(world, view_entity) {
|
2021-06-18 18:21:18 +00:00
|
|
|
for view_light_entity in view_lights.lights.iter().copied() {
|
|
|
|
let (view_light, shadow_phase) = self
|
|
|
|
.view_light_query
|
|
|
|
.get_manual(world, view_light_entity)
|
|
|
|
.unwrap();
|
2021-06-21 23:28:52 +00:00
|
|
|
let pass_descriptor = RenderPassDescriptor {
|
2021-07-08 02:49:33 +00:00
|
|
|
label: Some(&view_light.pass_name),
|
2021-06-21 23:28:52 +00:00
|
|
|
color_attachments: &[],
|
2021-06-18 18:21:18 +00:00
|
|
|
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
2021-07-01 23:48:55 +00:00
|
|
|
view: &view_light.depth_texture_view,
|
2021-06-18 18:21:18 +00:00
|
|
|
depth_ops: Some(Operations {
|
|
|
|
load: LoadOp::Clear(1.0),
|
|
|
|
store: true,
|
|
|
|
}),
|
|
|
|
stencil_ops: None,
|
2021-06-02 02:59:17 +00:00
|
|
|
}),
|
2021-06-18 18:21:18 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let draw_functions = world.get_resource::<DrawFunctions>().unwrap();
|
|
|
|
|
2021-06-21 23:28:52 +00:00
|
|
|
let render_pass = render_context
|
|
|
|
.command_encoder
|
|
|
|
.begin_render_pass(&pass_descriptor);
|
|
|
|
let mut draw_functions = draw_functions.write();
|
|
|
|
let mut tracked_pass = TrackedRenderPass::new(render_pass);
|
|
|
|
for drawable in shadow_phase.drawn_things.iter() {
|
|
|
|
let draw_function = draw_functions.get_mut(drawable.draw_function).unwrap();
|
|
|
|
draw_function.draw(
|
|
|
|
world,
|
|
|
|
&mut tracked_pass,
|
|
|
|
view_light_entity,
|
|
|
|
drawable.draw_key,
|
|
|
|
drawable.sort_key,
|
|
|
|
);
|
|
|
|
}
|
2021-06-18 18:21:18 +00:00
|
|
|
}
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-21 23:28:52 +00:00
|
|
|
type DrawShadowMeshParams<'s, 'w> = (
|
|
|
|
Res<'w, ShadowShaders>,
|
|
|
|
Res<'w, ExtractedMeshes>,
|
|
|
|
Res<'w, LightMeta>,
|
|
|
|
Res<'w, MeshMeta>,
|
2021-06-26 22:35:07 +00:00
|
|
|
Res<'w, RenderAssets<Mesh>>,
|
2021-06-21 23:28:52 +00:00
|
|
|
Query<'w, 's, &'w ViewUniformOffset>,
|
2021-06-02 02:59:17 +00:00
|
|
|
);
|
|
|
|
pub struct DrawShadowMesh {
|
2021-06-21 23:28:52 +00:00
|
|
|
params: SystemState<DrawShadowMeshParams<'static, 'static>>,
|
2021-06-02 02:59:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl DrawShadowMesh {
|
|
|
|
pub fn new(world: &mut World) -> Self {
|
|
|
|
Self {
|
|
|
|
params: SystemState::new(world),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Draw for DrawShadowMesh {
|
2021-06-21 23:28:52 +00:00
|
|
|
fn draw<'w>(
|
2021-06-02 02:59:17 +00:00
|
|
|
&mut self,
|
2021-06-21 23:28:52 +00:00
|
|
|
world: &'w World,
|
|
|
|
pass: &mut TrackedRenderPass<'w>,
|
2021-06-02 02:59:17 +00:00
|
|
|
view: Entity,
|
|
|
|
draw_key: usize,
|
|
|
|
_sort_key: usize,
|
|
|
|
) {
|
2021-06-26 22:35:07 +00:00
|
|
|
let (shadow_shaders, extracted_meshes, light_meta, mesh_meta, meshes, views) =
|
2021-06-21 23:28:52 +00:00
|
|
|
self.params.get(world);
|
|
|
|
let view_uniform_offset = views.get(view).unwrap();
|
|
|
|
let extracted_mesh = &extracted_meshes.into_inner().meshes[draw_key];
|
2021-07-01 23:48:55 +00:00
|
|
|
let shadow_shaders = shadow_shaders.into_inner();
|
|
|
|
pass.set_render_pipeline(&shadow_shaders.pipeline);
|
2021-06-02 02:59:17 +00:00
|
|
|
pass.set_bind_group(
|
|
|
|
0,
|
2021-06-21 23:28:52 +00:00
|
|
|
light_meta
|
|
|
|
.into_inner()
|
|
|
|
.shadow_view_bind_group
|
|
|
|
.as_ref()
|
|
|
|
.unwrap(),
|
|
|
|
&[view_uniform_offset.offset],
|
2021-06-02 02:59:17 +00:00
|
|
|
);
|
|
|
|
|
2021-07-01 23:03:33 +00:00
|
|
|
let transform_bindgroup_key = mesh_meta.mesh_transform_bind_group_key.unwrap();
|
2021-06-02 02:59:17 +00:00
|
|
|
pass.set_bind_group(
|
|
|
|
1,
|
2021-06-21 23:28:52 +00:00
|
|
|
mesh_meta
|
|
|
|
.into_inner()
|
|
|
|
.mesh_transform_bind_group
|
2021-07-01 23:03:33 +00:00
|
|
|
.get_value(transform_bindgroup_key)
|
2021-06-21 23:28:52 +00:00
|
|
|
.unwrap(),
|
|
|
|
&[extracted_mesh.transform_binding_offset],
|
2021-06-02 02:59:17 +00:00
|
|
|
);
|
2021-06-26 22:35:07 +00:00
|
|
|
|
|
|
|
let gpu_mesh = meshes.into_inner().get(&extracted_mesh.mesh).unwrap();
|
|
|
|
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
|
|
|
|
if let Some(index_info) = &gpu_mesh.index_info {
|
2021-06-21 23:28:52 +00:00
|
|
|
pass.set_index_buffer(index_info.buffer.slice(..), 0, IndexFormat::Uint32);
|
2021-06-02 02:59:17 +00:00
|
|
|
pass.draw_indexed(0..index_info.count, 0, 0..1);
|
|
|
|
} else {
|
|
|
|
panic!("non-indexed drawing not supported yet")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|