// Text pass: Slug algorithm ported from JSlug (GLSL3 → WGSL). // Evaluates TrueType quadratic Bézier curves directly in the fragment // shader for resolution-independent, anti-aliased text rendering. // No glyph atlas — curves are packed into a float texture, spatially // banded into a uint texture, and ray-traced per fragment. const TEXTURE_WIDTH = 4096u; const EPSILON = 0.0001; struct TextUniforms { resolution: vec2f, textColor: vec4f, } @group(0) @binding(0) var u: TextUniforms; @group(0) @binding(1) var curvesTex: texture_2d; @group(0) @binding(2) var bandsTex: texture_2d; struct VsIn { @location(0) position: vec2f, @location(1) aScaleBias: vec4f, @location(2) aGlyphBandScale: vec4f, @location(3) aBandMaxTexCoords: vec4u, } struct VsOut { @builtin(position) pos: vec4f, @location(0) vTexCoords: vec2f, @interpolate(flat) @location(1) vGlyphBandScale: vec4f, @interpolate(flat) @location(2) vBandMaxTexCoords: vec4u, @interpolate(flat) @location(3) vPixelsPerEm: vec2f, } @vertex fn vs_main(in: VsIn) -> VsOut { let px = in.position.x * in.aScaleBias.x + in.aScaleBias.z; let py = in.position.y * in.aScaleBias.y + in.aScaleBias.w; var out: VsOut; out.pos = vec4f( px / u.resolution.x * 2.0 - 1.0, py / u.resolution.y * 2.0 - 1.0, 0.0, 1.0 ); out.vTexCoords = in.position * 0.5 + vec2f(0.5); out.vGlyphBandScale = in.aGlyphBandScale; out.vBandMaxTexCoords = in.aBandMaxTexCoords; let ppe = vec2f(2.0 * in.aScaleBias.x, 2.0 * in.aScaleBias.y); out.vPixelsPerEm = clamp(ppe, vec2f(1.0), vec2f(200.0)); return out; } // ---------- Slug ray tracer ---------- fn trace_ray_curve_h(p1: vec2f, p2: vec2f, p3: vec2f, pixels_per_em: f32) -> f32 { if (max(max(p1.x, p2.x), p3.x) * pixels_per_em < -0.5) { return 0.0; } let y_flags = select(0u, 2u, p1.y > 0.0) + select(0u, 4u, p2.y > 0.0) + select(0u, 8u, p3.y > 0.0); let code = (0x2E74u >> y_flags) & 3u; if (code == 0u) { return 0.0; } let a = p1 - p2 * 2.0 + p3; let b = p1 - p2; let c = p1.y; let ayr = 1.0 / a.y; let disc = max(b.y * b.y - a.y * c, 0.0); let d = sqrt(disc); var t1 = (b.y - d) * ayr; var t2 = (b.y + d) * ayr; if (abs(a.y) < EPSILON) { t1 = c / (2.0 * b.y); t2 = t1; } var coverage = 0.0; if ((code & 1u) != 0u) { let x1 = (a.x * t1 - b.x * 2.0) * t1 + p1.x; coverage += clamp(x1 * pixels_per_em + 0.5, 0.0, 1.0); } if (code > 1u) { let x2 = (a.x * t2 - b.x * 2.0) * t2 + p1.x; coverage -= clamp(x2 * pixels_per_em + 0.5, 0.0, 1.0); } return coverage; } fn trace_ray_band_h( band_data: vec2u, pixels_per_em: f32, v_tex: vec2f, glyph_scale: vec2f ) -> f32 { var coverage = 0.0; for (var curve = 0u; curve < band_data.x; curve++) { let curve_offset = band_data.y + curve; let curve_loc = textureLoad(bandsTex, vec2u(curve_offset & 0xFFFu, curve_offset >> 12u), 0).xy; let p12 = textureLoad(curvesTex, curve_loc, 0) / vec4f(glyph_scale, glyph_scale) - vec4f(v_tex, v_tex); let p3 = textureLoad(curvesTex, vec2u(curve_loc.x + 1u, curve_loc.y), 0).xy / glyph_scale - v_tex; coverage += trace_ray_curve_h(p12.xy, p12.zw, p3.xy, pixels_per_em); } return coverage; } fn trace_ray_band_v( band_data: vec2u, pixels_per_em: f32, v_tex: vec2f, glyph_scale: vec2f ) -> f32 { var coverage = 0.0; for (var curve = 0u; curve < band_data.x; curve++) { let curve_offset = band_data.y + curve; let curve_loc = textureLoad(bandsTex, vec2u(curve_offset & 0xFFFu, curve_offset >> 12u), 0).xy; let p12 = textureLoad(curvesTex, curve_loc, 0) / vec4f(glyph_scale, glyph_scale) - vec4f(v_tex, v_tex); let p3 = textureLoad(curvesTex, vec2u(curve_loc.x + 1u, curve_loc.y), 0).xy / glyph_scale - v_tex; coverage += trace_ray_curve_h(p12.yx, p12.wz, p3.yx, pixels_per_em); } return coverage; } @fragment fn fs_main(in: VsOut) -> @location(0) vec4f { let glyph_scale = in.vGlyphBandScale.xy; let band_scale = in.vGlyphBandScale.zw; let band_max = in.vBandMaxTexCoords.xy; let bands_tex_coords = in.vBandMaxTexCoords.zw; let v_tex = in.vTexCoords; let pixels_per_em = in.vPixelsPerEm; let band_index = clamp( vec2u(v_tex * band_scale), vec2u(0u, 0u), band_max ); let h_band_offset = bands_tex_coords.y * TEXTURE_WIDTH + bands_tex_coords.x + band_index.y; let h_band_data = textureLoad(bandsTex, vec2u(h_band_offset & 0xFFFu, h_band_offset >> 12u), 0).xy; let v_band_offset = bands_tex_coords.y * TEXTURE_WIDTH + bands_tex_coords.x + band_max.y + 1u + band_index.x; let v_band_data = textureLoad(bandsTex, vec2u(v_band_offset & 0xFFFu, v_band_offset >> 12u), 0).xy; var coverage_x = trace_ray_band_h(h_band_data, pixels_per_em.x, v_tex, glyph_scale); var coverage_y = trace_ray_band_v(v_band_data, pixels_per_em.y, v_tex, glyph_scale); coverage_x = min(abs(coverage_x), 1.0); coverage_y = min(abs(coverage_y), 1.0); let slug_alpha = (coverage_x + coverage_y) * 0.5; return vec4f(u.textColor.rgb, u.textColor.a * slug_alpha); }