render: rewrite PCF for spot lights to somehow fix PCSS directional lights
CI / build (pull_request) Successful in 9m48s
Details
CI / build (pull_request) Successful in 9m48s
Details
This commit is contained in:
parent
a85178eeea
commit
8545e7e27d
|
@ -8,7 +8,7 @@ use lyra_engine::{
|
||||||
math::{self, Quat, Transform, Vec3},
|
math::{self, Quat, Transform, Vec3},
|
||||||
render::{
|
render::{
|
||||||
graph::{ShadowCasterSettings, ShadowFilteringMode},
|
graph::{ShadowCasterSettings, ShadowFilteringMode},
|
||||||
light::{directional::DirectionalLight, PointLight},
|
light::{directional::DirectionalLight, PointLight, SpotLight},
|
||||||
},
|
},
|
||||||
scene::{
|
scene::{
|
||||||
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
||||||
|
@ -184,12 +184,15 @@ fn setup_scene_plugin(game: &mut Game) {
|
||||||
},
|
},
|
||||||
ShadowCasterSettings {
|
ShadowCasterSettings {
|
||||||
filtering_mode: ShadowFilteringMode::Pcss,
|
filtering_mode: ShadowFilteringMode::Pcss,
|
||||||
|
pcf_samples_num: 64,
|
||||||
|
pcss_blocker_search_samples: 36,
|
||||||
|
constant_depth_bias_scale: 5.0,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
light_tran,
|
light_tran,
|
||||||
));
|
));
|
||||||
|
|
||||||
world.spawn((
|
/* world.spawn((
|
||||||
cube_mesh.clone(),
|
cube_mesh.clone(),
|
||||||
PointLight {
|
PointLight {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
@ -207,31 +210,40 @@ fn setup_scene_plugin(game: &mut Game) {
|
||||||
Quat::IDENTITY,
|
Quat::IDENTITY,
|
||||||
Vec3::new(0.5, 0.5, 0.5),
|
Vec3::new(0.5, 0.5, 0.5),
|
||||||
),
|
),
|
||||||
));
|
)); */
|
||||||
|
|
||||||
/* world.spawn((
|
let t = Transform::new(
|
||||||
//cube_mesh.clone(),
|
Vec3::new(4.0 - 1.43, -13.0, 0.0),
|
||||||
PointLight {
|
//Vec3::new(-5.0, 1.0, -0.28),
|
||||||
|
//Vec3::new(-10.0, 0.94, -0.28),
|
||||||
|
|
||||||
|
Quat::from_euler(math::EulerRot::XYZ, 0.0, math::Angle::Degrees(-45.0).to_radians(), 0.0),
|
||||||
|
Vec3::new(0.15, 0.15, 0.15),
|
||||||
|
);
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
SpotLight {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
color: Vec3::new(0.278, 0.984, 0.0),
|
color: Vec3::new(1.0, 0.0, 0.0),
|
||||||
intensity: 2.0,
|
intensity: 3.0,
|
||||||
range: 9.0,
|
range: 4.5,
|
||||||
|
//cutoff: math::Angle::Degrees(45.0),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
Transform::from_xyz(-0.5, 2.0, -5.0),
|
/* ShadowCasterSettings {
|
||||||
)); */
|
filtering_mode: ShadowFilteringMode::Pcf,
|
||||||
|
..Default::default()
|
||||||
|
}, */
|
||||||
|
WorldTransform::from(t),
|
||||||
|
t,
|
||||||
|
//cube_mesh.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut camera = CameraComponent::new_3d();
|
let mut camera = CameraComponent::new_3d();
|
||||||
//camera.transform.translation += math::Vec3::new(0.0, 2.0, 10.5);
|
camera.transform.translation = math::Vec3::new(-1.0, -10.0, -1.5);
|
||||||
/* camera.transform.translation = math::Vec3::new(-3.0, -8.0, -3.0);
|
|
||||||
camera.transform.rotate_x(math::Angle::Degrees(-27.0));
|
camera.transform.rotate_x(math::Angle::Degrees(-27.0));
|
||||||
camera.transform.rotate_y(math::Angle::Degrees(-55.0)); */
|
camera.transform.rotate_y(math::Angle::Degrees(-90.0));
|
||||||
|
|
||||||
camera.transform.translation = math::Vec3::new(15.0, -8.0, 1.0);
|
|
||||||
camera.transform.rotate_x(math::Angle::Degrees(-27.0));
|
|
||||||
//camera.transform.rotate_y(math::Angle::Degrees(-90.0));
|
|
||||||
camera.transform.rotate_y(math::Angle::Degrees(90.0));
|
|
||||||
|
|
||||||
world.spawn((camera, FreeFlyCamera::default()));
|
world.spawn((camera, FreeFlyCamera::default()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
use crate::render::{
|
use crate::render::{
|
||||||
graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue},
|
graph::{Node, NodeDesc, NodeType, SlotAttribute, SlotValue},
|
||||||
light::{directional::DirectionalLight, LightType, PointLight},
|
light::{directional::DirectionalLight, LightType, PointLight, SpotLight},
|
||||||
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
resource::{FragmentState, RenderPipeline, RenderPipelineDescriptor, Shader, VertexState},
|
||||||
transform_buffer_storage::TransformBuffers,
|
transform_buffer_storage::TransformBuffers,
|
||||||
vertex::Vertex,
|
vertex::Vertex,
|
||||||
|
@ -186,6 +186,7 @@ impl ShadowMapsPass {
|
||||||
light_type: LightType,
|
light_type: LightType,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
light_pos: Transform,
|
light_pos: Transform,
|
||||||
|
light_half_outer_angle: Option<Angle>,
|
||||||
are_settings_custom: bool,
|
are_settings_custom: bool,
|
||||||
shadow_settings: ShadowCasterSettings,
|
shadow_settings: ShadowCasterSettings,
|
||||||
) -> LightDepthMap {
|
) -> LightDepthMap {
|
||||||
|
@ -265,7 +266,58 @@ impl ShadowMapsPass {
|
||||||
indices[0] = uniform_index;
|
indices[0] = uniform_index;
|
||||||
(atlas_index, indices)
|
(atlas_index, indices)
|
||||||
}
|
}
|
||||||
LightType::Spotlight => todo!(),
|
LightType::Spotlight => {
|
||||||
|
let directional_size = SHADOW_SIZE * 4;
|
||||||
|
// directional lights require a single map, so allocate that in the atlas.
|
||||||
|
let atlas_index = atlas
|
||||||
|
.pack(directional_size.x as _, directional_size.y as _)
|
||||||
|
.expect("failed to pack new shadow map into texture atlas");
|
||||||
|
let atlas_frame = atlas.texture_frame(atlas_index).expect("Frame missing");
|
||||||
|
|
||||||
|
let aspect = SHADOW_SIZE.x as f32 / SHADOW_SIZE.y as f32;
|
||||||
|
let projection = glam::Mat4::perspective_rh(
|
||||||
|
(light_half_outer_angle.unwrap() * 2.0).to_radians(),
|
||||||
|
aspect,
|
||||||
|
shadow_settings.near_plane,
|
||||||
|
shadow_settings.far_plane,
|
||||||
|
);
|
||||||
|
|
||||||
|
// honestly no clue why this works, but I got it from here and the results are good
|
||||||
|
// https://github.com/asylum2010/Asylum_Tutorials/blob/423e5edfaee7b5ea450a450e65f2eabf641b2482/ShaderTutors/43_ShadowMapFiltering/main.cpp#L323
|
||||||
|
/* let frustum_size = Vec2::new(0.5 * projection.col(0).x, 0.5 * projection.col(1).y);
|
||||||
|
// maybe its better to make this a vec2 on the gpu?
|
||||||
|
let size_avg = (frustum_size.x + frustum_size.y) / 2.0;
|
||||||
|
let light_size_uv = 0.2 * size_avg; */
|
||||||
|
|
||||||
|
let light_trans = light_pos.translation;
|
||||||
|
let look_view = glam::Mat4::look_at_rh(
|
||||||
|
light_trans,
|
||||||
|
light_trans + glam::vec3(1.0, 0.0, 0.0),
|
||||||
|
glam::vec3(0.0, -1.0, 0.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
let light_proj = projection * look_view;
|
||||||
|
|
||||||
|
let u = LightShadowUniform {
|
||||||
|
space_mat: light_proj,
|
||||||
|
atlas_frame,
|
||||||
|
near_plane: shadow_settings.near_plane,
|
||||||
|
far_plane: shadow_settings.far_plane,
|
||||||
|
light_size_uv: 0.0,
|
||||||
|
_padding1: 0,
|
||||||
|
light_pos: light_pos.translation,
|
||||||
|
has_shadow_settings,
|
||||||
|
pcf_samples_num: u.pcf_samples_num,
|
||||||
|
pcss_blocker_search_samples: u.pcss_blocker_search_samples,
|
||||||
|
constant_depth_bias: DEFAULT_CONSTANT_DEPTH_BIAS * shadow_settings.constant_depth_bias_scale,
|
||||||
|
_padding2: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let uniform_index = self.light_uniforms_buffer.insert(queue, &u);
|
||||||
|
let mut indices = [0; 6];
|
||||||
|
indices[0] = uniform_index;
|
||||||
|
(atlas_index, indices)
|
||||||
|
},
|
||||||
LightType::Point => {
|
LightType::Point => {
|
||||||
let aspect = SHADOW_SIZE.x as f32 / SHADOW_SIZE.y as f32;
|
let aspect = SHADOW_SIZE.x as f32 / SHADOW_SIZE.y as f32;
|
||||||
let projection = glam::Mat4::perspective_rh(
|
let projection = glam::Mat4::perspective_rh(
|
||||||
|
@ -626,6 +678,7 @@ impl Node for ShadowMapsPass {
|
||||||
LightType::Directional,
|
LightType::Directional,
|
||||||
entity,
|
entity,
|
||||||
*pos,
|
*pos,
|
||||||
|
None,
|
||||||
custom_settings,
|
custom_settings,
|
||||||
shadow_settings,
|
shadow_settings,
|
||||||
);
|
);
|
||||||
|
@ -649,6 +702,31 @@ impl Node for ShadowMapsPass {
|
||||||
LightType::Point,
|
LightType::Point,
|
||||||
entity,
|
entity,
|
||||||
*pos,
|
*pos,
|
||||||
|
None,
|
||||||
|
custom_settings,
|
||||||
|
shadow_settings,
|
||||||
|
);
|
||||||
|
index_components_queue.push_back((entity, atlas_index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (entity, pos, shadow_settings, spot) in world.view_iter::<(
|
||||||
|
Entities,
|
||||||
|
&Transform,
|
||||||
|
Option<&ShadowCasterSettings>,
|
||||||
|
&SpotLight,
|
||||||
|
)>() {
|
||||||
|
if !self.depth_maps.contains_key(&entity) {
|
||||||
|
let (custom_settings, shadow_settings) = shadow_settings
|
||||||
|
.map(|ss| (true, ss.clone()))
|
||||||
|
.unwrap_or((false, settings));
|
||||||
|
|
||||||
|
let atlas_index = self.create_depth_map(
|
||||||
|
&context.queue,
|
||||||
|
LightType::Spotlight,
|
||||||
|
entity,
|
||||||
|
*pos,
|
||||||
|
Some(spot.outer_cutoff),
|
||||||
custom_settings,
|
custom_settings,
|
||||||
shadow_settings,
|
shadow_settings,
|
||||||
);
|
);
|
||||||
|
@ -807,7 +885,23 @@ impl Node for ShadowMapsPass {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LightType::Spotlight => todo!(),
|
LightType::Spotlight => {
|
||||||
|
pass.set_pipeline(&pipeline);
|
||||||
|
|
||||||
|
let frame = atlas
|
||||||
|
.texture_frame(light_depth_map.atlas_index)
|
||||||
|
.expect("missing atlas frame for light");
|
||||||
|
|
||||||
|
light_shadow_pass_impl(
|
||||||
|
&mut pass,
|
||||||
|
&self.uniforms_bg,
|
||||||
|
&render_meshes,
|
||||||
|
&mesh_buffers,
|
||||||
|
&transforms,
|
||||||
|
&frame,
|
||||||
|
light_depth_map.uniform_index[0] as _,
|
||||||
|
);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -190,7 +190,8 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let shadow = calc_shadow_point_light(in.world_position, in.world_normal, light_dir, light, atlas_dimensions);
|
let shadow = calc_shadow_point_light(in.world_position, in.world_normal, light_dir, light, atlas_dimensions);
|
||||||
light_res += blinn_phong_point_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow);
|
light_res += blinn_phong_point_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow);
|
||||||
} else if (light.light_ty == LIGHT_TY_SPOT) {
|
} else if (light.light_ty == LIGHT_TY_SPOT) {
|
||||||
light_res += blinn_phong_spot_light(in.world_position, in.world_normal, light, u_material, specular_color);
|
let shadow = calc_shadow_spot_light(in.world_position, in.world_normal, light_dir, light, atlas_dimensions);
|
||||||
|
light_res += blinn_phong_spot_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,11 +483,62 @@ fn pcf_point_light(tex_coords: vec3<f32>, test_depth: f32, shadow_us: array<Ligh
|
||||||
return saturate(shadow);
|
return saturate(shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*fn pcf_point_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: u32, uv_radius: f32) -> f32 {
|
fn calc_shadow_spot_light(world_pos: vec3<f32>, world_normal: vec3<f32>, light_dir: vec3<f32>, light: Light, atlas_dimensions: vec2<i32>) -> f32 {
|
||||||
|
let map_data: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]];
|
||||||
|
let frag_pos_light_space = map_data.light_space_matrix * vec4<f32>(world_pos, 1.0);
|
||||||
|
|
||||||
|
var proj_coords = frag_pos_light_space.xyz / frag_pos_light_space.w;
|
||||||
|
// for some reason the y component is flipped after transforming
|
||||||
|
proj_coords.y = -proj_coords.y;
|
||||||
|
|
||||||
|
// Remap xy to [0.0, 1.0]
|
||||||
|
let xy_remapped = proj_coords.xy * 0.5 + 0.5;
|
||||||
|
|
||||||
|
// use a bias to avoid shadow acne
|
||||||
|
let current_depth = proj_coords.z - map_data.constant_depth_bias;
|
||||||
|
|
||||||
|
// get settings
|
||||||
|
let settings = get_shadow_settings(map_data);
|
||||||
|
let pcf_samples_num = settings.x;
|
||||||
|
let pcss_blocker_search_samples = settings.y;
|
||||||
|
|
||||||
var shadow = 0.0;
|
var shadow = 0.0;
|
||||||
for (var i = 0; i < i32(samples_num); i++) {
|
// hardware 2x2 PCF via camparison sampler
|
||||||
|
if pcf_samples_num == 2u {
|
||||||
|
let region_coords = to_atlas_frame_coords(map_data, xy_remapped, false);
|
||||||
|
shadow = textureSampleCompareLevel(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, region_coords, current_depth);
|
||||||
|
}
|
||||||
|
// only PCF is supported for spot lights
|
||||||
|
else if pcf_samples_num > 0u {
|
||||||
|
let texel_size = 1.0 / f32(map_data.atlas_frame.width);
|
||||||
|
shadow = pcf_spot_light(xy_remapped, current_depth, map_data, i32(pcf_samples_num), texel_size);
|
||||||
|
}
|
||||||
|
// no filtering
|
||||||
|
else {
|
||||||
|
let region_coords = to_atlas_frame_coords(map_data, xy_remapped, false);
|
||||||
|
let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, region_coords, 0.0);
|
||||||
|
shadow = select(1.0, 0.0, current_depth > closest_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// dont cast shadows outside the light's far plane
|
||||||
|
if (proj_coords.z > 1.0) {
|
||||||
|
shadow = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// dont cast shadows if the texture coords would go past the shadow maps
|
||||||
|
if (xy_remapped.x > 1.0 || xy_remapped.x < 0.0 || xy_remapped.y > 1.0 || xy_remapped.y < 0.0) {
|
||||||
|
shadow = 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the shadow coefficient using PCF of a directional light
|
||||||
|
fn pcf_spot_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMapUniform, samples_num: i32, uv_radius: f32) -> f32 {
|
||||||
|
var shadow = 0.0;
|
||||||
|
for (var i = 0; i < samples_num; i++) {
|
||||||
let offset = tex_coords + u_pcf_poisson_disc[i] * uv_radius;
|
let offset = tex_coords + u_pcf_poisson_disc[i] * uv_radius;
|
||||||
let new_coords = to_atlas_frame_coords(shadow_u, offset);
|
let new_coords = to_atlas_frame_coords(shadow_u, offset, false);
|
||||||
|
|
||||||
shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth);
|
shadow += textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas_compare, new_coords, test_depth);
|
||||||
}
|
}
|
||||||
|
@ -494,7 +546,7 @@ fn pcf_point_light(tex_coords: vec3<f32>, test_depth: f32, shadow_us: array<Ligh
|
||||||
|
|
||||||
// clamp shadow to [0; 1]
|
// clamp shadow to [0; 1]
|
||||||
return saturate(shadow);
|
return saturate(shadow);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
fn debug_grid(in: VertexOutput) -> vec4<f32> {
|
fn debug_grid(in: VertexOutput) -> vec4<f32> {
|
||||||
let tile_index_float: vec2<f32> = in.clip_position.xy / 16.0;
|
let tile_index_float: vec2<f32> = in.clip_position.xy / 16.0;
|
||||||
|
@ -574,7 +626,7 @@ fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_li
|
||||||
return (shadow * (ambient_color + diffuse_color + specular_color)) * point_light.intensity;
|
return (shadow * (ambient_color + diffuse_color + specular_color)) * point_light.intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_light: Light, material: Material, specular_factor: vec3<f32>) -> vec3<f32> {
|
fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_light: Light, material: Material, specular_factor: vec3<f32>, shadow: f32) -> vec3<f32> {
|
||||||
let light_color = spot_light.color;
|
let light_color = spot_light.color;
|
||||||
let light_pos = spot_light.position;
|
let light_pos = spot_light.position;
|
||||||
let camera_view_pos = u_camera.position;
|
let camera_view_pos = u_camera.position;
|
||||||
|
@ -609,13 +661,13 @@ fn blinn_phong_spot_light(world_pos: vec3<f32>, world_norm: vec3<f32>, spot_ligh
|
||||||
let distance = length(light_pos - world_pos);
|
let distance = length(light_pos - world_pos);
|
||||||
let attenuation = calc_attenuation(spot_light, distance);
|
let attenuation = calc_attenuation(spot_light, distance);
|
||||||
|
|
||||||
ambient_color *= attenuation * spot_light.intensity * cone;
|
ambient_color *= attenuation * cone;
|
||||||
diffuse_color *= attenuation * spot_light.intensity * cone;
|
diffuse_color *= attenuation * cone;
|
||||||
specular_color *= attenuation * spot_light.intensity * cone;
|
specular_color *= attenuation * cone;
|
||||||
//// end of spot light attenuation ////
|
//// end of spot light attenuation ////
|
||||||
|
|
||||||
|
//return /*ambient_color +*/ diffuse_color + specular_color;
|
||||||
return /*ambient_color +*/ diffuse_color + specular_color;
|
return (shadow * (diffuse_color + specular_color)) * spot_light.intensity;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_attenuation(light: Light, distance: f32) -> f32 {
|
fn calc_attenuation(light: Light, distance: f32) -> f32 {
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub fn radians_to_degrees(radians: f32) -> f32 {
|
||||||
radians * 180.0 / PI
|
radians * 180.0 / PI
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Angle {
|
pub enum Angle {
|
||||||
Degrees(f32),
|
Degrees(f32),
|
||||||
Radians(f32),
|
Radians(f32),
|
||||||
|
@ -69,3 +69,17 @@ impl std::ops::SubAssign for Angle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<f32> for Angle {
|
||||||
|
type Output = Angle;
|
||||||
|
|
||||||
|
fn mul(self, rhs: f32) -> Self::Output {
|
||||||
|
Angle::Radians(self.to_radians() * rhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::MulAssign<f32> for Angle {
|
||||||
|
fn mul_assign(&mut self, rhs: f32) {
|
||||||
|
*self = *self * rhs;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue