graphics 0.5.12

A 3D rendering engine for rust programs, with GUI integration
Documentation
struct Camera {
    proj_view : mat4x4<f32>,
    position  : vec4<f32>,
};

// Can't directly calculate the required inverse on CPU.
struct CameraBasis {
    right : vec3<f32>,  _pad0 : f32,
    up    : vec3<f32>,  _pad1 : f32,
};

@group(0) @binding(0)
var<uniform> camera: Camera;
@group(0) @binding(1)
var<uniform> cameraBasis: CameraBasis;


// Vertex input (per-vertex and per-instance):
struct VertexInput {
    @location(0) pos: vec2<f32>,         // Corner position (–1 to +1)
    @location(1) center: vec3<f32>,       // Instance center position (world-space)
    @location(2) amplitude: f32,          // Instance peak brightness
    @location(3) width: f32,             // Gaussian width (std. dev.)
    @location(4) color: vec4<f32>,        // Instance RGBA color
};

// Vertex output (to fragment):
struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
    @location(0) local_offset: vec2<f32>, // Offset from center in world (along right/up plane)
    @location(1) inst_color: vec4<f32>,   // Pass through instance color
    @location(2) inst_amplitude: f32,     // Pass through amplitude
    @location(3) inst_width: f32,         // Pass through width
};

// Fragment input matches VertexOutput
struct FragmentInput {
    @location(0) local_offset: vec2<f32>,
    @location(1) inst_color: vec4<f32>,
    @location(2) inst_amplitude: f32,
    @location(3) inst_width: f32,
};

@vertex
fn vs_main(input: VertexInput) -> VertexOutput {
    // Compute world-space offset for this vertex using camera basis (billboarding)
    let right = cameraBasis.right;
    let up    = cameraBasis.up;

    // Treat `width` as the quad's half-size (so quad spans 2*width in world units)

     // This thresh affects saturation, and when the gauss stops drawing. 3. is a good default.
     // Higher values will draw fainter areas; less likely to show artifacts from the square cutoff.
    let cutoff_thresh = 3.5;
    let offset_world = input.pos.x * right * input.width * cutoff_thresh +
                       input.pos.y * up    * input.width * cutoff_thresh;

    // World-space position of this vertex (billboard oriented toward camera)
    let world_pos = input.center + offset_world;
    // Project to clip space
    let clip_pos = camera.proj_view * vec4<f32>(world_pos, 1.0);

    var output: VertexOutput;

    output.clip_position = clip_pos;
    // Pass the 2D offset in world-plane coordinates to fragment (for distance calc)
//    output.local_offset = input.pos * input.width;
    output.local_offset = input.pos * input.width * cutoff_thresh;
    // Pass through instance attributes needed in fragment
    output.inst_color = input.color;
    output.inst_amplitude = input.amplitude;
    output.inst_width = input.width;

    return output;
}

@fragment
fn fs_main(input: FragmentInput) -> @location(0) vec4<f32> {
    // Compute squared distance from center in world space (r^2 = x^2 + y^2)
    let offset = input.local_offset;
    let r_sq = offset.x * offset.x + offset.y * offset.y;

    // Gaussian radial falloff: exp(-r^2 / (2σ^2)), σ = inst_width
    let sigma = input.inst_width;

    // intensity = amplitude * exp(-(r^2) / (2 * sigma^2))
    let amp = input.inst_amplitude * 0.2; // todo: Experimenting. Maybe do this CPU-side.
    let intensity = amp * exp(-r_sq / (2.0 * sigma * sigma));

    // Modulate base color by intensity (premultiplied alpha)
    var color = input.inst_color * intensity;

    return color;
}