anchor-kit-wgpu 0.1.0

anchor-kit wgpu integration
Documentation
struct VertexInput {
    @location(0) position: vec2<f32>, // normalized pos
    @location(1) local_uv: vec2<f32>, // normalized uv within the object
    @location(2) background_color: vec4<f32>, // r, g, b, a
    @location(3) border_radius_local: vec4<f32>, // top-left, top-right, bottom-right, bottom-left (clockwise) in local units (0-1)
    @location(4) border_width_local: f32, // in local units (0-1)
    @location(5) border_color: vec4<f32>, // r, g, b, a
    @location(6) scale: vec2<f32>,
}

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
    @location(0) local_uv: vec2<f32>,
    @location(1) background_color: vec4<f32>,
    @location(2) border_radius_local: vec4<f32>, // top-left, top-right, bottom-right, bottom-left (clockwise)
    @location(3) border_width_local: f32,
    @location(4) border_color: vec4<f32>, // r, g, b, a
    @location(6) scale: vec2<f32>,
};

@vertex
fn vs_main(
    model: VertexInput    
) -> VertexOutput {
    let ndc_x = model.position.x * 2.0 - 1.0;
    let ndc_y = 1.0 - model.position.y * 2.0; // flip y (to bottom up) for GPU coords

    var out: VertexOutput;
    out.position = vec4<f32>(ndc_x, ndc_y, 0.0, 1.0); // just set z to 0 since we only deal with 2d rendering, 1.0 for w
    out.local_uv = model.local_uv;
    out.background_color = model.background_color;
    out.border_radius_local = model.border_radius_local;
    out.border_width_local = model.border_width_local;
    out.border_color = model.border_color;
    out.scale = model.scale;
    return out;
}

struct SDFInput {
    position: vec2<f32>, // normalized
    half_size: vec2<f32>, // SDFs work with half sizes
    border_radius: vec4<f32>, // top-left, top-right, bottom-right, bottom-left (clockwise)s
}

// sdf (signed distance function) inspired by: https://iquilezles.org/articles/distfunctions2d/
fn sdf_rounded(in: SDFInput) -> f32 {
    var radius_to_choose: f32;
    // we need to pick which radius we want to calculate the sdf on based on the position of the pixel (which quadrent of the box we are in)
    if in.position[0] >= 0.0 {
        if in.position[1] >= 0.0 {
            // top-right
            radius_to_choose = in.border_radius[1];
        } else {
            // bottom-right
            radius_to_choose = in.border_radius[2];
        }
    } else {
        if in.position[1] >= 0.0 {
            // top-left
            radius_to_choose = in.border_radius[0];
        } else {
            // bottom-left
            radius_to_choose = in.border_radius[3];
        }
    }

    // we need to make a vec of radius to choose to the math works here
    let q = abs(in.position) - in.half_size + vec2<f32>(radius_to_choose, radius_to_choose); 
    return min(max(q[0], q[1]), 0.0) + length(max(q, vec2<f32>(0.0, 0.0))) - radius_to_choose;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    // center at 0,0 (modify with aspect ratio)
    var position = in.local_uv - vec2<f32>(0.5, 0.5);
    position *= in.scale;
    var half_size = vec2<f32>(0.5, 0.5);
    half_size *= in.scale;

    var sdf_input: SDFInput;
    sdf_input.position = position;
    sdf_input.half_size = half_size;
    sdf_input.border_radius = in.border_radius_local;
    let d = sdf_rounded(sdf_input);

    // basic anti aliasing to get smooth corners
    let anti_aliasing = 1.0 / 50;
    let alpha_mul = 1.0 - smoothstep(0.0, anti_aliasing, d);

    // we need negative border width since with sdf d < 0.0 means inside the shape
    let border = (smoothstep(-in.border_width_local - anti_aliasing, -in.border_width_local + anti_aliasing, d) * (1.0 - smoothstep(0.0 - anti_aliasing, 0.0 + anti_aliasing, d))) * alpha_mul;
    let fill = (1.0 - smoothstep(-in.border_width_local - anti_aliasing, -in.border_width_local + anti_aliasing, d)) * alpha_mul;

    var output_color = vec4<f32>(0.0, 0.0, 0.0, 0.0);
    output_color += border * in.border_color;
    output_color += fill * in.background_color;
    return output_color;
}

@group(0) @binding(0)
var t_diffuse: texture_2d<f32>;
@group(0) @binding(1)
var s_diffuse: sampler;

@fragment
fn fs_image(in: VertexOutput) -> @location(0) vec4<f32> {
    // center at 0,0 (modify with aspect ratio)
    var position = in.local_uv - vec2<f32>(0.5, 0.5);
    position *= in.scale;
    var half_size = vec2<f32>(0.5, 0.5);
    half_size *= in.scale;

    var sdf_input: SDFInput;
    sdf_input.position = position;
    sdf_input.half_size = half_size;
    sdf_input.border_radius = in.border_radius_local;
    let d = sdf_rounded(sdf_input);

    // basic anti aliasing to get smooth corners
    let anti_aliasing = 1.0 / 50;
    let alpha_mul = 1.0 - smoothstep(0.0, anti_aliasing, d);

    // we need negative border width since with sdf d < 0.0 means inside the shape
    let border = (smoothstep(-in.border_width_local - anti_aliasing, -in.border_width_local + anti_aliasing, d) * (1.0 - smoothstep(0.0 - anti_aliasing, 0.0 + anti_aliasing, d))) * alpha_mul;
    let fill = (1.0 - smoothstep(-in.border_width_local - anti_aliasing, -in.border_width_local + anti_aliasing, d)) * alpha_mul;

    var output_color = vec4<f32>(0.0, 0.0, 0.0, 0.0);
    output_color += border * in.border_color;
    output_color += fill * textureSample(t_diffuse, s_diffuse, in.local_uv); // use the texture from the bindings for the fill colour
    return output_color;
}