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