17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-31 13:29:07 +01:00

Build view-space light cluster grid in a compute pass

This commit is contained in:
2026-10-15 14:16:55 +02:00
parent c0f45184b8
commit f85cf5fe52
11 changed files with 488 additions and 0 deletions

View File

@@ -0,0 +1,57 @@
// bounding boxes of the light clusters in view space, see gl/cluster_grid.h
// one invocation per cluster, run when the projection or the depth range of the pass changes
#include <common>
layout(local_size_x = CLUSTER_BUILD_GROUP_SIZE) in;
// clip space w at the lower boundary of slice k: the inverse of the slice formula used when shading
float slice_depth(uint k)
{
return exp((float(k) - cluster_slice_bias) / cluster_slice_scale);
}
void main()
{
uint index = gl_GlobalInvocationID.x;
if (index >= uint(CLUSTER_COUNT))
return;
uvec3 cell = uvec3(
index % uint(CLUSTER_GRID_X),
(index / uint(CLUSTER_GRID_X)) % uint(CLUSTER_GRID_Y),
index / uint(CLUSTER_GRID_X * CLUSTER_GRID_Y));
// clip x, y and w are affine functions of the view space position, while clip z is constant (reverse z with
// the far plane at infinity), which makes the projection matrix singular. the position is recovered from
// those three alone, so off-axis and rotated screens of custom viewports and vr eyes work too
mat3 linear = mat3(
vec3(cluster_projection[0][0], cluster_projection[0][1], cluster_projection[0][3]),
vec3(cluster_projection[1][0], cluster_projection[1][1], cluster_projection[1][3]),
vec3(cluster_projection[2][0], cluster_projection[2][1], cluster_projection[2][3]));
vec3 translation = vec3(cluster_projection[3][0], cluster_projection[3][1], cluster_projection[3][3]);
mat3 unproject = inverse(linear);
// the tile in clip space xy, the slice in clip space w
vec2 tiles = vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y);
vec2 ndc_min = vec2(cell.xy) / tiles * 2.0 - 1.0;
vec2 ndc_max = vec2(cell.xy + 1U) / tiles * 2.0 - 1.0;
float w_near = slice_depth(cell.z);
float w_far = slice_depth(cell.z + 1U);
vec3 lower = vec3(3.402823e38);
vec3 upper = vec3(-3.402823e38);
for (int corner = 0; corner < 8; ++corner)
{
vec2 ndc = vec2(
(corner & 1) != 0 ? ndc_max.x : ndc_min.x,
(corner & 2) != 0 ? ndc_max.y : ndc_min.y);
float w = (corner & 4) != 0 ? w_far : w_near;
vec3 position = unproject * (vec3(ndc * w, w) - translation);
lower = min(lower, position);
upper = max(upper, position);
}
cluster_aabbs[index].lower = vec4(lower, 0.0);
cluster_aabbs[index].upper = vec4(upper, 0.0);
}

View File

@@ -0,0 +1,38 @@
// debug view of the light clusters: tints every pixel of the scene with the colour of its depth slice,
// and darkens every other screen tile to show the grid. drawn over the resolved image, see gl/cluster_grid.h
in vec2 f_coords;
#include <common>
#texture (depth_tex, 0, R)
uniform sampler2D depth_tex;
layout(location = 0) out vec4 out_color;
const float TINT = 0.55;
// blue (near) through green to red (far)
vec3 ramp(float t)
{
return clamp(vec3(1.5) - abs(4.0 * t - vec3(3.0, 2.0, 1.0)), 0.0, 1.0);
}
void main()
{
// reverse z: 0 is the far plane at infinity, i.e. the sky, which has no cluster
float depth = texelFetch(depth_tex, ivec2(f_coords * vec2(textureSize(depth_tex, 0))), 0).r;
if (depth <= 0.0)
discard;
// depth is znear / w
float w = cluster_znear / depth;
int slice = clamp(int(floor(log(w) * cluster_slice_scale + cluster_slice_bias)), 0, CLUSTER_GRID_Z - 1);
ivec2 tile = clamp(ivec2(floor(f_coords * vec2(CLUSTER_GRID_X, CLUSTER_GRID_Y))), ivec2(0), ivec2(CLUSTER_GRID_X - 1, CLUSTER_GRID_Y - 1));
vec3 color = ramp(float(slice) / float(CLUSTER_GRID_Z - 1));
if (((tile.x + tile.y) & 1) != 0)
color *= 0.8;
out_color = vec4(color, TINT);
}