bevy_lit 0.9.1

A lighting 2d library for Bevy
Documentation
#import bevy_core_pipeline::fullscreen_vertex_shader::FullscreenVertexOutput
#import bevy_render::view::View
#import bevy_lit::{
    settings_types::Lighting2dSettings,
    view_transformations::{frag_to_world, world_to_uv},
}

@group(0) @binding(0) var<uniform> view: View;
@group(0) @binding(1) var<uniform> settings: Lighting2dSettings;
@group(0) @binding(2) var lighting_texture: texture_2d<f32>;
@group(0) @binding(3) var voronoi_texture: texture_2d<f32>;
@group(0) @binding(4) var sampler_obj: sampler;

@fragment
fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4<f32> {
    let pos = frag_to_world(in.position / settings.scale, view).xy;
    let current = textureSample(lighting_texture, sampler_obj, in.uv);
    let sdf = get_sdf(pos);
    let p = settings.penetration;

    // Skip if outside occluder or penetration range
    if sdf > 0.0 || sdf < -p.max {
        return vec4(current.rgb, 1.0);
    }

    var penetration_color = vec3(0.0);
    var total_weight = 0.0;

    // Sampling configuration
    let two_pi = 6.2831853;
    let angle_step = two_pi / f32(p.directions);

    for (var dir_index = 0u; dir_index < p.directions; dir_index++) {
        let angle = f32(dir_index) * angle_step;
        let direction = vec2(cos(angle), sin(angle));

        for (var i = 0u; i < p.steps; i++) {
            let t = (f32(i) + 0.5) / f32(p.steps); // [0.03125 ... 0.96875]
            let distance = t * p.max;
            let offset = direction * distance;
            let sample_pos = pos + offset;
            let uv = world_to_uv(vec3(sample_pos, 0.0), view);
            let sample = textureSampleLevel(lighting_texture, sampler_obj, uv, 0.0);

            // Smooth falloff weight
            let weight = pow(1.0 - t, p.falloff);
            penetration_color += sample.rgb * weight;
            total_weight += weight;
        }
    }

    // Normalize and apply intensity
    if total_weight > 0.0 {
        penetration_color = (penetration_color / total_weight) * p.intensity;
    }

    return vec4(penetration_color, sdf);
}

fn get_sdf(pos: vec2<f32>) -> f32 {
    let uv = world_to_uv(vec3(pos, 0.0), view);
    let samp = textureSampleLevel(voronoi_texture, sampler_obj, uv, 0.0);

    // Original seed
    if samp.z == 1.0 {
        return 0.0;
    }

    let seed = frag_to_world(samp / settings.scale, view).xy;
    let dist = length(pos - seed);

    // Determine if the pixel is inside or outside the shape
    return select(dist, -dist, samp.w == 1.0);
}