mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-05 00:45:42 +01:00
Fit the cascades of the sun shadows to the depth of the view
This commit is contained in:
@@ -87,13 +87,22 @@ float sun_shadow(vec3 position)
|
||||
for (cascade = 0U; cascade < MAX_CASCADES; cascade++)
|
||||
if (span_distance <= cascade_end[cascade])
|
||||
break;
|
||||
if (cascade >= MAX_CASCADES)
|
||||
return 0.0;
|
||||
vec4 lightpos = lightview[cascade] * vec4(position, 1.0);
|
||||
vec3 coords = lightpos.xyz / lightpos.w;
|
||||
if (coords.z < 0.0 || any(lessThan(coords.xy, vec2(0.0))) || any(greaterThan(coords.xy, vec2(1.0))))
|
||||
return 0.0;
|
||||
return texture(shadowmap, vec4(coords.xy, float(cascade), coords.z + 0.0001 * float(cascade + 1U)));
|
||||
for (; cascade < MAX_CASCADES; cascade++)
|
||||
{
|
||||
vec4 lightpos = lightview[cascade] * vec4(position, 1.0);
|
||||
vec3 coords = lightpos.xyz / lightpos.w;
|
||||
if (all(greaterThanEqual(coords.xy, vec2(0.0))) && all(lessThanEqual(coords.xy, vec2(1.0))))
|
||||
{
|
||||
if (coords.z < 0.0)
|
||||
return 0.0;
|
||||
return texture(shadowmap, vec4(coords.xy, float(cascade), coords.z + 0.0001 * float(cascade + 1U)));
|
||||
}
|
||||
// outside the map: lit, unless the cascades are fitted to the depth of the view (gfx.shadow.sdsm), whose maps cover
|
||||
// only the area of the surfaces seen; then the first further cascade whose map holds the point
|
||||
if (shadow_fit.x <= 0.0)
|
||||
return 0.0;
|
||||
}
|
||||
return 0.0;
|
||||
#else
|
||||
return 0.0;
|
||||
#endif
|
||||
|
||||
@@ -128,6 +128,21 @@ float calc_shadow_map()
|
||||
for (cascade = 0U; cascade < MAX_CASCADES; cascade++)
|
||||
if (distance <= cascade_end[cascade])
|
||||
break;
|
||||
// cascades fitted to the depth of the view of an earlier frame (gfx.shadow.sdsm) cover only the area of the points seen
|
||||
// then: a point at the edge of the view or one which came closer since may be outside the map of its cascade. it goes to
|
||||
// the first further cascade whose map holds it with the reach of the filter, and is lit past the last one
|
||||
if (shadow_fit.x > 0.0)
|
||||
{
|
||||
float border = 4.0 / float(textureSize(shadowmap, 0).x);
|
||||
for (; cascade < MAX_CASCADES; cascade++)
|
||||
{
|
||||
vec2 fitcoords = (lightview[cascade] * vec4(f_pos.xyz, 1.0)).xy;
|
||||
if (all(greaterThanEqual(fitcoords, vec2(border))) && all(lessThanEqual(fitcoords, vec2(1.0 - border))))
|
||||
break;
|
||||
}
|
||||
if (cascade >= MAX_CASCADES)
|
||||
return 0.0;
|
||||
}
|
||||
// Normal offset: sample the shadow map from a point pushed along the surface normal
|
||||
// by a fraction of the cascade's texel footprint. A constant depth bias can't keep up
|
||||
// with large texels (far cascades, small shadow maps) on surfaces at a grazing angle
|
||||
|
||||
126
shaders/shadow_fit_bounds.comp
Normal file
126
shaders/shadow_fit_bounds.comp
Normal file
@@ -0,0 +1,126 @@
|
||||
// the cascades of the sun shadows fitted to the depth of the main view (opengl46_shadow_fit), second pass: the distances
|
||||
// from the nearest to the farthest pixel which isn't the sky (shadow_fit_depth.comp) split between the cascades, and for each
|
||||
// cascade the box of the pixels it takes, in the space of the light. the cpu makes the maps of the cascades of a frame or
|
||||
// two later cover the boxes: the camera has moved since, so a cascade takes the pixels a little past its distances as well,
|
||||
// and the shading goes to the next cascade with a point outside the map of its own (light_common.glsl)
|
||||
|
||||
#include <common>
|
||||
|
||||
layout(local_size_x = SHADOW_FIT_GROUP_SIZE, local_size_y = SHADOW_FIT_GROUP_SIZE) in;
|
||||
|
||||
layout(binding = 0) uniform sampler2D scene_depth;
|
||||
|
||||
layout(std430, binding = SHADOW_FIT_SSBO_BINDING) buffer shadow_fit_ssbo
|
||||
{
|
||||
uint measurement;
|
||||
uint samples;
|
||||
// positive floats, as bits
|
||||
uint depthmin;
|
||||
uint depthmax;
|
||||
float splits[4];
|
||||
uint counts[4];
|
||||
// per cascade the lowest x, y, z, unused, then the highest x, y, z, unused, as shadow_fit_order() gives them
|
||||
uint bounds[MAX_CASCADES * 8U];
|
||||
};
|
||||
|
||||
// the inverse of the projection of the view
|
||||
layout(location = 0) uniform mat4 inv_projection;
|
||||
// rotation from the view space to the space of the light: x and y across the maps, z along the light
|
||||
layout(location = 4) uniform mat4 view_to_light;
|
||||
// x: the farthest distance the cascades may reach (the one of the fixed cascades), y: the nearest one the first may start
|
||||
// from, z: the least one the last reaches, w: the share of equal ratios in the splits, the rest in equal lengths
|
||||
layout(location = 8) uniform vec4 split_params;
|
||||
// how far past its distances a cascade takes the pixels, x + y * distance metres
|
||||
layout(location = 9) uniform vec4 margin_params;
|
||||
|
||||
shared uint shared_bounds[MAX_CASCADES * 8U];
|
||||
shared uint shared_counts[MAX_CASCADES];
|
||||
|
||||
// a float as a uint which orders the same way, also the negative ones
|
||||
uint shadow_fit_order(float value)
|
||||
{
|
||||
uint bits = floatBitsToUint(value);
|
||||
return ((bits & 0x80000000U) != 0U) ? ~bits : (bits | 0x80000000U);
|
||||
}
|
||||
|
||||
// the distances the cascades go from and to. both ends are moved out to the next step of 1/8 of a power of two, so that
|
||||
// the cascades change only now and then with the pixels coming and going
|
||||
void shadow_fit_splits(out float result[MAX_CASCADES + 1U])
|
||||
{
|
||||
float rangemax = split_params.x;
|
||||
float far = clamp(uintBitsToFloat(depthmax), split_params.z, rangemax);
|
||||
far = min(exp2(ceil(log2(far) * 8.0) / 8.0), rangemax);
|
||||
float near = clamp(uintBitsToFloat(depthmin), split_params.y, far * 0.25);
|
||||
near = exp2(floor(log2(near) * 8.0) / 8.0);
|
||||
result[0] = near;
|
||||
result[MAX_CASCADES] = far;
|
||||
for (uint cascade = 1U; cascade < MAX_CASCADES; ++cascade)
|
||||
{
|
||||
float share = float(cascade) / float(MAX_CASCADES);
|
||||
result[cascade] = mix(near + (far - near) * share, near * pow(far / near, share), split_params.w);
|
||||
}
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint index = gl_LocalInvocationIndex;
|
||||
if (index < MAX_CASCADES * 8U)
|
||||
shared_bounds[index] = ((index & 4U) == 0U) ? 0xFFFFFFFFU : 0U;
|
||||
if (index < MAX_CASCADES)
|
||||
shared_counts[index] = 0U;
|
||||
barrier();
|
||||
|
||||
float cascadesplits[MAX_CASCADES + 1U];
|
||||
shadow_fit_splits(cascadesplits);
|
||||
|
||||
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = textureSize(scene_depth, 0);
|
||||
if ((samples > 0U) && all(lessThan(pixel, size)))
|
||||
{
|
||||
float depth = texelFetch(scene_depth, pixel, 0).r;
|
||||
if (depth > 0.0)
|
||||
{
|
||||
vec2 ndc = (vec2(pixel) + 0.5) / vec2(size) * 2.0 - 1.0;
|
||||
vec4 position = inv_projection * vec4(ndc, depth, 1.0);
|
||||
vec3 view = position.xyz / position.w;
|
||||
float distance = length(view);
|
||||
vec3 light = mat3(view_to_light) * view;
|
||||
uvec3 ordered = uvec3(shadow_fit_order(light.x), shadow_fit_order(light.y), shadow_fit_order(light.z));
|
||||
for (uint cascade = 0U; cascade < MAX_CASCADES; ++cascade)
|
||||
{
|
||||
// the end of a cascade has the padding of opengl46_renderer::Render_pass(), MAX_CASCADES - cascade metres
|
||||
float low = (cascade == 0U) ? 0.0 : cascadesplits[cascade] - (margin_params.x + margin_params.y * cascadesplits[cascade]);
|
||||
float high = cascadesplits[cascade + 1U] + float(MAX_CASCADES - cascade) + margin_params.x + margin_params.y * cascadesplits[cascade + 1U];
|
||||
if ((distance < low) || (distance > high))
|
||||
continue;
|
||||
uint first = cascade * 8U;
|
||||
atomicAdd(shared_counts[cascade], 1U);
|
||||
atomicMin(shared_bounds[first + 0U], ordered.x);
|
||||
atomicMin(shared_bounds[first + 1U], ordered.y);
|
||||
atomicMin(shared_bounds[first + 2U], ordered.z);
|
||||
atomicMax(shared_bounds[first + 4U], ordered.x);
|
||||
atomicMax(shared_bounds[first + 5U], ordered.y);
|
||||
atomicMax(shared_bounds[first + 6U], ordered.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
barrier();
|
||||
|
||||
if ((index < MAX_CASCADES * 8U) && ((index & 3U) != 3U))
|
||||
{
|
||||
if ((index & 4U) == 0U)
|
||||
{
|
||||
if (shared_bounds[index] != 0xFFFFFFFFU)
|
||||
atomicMin(bounds[index], shared_bounds[index]);
|
||||
}
|
||||
else if (shared_bounds[index] != 0U)
|
||||
atomicMax(bounds[index], shared_bounds[index]);
|
||||
}
|
||||
if ((index < MAX_CASCADES) && (shared_counts[index] > 0U))
|
||||
atomicAdd(counts[index], shared_counts[index]);
|
||||
if (all(equal(gl_GlobalInvocationID.xy, uvec2(0U))))
|
||||
{
|
||||
for (uint split = 0U; split <= MAX_CASCADES; ++split)
|
||||
splits[split] = cascadesplits[split];
|
||||
}
|
||||
}
|
||||
65
shaders/shadow_fit_depth.comp
Normal file
65
shaders/shadow_fit_depth.comp
Normal file
@@ -0,0 +1,65 @@
|
||||
// the cascades of the sun shadows fitted to the depth of the main view (opengl46_shadow_fit), first pass: how many pixels of
|
||||
// the resolved depth of the view aren't the sky, and the nearest and the farthest of their distances from the camera.
|
||||
// the depth is reversed with the far plane at infinity: the sky, which writes no depth, stays at 0.
|
||||
// shadow_fit_bounds.comp splits the distances between the cascades
|
||||
|
||||
#include <common>
|
||||
|
||||
layout(local_size_x = SHADOW_FIT_GROUP_SIZE, local_size_y = SHADOW_FIT_GROUP_SIZE) in;
|
||||
|
||||
layout(binding = 0) uniform sampler2D scene_depth;
|
||||
|
||||
layout(std430, binding = SHADOW_FIT_SSBO_BINDING) buffer shadow_fit_ssbo
|
||||
{
|
||||
uint measurement;
|
||||
uint samples;
|
||||
// positive floats, as bits
|
||||
uint depthmin;
|
||||
uint depthmax;
|
||||
float splits[4];
|
||||
uint counts[4];
|
||||
uint bounds[MAX_CASCADES * 8U];
|
||||
};
|
||||
|
||||
// the inverse of the projection of the view
|
||||
layout(location = 0) uniform mat4 inv_projection;
|
||||
|
||||
shared uint shared_samples;
|
||||
shared uint shared_min;
|
||||
shared uint shared_max;
|
||||
|
||||
void main()
|
||||
{
|
||||
if (gl_LocalInvocationIndex == 0U)
|
||||
{
|
||||
shared_samples = 0U;
|
||||
// infinity
|
||||
shared_min = 0x7F800000U;
|
||||
shared_max = 0U;
|
||||
}
|
||||
barrier();
|
||||
|
||||
ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 size = textureSize(scene_depth, 0);
|
||||
if (all(lessThan(pixel, size)))
|
||||
{
|
||||
float depth = texelFetch(scene_depth, pixel, 0).r;
|
||||
if (depth > 0.0)
|
||||
{
|
||||
vec2 ndc = (vec2(pixel) + 0.5) / vec2(size) * 2.0 - 1.0;
|
||||
vec4 position = inv_projection * vec4(ndc, depth, 1.0);
|
||||
uint distance = floatBitsToUint(length(position.xyz / position.w));
|
||||
atomicAdd(shared_samples, 1U);
|
||||
atomicMin(shared_min, distance);
|
||||
atomicMax(shared_max, distance);
|
||||
}
|
||||
}
|
||||
barrier();
|
||||
|
||||
if ((gl_LocalInvocationIndex == 0U) && (shared_samples > 0U))
|
||||
{
|
||||
atomicAdd(samples, shared_samples);
|
||||
atomicMin(depthmin, shared_min);
|
||||
atomicMax(depthmax, shared_max);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user