aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Olav Tvete <paul.tvete@qt.io>2023-08-22 16:26:33 +0200
committerQt Cherry-pick Bot <cherrypick_bot@qt-project.org>2023-08-25 18:17:57 +0000
commit8ee3871d81a29fb7b4a8c30a439002373a1e323d (patch)
tree4ec8d4ef9067ca98adf49ca27cce05c838caa9b4
parentff4c80138dcb81fb844ecf681a9487e14f452066 (diff)
Curve renderer: reduce size of external triangles
Use the same trick for fill as we already do for stroke: calculate the space needed for antialiasing in the vertex shader. Convex curves need special magic to avoid artifacts from the negative part of the parabola. Task-number: QTBUG-104122 Task-number: QTBUG-116143 Change-Id: I76803f544b3bb257a3454745a60be7adc9a45efe Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io> (cherry picked from commit 5546f41b0c7ba10fc45a0b4aebe00400bbe5da44) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r--src/quickshapes/qquickshapecurvenode.cpp3
-rw-r--r--src/quickshapes/qquickshapecurvenode_p.h53
-rw-r--r--src/quickshapes/qquickshapecurverenderer.cpp77
-rw-r--r--src/quickshapes/shaders_ng/shapecurve.frag6
-rw-r--r--src/quickshapes/shaders_ng/shapecurve.vert23
5 files changed, 90 insertions, 72 deletions
diff --git a/src/quickshapes/qquickshapecurvenode.cpp b/src/quickshapes/qquickshapecurvenode.cpp
index 6d104bdc17..11b5ecbaf4 100644
--- a/src/quickshapes/qquickshapecurvenode.cpp
+++ b/src/quickshapes/qquickshapecurvenode.cpp
@@ -54,8 +54,9 @@ const QSGGeometry::AttributeSet &QQuickShapeCurveNode::attributes()
QSGGeometry::Attribute::createWithAttributeType(0, 2, QSGGeometry::FloatType, QSGGeometry::PositionAttribute),
QSGGeometry::Attribute::createWithAttributeType(1, 3, QSGGeometry::FloatType, QSGGeometry::TexCoordAttribute),
QSGGeometry::Attribute::createWithAttributeType(2, 4, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute),
+ QSGGeometry::Attribute::createWithAttributeType(3, 2, QSGGeometry::FloatType, QSGGeometry::UnknownAttribute),
};
- static QSGGeometry::AttributeSet attrs = { 3, sizeof(CurveNodeVertex), data };
+ static QSGGeometry::AttributeSet attrs = { 4, sizeof(CurveNodeVertex), data };
return attrs;
}
diff --git a/src/quickshapes/qquickshapecurvenode_p.h b/src/quickshapes/qquickshapecurvenode_p.h
index be27fa665d..cce6052cee 100644
--- a/src/quickshapes/qquickshapecurvenode_p.h
+++ b/src/quickshapes/qquickshapecurvenode_p.h
@@ -100,54 +100,50 @@ public:
return m_strokeWidth > 0.0f && m_strokeColor.alpha() > 0;
}
- void appendTriangle(const QVector2D &v1,
- const QVector2D &v2,
- const QVector2D &v3,
- std::function<QVector3D(QVector2D)> uvForPoint)
+ void appendTriangle(const std::array<QVector2D, 3> &v, // triangle vertices
+ const std::array<QVector2D, 3> &n, // vertex normals
+ std::function<QVector3D(QVector2D)> uvForPoint
+ )
{
- QVector3D uv1 = uvForPoint(v1);
- QVector3D uv2 = uvForPoint(v2);
- QVector3D uv3 = uvForPoint(v3);
+ QVector3D uv1 = uvForPoint(v[0]);
+ QVector3D uv2 = uvForPoint(v[1]);
+ QVector3D uv3 = uvForPoint(v[2]);
- QVector2D duvdx = QVector2D(uvForPoint(v1 + QVector2D(1, 0))) - QVector2D(uv1);
- QVector2D duvdy = QVector2D(uvForPoint(v1 + QVector2D(0, 1))) - QVector2D(uv1);
+ QVector2D duvdx = QVector2D(uvForPoint(v[0] + QVector2D(1, 0))) - QVector2D(uv1);
+ QVector2D duvdy = QVector2D(uvForPoint(v[0] + QVector2D(0, 1))) - QVector2D(uv1);
m_uncookedIndexes.append(m_uncookedVertexes.size());
- m_uncookedVertexes.append( { v1.x(), v1.y(),
+ m_uncookedVertexes.append( { v[0].x(), v[0].y(),
uv1.x(), uv1.y(), uv1.z(),
duvdx.x(), duvdx.y(),
- duvdy.x(), duvdy.y()
+ duvdy.x(), duvdy.y(),
+ n[0].x(), n[0].y()
});
m_uncookedIndexes.append(m_uncookedVertexes.size());
- m_uncookedVertexes.append( { v2.x(), v2.y(),
+ m_uncookedVertexes.append( { v[1].x(), v[1].y(),
uv2.x(), uv2.y(), uv2.z(),
duvdx.x(), duvdx.y(),
- duvdy.x(), duvdy.y()
+ duvdy.x(), duvdy.y(),
+ n[1].x(), n[1].y()
});
m_uncookedIndexes.append(m_uncookedVertexes.size());
- m_uncookedVertexes.append( { v3.x(), v3.y(),
+ m_uncookedVertexes.append( { v[2].x(), v[2].y(),
uv3.x(), uv3.y(), uv3.z(),
duvdx.x(), duvdx.y(),
- duvdy.x(), duvdy.y()
+ duvdy.x(), duvdy.y(),
+ n[2].x(), n[2].y()
});
+
}
- void appendVertex(const QVector2D &vertex,
- std::function<QVector3D(QVector2D)> uvForPoint)
+ void appendTriangle(const QVector2D &v1,
+ const QVector2D &v2,
+ const QVector2D &v3,
+ std::function<QVector3D(QVector2D)> uvForPoint)
{
- QVector3D uv = uvForPoint(vertex);
-
- QVector2D duvdx = QVector2D(uvForPoint(vertex + QVector2D(1, 0))) - QVector2D(uv);
- QVector2D duvdy = QVector2D(uvForPoint(vertex + QVector2D(0, 1))) - QVector2D(uv);
-
- m_uncookedVertexes.append( { vertex.x(), vertex.y(),
- uv.x(), uv.y(), uv.z(),
- duvdx.x(), duvdx.y(),
- duvdy.x(), duvdy.y()
- }
- );
+ appendTriangle({v1, v2, v3}, {}, uvForPoint);
}
void appendIndex(quint32 index)
@@ -172,6 +168,7 @@ private:
{
float x, y, u, v, w;
float dudx, dvdx, dudy, dvdy; // Size of pixel in curve space (must be same for all vertices in triangle)
+ float nx, ny; // normal vector describing the direction to shift the vertex for AA
};
void updateMaterial();
diff --git a/src/quickshapes/qquickshapecurverenderer.cpp b/src/quickshapes/qquickshapecurverenderer.cpp
index ab74288739..8ce7c0c823 100644
--- a/src/quickshapes/qquickshapecurverenderer.cpp
+++ b/src/quickshapes/qquickshapecurverenderer.cpp
@@ -426,72 +426,69 @@ QVector<QSGGeometryNode *> QQuickShapeCurveRenderer::addFillNodes(const PathData
wfVertices.append({ep.x(), ep.y(), 0.0f, 0.0f, 1.0f}); // 2
};
- // Find a point on the other side of the line
- auto findPointOtherSide = [](const QVector2D &startPoint,
+ auto addCurveTriangleWithNormals = [&](const QQuadPath::Element &element,
+ const std::array<QVector2D, 3> &v,
+ const std::array<QVector2D, 3> &n) {
+ node->appendTriangle(v, n, [&element](QVector2D v) { return elementUvForPoint(element, v); });
+ wfVertices.append({v[0].x(), v[0].y(), 1.0f, 0.0f, 0.0f}); // 0
+ wfVertices.append({v[1].x(), v[1].y(), 0.0f, 1.0f, 0.0f}); // 1
+ wfVertices.append({v[2].x(), v[2].y(), 0.0f, 0.0f, 1.0f}); // 2
+ };
+
+ auto outsideNormal = [](const QVector2D &startPoint,
const QVector2D &endPoint,
- const QVector2D &referencePoint) {
+ const QVector2D &insidePoint) {
QVector2D baseLine = endPoint - startPoint;
- QVector2D insideVector = referencePoint - startPoint;
- QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()); // TODO: limit size of triangle
+ QVector2D insideVector = insidePoint - startPoint;
+ QVector2D normal = QVector2D(-baseLine.y(), baseLine.x()).normalized();
bool swap = QVector2D::dotProduct(insideVector, normal) < 0;
- return swap ? startPoint + normal : startPoint - normal;
+ return swap ? normal : -normal;
};
- auto addLineTriangle = [&](const QQuadPath::Element &element,
+ auto addTriangleForLine = [&](const QQuadPath::Element &element,
const QVector2D &sp,
const QVector2D &ep,
const QVector2D &cp) {
addCurveTriangle(element, sp, ep, cp);
- // Add a triangle on the outer side of the line to get some more AA
- // The new point replaces cp
- QVector2D op = findPointOtherSide(sp, ep, cp);
- addCurveTriangle(element, sp, op, ep);
+
+ // Add triangles on the outer side to make room for AA
+ const QVector2D normal = outsideNormal(sp, ep, cp);
+ constexpr QVector2D null;
+ addCurveTriangleWithNormals(element, {sp, sp, ep}, {null, normal, null});
+ addCurveTriangleWithNormals(element, {sp, ep, ep}, {normal, normal, null});
};
- auto addConvexTriangle = [&](const QQuadPath::Element &element,
+ auto addTriangleForConcave = [&](const QQuadPath::Element &element,
+ const QVector2D &sp,
+ const QVector2D &ep,
+ const QVector2D &cp) {
+ addTriangleForLine(element, sp, ep, cp);
+ };
+
+ auto addTriangleForConvex = [&](const QQuadPath::Element &element,
const QVector2D &sp,
const QVector2D &ep,
const QVector2D &cp) {
addCurveTriangle(element, sp, ep, cp);
// Add two triangles on the outer side to get some more AA
+ constexpr QVector2D null;
// First triangle on the line sp-cp, replacing ep
{
- QVector2D op = findPointOtherSide(sp, cp, ep);
- addCurveTriangle(element, sp, cp, op);
+ const QVector2D normal = outsideNormal(sp, cp, ep);
+ addCurveTriangleWithNormals(element, {sp, sp, cp}, {null, normal, null});
}
// Second triangle on the line ep-cp, replacing sp
{
- QVector2D op = findPointOtherSide(ep, cp, sp);
- addCurveTriangle(element, op, cp, ep);
+ const QVector2D normal = outsideNormal(ep, cp, sp);
+ addCurveTriangleWithNormals(element, {ep, ep, cp}, {null, normal, null});
}
};
- // This is guaranteed to be in safe space (the curve will never enter the triangle)
- // ### This is the opposite of what we really want: it's going to be extremely thin when we need it,
- // and big when it's completely pointless, but a thicker triangle could be going into negative space
- auto oppositePoint = [](const QVector2D &startPoint,
- const QVector2D &endPoint,
- const QVector2D &controlPoint) -> QVector2D {
- return startPoint + 2 * (endPoint - controlPoint);
- };
-
- // Identical to addLineTriangle, except for how op is calculated
- auto addConcaveTriangle = [&](const QQuadPath::Element &element,
- const QVector2D &sp,
- const QVector2D &ep,
- const QVector2D &cp) {
- addCurveTriangle(element, sp, ep, cp);
- // Add an outer triangle to give extra AA for very flat curves
- QVector2D op = oppositePoint(sp, ep, cp);
- // The new point replaces cp
- addCurveTriangle(element, sp, op, ep);
- };
-
auto addFillTriangle = [&](const QVector2D &p1, const QVector2D &p2, const QVector2D &p3) {
constexpr QVector3D uv(0.0, 1.0, -1.0);
node->appendTriangle(p1, p2, p3, [&uv](QVector2D) { return uv; } );
@@ -514,12 +511,12 @@ QVector<QSGGeometryNode *> QQuickShapeCurveRenderer::addFillNodes(const PathData
} else {
if (element.isConvex()) {
internalHull.lineTo(ep);
- addConvexTriangle(element, toRoundedVec2D(sp), toRoundedVec2D(ep), toRoundedVec2D(cp));
+ addTriangleForConvex(element, toRoundedVec2D(sp), toRoundedVec2D(ep), toRoundedVec2D(cp));
convexPointHash.insert(toRoundedPair(sp), index);
} else {
internalHull.lineTo(cp);
internalHull.lineTo(ep);
- addConcaveTriangle(element, toRoundedVec2D(sp), toRoundedVec2D(ep), toRoundedVec2D(cp));
+ addTriangleForConcave(element, toRoundedVec2D(sp), toRoundedVec2D(ep), toRoundedVec2D(cp));
concaveControlPointHash.insert(toRoundedPair(cp), index);
}
}
@@ -613,7 +610,7 @@ QVector<QSGGeometryNode *> QQuickShapeCurveRenderer::addFillNodes(const PathData
}
if (lineElementIndex != -1) {
int ci = (6 - si - ei) % 3; // 1+2+3 is 6, so missing number is 6-n1-n2
- addLineTriangle(pathData.fillPath.elementAt(lineElementIndex), p[si], p[ei], p[ci]);
+ addTriangleForLine(pathData.fillPath.elementAt(lineElementIndex), p[si], p[ei], p[ci]);
} else if (concaveElementIndex != -1) {
addCurveTriangle(pathData.fillPath.elementAt(concaveElementIndex), p[0], p[1], p[2]);
} else if (convexElementIndex != -1) {
diff --git a/src/quickshapes/shaders_ng/shapecurve.frag b/src/quickshapes/shaders_ng/shapecurve.frag
index 1b58d4c439..795aaa591c 100644
--- a/src/quickshapes/shaders_ng/shapecurve.frag
+++ b/src/quickshapes/shaders_ng/shapecurve.frag
@@ -152,6 +152,10 @@ void main()
// finally mix in debug
fragColor = mix(combined, vec4(debugColor, 1.0), ubuf.debug) * ubuf.opacity;
#else
- fragColor = mix(baseColor() * clamp(0.5 + f / df, 0.0, 1.0), vec4(debugColor, 1.0), ubuf.debug) * ubuf.opacity;
+ // Special case: mask out concave curve in "negative space".
+ int specialCaseMask = 1 - int(qt_TexCoord.w != 0.0) * (int(qt_TexCoord.x < 0.0) + int(qt_TexCoord.x > 1.0));
+ float fillCoverage = clamp(0.5 + f / df, 0.0, 1.0) * float(specialCaseMask);
+
+ fragColor = mix(baseColor() * fillCoverage, vec4(debugColor, 1.0), ubuf.debug) * ubuf.opacity;
#endif
}
diff --git a/src/quickshapes/shaders_ng/shapecurve.vert b/src/quickshapes/shaders_ng/shapecurve.vert
index c9dfc93a4f..64e32b5517 100644
--- a/src/quickshapes/shaders_ng/shapecurve.vert
+++ b/src/quickshapes/shaders_ng/shapecurve.vert
@@ -3,6 +3,7 @@
layout(location = 0) in vec4 vertexCoord;
layout(location = 1) in vec4 vertexTexCoord;
layout(location = 2) in vec4 vertexGradient;
+layout(location = 3) in vec2 normalVector;
layout(location = 0) out vec4 qt_TexCoord;
layout(location = 1) out vec4 gradient;
@@ -46,9 +47,27 @@ layout(std140, binding = 0) uniform buf {
out gl_PerVertex { vec4 gl_Position; };
+#define SQRT2 1.41421356237
+
+vec4 addOffset(vec4 texCoord, vec2 offset, vec4 duvdxy)
+{
+ float dudx = duvdxy.x;
+ float dvdx = duvdxy.y;
+ float dudy = duvdxy.z;
+ float dvdy = duvdxy.w;
+ float u = offset.x * dudx + offset.y * dudy;
+ float v = offset.x * dvdx + offset.y * dvdy;
+ // special case external triangles for concave curves
+ int specialCase = int(texCoord.z > 0) * (int(offset.x != 0) + int(offset.y != 0));
+ return vec4(texCoord.x + u, texCoord.y + v, texCoord.z, float(specialCase));
+}
+
void main()
{
- qt_TexCoord = vertexTexCoord;
+ vec2 offset = normalVector * SQRT2/ubuf.matrixScale;
+
+ qt_TexCoord = addOffset(vertexTexCoord, offset, vertexGradient);
+
gradient = vertexGradient / ubuf.matrixScale;
#if defined(LINEARGRADIENT)
@@ -58,5 +77,5 @@ void main()
coord = vertexCoord.xy - ubuf.translationPoint;
#endif
- gl_Position = ubuf.qt_Matrix * vertexCoord;
+ gl_Position = ubuf.qt_Matrix * (vertexCoord + vec4(offset, 0, 0));
}