helion_core/
shaders.rs

1// Graphics Pipeline Overview:
2// ========================
3//
4// The GPU rendering pipeline has several stages:
5//
6// 1. VERTEX SHADER (runs once per vertex/point)
7//    - Input: Raw vertex data (position, color, size) from CPU
8//    - Process: Transform coordinates to GPU clip space [-1, 1]
9//    - Output: Transformed vertices passed to next stage
10//
11// 2. RASTERIZATION (automatic, GPU handles this)
12//    - Takes transformed vertices
13//    - Determines which pixels are covered by geometry
14//    - Interpolates vertex data across pixels
15//
16// 3. FRAGMENT SHADER (runs once per pixel)
17//    - Input: Interpolated vertex data for this pixel
18//    - Process: Calculate final pixel color
19//    - Output: RGBA color written to render target (canvas)
20//
21// For scatter plots:
22// - We send vertex positions as points (not triangles)
23// - Each point becomes one or more pixels on screen
24// - Fragment shader can create circular shapes from square pixels
25
26/// Vertex shader for scatter plots (advanced, with circular point support)
27///
28/// This shader prepares point data for rendering circles. The point_coord
29/// will be used by the fragment shader to create smooth circular points.
30///
31/// Note: Currently not used - requires geometry shader or point sprite setup.
32pub const SCATTER_VERTEX_SHADER: &str = r#"
33struct VertexInput {
34    @location(0) position: vec2<f32>,
35    @location(1) color: vec4<f32>,
36    @location(2) size: f32,
37}
38
39struct VertexOutput {
40    @builtin(position) clip_position: vec4<f32>,
41    @location(0) color: vec4<f32>,
42    @location(1) point_coord: vec2<f32>,
43}
44
45@vertex
46fn vs_main(vertex: VertexInput) -> VertexOutput {
47    var out: VertexOutput;
48    out.clip_position = vec4<f32>(vertex.position, 0.0, 1.0);
49    out.color = vertex.color;
50    out.point_coord = vec2<f32>(0.0, 0.0); // Will be set in geometry shader alternative
51    return out;
52}
53"#;
54
55/// Fragment shader for scatter plots (advanced, with anti-aliased circles)
56///
57/// Creates smooth circular points using distance field rendering:
58/// - Calculates distance from pixel to point center
59/// - Uses smoothstep for anti-aliased edges (no jagged pixels)
60/// - Pixels far from center are transparent (creates circle shape)
61///
62/// This produces much nicer looking scatter plots compared to square pixels.
63///
64/// Note: Currently not used - requires corresponding vertex shader setup.
65pub const SCATTER_FRAGMENT_SHADER: &str = r#"
66struct VertexOutput {
67    @builtin(position) clip_position: vec4<f32>,
68    @location(0) color: vec4<f32>,
69    @location(1) point_coord: vec2<f32>,
70}
71
72@fragment
73fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
74    // Simple circular points - can be enhanced with distance field
75    let center = vec2<f32>(0.5, 0.5);
76    let dist = distance(in.point_coord, center);
77    
78    // Anti-aliased circle
79    let radius = 0.5;
80    let alpha = smoothstep(radius, radius - 0.05, dist);
81    
82    return vec4<f32>(in.color.rgb, in.color.a * alpha);
83}
84"#;
85
86/// Simple vertex shader (currently used for basic point rendering)
87///
88/// Pipeline Stage 1: VERTEX PROCESSING
89/// - Takes each vertex (point) from our Rust Vertex struct
90/// - Converts 2D position (x, y) to 4D GPU clip space (x, y, z, w)
91/// - Passes color through unchanged to fragment shader
92///
93/// Input layout matches our Rust Vertex struct:
94/// - @location(0): position [x, y]
95/// - @location(1): color [r, g, b, a]
96/// - @location(2): size (not currently used)
97///
98/// This shader does minimal work - just format conversion.
99/// Perfect for rendering millions of points quickly.
100pub const SIMPLE_VERTEX_SHADER: &str = r#"
101struct VertexInput {
102    @location(0) position: vec2<f32>,
103    @location(1) color: vec4<f32>,
104    @location(2) size: f32,
105}
106
107struct VertexOutput {
108    @builtin(position) clip_position: vec4<f32>,
109    @location(0) color: vec4<f32>,
110}
111
112@vertex
113fn vs_main(vertex: VertexInput) -> VertexOutput {
114    var out: VertexOutput;
115    out.clip_position = vec4<f32>(vertex.position, 0.0, 1.0);
116    out.color = vertex.color;
117    return out;
118}
119"#;
120
121/// Simple fragment shader (currently used, solid color output)
122///
123/// Pipeline Stage 3: FRAGMENT/PIXEL PROCESSING
124/// - Runs once for each pixel that the point covers
125/// - Receives interpolated color from vertex shader
126/// - Returns color directly (no modifications)
127///
128/// For a scatter plot with 1 million points, this shader may run
129/// 1-4 million times per frame (depending on point sizes).
130///
131/// GPU runs these in parallel, which is why we can render so fast!
132pub const SIMPLE_FRAGMENT_SHADER: &str = r#"
133struct VertexOutput {
134    @builtin(position) clip_position: vec4<f32>,
135    @location(0) color: vec4<f32>,
136}
137
138@fragment
139fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
140    return in.color;
141}
142"#;