|
|
@ -8,6 +8,8 @@ const LIGHT_TY_SPOT = 2u;
|
|
|
|
|
|
|
|
|
|
|
|
const ALPHA_CUTOFF = 0.1;
|
|
|
|
const ALPHA_CUTOFF = 0.1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const SHADOW_MAP_PCF_SIZE = 4.0;
|
|
|
|
|
|
|
|
|
|
|
|
struct VertexInput {
|
|
|
|
struct VertexInput {
|
|
|
|
@location(0) position: vec3<f32>,
|
|
|
|
@location(0) position: vec3<f32>,
|
|
|
|
@location(1) tex_coords: vec2<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)
|
|
|
|
@group(5) @binding(0)
|
|
|
|
var t_shadow_maps_atlas: texture_depth_2d;
|
|
|
|
var t_shadow_maps_atlas: texture_depth_2d;
|
|
|
|
@group(5) @binding(1)
|
|
|
|
@group(5) @binding(1)
|
|
|
|
var s_shadow_maps_atlas: sampler;
|
|
|
|
var s_shadow_maps_atlas: sampler_comparison;
|
|
|
|
@group(5) @binding(2)
|
|
|
|
@group(5) @binding(2)
|
|
|
|
var<uniform> u_shadow_maps_atlas_size: vec2<u32>;
|
|
|
|
var<uniform> u_shadow_maps_atlas_size: vec2<u32>;
|
|
|
|
@group(5) @binding(3)
|
|
|
|
@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 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 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);
|
|
|
|
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) {
|
|
|
|
} else if (light.light_ty == LIGHT_TY_POINT) {
|
|
|
|
let shadow = calc_shadow_point(in.world_position, in.world_normal, light_dir, light, atlas_dimensions);
|
|
|
|
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));
|
|
|
|
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;
|
|
|
|
var proj_coords = frag_pos_light_space.xyz / frag_pos_light_space.w;
|
|
|
|
// for some reason the y component is flipped after transforming
|
|
|
|
// for some reason the y component is flipped after transforming
|
|
|
|
proj_coords.y = -proj_coords.y;
|
|
|
|
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]
|
|
|
|
// Remap xy to [0.0, 1.0]
|
|
|
|
let xy_remapped = proj_coords.xy * 0.5 + 0.5;
|
|
|
|
let xy_remapped = proj_coords.xy * 0.5 + 0.5;
|
|
|
|
|
|
|
|
|
|
|
|
// no need to get the y since the maps are square
|
|
|
|
// get the atlas frame in [0; 1] in the atlas texture
|
|
|
|
let atlas_start = f32(atlas_region.x) / f32(atlas_dimensions.x);
|
|
|
|
// z is width, w is height
|
|
|
|
let atlas_end = f32(atlas_region.x + atlas_region.width) / f32(atlas_dimensions.x);
|
|
|
|
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));
|
|
|
|
// lerp the tex coords to the shadow map for this light.
|
|
|
|
region_rect /= f32(atlas_dimensions.x);
|
|
|
|
proj_coords.x = mix(atlas_start, atlas_end, xy_remapped.x);
|
|
|
|
let region_coords = vec2<f32>(
|
|
|
|
proj_coords.y = mix(atlas_start, atlas_end, xy_remapped.y);
|
|
|
|
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)
|
|
|
|
// 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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// use a bias to avoid shadow acne
|
|
|
|
// use a bias to avoid shadow acne
|
|
|
|
let bias = max(0.05 * (1.0 - dot(normal, light_dir)), 0.005);
|
|
|
|
let bias = max(0.05 * (1.0 - dot(normal, light_dir)), 0.005);
|
|
|
|
var shadow = 0.0;
|
|
|
|
let current_depth = proj_coords.z - bias;
|
|
|
|
if current_depth - bias > closest_depth {
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
let cube_idx = i32(temp.z);
|
|
|
|
|
|
|
|
|
|
|
|
/// if an unknown cube side was returned, something is broken
|
|
|
|
/// if an unknown cube side was returned, something is broken
|
|
|
|
if cube_idx == 0 {
|
|
|
|
/*if cube_idx == 0 {
|
|
|
|
return 0.0;
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
|
|
var indices = light.light_shadow_uniform_index;
|
|
|
|
var indices = light.light_shadow_uniform_index;
|
|
|
|
let i = indices[cube_idx - 1];
|
|
|
|
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);
|
|
|
|
region_coords /= f32(atlas_dimensions.x);
|
|
|
|
|
|
|
|
|
|
|
|
// simulate `ClampToBorder`, not creating shadows past the shadow map regions
|
|
|
|
// 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;
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
|
|
// get the coords inside of the region
|
|
|
|
// 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.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);
|
|
|
|
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
|
|
|
|
// use a bias to avoid shadow acne
|
|
|
|
let bias = max(0.05 * (1.0 - dot(world_normal, light_dir)), 0.005);
|
|
|
|
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;
|
|
|
|
var shadow = textureSampleCompare(t_shadow_maps_atlas, s_shadow_maps_atlas, coords_2d, current_depth);
|
|
|
|
if current_depth - bias > closest_depth {
|
|
|
|
|
|
|
|
shadow = 1.0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return shadow;
|
|
|
|
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;
|
|
|
|
diffuse_color *= dir_light.diffuse;
|
|
|
|
specular_color *= dir_light.specular;*/
|
|
|
|
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> {
|
|
|
|
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> {
|
|
|
|