Improvements to SpotLight3D and OmniLight3D's shadows
OmniLight3D: * Fixed lack of precision in cube map mode by scaling the projection's znear. * Fixed aliasing issues by making the paraboloids use two square regions instead of two half squares. * Fixed shadowmap atlas bleeding by adding padding. * Fixed sihadow blur's inconsistent radius and unclamped sampling. SpotLight3D: * Fixed lack of precision by scaling the projection's znear. * Fixed normal biasing. Both: * Tweaked biasing to make sure it works out of the box in most situations.
This commit is contained in:
parent
8b6c168b3a
commit
55e7832d7b
12 changed files with 308 additions and 172 deletions
|
|
@ -7,8 +7,7 @@
|
|||
layout(push_constant, binding = 1, std430) uniform Params {
|
||||
float z_far;
|
||||
float z_near;
|
||||
bool z_flip;
|
||||
uint pad;
|
||||
vec2 texel_size;
|
||||
vec4 screen_rect;
|
||||
}
|
||||
params;
|
||||
|
|
@ -35,22 +34,23 @@ layout(set = 0, binding = 0) uniform samplerCube source_cube;
|
|||
layout(push_constant, binding = 1, std430) uniform Params {
|
||||
float z_far;
|
||||
float z_near;
|
||||
bool z_flip;
|
||||
uint pad;
|
||||
vec2 texel_size;
|
||||
vec4 screen_rect;
|
||||
}
|
||||
params;
|
||||
|
||||
void main() {
|
||||
vec2 uv = uv_interp;
|
||||
vec2 texel_size = abs(params.texel_size);
|
||||
|
||||
uv = clamp(uv * (1.0 + 2.0 * texel_size) - texel_size, vec2(0.0), vec2(1.0));
|
||||
|
||||
vec3 normal = vec3(uv * 2.0 - 1.0, 0.0);
|
||||
|
||||
normal.z = 0.5 - 0.5 * ((normal.x * normal.x) + (normal.y * normal.y));
|
||||
normal.z = 0.5 * (1.0 - dot(normal.xy, normal.xy)); // z = 1/2 - 1/2 * (x^2 + y^2)
|
||||
normal = normalize(normal);
|
||||
|
||||
normal.y = -normal.y; //needs to be flipped to match projection matrix
|
||||
if (!params.z_flip) {
|
||||
if (params.texel_size.x >= 0.0) { // Sign is used to encode Z flip
|
||||
normal.z = -normal.z;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1601,7 +1601,7 @@ void main() {
|
|||
continue; // Statically baked light and object uses lightmap, skip
|
||||
}
|
||||
|
||||
float shadow = light_process_omni_shadow(light_index, vertex, view);
|
||||
float shadow = light_process_omni_shadow(light_index, vertex, normal);
|
||||
|
||||
shadow = blur_shadow(shadow);
|
||||
|
||||
|
|
@ -1677,7 +1677,7 @@ void main() {
|
|||
continue; // Statically baked light and object uses lightmap, skip
|
||||
}
|
||||
|
||||
float shadow = light_process_spot_shadow(light_index, vertex, view);
|
||||
float shadow = light_process_spot_shadow(light_index, vertex, normal);
|
||||
|
||||
shadow = blur_shadow(shadow);
|
||||
|
||||
|
|
|
|||
|
|
@ -279,7 +279,7 @@ void light_compute(vec3 N, vec3 L, vec3 V, float A, vec3 light_color, float atte
|
|||
}
|
||||
|
||||
#ifdef USE_SHADOW_TO_OPACITY
|
||||
alpha = min(alpha, clamp(1.0 - attenuation), 0.0, 1.0));
|
||||
alpha = min(alpha, clamp(1.0 - attenuation, 0.0, 1.0));
|
||||
#endif
|
||||
|
||||
#endif //defined(LIGHT_CODE_USED)
|
||||
|
|
@ -320,7 +320,7 @@ float sample_directional_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, ve
|
|||
return avg * (1.0 / float(sc_directional_soft_shadow_samples));
|
||||
}
|
||||
|
||||
float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec4 coord) {
|
||||
float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec3 coord) {
|
||||
vec2 pos = coord.xy;
|
||||
float depth = coord.z;
|
||||
|
||||
|
|
@ -346,6 +346,49 @@ float sample_pcf_shadow(texture2D shadow, vec2 shadow_pixel_size, vec4 coord) {
|
|||
return avg * (1.0 / float(sc_soft_shadow_samples));
|
||||
}
|
||||
|
||||
float sample_omni_pcf_shadow(texture2D shadow, float blur_scale, vec2 coord, vec4 uv_rect, vec2 flip_offset, float depth) {
|
||||
//if only one sample is taken, take it from the center
|
||||
if (sc_soft_shadow_samples == 1) {
|
||||
vec2 pos = coord * 0.5 + 0.5;
|
||||
pos = uv_rect.xy + pos * uv_rect.zw;
|
||||
return textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(pos, depth, 1.0));
|
||||
}
|
||||
|
||||
mat2 disk_rotation;
|
||||
{
|
||||
float r = quick_hash(gl_FragCoord.xy) * 2.0 * M_PI;
|
||||
float sr = sin(r);
|
||||
float cr = cos(r);
|
||||
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
|
||||
}
|
||||
|
||||
float avg = 0.0;
|
||||
vec2 offset_scale = blur_scale * 2.0 * scene_data.shadow_atlas_pixel_size / uv_rect.zw;
|
||||
|
||||
for (uint i = 0; i < sc_soft_shadow_samples; i++) {
|
||||
vec2 offset = offset_scale * (disk_rotation * scene_data.soft_shadow_kernel[i].xy);
|
||||
vec2 sample_coord = coord + offset;
|
||||
|
||||
float sample_coord_length_sqaured = dot(sample_coord, sample_coord);
|
||||
bool do_flip = sample_coord_length_sqaured > 1.0;
|
||||
|
||||
if (do_flip) {
|
||||
float len = sqrt(sample_coord_length_sqaured);
|
||||
sample_coord = sample_coord * (2.0 / len - 1.0);
|
||||
}
|
||||
|
||||
sample_coord = sample_coord * 0.5 + 0.5;
|
||||
sample_coord = uv_rect.xy + sample_coord * uv_rect.zw;
|
||||
|
||||
if (do_flip) {
|
||||
sample_coord += flip_offset;
|
||||
}
|
||||
avg += textureProj(sampler2DShadow(shadow, shadow_sampler), vec4(sample_coord, depth, 1.0));
|
||||
}
|
||||
|
||||
return avg * (1.0 / float(sc_soft_shadow_samples));
|
||||
}
|
||||
|
||||
float sample_directional_soft_shadow(texture2D shadow, vec3 pssm_coord, vec2 tex_scale) {
|
||||
//find blocker
|
||||
float blocker_count = 0.0;
|
||||
|
|
@ -403,15 +446,21 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
|
|||
#ifndef USE_NO_SHADOWS
|
||||
if (omni_lights.data[idx].shadow_enabled) {
|
||||
// there is a shadowmap
|
||||
vec2 texel_size = scene_data.shadow_atlas_pixel_size;
|
||||
vec4 base_uv_rect = omni_lights.data[idx].atlas_rect;
|
||||
base_uv_rect.xy += texel_size;
|
||||
base_uv_rect.zw -= texel_size * 2.0;
|
||||
|
||||
vec3 light_rel_vec = omni_lights.data[idx].position - vertex;
|
||||
float light_length = length(light_rel_vec);
|
||||
// Omni lights use direction.xy to store to store the offset between the two paraboloid regions
|
||||
vec2 flip_offset = omni_lights.data[idx].direction.xy;
|
||||
|
||||
vec4 v = vec4(vertex, 1.0);
|
||||
vec3 local_vert = (omni_lights.data[idx].shadow_matrix * vec4(vertex, 1.0)).xyz;
|
||||
|
||||
vec4 splane = (omni_lights.data[idx].shadow_matrix * v);
|
||||
float shadow_len = length(local_vert); //need to remember shadow len from here
|
||||
vec3 shadow_dir = normalize(local_vert);
|
||||
|
||||
float shadow_len = length(splane.xyz); //need to remember shadow len from here
|
||||
vec3 local_normal = normalize(mat3(omni_lights.data[idx].shadow_matrix) * normal);
|
||||
vec3 normal_bias = local_normal * omni_lights.data[idx].shadow_normal_bias * (1.0 - abs(dot(local_normal, shadow_dir)));
|
||||
|
||||
float shadow;
|
||||
|
||||
|
|
@ -431,10 +480,10 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
|
|||
disk_rotation = mat2(vec2(cr, -sr), vec2(sr, cr));
|
||||
}
|
||||
|
||||
vec3 normal = normalize(splane.xyz);
|
||||
vec3 v0 = abs(normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
|
||||
vec3 tangent = normalize(cross(v0, normal));
|
||||
vec3 bitangent = normalize(cross(tangent, normal));
|
||||
vec3 basis_normal = shadow_dir;
|
||||
vec3 v0 = abs(basis_normal.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(0.0, 1.0, 0.0);
|
||||
vec3 tangent = normalize(cross(v0, basis_normal));
|
||||
vec3 bitangent = normalize(cross(tangent, basis_normal));
|
||||
float z_norm = shadow_len * omni_lights.data[idx].inv_radius;
|
||||
|
||||
tangent *= omni_lights.data[idx].soft_shadow_size * omni_lights.data[idx].soft_shadow_scale;
|
||||
|
|
@ -443,18 +492,17 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
|
|||
for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
|
||||
vec2 disk = disk_rotation * scene_data.penumbra_shadow_kernel[i].xy;
|
||||
|
||||
vec3 pos = splane.xyz + tangent * disk.x + bitangent * disk.y;
|
||||
vec3 pos = local_vert + tangent * disk.x + bitangent * disk.y;
|
||||
|
||||
pos = normalize(pos);
|
||||
vec4 uv_rect = omni_lights.data[idx].atlas_rect;
|
||||
|
||||
vec4 uv_rect = base_uv_rect;
|
||||
|
||||
if (pos.z >= 0.0) {
|
||||
pos.z += 1.0;
|
||||
uv_rect.y += uv_rect.w;
|
||||
} else {
|
||||
pos.z = 1.0 - pos.z;
|
||||
uv_rect.xy += flip_offset;
|
||||
}
|
||||
|
||||
pos.z = 1.0 + abs(pos.z);
|
||||
pos.xy /= pos.z;
|
||||
|
||||
pos.xy = pos.xy * 0.5 + 0.5;
|
||||
|
|
@ -479,18 +527,18 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
|
|||
shadow = 0.0;
|
||||
for (uint i = 0; i < sc_penumbra_shadow_samples; i++) {
|
||||
vec2 disk = disk_rotation * scene_data.penumbra_shadow_kernel[i].xy;
|
||||
vec3 pos = splane.xyz + tangent * disk.x + bitangent * disk.y;
|
||||
vec3 pos = local_vert + tangent * disk.x + bitangent * disk.y;
|
||||
|
||||
pos = normalize(pos);
|
||||
vec4 uv_rect = omni_lights.data[idx].atlas_rect;
|
||||
pos = normalize(pos + normal_bias);
|
||||
|
||||
vec4 uv_rect = base_uv_rect;
|
||||
|
||||
if (pos.z >= 0.0) {
|
||||
pos.z += 1.0;
|
||||
uv_rect.y += uv_rect.w;
|
||||
} else {
|
||||
pos.z = 1.0 - pos.z;
|
||||
uv_rect.xy += flip_offset;
|
||||
}
|
||||
|
||||
pos.z = 1.0 + abs(pos.z);
|
||||
pos.xy /= pos.z;
|
||||
|
||||
pos.xy = pos.xy * 0.5 + 0.5;
|
||||
|
|
@ -505,26 +553,19 @@ float light_process_omni_shadow(uint idx, vec3 vertex, vec3 normal) {
|
|||
shadow = 1.0;
|
||||
}
|
||||
} else {
|
||||
splane.xyz = normalize(splane.xyz);
|
||||
vec4 clamp_rect = omni_lights.data[idx].atlas_rect;
|
||||
vec4 uv_rect = base_uv_rect;
|
||||
|
||||
if (splane.z >= 0.0) {
|
||||
splane.z += 1.0;
|
||||
|
||||
clamp_rect.y += clamp_rect.w;
|
||||
|
||||
} else {
|
||||
splane.z = 1.0 - splane.z;
|
||||
vec3 shadow_sample = normalize(shadow_dir + normal_bias);
|
||||
if (shadow_sample.z >= 0.0) {
|
||||
uv_rect.xy += flip_offset;
|
||||
flip_offset *= -1.0;
|
||||
}
|
||||
|
||||
splane.xy /= splane.z;
|
||||
|
||||
splane.xy = splane.xy * 0.5 + 0.5;
|
||||
splane.z = shadow_len * omni_lights.data[idx].inv_radius;
|
||||
splane.z -= omni_lights.data[idx].shadow_bias;
|
||||
splane.xy = clamp_rect.xy + splane.xy * clamp_rect.zw;
|
||||
splane.w = 1.0; //needed? i think it should be 1 already
|
||||
shadow = sample_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, splane);
|
||||
shadow_sample.z = 1.0 + abs(shadow_sample.z);
|
||||
vec2 pos = shadow_sample.xy / shadow_sample.z;
|
||||
float depth = shadow_len - omni_lights.data[idx].shadow_bias;
|
||||
depth *= omni_lights.data[idx].inv_radius;
|
||||
shadow = sample_omni_pcf_shadow(shadow_atlas, omni_lights.data[idx].soft_shadow_scale / shadow_sample.z, pos, uv_rect, flip_offset, depth);
|
||||
}
|
||||
|
||||
return shadow;
|
||||
|
|
@ -608,13 +649,11 @@ void light_process_omni(uint idx, vec3 vertex, vec3 eye_vec, vec3 normal, vec3 v
|
|||
vec4 atlas_rect = omni_lights.data[idx].projector_rect;
|
||||
|
||||
if (local_v.z >= 0.0) {
|
||||
local_v.z += 1.0;
|
||||
atlas_rect.y += atlas_rect.w;
|
||||
|
||||
} else {
|
||||
local_v.z = 1.0 - local_v.z;
|
||||
}
|
||||
|
||||
local_v.z = 1.0 + abs(local_v.z);
|
||||
|
||||
local_v.xy /= local_v.z;
|
||||
local_v.xy = local_v.xy * 0.5 + 0.5;
|
||||
vec2 proj_uv = local_v.xy * atlas_rect.zw;
|
||||
|
|
@ -694,15 +733,18 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
|
|||
vec3 light_rel_vec = spot_lights.data[idx].position - vertex;
|
||||
float light_length = length(light_rel_vec);
|
||||
vec3 spot_dir = spot_lights.data[idx].direction;
|
||||
//there is a shadowmap
|
||||
vec4 v = vec4(vertex, 1.0);
|
||||
|
||||
float shadow;
|
||||
vec3 shadow_dir = light_rel_vec / light_length;
|
||||
vec3 normal_bias = normal * light_length * spot_lights.data[idx].shadow_normal_bias * (1.0 - abs(dot(normal, shadow_dir)));
|
||||
|
||||
//there is a shadowmap
|
||||
vec4 v = vec4(vertex + normal_bias, 1.0);
|
||||
|
||||
vec4 splane = (spot_lights.data[idx].shadow_matrix * v);
|
||||
splane.z -= spot_lights.data[idx].shadow_bias / (light_length * spot_lights.data[idx].inv_radius);
|
||||
splane /= splane.w;
|
||||
splane.z -= spot_lights.data[idx].shadow_bias;
|
||||
|
||||
float shadow;
|
||||
if (sc_use_light_soft_shadows && spot_lights.data[idx].soft_shadow_size > 0.0) {
|
||||
//soft shadow
|
||||
|
||||
|
|
@ -753,11 +795,9 @@ float light_process_spot_shadow(uint idx, vec3 vertex, vec3 normal) {
|
|||
//no blockers found, so no shadow
|
||||
shadow = 1.0;
|
||||
}
|
||||
|
||||
} else {
|
||||
//hard shadow
|
||||
vec4 shadow_uv = vec4(splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy, splane.z, 1.0);
|
||||
|
||||
vec3 shadow_uv = vec3(splane.xy * spot_lights.data[idx].atlas_rect.zw + spot_lights.data[idx].atlas_rect.xy, splane.z);
|
||||
shadow = sample_pcf_shadow(shadow_atlas, spot_lights.data[idx].soft_shadow_scale * scene_data.shadow_atlas_pixel_size, shadow_uv);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1387,7 +1387,7 @@ void main() {
|
|||
break;
|
||||
}
|
||||
|
||||
float shadow = light_process_omni_shadow(light_index, vertex, view);
|
||||
float shadow = light_process_omni_shadow(light_index, vertex, normal);
|
||||
|
||||
shadow = blur_shadow(shadow);
|
||||
|
||||
|
|
@ -1435,7 +1435,7 @@ void main() {
|
|||
break;
|
||||
}
|
||||
|
||||
float shadow = light_process_spot_shadow(light_index, vertex, view);
|
||||
float shadow = light_process_spot_shadow(light_index, vertex, normal);
|
||||
|
||||
shadow = blur_shadow(shadow);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue