#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput
#import bevy_render::view::View
#import bevy_light_2d::types::LightOccluder2d
#import bevy_light_2d::view_transformations::{frag_coord_to_ndc, ndc_to_world};
// We're currently only using a single uniform binding for occluders in
// WebGL2, which is limited to 4kb in BatchedUniformBuffer, so we need to
// ensure our occluders can fit in 4kb.
//
// As each occluder is 16 bytes, we can fit 4096 / 16 = 256 occluders.
const MAX_OCCLUDERS: u32 = 256u;
@group(0) @binding(0)
var<uniform> view: View;
// WebGL2 does not support storage buffers, so we fall back to a fixed length
// array in a uniform buffer.
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6
@group(0) @binding(1)
var<storage> occluders: array<LightOccluder2d>;
#else
@group(0) @binding(1)
var<uniform> occluders: array<LightOccluder2d, MAX_OCCLUDERS>;
#endif
@fragment
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
let pos = ndc_to_world(frag_coord_to_ndc(in.position.xy));
var sdf = occluder_sd(pos, occluders[0]);
// WebGL2 does not support storage buffers (or runtime sized arrays), so we
// need to use a fixed number of occluders.
#if AVAILABLE_STORAGE_BUFFER_BINDINGS >= 6
let occluder_count = arrayLength(&occluders);
#else
let occluder_count = MAX_OCCLUDERS;
#endif
for (var i = 1u; i < occluder_count; i++) {
sdf = min(sdf, occluder_sd(pos, occluders[i]));
}
return vec4(sdf, 0.0, 0.0, 1.0);
}
fn occluder_sd(p: vec2f, occluder: LightOccluder2d) -> f32 {
let local_pos = occluder.center - p;
let d = abs(local_pos) - occluder.half_size;
return length(max(d, vec2f(0.))) + min(max(d.x, d.y), 0.);
}