bevy_fast_light 0.6.0

Simple 2D lighting for Bevy focused on performance over features.
Documentation
#import bevy_render::view::{View, uv_to_ndc, position_ndc_to_world}

#import bevy_fast_light::types::{ExtractedPointLight2d, ExtractedLight2dMeta, Light2dVertexOutput}

@group(0) @binding(0)
var<uniform> view: View;

@group(1) @binding(0)
var occluder_texture: texture_2d<f32>;
@group(1) @binding(1)
var occluder_sampler: sampler;
@group(1) @binding(2)
var<uniform> light_meta: ExtractedLight2dMeta;
// NOTE: WebGL2 does not support storage buffers and only supports up to 4096 bytes per uniform buffer.
#if AVAILABLE_STORAGE_BUFFER_BINDINGS == 0
    // NOTE: `ExtractedPointLight2d` is 48 bytes and `4096. / 32. = 128.`.
    const MAX_LIGHTS = 128u;
    @group(1) @binding(3)
    var<uniform> point_lights: array<ExtractedPointLight2d, MAX_LIGHTS>;
#else
    @group(1) @binding(3)
    var<storage, read> point_lights: array<ExtractedPointLight2d>;
#endif

@vertex
fn vertex(@builtin(vertex_index) vertex_index: u32) -> Light2dVertexOutput {
    let uv = vec2<f32>(f32(vertex_index >> 1u), f32(vertex_index & 1u)) * 2.0;
    let clip_position = vec4<f32>(uv * vec2<f32>(2.0, -2.0) + vec2<f32>(-1.0, 1.0), 0.0, 1.0);
    let ndc = uv_to_ndc(uv);
    let world_position = position_ndc_to_world(vec3<f32>(ndc, 0.0), view.world_from_clip).xy;

    return Light2dVertexOutput(
        clip_position,
        uv,
        world_position,
    );
}

@fragment
fn fragment(in: Light2dVertexOutput) -> @location(0) vec4<f32> {
    if textureSample(occluder_texture, occluder_sampler, in.uv).r > 0.5 {
        return vec4<f32>(0.);
    }

    var light_2d_color = vec3<f32>(0.);
    for (var i = 0u; i < light_meta.count; i++) {
        let light = point_lights[i];
        let dist = in.world_position - light.world_pos;
        let dist_sq = dot(dist, dist);

        if dist_sq > light.outer_radius_sq {
            continue;
        }

        if dist_sq <= light.inner_radius_sq {
            light_2d_color += light.color;
        } else {
            let radius_delta_frac = (dist_sq - light.inner_radius_sq) * light.inv_radius_delta_sq;
            let falloff = smoothstep(0., 1., 1. - radius_delta_frac);
            let attenuation = falloff * falloff;

            light_2d_color += light.color * attenuation;
        }
    }

    return vec4<f32>(light_2d_color, 1.);
}