Skip to main content

runmat_plot/gpu/shaders/vertex/
point_direct.rs

1pub const SHADER: &str = r#"// Direct point shader: expands each input point into a screen-space quad
2
3struct Uniforms {
4    data_min: vec2<f32>,
5    data_max: vec2<f32>,
6    viewport_min: vec2<f32>,
7    viewport_max: vec2<f32>,
8    viewport_px: vec2<f32>,
9}
10
11struct PointStyleUniforms {
12    face_color: vec4<f32>,
13    edge_color: vec4<f32>,
14    edge_thickness_px: f32,
15    marker_shape: u32,
16    _pad: vec2<f32>,
17}
18
19@group(0) @binding(0)
20var<uniform> uniforms: Uniforms;
21@group(1) @binding(0)
22var<uniform> styleU: PointStyleUniforms;
23
24struct VertexInput {
25    @location(0) position: vec3<f32>,
26    @location(1) color: vec4<f32>,
27    @location(2) normal: vec3<f32>,
28    @location(3) tex_coords: vec2<f32>,
29}
30
31struct VSOut {
32    @builtin(position) clip_position: vec4<f32>,
33    @location(0) color: vec4<f32>,
34    @location(1) uv: vec2<f32>,
35    @location(2) center_ndc: vec2<f32>,
36    @location(3) half_ndc: vec2<f32>,
37    @location(4) size_px: f32,
38}
39
40@vertex
41fn vs_main(input: VertexInput) -> VSOut {
42    var out: VSOut;
43
44    let data_range = uniforms.data_max - uniforms.data_min;
45    let viewport_range = uniforms.viewport_max - uniforms.viewport_min;
46    let normalized = (input.position.xy - uniforms.data_min) / data_range;
47    let center = uniforms.viewport_min + normalized * viewport_range;
48
49    let size_px = max(2.0, input.normal.z);
50    let half_w_ndc = (0.5 * size_px) / uniforms.viewport_px.x * viewport_range.x;
51    let half_h_ndc = (0.5 * size_px) / uniforms.viewport_px.y * viewport_range.y;
52
53    let corner = input.tex_coords;
54    let offset_ndc = vec2<f32>(corner.x * half_w_ndc, corner.y * half_h_ndc);
55
56    let ndc = center + offset_ndc;
57    out.clip_position = vec4<f32>(ndc, 0.0, 1.0);
58    out.color = input.color;
59    out.uv = (corner + vec2<f32>(1.0, 1.0)) * 0.5;
60    out.center_ndc = center;
61    out.half_ndc = vec2<f32>(half_w_ndc, half_h_ndc);
62    out.size_px = size_px;
63    return out;
64}
65
66fn inside_triangle(pt: vec2<f32>) -> bool {
67    let a = vec2<f32>(-1.0, -1.0);
68    let b = vec2<f32>(1.0, -1.0);
69    let c = vec2<f32>(0.0, 1.0);
70    let v0 = c - a;
71    let v1 = b - a;
72    let v2 = pt - a;
73    let d00 = dot(v0, v0);
74    let d01 = dot(v0, v1);
75    let d11 = dot(v1, v1);
76    let d20 = dot(v2, v0);
77    let d21 = dot(v2, v1);
78    let denom = d00 * d11 - d01 * d01;
79    if (denom == 0.0) { return false; }
80    let v = (d11 * d20 - d01 * d21) / denom;
81    let w = (d00 * d21 - d01 * d20) / denom;
82    let u = 1.0 - v - w;
83    return (u >= 0.0) && (v >= 0.0) && (w >= 0.0);
84}
85
86fn inside_hexagon(pt: vec2<f32>) -> bool {
87    let a = vec2<f32>(1.0, 0.0);
88    let b = vec2<f32>(0.5, 0.8660254);
89    let c = vec2<f32>(-0.5, 0.8660254);
90    let da = abs(dot(pt, a));
91    let db = abs(dot(pt, b));
92    let dc = abs(dot(pt, c));
93    return max(da, max(db, dc)) <= 1.0;
94}
95
96@fragment
97fn fs_main(input: VSOut) -> @location(0) vec4<f32> {
98    let p = input.uv * 2.0 - vec2<f32>(1.0, 1.0);
99    let r = length(p);
100
101    var inside: bool;
102    switch (styleU.marker_shape) {
103        case 0u: { inside = r <= 1.0; }
104        case 1u: { inside = true; }
105        case 2u: { inside = inside_triangle(p); }
106        case 3u: { inside = (abs(p.x) + abs(p.y)) <= 1.0; }
107        case 4u: {
108            let w = 0.35;
109            inside = (abs(p.x) <= w) || (abs(p.y) <= w);
110        }
111        case 5u: {
112            let w = 0.30;
113            inside = min(abs(p.x - p.y), abs(p.x + p.y)) <= w;
114        }
115        case 6u: {
116            let w1 = 0.30;
117            let w2 = 0.25;
118            let plus = (abs(p.x) <= w1) || (abs(p.y) <= w1);
119            let cross = min(abs(p.x - p.y), abs(p.x + p.y)) <= w2;
120            inside = plus || cross;
121        }
122        case 7u: {
123            inside = inside_hexagon(p);
124        }
125        default: {
126            inside = true;
127        }
128    }
129    if (!inside) {
130        discard;
131    }
132
133    let edge_norm = clamp(2.0 * styleU.edge_thickness_px / max(input.size_px, 1.0), 0.0, 1.0);
134    let face_rgb = mix(input.color.rgb, styleU.face_color.rgb, styleU.face_color.a);
135    let edge_mix = clamp(styleU.edge_color.a, 0.0, 1.0);
136    let edge_rgb = mix(input.color.rgb, styleU.edge_color.rgb, edge_mix);
137
138    if (styleU.marker_shape == 0u) {
139        if (edge_norm > 0.0 && r > (1.0 - edge_norm)) {
140            return vec4<f32>(edge_rgb, input.color.a);
141        }
142    }
143
144    return vec4<f32>(face_rgb, input.color.a);
145}
146"#;