Skip to content

Instantly share code, notes, and snippets.

@NotAPenguin0
Created July 28, 2020 09:19
Show Gist options
  • Select an option

  • Save NotAPenguin0/d792a560653216fb53b3eb07f21cce6e to your computer and use it in GitHub Desktop.

Select an option

Save NotAPenguin0/d792a560653216fb53b3eb07f21cce6e to your computer and use it in GitHub Desktop.
void calculate_depth_range() {
// Coordinate of the pixel on screen
ivec2 screen_pixel = ivec2(gl_GlobalInvocationID.xy);
vec2 UV = vec2(screen_pixel) / pc.screen_size;
ivec2 tex_size = ivec2(textureSize(depth_map));
ivec2 texels = ivec2(UV * tex_size);
float depth = texelFetch(depth_map, texels, 0).r;
// Depth values must be linearized because we stored them after applying projection
depth = (0.5 * camera.projection[3][2]) / (depth + 0.5 * camera.projection[2][2] - 0.5);
// Convert depth to uint so we can do atomic comparisons between threads
uint depth_int = floatBitsToUint(depth);
atomicMin(tile_data.min_depth_int, depth_int);
atomicMax(tile_data.min_depth_int, depth_int);
}
void create_frustum() {
if (gl_LocalInvocationIndex == 0) {
// ID of this tile.
ivec2 tile_id = ivec2(gl_WorkGroupID.xy);
// Total amount of tiles, specified in vkCmdDispatch. Same as the workgroup count.
ivec2 tile_count = ivec2(gl_NumWorkGroups.xy);
// Get back floating point values for the depth
float min_depth = uintBitsToFloat(tile_data.min_depth_int);
float max_depth = uintBitsToFloat(tile_data.max_depth_int);
// Scale step with tile size
vec2 negative_step = (2.0 * vec2(tile_id)) / vec2(tile_count);
vec2 positive_step = (2.0 * vec2(tile_id + ivec2(1, 1))) / vec2(tile_count);
// Create basic planes, these still need to be transformed (multiply by view_projection and apply perspective division).
tile_data.frustum.planes[0] = vec4(1.0, 0.0, 0.0, 1.0 - negative_step.x); // Left
tile_data.frustum.planes[1] = vec4(-1.0, 0.0, 0.0, -1.0 + positive_step.x); // Right
tile_data.frustum.planes[2] = vec4(0.0, 1.0, 0.0, 1.0 - negative_step.y); // Bottom
tile_data.frustum.planes[3] = vec4(0.0, -1.0, 0.0, -1.0 + positive_step.y); // Top
tile_data.frustum.planes[4] = vec4(0.0, 0.0, -1.0, -min_depth); // Near
tile_data.frustum.planes[5] = vec4(0.0, 0.0, 1.0, max_depth); // Far
// Transform the first four planes (left, right, bottom, top)
for (uint i = 0; i < 4; i++) {
tile_data.frustum.planes[i] *= camera.projection_view;
tile_data.frustum.planes[i] /= length(tile_data.frustum.planes[i].xyz);
}
// Transform the depth planes. We do these separately because we don't want to apply the projection matrix here (since we're working
// with linearized depth values).
tile_data.frustum.planes[4] *= camera.view;
tile_data.frustum.planes[4] /= length(tile_data.frustum.planes[4].xyz);
tile_data.frustum.planes[5] *= camera.view;
tile_data.frustum.planes[5] /= length(tile_data.frustum.planes[5].xyz);
}
}
void cull_lights() {
const uint thread_count = TILE_SIZE * TILE_SIZE;
const uint thread_index = gl_LocalInvocationIndex;
// Since we can process 256 lights simultaneously, we need additional passes of 256 lights if we exceed this limit.
const uint light_count = lights.data.length();
const uint pass_count = (light_count + thread_count - 1) / thread_count;
// Each thread processes one light per pass
for (uint pass = 0; pass < pass_count; ++pass) {
// Get the light index for this thread in this pass. If this index is out of bounds, we can stop testing lights on this thread.
const uint light_index = pass * thread_count + thread_index;
if (light_index >= light_count) { break; }
// Read relevant light data from buffer
const vec4 position = vec4(lights.data[light_index].transform.xyz, 1);
const float radius = lights.data[light_index].transform.w;
// Check if the light is inside the tile frustum
float dist = 0;
for (uint plane = 0; plane < 6; ++plane) {
dist = dot(position, tile_data.frustum.planes[plane]) + radius;
// If one of the distance tests fails, the light is not inside the tile frustum and we don't need to check the other planes
if (dist < 0.0) {
break;
}
}
// If all distance tests succeeded, dist is now positive and the light is visible in the current tile.
if (dist > 0.0) {
// Add one to the amount of visible lights in this tile, and retrieve old amount. This old amount of the index at which we
// have to store the light's index.
const uint write_index = atomicAdd(tile_data.visible_light_count, 1);
tile_data.visible_lights[write_index] = int(light_index);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment