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"#;