polyscope-render 0.5.10

Rendering backend for polyscope-rs: wgpu engine, shaders, and materials
Documentation
// Ground plane stencil shader
// Writes to stencil buffer to mark ground plane pixels for reflection
// Uses the same geometry as the main ground plane shader

struct CameraUniforms {
    view: mat4x4<f32>,
    proj: mat4x4<f32>,
    view_proj: mat4x4<f32>,
    inv_view_proj: mat4x4<f32>,
    camera_pos: vec4<f32>,
}

struct GroundUniforms {
    center: vec4<f32>,
    basis_x: vec4<f32>,
    basis_y: vec4<f32>,
    basis_z: vec4<f32>,
    height: f32,
    length_scale: f32,
    camera_height: f32,
    up_sign: f32,
    shadow_darkness: f32,
    shadow_mode: u32,
    is_orthographic: u32,
    _padding: f32,
}

@group(0) @binding(0) var<uniform> camera: CameraUniforms;
@group(0) @binding(1) var<uniform> ground: GroundUniforms;

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

// Same vertex shader as ground plane (handles both perspective and orthographic)
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput {
    var out: VertexOutput;

    // Triangle index (0-3) and vertex within triangle (0-2)
    let tri_idx = vertex_index / 3u;
    let vert_idx = vertex_index % 3u;

    // For orthographic mode, use large finite vertices instead of infinite
    let ortho_scale = ground.length_scale * 100.0;

    var center: vec4<f32>;
    var corners: array<vec4<f32>, 4>;

    if (ground.is_orthographic == 1u) {
        // Orthographic: use finite vertices centered on ground.center
        let base = ground.center.xyz + ground.basis_z.xyz * ground.height;
        center = vec4<f32>(base, 1.0);

        let offset_x = ground.basis_x.xyz * ortho_scale;
        let offset_y = ground.basis_y.xyz * ortho_scale;
        corners[0] = vec4<f32>(base + offset_x + offset_y, 1.0);
        corners[1] = vec4<f32>(base - offset_x + offset_y, 1.0);
        corners[2] = vec4<f32>(base - offset_x - offset_y, 1.0);
        corners[3] = vec4<f32>(base + offset_x - offset_y, 1.0);
    } else {
        // Perspective: use original infinite vertex technique
        // Center at ground height relative to scene center (consistent with orthographic)
        let base = ground.center.xyz + ground.basis_z.xyz * ground.height;
        center = vec4<f32>(base, 1.0);

        // Corners at infinity (w=0)
        corners[0] = vec4<f32>( ground.basis_x.xyz + ground.basis_y.xyz, 0.0);
        corners[1] = vec4<f32>(-ground.basis_x.xyz + ground.basis_y.xyz, 0.0);
        corners[2] = vec4<f32>(-ground.basis_x.xyz - ground.basis_y.xyz, 0.0);
        corners[3] = vec4<f32>( ground.basis_x.xyz - ground.basis_y.xyz, 0.0);
    }

    // Select vertices for this triangle
    var world_pos: vec4<f32>;
    if (vert_idx == 0u) {
        world_pos = center;
    } else if (vert_idx == 1u) {
        world_pos = corners[(tri_idx + 1u) % 4u];
    } else {
        world_pos = corners[tri_idx];
    }

    // Both perspective and orthographic vertices are already positioned at the correct height
    // (center at ground.center + height, corners either at infinity or at large offsets)
    let adjusted_pos = world_pos;

    out.position = camera.view_proj * adjusted_pos;
    out.pos_world_homog = adjusted_pos;

    return out;
}

// Fragment shader for stencil - applies fade logic but writes no color
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    // Convert homogeneous coords to world position
    let world_pos = in.pos_world_homog.xyz / in.pos_world_homog.w;

    // Compute 2D coordinates on ground plane relative to center
    let coord = world_pos - ground.center.xyz;
    let scaled_coord = coord / (ground.length_scale * 0.5);
    let coord_2d = vec2<f32>(
        dot(ground.basis_x.xyz, scaled_coord),
        dot(ground.basis_y.xyz, scaled_coord)
    );

    // Fade off far away (at ~8 length scales from center)
    let dist_from_center = length(coord_2d);
    let dist_fade = 1.0 - smoothstep(8.0, 8.5, dist_from_center);

    // Fade when viewing from below
    let height_diff = ground.up_sign * (ground.camera_height - ground.height) / ground.length_scale;
    let below_fade = smoothstep(0.0, 0.1, height_diff);

    let fade_factor = min(dist_fade, below_fade);
    if (fade_factor <= 0.0) {
        discard;
    }

    // Return transparent - we only care about stencil writes
    // The stencil operation is set in the pipeline state
    return vec4<f32>(0.0, 0.0, 0.0, 0.0);
}