|
| 1 | +#version 300 es |
| 2 | + |
| 3 | +precision highp float; |
| 4 | + |
| 5 | +in vec2 vWorldPos; |
| 6 | +in vec2 vStart; |
| 7 | +in vec2 vEnd; |
| 8 | +in vec2 vControl; |
| 9 | +in float vHalfWidth; |
| 10 | +in float vLoopbackRadius; |
| 11 | +in float vArrowSize; |
| 12 | +in vec2 vArrowTip; |
| 13 | +in vec2 vArrowDir; |
| 14 | +in vec4 vColor; |
| 15 | +in vec4 vShadowColor; |
| 16 | +in float vShadowSize; |
| 17 | +in vec2 vShadowOffset; |
| 18 | +flat in int vEdgeType; |
| 19 | + |
| 20 | +uniform bool uSimpleMode; |
| 21 | + |
| 22 | +out vec4 fragColor; |
| 23 | + |
| 24 | +float sdSegment(vec2 p, vec2 a, vec2 b) { |
| 25 | + vec2 pa = p - a; |
| 26 | + vec2 ba = b - a; |
| 27 | + float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); |
| 28 | + return length(pa - ba * h); |
| 29 | +} |
| 30 | + |
| 31 | +float sdBezier(vec2 pos, vec2 A, vec2 B, vec2 C) { |
| 32 | + vec2 a = B - A; |
| 33 | + vec2 b = A - 2.0 * B + C; |
| 34 | + vec2 c = a * 2.0; |
| 35 | + vec2 d = A - pos; |
| 36 | + |
| 37 | + float kk = 1.0 / max(dot(b, b), 0.0001); |
| 38 | + float kx = kk * dot(a, b); |
| 39 | + float ky = kk * (2.0 * dot(a, a) + dot(d, b)) / 3.0; |
| 40 | + float kz = kk * dot(d, a); |
| 41 | + |
| 42 | + float p = ky - kx * kx; |
| 43 | + float q = kx * (2.0 * kx * kx - 3.0 * ky) + kz; |
| 44 | + float p3 = p * p * p; |
| 45 | + float q2 = q * q; |
| 46 | + float h = q2 + 4.0 * p3; |
| 47 | + |
| 48 | + float res; |
| 49 | + if (h >= 0.0) { |
| 50 | + h = sqrt(h); |
| 51 | + vec2 x = (vec2(h, -h) - q) / 2.0; |
| 52 | + vec2 uv = sign(x) * pow(abs(x), vec2(1.0 / 3.0)); |
| 53 | + float t = clamp(uv.x + uv.y - kx, 0.0, 1.0); |
| 54 | + vec2 qo = d + (c + b * t) * t; |
| 55 | + res = dot(qo, qo); |
| 56 | + } else { |
| 57 | + float z = sqrt(-p); |
| 58 | + float v = acos(q / (p * z * 2.0)) / 3.0; |
| 59 | + float m = cos(v); |
| 60 | + float n = sin(v) * 1.732050808; |
| 61 | + vec3 t = clamp(vec3(m + m, -n - m, n - m) * z - kx, 0.0, 1.0); |
| 62 | + vec2 qx = d + (c + b * t.x) * t.x; |
| 63 | + float dx = dot(qx, qx); |
| 64 | + vec2 qy = d + (c + b * t.y) * t.y; |
| 65 | + float dy = dot(qy, qy); |
| 66 | + res = min(dx, dy); |
| 67 | + } |
| 68 | + |
| 69 | + return sqrt(res); |
| 70 | +} |
| 71 | + |
| 72 | +float sdArrow(vec2 p, vec2 tip, vec2 dir, float size) { |
| 73 | + if (size <= 0.0) return 1e6; |
| 74 | + |
| 75 | + vec2 perp = vec2(-dir.y, dir.x); |
| 76 | + vec2 rel = p - tip; |
| 77 | + float along = dot(rel, -dir); |
| 78 | + float across = dot(rel, perp); |
| 79 | + |
| 80 | + if (along < 0.0) return length(rel); |
| 81 | + if (along > size) { |
| 82 | + float hw = size * 0.4; |
| 83 | + float closest = clamp(across, -hw, hw); |
| 84 | + vec2 pt = tip - dir * size + perp * closest; |
| 85 | + return length(p - pt); |
| 86 | + } |
| 87 | + |
| 88 | + float halfW = (along / size) * size * 0.4; |
| 89 | + float d = abs(across) - halfW; |
| 90 | + return d; |
| 91 | +} |
| 92 | + |
| 93 | +void main() { |
| 94 | + if (uSimpleMode && vEdgeType == 0) { |
| 95 | + fragColor = vColor; |
| 96 | + return; |
| 97 | + } |
| 98 | + |
| 99 | + float dist; |
| 100 | + if (vEdgeType == 0) { |
| 101 | + dist = sdSegment(vWorldPos, vStart, vEnd); |
| 102 | + } else if (vEdgeType == 1) { |
| 103 | + dist = sdBezier(vWorldPos, vStart, vControl, vEnd); |
| 104 | + } else { |
| 105 | + dist = abs(length(vWorldPos - vControl) - vLoopbackRadius); |
| 106 | + } |
| 107 | + |
| 108 | + float edgeSdf = dist - vHalfWidth; |
| 109 | + float combinedSdf = edgeSdf; |
| 110 | + |
| 111 | + if (vArrowSize > 0.0) { |
| 112 | + float arrowDist = sdArrow(vWorldPos, vArrowTip, vArrowDir, vArrowSize); |
| 113 | + combinedSdf = min(edgeSdf, arrowDist); |
| 114 | + } |
| 115 | + |
| 116 | + float shadowAlpha = 0.0; |
| 117 | + if (vShadowSize > 0.0) { |
| 118 | + vec2 shadowPos = vWorldPos - vShadowOffset; |
| 119 | + float shadowDist; |
| 120 | + if (vEdgeType == 0) { |
| 121 | + shadowDist = sdSegment(shadowPos, vStart, vEnd); |
| 122 | + } else if (vEdgeType == 1) { |
| 123 | + shadowDist = sdBezier(shadowPos, vStart, vControl, vEnd); |
| 124 | + } else { |
| 125 | + shadowDist = abs(length(shadowPos - vControl) - vLoopbackRadius); |
| 126 | + } |
| 127 | + float shadowArrowDist = vArrowSize > 0.0 |
| 128 | + ? sdArrow(shadowPos, vArrowTip, vArrowDir, vArrowSize) |
| 129 | + : 1.0e6; |
| 130 | + float shadowCombined = min(shadowDist - vHalfWidth, shadowArrowDist); |
| 131 | + float t = max(shadowCombined, 0.0) / vShadowSize; |
| 132 | + shadowAlpha = exp(-t * t * 1.5) * 0.5 * vShadowColor.a; |
| 133 | + } |
| 134 | + |
| 135 | + float aa = fwidth(combinedSdf); |
| 136 | + float edgeAlpha = 1.0 - smoothstep(-aa, aa, combinedSdf); |
| 137 | + vec4 edgeColor = vColor; |
| 138 | + edgeColor.a *= edgeAlpha; |
| 139 | + |
| 140 | + float finalAlpha = edgeColor.a + shadowAlpha * (1.0 - edgeColor.a); |
| 141 | + |
| 142 | + if (finalAlpha < 0.001) discard; |
| 143 | + |
| 144 | + if (shadowAlpha > 0.0) { |
| 145 | + vec3 finalRGB = (edgeColor.rgb * edgeColor.a + vShadowColor.rgb * shadowAlpha * (1.0 - edgeColor.a)) / finalAlpha; |
| 146 | + fragColor = vec4(finalRGB, finalAlpha); |
| 147 | + } else { |
| 148 | + fragColor = edgeColor; |
| 149 | + } |
| 150 | +} |
0 commit comments