rustial-renderer-wgpu 0.0.1

Pure WGPU renderer for the rustial 2.5D map engine
Documentation
// SDF circle shader with stroke, blur, and fog support.

struct Uniforms {
    view_proj:  mat4x4<f32>,
    fog_color:  vec4<f32>,
    eye_pos:    vec4<f32>,
    fog_params: vec4<f32>,   // (start, end, density, 0)
};

struct CircleVertexInput {
    @location(0) position:     vec3<f32>,
    @location(1) quad_offset:  vec2<f32>,
    @location(2) color:        vec4<f32>,
    @location(3) stroke_color: vec4<f32>,
    @location(4) params:       vec4<f32>,   // (radius, stroke_width, blur, 0)
};

struct CircleVertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) color:        vec4<f32>,
    @location(1) stroke_color: vec4<f32>,
    @location(2) quad_uv:      vec2<f32>,
    @location(3) params:       vec4<f32>,
    @location(4) world_pos:    vec3<f32>,
};

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

@vertex
fn vs_main(in: CircleVertexInput) -> CircleVertexOutput {
    let radius = in.params.x;
    let stroke_width = in.params.y;
    let total_radius = radius + stroke_width;

    // Expand the quad: position is the circle centre, quad_offset is ±1.
    let expanded = in.position + vec3<f32>(
        in.quad_offset.x * total_radius,
        in.quad_offset.y * total_radius,
        0.0,
    );

    var out: CircleVertexOutput;
    out.clip_position = u.view_proj * vec4<f32>(expanded, 1.0);
    out.color = in.color;
    out.stroke_color = in.stroke_color;
    out.quad_uv = in.quad_offset;
    out.params = in.params;
    out.world_pos = in.position;
    return out;
}

@fragment
fn fs_main(in: CircleVertexOutput) -> @location(0) vec4<f32> {
    let radius = in.params.x;
    let stroke_width = in.params.y;
    let blur = in.params.z;
    let total_radius = radius + stroke_width;

    // SDF: distance from quad centre in normalised space.
    let dist = length(in.quad_uv) * total_radius;

    // Anti-aliasing width (in world units, proportional to total radius).
    let aa = max(blur * total_radius, 0.5);

    // Outer edge: smoothstep fade.
    let outer_alpha = 1.0 - smoothstep(total_radius - aa, total_radius, dist);

    // Stroke ring: inside stroke zone.
    let inner_edge = radius;
    let stroke_alpha = smoothstep(inner_edge - aa, inner_edge, dist);

    // Blend stroke and fill colours.
    let fill_alpha = (1.0 - stroke_alpha) * outer_alpha;
    let ring_alpha = stroke_alpha * outer_alpha;
    let blended = in.color.rgb * fill_alpha * in.color.a +
                  in.stroke_color.rgb * ring_alpha * in.stroke_color.a;
    let alpha = fill_alpha * in.color.a + ring_alpha * in.stroke_color.a;

    if alpha < 0.001 {
        discard;
    }

    // --- Horizon fog ---
    let dx = in.world_pos.x - u.eye_pos.x;
    let dy = in.world_pos.y - u.eye_pos.y;
    let ground_dist = sqrt(dx * dx + dy * dy);
    let fog_start = u.fog_params.x;
    let fog_end   = u.fog_params.y;
    let density   = u.fog_params.z;
    let fog_t = clamp(
        (ground_dist - fog_start) / max(fog_end - fog_start, 0.001),
        0.0, 1.0,
    ) * density;
    let fog_mix = fog_t * 0.7;

    let final_rgb = mix(blended / max(alpha, 0.001), u.fog_color.rgb, fog_mix);
    return vec4<f32>(final_rgb, alpha * (1.0 - fog_mix));
}