zenthra-render 0.2.2

Wgpu-based GPU renderer for the Zenthra UI framework
Documentation
// blit.wgsl — Fullscreen texture blit
// Blits an offscreen texture onto the surface (used for the final present pass
// and for sampling the scene behind a backdrop-blur element).

@group(0) @binding(0) var t_src: texture_2d<f32>;
@group(0) @binding(1) var s_src: sampler;

struct VsOut {
    @builtin(position) pos: vec4<f32>,
    @location(0)       uv:  vec2<f32>,
}

@vertex
fn vs_main(@builtin(vertex_index) vi: u32) -> VsOut {
    var positions = array<vec2<f32>, 3>(
        vec2<f32>(-1.0, -1.0),
        vec2<f32>( 3.0, -1.0),
        vec2<f32>(-1.0,  3.0),
    );
    // wgpu: y=0 is top in NDC texture coords
    var uvs = array<vec2<f32>, 3>(
        vec2<f32>(0.0, 1.0),
        vec2<f32>(2.0, 1.0),
        vec2<f32>(0.0, -1.0),
    );
    var out: VsOut;
    out.pos = vec4<f32>(positions[vi], 0.0, 1.0);
    out.uv  = uvs[vi];
    return out;
}

@fragment
fn fs_main(in: VsOut) -> @location(0) vec4<f32> {
    return textureSample(t_src, s_src, in.uv);
}

struct BackdropUniforms {
    radius: vec4<f32>,
    rect_pos: vec2<f32>,
    rect_size: vec2<f32>,
    screen_size: vec2<f32>,
    time: f32,
    brightness: f32,
    saturation: f32,
    contrast: f32,
    blur_type: f32,
    opacity: f32,
    padding: vec2<f32>,
    _end_padding: vec2<f32>,
}

@group(1) @binding(0) var<uniform> u: BackdropUniforms;

struct BackdropVsOut {
    @builtin(position) pos: vec4<f32>,
    @location(0)       uv:  vec2<f32>,
    @location(1)       local_pos: vec2<f32>,
    @location(2)       half_size: vec2<f32>,
    @location(3)       radius: vec4<f32>,
}

@vertex
fn vs_backdrop(@builtin(vertex_index) vi: u32) -> BackdropVsOut {
    var quad = array<vec2<f32>, 6>(
        vec2<f32>(0.0, 0.0),
        vec2<f32>(1.0, 0.0),
        vec2<f32>(0.0, 1.0),
        vec2<f32>(0.0, 1.0),
        vec2<f32>(1.0, 0.0),
        vec2<f32>(1.0, 1.0),
    );
    let corner = quad[vi];
    let pixel_pos = u.rect_pos + corner * u.rect_size;

    let clip_x = (pixel_pos.x / u.screen_size.x) * 2.0 - 1.0;
    let clip_y = 1.0 - (pixel_pos.y / u.screen_size.y) * 2.0;

    var out: BackdropVsOut;
    out.pos = vec4<f32>(clip_x, clip_y, 0.0, 1.0);
    out.uv  = pixel_pos / u.screen_size;
    out.half_size = u.rect_size * 0.5;
    out.local_pos = pixel_pos - (u.rect_pos + out.half_size);
    out.radius = u.radius;
    return out;
}

fn sdf_rounded_box(p: vec2<f32>, b: vec2<f32>, r: vec4<f32>) -> f32 {
    let corner = select(
        select(r.w, r.z, p.x > 0.0),
        select(r.x, r.y, p.x > 0.0),
        p.y > 0.0
    );
    let q = abs(p) - b + corner;
    return length(max(q, vec2<f32>(0.0))) + min(max(q.x, q.y), 0.0) - corner;
}

fn rand(co: vec2<f32>) -> f32 {
    return fract(sin(dot(co, vec2<f32>(12.9898, 78.233))) * 43758.5453);
}

@fragment
fn fs_backdrop(in: BackdropVsOut) -> @location(0) vec4<f32> {
    let d = sdf_rounded_box(in.local_pos, in.half_size, in.radius);
    let aa_width = fwidth(d);
    let rect_alpha = 1.0 - smoothstep(-aa_width, aa_width, d);

    if (rect_alpha < 0.01) {
        discard;
    }

    var color = textureSample(t_src, s_src, in.uv);

    // ── CSS Backdrop Filters ─────────────────────────────────────────────────
    // 1. Brightness
    color = vec4<f32>(color.rgb * u.brightness, color.a);
    // 2. Saturation
    let luma = dot(color.rgb, vec3<f32>(0.2126, 0.7152, 0.0722));
    color = vec4<f32>(mix(vec3<f32>(luma), color.rgb, u.saturation), color.a);
    // 3. Contrast
    color = vec4<f32>((color.rgb - 0.5) * u.contrast + 0.5, color.a);
    // 4. Opacity
    color = vec4<f32>(color.rgb * u.opacity, color.a * u.opacity);

    // ── Apply Preset styles based on u.blur_type ──────────────────────────────
    // 0.0: Normal (raw blur) -> do nothing extra
    // 1.0: Frosted -> add grain
    // 2.0: Glassmorphism -> add rim highlight + grain
    // 3.0: OpaqueGlass -> add white tint + rim highlight + grain
    
    if (u.blur_type >= 3.0) {
        color = vec4<f32>(mix(color.rgb, vec3<f32>(1.0), 0.15), color.a);
    }
    
    if (u.blur_type >= 2.0) {
        // Specular Rim Highlight
        let edge_dist = abs(d);
        if (d <= 0.0 && edge_dist < 1.5) {
            let len = max(length(in.local_pos), 0.0001);
            let normal = in.local_pos / len;
            let tl_dir = normalize(vec2<f32>(-1.0, -1.0));
            let alignment = dot(normal, tl_dir);
            let rim_strength = smoothstep(0.0, 1.5, 1.5 - edge_dist);
            let rim_color = mix(vec3<f32>(0.2), vec3<f32>(1.0), smoothstep(-0.5, 0.5, alignment));
            color = vec4<f32>(mix(color.rgb, rim_color, rim_strength * 0.35), color.a);
        }
    }
    
    if (u.blur_type >= 1.0) {
        // Frosted Grain/Noise Overlay
        let n = rand(in.local_pos * 1.83);
        let grain = (n - 0.5) * 0.025;
        color = vec4<f32>(color.rgb + vec3<f32>(grain), color.a);
    }

    return vec4<f32>(color.rgb * rect_alpha, color.a * rect_alpha);
}