stencil.frag 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. #version 310 es
  2. // SPDX-License-Identifier: Unlicense OR MIT
  3. precision mediump float;
  4. layout(location=0) in highp vec2 vFrom;
  5. layout(location=1) in highp vec2 vCtrl;
  6. layout(location=2) in highp vec2 vTo;
  7. layout(location = 0) out vec4 fragCover;
  8. void main() {
  9. float dx = vTo.x - vFrom.x;
  10. // Sort from and to in increasing order so the root below
  11. // is always the positive square root, if any.
  12. // We need the direction of the curve below, so this can't be
  13. // done from the vertex shader.
  14. bool increasing = vTo.x >= vFrom.x;
  15. vec2 left = increasing ? vFrom : vTo;
  16. vec2 right = increasing ? vTo : vFrom;
  17. // The signed horizontal extent of the fragment.
  18. vec2 extent = clamp(vec2(vFrom.x, vTo.x), -0.5, 0.5);
  19. // Find the t where the curve crosses the middle of the
  20. // extent, x₀.
  21. // Given the Bézier curve with x coordinates P₀, P₁, P₂
  22. // where P₀ is at the origin, its x coordinate in t
  23. // is given by:
  24. //
  25. // x(t) = 2(1-t)tP₁ + t²P₂
  26. //
  27. // Rearranging:
  28. //
  29. // x(t) = (P₂ - 2P₁)t² + 2P₁t
  30. //
  31. // Setting x(t) = x₀ and using Muller's quadratic formula ("Citardauq")
  32. // for robustnesss,
  33. //
  34. // t = 2x₀/(2P₁±√(4P₁²+4(P₂-2P₁)x₀))
  35. //
  36. // which simplifies to
  37. //
  38. // t = x₀/(P₁±√(P₁²+(P₂-2P₁)x₀))
  39. //
  40. // Setting v = P₂-P₁,
  41. //
  42. // t = x₀/(P₁±√(P₁²+(v-P₁)x₀))
  43. //
  44. // t lie in [0; 1]; P₂ ≥ P₁ and P₁ ≥ 0 since we split curves where
  45. // the control point lies before the start point or after the end point.
  46. // It can then be shown that only the positive square root is valid.
  47. float midx = mix(extent.x, extent.y, 0.5);
  48. float x0 = midx - left.x;
  49. vec2 p1 = vCtrl - left;
  50. vec2 v = right - vCtrl;
  51. float t = x0/(p1.x+sqrt(p1.x*p1.x+(v.x-p1.x)*x0));
  52. // Find y(t) on the curve.
  53. float y = mix(mix(left.y, vCtrl.y, t), mix(vCtrl.y, right.y, t), t);
  54. // And the slope.
  55. vec2 d_half = mix(p1, v, t);
  56. float dy = d_half.y/d_half.x;
  57. // Together, y and dy form a line approximation.
  58. // Compute the fragment area above the line.
  59. // The area is symmetric around dy = 0. Scale slope with extent width.
  60. float width = extent.y - extent.x;
  61. dy = abs(dy*width);
  62. vec4 sides = vec4(dy*+0.5 + y, dy*-0.5 + y, (+0.5-y)/dy, (-0.5-y)/dy);
  63. sides = clamp(sides+0.5, 0.0, 1.0);
  64. float area = 0.5*(sides.z - sides.z*sides.y + 1.0 - sides.x+sides.x*sides.w);
  65. area *= width;
  66. // Work around issue #13.
  67. if (width == 0.0)
  68. area = 0.0;
  69. fragCover.r = area;
  70. }