2021-07-08 02:49:33 +00:00
|
|
|
use crate::{AmbientLight, DirectionalLight, ExtractedMeshes, MeshMeta, PbrShaders, PointLight};
|
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,
|
|
|
|
core_pipeline::Transparent3dPhase,
|
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,
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
#[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;
|
|
|
|
pub const POINT_SHADOW_SIZE: Extent3d = Extent3d {
|
2021-06-02 02:59:17 +00:00
|
|
|
width: 1024,
|
|
|
|
height: 1024,
|
2021-07-08 02:49:33 +00:00
|
|
|
depth_or_array_layers: (6 * MAX_POINT_LIGHTS) as u32,
|
|
|
|
};
|
|
|
|
pub const DIRECTIONAL_SHADOW_SIZE: Extent3d = Extent3d {
|
|
|
|
width: 4096,
|
|
|
|
height: 4096,
|
|
|
|
depth_or_array_layers: 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 {
|
|
|
|
constant: 2,
|
|
|
|
slope_scale: 2.0,
|
|
|
|
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-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-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,
|
|
|
|
intensity: point_light.intensity,
|
|
|
|
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,
|
|
|
|
shadow_normal_bias: point_light.shadow_normal_bias,
|
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() {
|
|
|
|
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,
|
|
|
|
shadow_normal_bias: directional_light.shadow_normal_bias,
|
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-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 {
|
|
|
|
size: POINT_SHADOW_SIZE,
|
|
|
|
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-07-08 02:49:33 +00:00
|
|
|
size: DIRECTIONAL_SHADOW_SIZE,
|
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-07-08 02:49:33 +00:00
|
|
|
width: POINT_SHADOW_SIZE.width,
|
|
|
|
height: POINT_SHADOW_SIZE.height,
|
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 {
|
|
|
|
width: DIRECTIONAL_SHADOW_SIZE.width,
|
|
|
|
height: DIRECTIONAL_SHADOW_SIZE.height,
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|