vtk-pure-rs 0.2.0

Pure Rust visualization toolkit — data structures, filters, I/O, rendering
Documentation
// Volume ray marching shader.
// Renders a 3D texture as a volume using front-to-back compositing.

struct VolumeUniforms {
    mvp: mat4x4<f32>,
    camera_pos: vec3<f32>,
    num_steps: f32,
    volume_min: vec3<f32>,
    opacity_scale: f32,
    volume_max: vec3<f32>,
    _pad: f32,
};

@group(0) @binding(0) var<uniform> params: VolumeUniforms;
@group(0) @binding(1) var volume_tex: texture_3d<f32>;
@group(0) @binding(2) var volume_sampler: sampler;
@group(0) @binding(3) var lut_tex: texture_1d<f32>;
@group(0) @binding(4) var lut_sampler: sampler;

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

// Proxy cube vertices (8 corners, 36 indices for 12 triangles)
@vertex
fn vs_volume(@builtin(vertex_index) idx: u32) -> VertexOutput {
    // Generate cube vertices procedurally
    let corners = array<vec3<f32>, 8>(
        vec3<f32>(0.0, 0.0, 0.0), vec3<f32>(1.0, 0.0, 0.0),
        vec3<f32>(1.0, 1.0, 0.0), vec3<f32>(0.0, 1.0, 0.0),
        vec3<f32>(0.0, 0.0, 1.0), vec3<f32>(1.0, 0.0, 1.0),
        vec3<f32>(1.0, 1.0, 1.0), vec3<f32>(0.0, 1.0, 1.0),
    );
    let indices = array<u32, 36>(
        0u,1u,2u, 0u,2u,3u, // -Z
        4u,6u,5u, 4u,7u,6u, // +Z
        0u,4u,5u, 0u,5u,1u, // -Y
        3u,2u,6u, 3u,6u,7u, // +Y
        0u,3u,7u, 0u,7u,4u, // -X
        1u,5u,6u, 1u,6u,2u, // +X
    );

    let vi = indices[idx];
    let unit_pos = corners[vi];
    let world_pos = params.volume_min + unit_pos * (params.volume_max - params.volume_min);

    var out: VertexOutput;
    out.position = params.mvp * vec4<f32>(world_pos, 1.0);
    out.world_pos = world_pos;
    return out;
}

@fragment
fn fs_volume(in: VertexOutput) -> @location(0) vec4<f32> {
    let ray_origin = params.camera_pos;
    let ray_dir = normalize(in.world_pos - params.camera_pos);

    // Ray-AABB intersection
    let inv_dir = 1.0 / ray_dir;
    let t1 = (params.volume_min - ray_origin) * inv_dir;
    let t2 = (params.volume_max - ray_origin) * inv_dir;
    let tmin_v = min(t1, t2);
    let tmax_v = max(t1, t2);
    let t_enter = max(max(tmin_v.x, tmin_v.y), tmin_v.z);
    let t_exit = min(min(tmax_v.x, tmax_v.y), tmax_v.z);

    if t_enter > t_exit || t_exit < 0.0 {
        discard;
    }

    let t_start = max(t_enter, 0.0);
    let steps = u32(params.num_steps);
    let step_size = (t_exit - t_start) / f32(steps);
    let volume_size = params.volume_max - params.volume_min;

    var color = vec3<f32>(0.0);
    var alpha = 0.0;

    for (var i = 0u; i < steps; i = i + 1u) {
        if alpha >= 0.99 { break; }

        let t = t_start + (f32(i) + 0.5) * step_size;
        let pos = ray_origin + t * ray_dir;

        // Normalize to [0,1] texture coordinates
        let uvw = (pos - params.volume_min) / volume_size;

        if any(uvw < vec3<f32>(0.0)) || any(uvw > vec3<f32>(1.0)) {
            continue;
        }

        // Sample 3D volume
        let scalar = textureSample(volume_tex, volume_sampler, uvw).r;

        // Look up transfer function
        let rgba = textureSample(lut_tex, lut_sampler, scalar);

        let sample_alpha = rgba.a * params.opacity_scale * step_size * 100.0;
        let sa = clamp(sample_alpha, 0.0, 1.0);

        // Front-to-back compositing
        color += (1.0 - alpha) * sa * rgba.rgb;
        alpha += (1.0 - alpha) * sa;
    }

    if alpha < 0.001 {
        discard;
    }

    return vec4<f32>(color, alpha);
}