render: simple PCF
This commit is contained in:
parent
d02258224a
commit
ff06bd55f3
|
@ -166,7 +166,7 @@ fn setup_scene_plugin(game: &mut Game) {
|
|||
light_tran,
|
||||
));
|
||||
|
||||
world.spawn((
|
||||
/* world.spawn((
|
||||
//cube_mesh.clone(),
|
||||
PointLight {
|
||||
enabled: true,
|
||||
|
@ -188,7 +188,7 @@ fn setup_scene_plugin(game: &mut Game) {
|
|||
..Default::default()
|
||||
},
|
||||
Transform::from_xyz(-0.5, 2.0, -5.0),
|
||||
));
|
||||
)); */
|
||||
}
|
||||
|
||||
let mut camera = CameraComponent::new_3d();
|
||||
|
|
|
@ -142,7 +142,7 @@ impl Node for MeshPass {
|
|||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Comparison),
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
|
|
|
@ -114,6 +114,7 @@ impl ShadowMapsPass {
|
|||
min_filter: wgpu::FilterMode::Linear,
|
||||
mipmap_filter: wgpu::FilterMode::Linear,
|
||||
border_color: Some(wgpu::SamplerBorderColor::OpaqueWhite),
|
||||
compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ impl Material {
|
|||
//diffuse: glam::Vec3::new(value.base_color.x, value.base_color.y, value.base_color.z),
|
||||
//diffuse: glam::Vec3::new(1.0, 0.5, 0.31),
|
||||
//specular: glam::Vec3::new(0.5, 0.5, 0.5),
|
||||
ambient: glam::Vec3::new(1.0, 1.0, 1.0),
|
||||
ambient: glam::Vec3::new(1.0, 1.0, 1.0) * 0.5,
|
||||
diffuse: glam::Vec3::new(1.0, 1.0, 1.0),
|
||||
shininess: 32.0,
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ const LIGHT_TY_SPOT = 2u;
|
|||
|
||||
const ALPHA_CUTOFF = 0.1;
|
||||
|
||||
const SHADOW_MAP_PCF_SIZE = 4.0;
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec3<f32>,
|
||||
@location(1) tex_coords: vec2<f32>,
|
||||
|
@ -128,7 +130,7 @@ var t_light_grid: texture_storage_2d<rg32uint, read_write>; // rg32uint = vec2<u
|
|||
@group(5) @binding(0)
|
||||
var t_shadow_maps_atlas: texture_depth_2d;
|
||||
@group(5) @binding(1)
|
||||
var s_shadow_maps_atlas: sampler;
|
||||
var s_shadow_maps_atlas: sampler_comparison;
|
||||
@group(5) @binding(2)
|
||||
var<uniform> u_shadow_maps_atlas_size: vec2<u32>;
|
||||
@group(5) @binding(3)
|
||||
|
@ -165,7 +167,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
let shadow_u: LightShadowMapUniform = u_light_shadow[light.light_shadow_uniform_index[0]];
|
||||
let frag_pos_light_space = shadow_u.light_space_matrix * vec4<f32>(in.world_position, 1.0);
|
||||
|
||||
let shadow = calc_shadow_dir_light(in.world_normal, light_dir, frag_pos_light_space, atlas_dimensions, shadow_u.atlas_frame);
|
||||
let shadow = calc_shadow_dir_light(in.world_normal, light_dir, frag_pos_light_space, atlas_dimensions, shadow_u);
|
||||
light_res += blinn_phong_dir_light(in.world_position, in.world_normal, light, u_material, specular_color, shadow);
|
||||
} else if (light.light_ty == LIGHT_TY_POINT) {
|
||||
let shadow = calc_shadow_point(in.world_position, in.world_normal, light_dir, light, atlas_dimensions);
|
||||
|
@ -240,45 +242,60 @@ fn get_side_idx(tex_coord: vec3<f32>) -> vec3<f32> {
|
|||
return vec3<f32>(res, f32(cube_idx));
|
||||
}
|
||||
|
||||
fn calc_shadow_dir_light(normal: vec3<f32>, light_dir: vec3<f32>, frag_pos_light_space: vec4<f32>, atlas_dimensions: vec2<i32>, atlas_region: TextureAtlasFrame) -> f32 {
|
||||
fn calc_shadow_dir_light(normal: vec3<f32>, light_dir: vec3<f32>, frag_pos_light_space: vec4<f32>, atlas_dimensions: vec2<i32>, shadow_u: LightShadowMapUniform) -> f32 {
|
||||
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;
|
||||
|
||||
// dont cast shadows outside the light's far plane
|
||||
if (proj_coords.z > 1.0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Remap xy to [0.0, 1.0]
|
||||
let xy_remapped = proj_coords.xy * 0.5 + 0.5;
|
||||
|
||||
// no need to get the y since the maps are square
|
||||
let atlas_start = f32(atlas_region.x) / f32(atlas_dimensions.x);
|
||||
let atlas_end = f32(atlas_region.x + atlas_region.width) / f32(atlas_dimensions.x);
|
||||
// lerp the tex coords to the shadow map for this light.
|
||||
proj_coords.x = mix(atlas_start, atlas_end, xy_remapped.x);
|
||||
proj_coords.y = mix(atlas_start, atlas_end, xy_remapped.y);
|
||||
|
||||
// simulate `ClampToBorder`, not creating shadows past the shadow map regions
|
||||
if (proj_coords.x > atlas_end && proj_coords.y > atlas_end)
|
||||
|| (proj_coords.x < atlas_start && proj_coords.y < atlas_start) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// must manually apply offset to the texture coords since `textureSampleLevel` requires a
|
||||
// const value.
|
||||
let offset_coords = proj_coords.xy + (vec2<f32>(f32(atlas_region.x), f32(atlas_region.y)) / vec2<f32>(atlas_dimensions));
|
||||
let closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, offset_coords, 0.0);
|
||||
let current_depth = proj_coords.z;
|
||||
// get the atlas frame in [0; 1] in the atlas texture
|
||||
// z is width, w is height
|
||||
var region_rect = vec4<f32>(f32(shadow_u.atlas_frame.x), f32(shadow_u.atlas_frame.y), f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height));
|
||||
region_rect /= f32(atlas_dimensions.x);
|
||||
let region_coords = vec2<f32>(
|
||||
mix(region_rect.x, region_rect.x + region_rect.z, xy_remapped.x),
|
||||
mix(region_rect.y, region_rect.y + region_rect.w, xy_remapped.y)
|
||||
);
|
||||
|
||||
// use a bias to avoid shadow acne
|
||||
let bias = max(0.05 * (1.0 - dot(normal, light_dir)), 0.005);
|
||||
var shadow = 0.0;
|
||||
if current_depth - bias > closest_depth {
|
||||
let current_depth = proj_coords.z - bias;
|
||||
|
||||
var shadow = pcf_dir_light(region_coords, current_depth, shadow_u);
|
||||
|
||||
// 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_dir_light(tex_coords: vec2<f32>, test_depth: f32, shadow_u: LightShadowMapUniform) -> f32 {
|
||||
let half_filter_size = SHADOW_MAP_PCF_SIZE / 2.0;
|
||||
let texel_size = 1.0 / vec2<f32>(f32(shadow_u.atlas_frame.width), f32(shadow_u.atlas_frame.height));
|
||||
|
||||
// Sample PCF
|
||||
var shadow = 0.0;
|
||||
for (var x = -half_filter_size; x <= half_filter_size; x += 1.0) {
|
||||
for (var y = -half_filter_size; y <= half_filter_size; y += 1.0) {
|
||||
let offset = tex_coords + vec2<f32>(x, y) * texel_size;
|
||||
let pcf_depth = textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas, offset, test_depth);
|
||||
shadow += pcf_depth;
|
||||
}
|
||||
}
|
||||
shadow /= pow(SHADOW_MAP_PCF_SIZE, 2.0);
|
||||
// ensure the shadow value does not go above 1.0
|
||||
shadow = min(shadow, 1.0);
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
|
@ -289,9 +306,9 @@ fn calc_shadow_point(world_pos: vec3<f32>, world_normal: vec3<f32>, light_dir: v
|
|||
let cube_idx = i32(temp.z);
|
||||
|
||||
/// if an unknown cube side was returned, something is broken
|
||||
if cube_idx == 0 {
|
||||
/*if cube_idx == 0 {
|
||||
return 0.0;
|
||||
}
|
||||
}*/
|
||||
|
||||
var indices = light.light_shadow_uniform_index;
|
||||
let i = indices[cube_idx - 1];
|
||||
|
@ -303,27 +320,20 @@ fn calc_shadow_point(world_pos: vec3<f32>, world_normal: vec3<f32>, light_dir: v
|
|||
region_coords /= f32(atlas_dimensions.x);
|
||||
|
||||
// simulate `ClampToBorder`, not creating shadows past the shadow map regions
|
||||
if (coords_2d.x >= 1.0 || coords_2d.y >= 1.0) {
|
||||
/*if (coords_2d.x >= 1.0 || coords_2d.y >= 1.0) {
|
||||
return 0.0;
|
||||
}
|
||||
}*/
|
||||
|
||||
// get the coords inside of the region
|
||||
coords_2d.x = mix(region_coords.x, region_coords.x + region_coords.z, coords_2d.x);
|
||||
coords_2d.y = mix(region_coords.y, region_coords.y + region_coords.w, coords_2d.y);
|
||||
|
||||
var closest_depth = textureSampleLevel(t_shadow_maps_atlas, s_shadow_maps_atlas, coords_2d, 0.0);
|
||||
let current_depth = length(frag_to_light);
|
||||
|
||||
// convert depth from [0; 1] to the original depth value
|
||||
closest_depth *= u.far_plane;
|
||||
|
||||
// use a bias to avoid shadow acne
|
||||
let bias = max(0.05 * (1.0 - dot(world_normal, light_dir)), 0.005);
|
||||
var current_depth = length(frag_to_light) - bias;
|
||||
current_depth /= u.far_plane;
|
||||
|
||||
var shadow = 0.0;
|
||||
if current_depth - bias > closest_depth {
|
||||
shadow = 1.0;
|
||||
}
|
||||
var shadow = textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas, coords_2d, current_depth);
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
@ -369,7 +379,7 @@ fn blinn_phong_dir_light(world_pos: vec3<f32>, world_norm: vec3<f32>, dir_light:
|
|||
diffuse_color *= dir_light.diffuse;
|
||||
specular_color *= dir_light.specular;*/
|
||||
|
||||
return (ambient_color + (1.0 - shadow) * (diffuse_color + specular_color)) * dir_light.intensity;
|
||||
return (ambient_color + (shadow) * (diffuse_color + specular_color)) * dir_light.intensity;
|
||||
}
|
||||
|
||||
fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_light: Light, material: Material, specular_factor: vec3<f32>, shadow: f32) -> vec3<f32> {
|
||||
|
|
Loading…
Reference in New Issue