vtk-pure-rs 0.2.0

Pure Rust visualization toolkit — data structures, filters, I/O, rendering
Documentation
// Depth-of-field post-processing shader.
//
// Three passes:
// 1. CoC computation: sample depth buffer, compute circle of confusion
// 2. Blur: variable-radius blur based on CoC
// 3. Composite: blend blurred result with original

struct DofUniforms {
    focal_distance: f32,
    aperture: f32,
    max_blur: f32,
    texel_size_x: f32,
    texel_size_y: f32,
    near: f32,
    far: f32,
    horizontal: f32,
};

@group(0) @binding(0) var<uniform> u: DofUniforms;
@group(0) @binding(1) var color_tex: texture_2d<f32>;
@group(0) @binding(2) var depth_tex: texture_2d<f32>;
@group(0) @binding(3) var tex_sampler: sampler;

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

// Full-screen triangle
@vertex
fn vs_main(@builtin(vertex_index) idx: u32) -> VertexOutput {
    var out: VertexOutput;
    let x = f32(i32(idx & 1u)) * 4.0 - 1.0;
    let y = f32(i32(idx >> 1u)) * 4.0 - 1.0;
    out.position = vec4<f32>(x, y, 0.0, 1.0);
    out.uv = vec2<f32>((x + 1.0) * 0.5, (1.0 - y) * 0.5);
    return out;
}

fn linearize_depth(d: f32) -> f32 {
    let z_ndc = d * 2.0 - 1.0;
    return (2.0 * u.near * u.far) / (u.far + u.near - z_ndc * (u.far - u.near));
}

// Pass 1: compute CoC and store in alpha channel
@fragment
fn fs_coc(in: VertexOutput) -> @location(0) vec4<f32> {
    let color = textureSample(color_tex, tex_sampler, in.uv);
    let depth_raw = textureSample(depth_tex, tex_sampler, in.uv).r;
    let depth = linearize_depth(depth_raw);
    let coc = clamp(u.aperture * abs(depth - u.focal_distance) / max(depth, 0.001), 0.0, u.max_blur);
    return vec4<f32>(color.rgb, coc);
}

// Pass 2: separable Gaussian blur weighted by CoC
@fragment
fn fs_blur(in: VertexOutput) -> @location(0) vec4<f32> {
    let center = textureSample(color_tex, tex_sampler, in.uv);
    let coc = center.a;
    let radius = coc;

    if (radius < 0.5) {
        return center;
    }

    var dir: vec2<f32>;
    if (u.horizontal > 0.5) {
        dir = vec2<f32>(u.texel_size_x, 0.0);
    } else {
        dir = vec2<f32>(0.0, u.texel_size_y);
    }

    var result = center.rgb;
    var weight_sum = 1.0;
    let steps = i32(min(radius, 10.0));

    for (var i = 1; i <= steps; i = i + 1) {
        let offset = dir * f32(i);
        let s1 = textureSample(color_tex, tex_sampler, in.uv + offset);
        let s2 = textureSample(color_tex, tex_sampler, in.uv - offset);
        let w = 1.0 - f32(i) / (f32(steps) + 1.0);
        result = result + s1.rgb * w + s2.rgb * w;
        weight_sum = weight_sum + 2.0 * w;
    }

    return vec4<f32>(result / weight_sum, coc);
}

// Pass 3: composite (just output the blurred result)
@fragment
fn fs_composite(in: VertexOutput) -> @location(0) vec4<f32> {
    let color = textureSample(color_tex, tex_sampler, in.uv);
    return vec4<f32>(color.rgb, 1.0);
}