diff options
author | Paul Olav Tvete <paul.tvete@qt.io> | 2023-08-22 16:26:33 +0200 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2023-08-25 18:17:57 +0000 |
commit | 8ee3871d81a29fb7b4a8c30a439002373a1e323d (patch) | |
tree | 4ec8d4ef9067ca98adf49ca27cce05c838caa9b4 | |
parent | ff4c80138dcb81fb844ecf681a9487e14f452066 (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.cpp | 3 | ||||
-rw-r--r-- | src/quickshapes/qquickshapecurvenode_p.h | 53 | ||||
-rw-r--r-- | src/quickshapes/qquickshapecurverenderer.cpp | 77 | ||||
-rw-r--r-- | src/quickshapes/shaders_ng/shapecurve.frag | 6 | ||||
-rw-r--r-- | src/quickshapes/shaders_ng/shapecurve.vert | 23 |
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)); } |