aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/scenegraph/shaders_ng/shapestroke.frag
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/scenegraph/shaders_ng/shapestroke.frag')
-rw-r--r--src/quick/scenegraph/shaders_ng/shapestroke.frag134
1 files changed, 134 insertions, 0 deletions
diff --git a/src/quick/scenegraph/shaders_ng/shapestroke.frag b/src/quick/scenegraph/shaders_ng/shapestroke.frag
new file mode 100644
index 0000000000..cedfa0845a
--- /dev/null
+++ b/src/quick/scenegraph/shaders_ng/shapestroke.frag
@@ -0,0 +1,134 @@
+#version 440
+
+layout(location = 0) in vec4 P;
+layout(location = 1) in vec2 A;
+layout(location = 2) in vec2 B;
+layout(location = 3) in vec2 C;
+layout(location = 4) in vec2 HG;
+layout(location = 5) in float offset;
+
+layout(location = 0) out vec4 fragColor;
+
+layout(std140, binding = 0) uniform buf {
+#if QSHADER_VIEW_COUNT >= 2
+ mat4 qt_Matrix[QSHADER_VIEW_COUNT];
+#else
+ mat4 qt_Matrix;
+#endif
+
+ float matrixScale;
+ float opacity;
+ float reserved2;
+ float reserved3;
+
+ vec4 strokeColor;
+
+ float strokeWidth;
+ float debug;
+ float reserved5;
+ float reserved6;
+} ubuf;
+
+float cuberoot(float x)
+{
+ return sign(x) * pow(abs(x), 1 / 3.);
+}
+
+#define PI 3.1415926538
+
+vec3 solveDepressedCubic(float p, float q)
+{
+ float D = q * q / 4. + p * p * p / 27.;
+
+ float u1 = cuberoot(-q / 2. - sqrt(D));
+ float u2 = cuberoot(-q / 2. + sqrt(D));
+ vec3 rootsD1 = vec3(u1 - p / (3. * u1), u2 - p / (3. * u2), 0);
+
+ float v = 2.*sqrt(-p / 3.);
+ float t = acos(3. * q / p / v) / 3.;
+ float k = 2. * PI / 3.;
+ vec3 rootsD2 = vec3(v * cos(t), v * cos(t - k), v * cos(t - 2. * k));
+
+ return D > 0 ? rootsD1 : rootsD2;
+}
+
+mat2 qInverse(mat2 matrix) {
+ float a = matrix[0][0], b = matrix[0][1];
+ float c = matrix[1][0], d = matrix[1][1];
+
+ float determinant = a * d - b * c;
+ float invDet = 1.0 / determinant;
+
+ mat2 inverseMatrix;
+ inverseMatrix[0][0] = d * invDet;
+ inverseMatrix[0][1] = -b * invDet;
+ inverseMatrix[1][0] = -c * invDet;
+ inverseMatrix[1][1] = a * invDet;
+
+ return inverseMatrix;
+}
+
+void main()
+{
+ vec3 s = solveDepressedCubic(HG.x, HG.y) - vec3(offset, offset, offset);
+
+ vec2 Qmin = vec2(1e10, 1e10);
+ float dmin = 1e4;
+ for (int i = 0; i < 3; i++) {
+ float t = clamp(s[i], 0., 1.);
+ vec2 Q = A * t * t + B * t + C;
+ float d = length(Q - P.xy);
+ float foundNewMin = step(d, dmin);
+ dmin = min(d, dmin);
+ Qmin = foundNewMin * Q + (1. - foundNewMin) * Qmin;
+ }
+ vec2 n = (P.xy - Qmin) / dmin;
+ vec2 Q1 = (Qmin + ubuf.strokeWidth / 2. * n);
+ vec2 Q2 = (Qmin - ubuf.strokeWidth / 2. * n);
+
+ // Converting to screen coordinates:
+#if defined(USE_DERIVATIVES)
+ mat2 T = mat2(dFdx(P.x), dFdy(P.x), dFdx(P.y), dFdy(P.y));
+ mat2 Tinv = qInverse(T);
+ vec2 Q1_s = Tinv * Q1;
+ vec2 Q2_s = Tinv * Q2;
+ vec2 P_s = Tinv * P.xy;
+ vec2 n_s = Tinv * n;
+ n_s = n_s / length(n_s);
+#else
+ vec2 Q1_s = ubuf.matrixScale * Q1;
+ vec2 Q2_s = ubuf.matrixScale * Q2;
+ vec2 P_s = ubuf.matrixScale * P.xy;
+ vec2 n_s = n;
+#endif
+
+ // Geometric solution for anti aliasing using the known distances
+ // to the edges of the path in the screen coordinate system.
+ float dist1 = dot(P_s - Q1_s, n_s);
+ float dist2 = dot(P_s - Q2_s, n_s);
+
+ // Calculate the fill coverage if the line is crossing the square cell
+ // normally (vertically or horizontally).
+ // float fillCoverageLin = clamp(0.5-dist1, 0., 1.) - clamp(0.5-dist2, 0., 1.);
+
+ // Calculate the fill coverage if the line is crossing the square cell
+ // diagonally.
+ float fillCoverageDia = clamp(step(0., -dist1) + sign(dist1) * pow(max(0., sqrt(2.) / 2. - abs(dist1)), 2.), 0., 1.) -
+ clamp(step(0., -dist2) + sign(dist2) * pow(max(0., sqrt(2.) / 2. - abs(dist2)), 2.), 0., 1.);
+
+ // Merge the normal and the diagonal solution. The merge factor is periodic
+ // in 90 degrees and 0/1 at 0 and 45 degree. The simple equation was
+ // estimated after numerical experiments.
+ // float mergeFactor = 2 * abs(n_s.x) * abs(n_s.y);
+ // float fillCoverage = mergeFactor * fillCoverageDia + (1-mergeFactor) * fillCoverageLin;
+
+ // It seems to be sufficient to use the equation for the diagonal.
+ float fillCoverage = fillCoverageDia;
+
+ // The center line is sometimes not filled because of numerical issues. This fixes this.
+ float centerline = step(ubuf.strokeWidth * 0.01, dmin);
+ fillCoverage = fillCoverage * centerline + min(1., ubuf.strokeWidth * ubuf.matrixScale) * (1. - centerline);
+
+ fragColor = vec4(ubuf.strokeColor.rgb, 1.0) *ubuf.strokeColor.a * fillCoverage * ubuf.opacity
+ + ubuf.debug * vec4(0.0, 0.5, 1.0, 1.0) * (1.0 - fillCoverage) * ubuf.opacity;
+}