pub const KERNEL_FRUSTUM_CULL: &str = r#"
// Per-instance frustum culling with optional LOD selection.
// Tests each instance's bounding sphere against 6 frustum planes.
layout(local_size_x = 256) in;
struct Instance {
vec4 position_radius; // xyz = position, w = bounding sphere radius
vec4 extra; // xyz = scale, w = LOD override (-1 = auto)
};
struct VisibleInstance {
uint original_index;
uint lod_level;
vec2 _padding;
};
layout(std430, binding = 0) readonly buffer Instances {
Instance instances[];
};
layout(std430, binding = 1) writeonly buffer VisibleOut {
VisibleInstance visible[];
};
layout(binding = 0, offset = 0) uniform atomic_uint u_visible_count;
uniform uint u_instance_count;
uniform vec4 u_planes[6]; // frustum planes (normal.xyz, distance.w)
uniform vec3 u_camera_pos;
uniform vec4 u_lod_distances; // x=lod0, y=lod1, z=lod2, w=lod3
uniform uint u_enable_lod;
bool sphere_vs_frustum(vec3 center, float radius) {
for (int i = 0; i < 6; i++) {
float dist = dot(u_planes[i].xyz, center) + u_planes[i].w;
if (dist < -radius) {
return false; // fully outside this plane
}
}
return true;
}
uint compute_lod(vec3 pos, float radius) {
if (u_enable_lod == 0u) return 0u;
float dist = length(pos - u_camera_pos) - radius;
if (dist < u_lod_distances.x) return 0u;
if (dist < u_lod_distances.y) return 1u;
if (dist < u_lod_distances.z) return 2u;
return 3u;
}
void main() {
uint idx = gl_GlobalInvocationID.x;
if (idx >= u_instance_count) return;
Instance inst = instances[idx];
vec3 center = inst.position_radius.xyz;
float radius = inst.position_radius.w * max(inst.extra.x, max(inst.extra.y, inst.extra.z));
if (sphere_vs_frustum(center, radius)) {
uint lod = (inst.extra.w >= 0.0)
? uint(inst.extra.w)
: compute_lod(center, radius);
uint slot = atomicCounterIncrement(u_visible_count);
visible[slot].original_index = idx;
visible[slot].lod_level = lod;
}
}
"#;Expand description
Frustum culling kernel: per-instance visibility testing.