lumen-engine 0.2.2

Core composition model and rendering orchestration for Lumen.
Documentation
struct HueSaturationParams {
    hue_offset: f32,
    saturation: f32,
    lightness: f32,
    _pad: f32,
}

@group(0) @binding(0) var input_tex: texture_2d<f32>;
@group(0) @binding(1) var<uniform> params: HueSaturationParams;
@group(0) @binding(2) var output_tex: texture_storage_2d<rgba8unorm, write>;

fn rgb_to_hsl(rgb: vec3<f32>) -> vec3<f32> {
    let max_value = max(max(rgb.r, rgb.g), rgb.b);
    let min_value = min(min(rgb.r, rgb.g), rgb.b);
    let lightness = (max_value + min_value) * 0.5;
    let delta = max_value - min_value;
    if (delta <= 0.000001) {
        return vec3<f32>(0.0, 0.0, lightness);
    }

    let saturation = select(
        delta / (max_value + min_value),
        delta / (2.0 - max_value - min_value),
        lightness > 0.5
    );
    var hue = 0.0;
    if (abs(max_value - rgb.r) <= 0.000001) {
        hue = ((rgb.g - rgb.b) / delta + select(0.0, 6.0, rgb.g < rgb.b)) / 6.0;
    } else if (abs(max_value - rgb.g) <= 0.000001) {
        hue = ((rgb.b - rgb.r) / delta + 2.0) / 6.0;
    } else {
        hue = ((rgb.r - rgb.g) / delta + 4.0) / 6.0;
    }
    return vec3<f32>(hue, saturation, lightness);
}

fn hue_to_rgb(p: f32, q: f32, initial_t: f32) -> f32 {
    let t = fract(initial_t);
    if (t < 1.0 / 6.0) {
        return p + (q - p) * 6.0 * t;
    }
    if (t < 1.0 / 2.0) {
        return q;
    }
    if (t < 2.0 / 3.0) {
        return p + (q - p) * (2.0 / 3.0 - t) * 6.0;
    }
    return p;
}

fn hsl_to_rgb(hsl: vec3<f32>) -> vec3<f32> {
    if (hsl.y <= 0.000001) {
        return vec3<f32>(hsl.z);
    }
    let q = select(
        hsl.z + hsl.y - hsl.z * hsl.y,
        hsl.z * (1.0 + hsl.y),
        hsl.z < 0.5
    );
    let p = 2.0 * hsl.z - q;
    return vec3<f32>(
        hue_to_rgb(p, q, hsl.x + 1.0 / 3.0),
        hue_to_rgb(p, q, hsl.x),
        hue_to_rgb(p, q, hsl.x - 1.0 / 3.0)
    );
}

@compute @workgroup_size(8, 8, 1)
fn cs_main(@builtin(global_invocation_id) id: vec3<u32>) {
    let size = textureDimensions(output_tex);
    if (id.x >= size.x || id.y >= size.y) {
        return;
    }

    let color = textureLoad(input_tex, vec2<i32>(id.xy), 0);
    var hsl = rgb_to_hsl(color.rgb);
    hsl.x = fract(hsl.x + params.hue_offset);
    hsl.y = clamp(hsl.y * params.saturation, 0.0, 1.0);
    hsl.z = clamp(hsl.z + params.lightness, 0.0, 1.0);
    textureStore(output_tex, vec2<i32>(id.xy), vec4<f32>(hsl_to_rgb(hsl), color.a));
}