Skip to content

Variable: LINE_FRAGMENT_SHADER ​

ts
const LINE_FRAGMENT_SHADER: '\nprecision highp float;\n\nuniform vec2 uResolution;\nuniform float uPixelRatio;\nuniform vec4 uViewport; // GL viewport in device px (xy origin, zw size)\n\nvec2 hcClipToScreen(vec4 clip) {\n  return (clip.xy / clip.w * 0.5 + 0.5) * uResolution;\n}\n\nvec4 hcScreenToClip(vec2 screen, float ndcZ) {\n  return vec4(screen / uResolution * 2.0 - 1.0, ndcZ, 1.0);\n}\n\nvec4 hcOffsetClip(vec4 clip, vec2 offsetPx) {\n  clip.xy += offsetPx / uResolution * 2.0 * clip.w;\n  return clip;\n}\n\n/** Snap CSS px to the device-pixel grid; phase 0 = pixel edges, 0.5 = pixel centers. */\nfloat hcSnap(float px, float phase) {\n  return (floor(px * uPixelRatio - phase + 0.5) + phase) / uPixelRatio;\n}\n\n/** Width of one device pixel in CSS px: the anti-aliasing ramp used by SDF fragment shaders. */\nfloat hcAAWidth() {\n  return 1.0 / uPixelRatio;\n}\n\n\nuniform float uCap;\nuniform float uOpacity;\nuniform float uDash[16];\nuniform float uDashCount;\nuniform float uDashPeriod;\n\nflat in vec4 vAB;\nflat in vec4 vFrame;\nflat in vec4 vTangents;\nflat in vec4 vEndA;\nflat in vec4 vEndB;\nflat in vec3 vDash;\n#ifdef LINE_UNIFORM_COLOR\nuniform vec4 uColor;\n#else\nflat in vec4 vColorA;\nflat in vec4 vColorB;\n#endif\n\nout highp vec4 fragColor;\n\nconst float END_BUTT = 1.0;\nconst float END_SQUARE = 2.0;\nconst float END_ROUND = 3.0;\nconst float END_BEVEL = 4.0;\nconst float OWN_EPS = 1.0 / 512.0; // px; see the ownership partition in main()\n\nfloat endDistance(vec4 info, vec2 rel, float beyond, float hw) {\n  if (info.w == END_BUTT || info.w == END_ROUND) return beyond;\n  if (info.w == END_SQUARE) return beyond - hw;\n  if (info.w == END_BEVEL) return dot(rel, info.xy) - info.z;\n  return -1e20; // miter: bounded by the ownership clip only\n}\n\n// The nearest of best and the distances to the "on" interval [a, b] and its two neighbours.\nfloat hcDashInterval(float best, float m, float a, float b) {\n  best = min(best, max(a - m, m - b));\n  best = min(best, max(a - (m - uDashPeriod), (m - uDashPeriod) - b));\n  best = min(best, max(a - (m + uDashPeriod), (m + uDashPeriod) - b));\n  return best;\n}\n\n// Signed distance to the nearest "on" interval of the repeating dash pattern (mirrors\n// dashDistance() in line-dash.ts).\nfloat hcDashDistance(float along) {\n  float m = along - floor(along / uDashPeriod) * uDashPeriod;\n  if (uDashCount < 2.5) return hcDashInterval(1e20, m, 0.0, 0.0 + uDash[0]);\n  float best = 1e20;\n  float start = 0.0;\n  int count = min(int(uDashCount), 16);\n  for (int i = 0; i < count; i += 2) {\n    float a = start;\n    float b = start + uDash[i];\n    best = hcDashInterval(best, m, a, b);\n    start = b + uDash[i + 1];\n  }\n  return best;\n}\n\nvoid main() {\n  // Screen px, from gl_FragCoord: bit-identical for both segments at a join.\n  vec2 vPos = (gl_FragCoord.xy - uViewport.xy) / uViewport.zw * uResolution;\n  vec2 a = vAB.xy;\n  vec2 b = vAB.zw;\n  vec2 dir = vFrame.xy;\n  float len = vFrame.z;\n  float hw = vFrame.w;\n  vec2 rel = vPos - a;\n  vec2 relB = vPos - b;\n\n  // Exact ownership partition at joins, offset by OWN_EPS off the pixel centers.\n  if (vTangents.xy != vec2(0.0) && dot(rel, vTangents.xy) < OWN_EPS) discard;\n  if (vTangents.zw != vec2(0.0) && dot(relB, vTangents.zw) >= OWN_EPS) discard;\n\n  float t = dot(rel, dir);\n  float perp = dot(rel, vec2(-dir.y, dir.x));\n  float d = abs(perp) - hw;\n  d = max(d, endDistance(vEndA, rel, -t, hw));\n  d = max(d, endDistance(vEndB, relB, t - len, hw));\n  if (vEndA.w == END_ROUND) d = min(d, length(rel) - hw);\n  if (vEndB.w == END_ROUND) d = min(d, length(relB) - hw);\n\n  if (uDashCount > 0.5 && uDashPeriod > 0.0) {\n    // Joins and caps take the dash state of their vertex (t clamped), so both segments agree.\n    float scale = max(vDash.y, 1e-6);\n    float along = vDash.x + clamp(t, 0.0, len) * scale;\n    float dd = hcDashDistance(along) / scale;\n    float dashD = dd;\n    if (uCap > 1.5) dashD = dd - hw;\n    else if (uCap > 0.5) dashD = length(vec2(max(dd, 0.0), perp)) - hw;\n    d = max(d, dashD);\n  }\n\n  float coverage = clamp(0.5 - d / hcAAWidth(), 0.0, 1.0) * vDash.z;\n#ifdef LINE_UNIFORM_COLOR\n  vec4 color = uColor;\n#else\n  vec4 color = mix(vColorA, vColorB, len > 0.0 ? clamp(t / len, 0.0, 1.0) : 0.0);\n#endif\n  float alpha = color.a * coverage * uOpacity;\n  if (alpha <= 0.0) discard;\n  fragColor = vec4(color.rgb, alpha);\n}\n';

Experimental

Released under the MIT License.