Плавная покраска в пользовательском элементе QML

Теперь я пытаюсь создать пользовательский элемент QML, производный от QQuickItem. Поэтому я переопределил QQuickItem::updatePaintNode и хочу теперь нарисовать линию. Мой код:

QSGNode *StrikeLine::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
    QSGGeometryNode *node = 0;

    QSGGeometry *geometry;
    QSGFlatColorMaterial *material;
    node = static_cast<QSGGeometryNode *>(oldNode);
    if(!node) {
        node = new QSGGeometryNode;
        geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 2);
        geometry->setDrawingMode(GL_LINES);
        geometry->setLineWidth(3);
        material = new QSGFlatColorMaterial;
        material->setColor(QColor(255, 0, 0));
        node->setGeometry(geometry);
        node->setFlag(QSGNode::OwnsGeometry);
        node->setMaterial(material);
        node->setFlag(QSGNode::OwnsMaterial);
        getColor();
    } else {
        geometry = node->geometry();
        material = static_cast<QSGFlatColorMaterial *>(node->material());
    }
    geometry->vertexDataAsPoint2D()[0].set(p_startPoint.x(), p_startPoint.y());
    geometry->vertexDataAsPoint2D()[1].set(p_endPoint.x(), p_endPoint.y());
    material->setColor(getColor());
    node->markDirty(QSGNode::DirtyGeometry);

    return node;
}

но моя линия выглядит так некрасиво. Края грубые, и это похоже на графику DOS вообще. Итак, мой вопрос-как я могу применить гладкую живопись? Я теперь это может быть какой-то шейдер или что-то еще, но я не могу найти никакой документации.

1 ответов


график сцены поддерживает два типа сглаживания. Примитивы, такие как прямоугольники и изображения, будут сглажены путем добавления большего количества вершин вдоль края примитивов, чтобы края стали прозрачными. Этот метод называется сглаживанием вершин. Если вы запросите многоплановый контекст OpenGL, граф сцены предпочтет многоплановое сглаживание (MSAA).

сглаживание вершин может создавать швы между краями соседних примитивов, даже если два края матматически то же самое. Multisample сглаживание не делает.

Множественное Сглаживание

Multisample сглаживание является аппаратной функцией, где оборудование вычисляет значение покрытия на пиксель в примитиве. Некоторые аппаратные средства могут быть многоразрядными по очень низкой цене, в то время как другим аппаратным средствам может потребоваться больше памяти и больше циклов GPU для визуализации кадра.

включить множественное сглаживание, вы должны установить QSurfaceFormat с образцами больше 0 с помощью QQuickWindow::setFormat()

QQuickView view;
QSurfaceFormat format = view.format();
format.setSamples(16);
view.setFormat(format);
view.show();

Сглаживание Вершин

сглаживание вершин может быть включено и отключено на основе каждого элемента с помощью Item::antialiasing собственность. Он будет работать независимо от того, что поддерживает базовое оборудование и обеспечивает более высокое качество сглаживания, как для нормально отображаемых примитивов, так и для примитивов, захваченных в объекты framebuffer.

недостатком использования сглаживания вершин является то, что каждый примитив с включенным сглаживанием будет нужно смешивать. В терминах дозировки, это означает, что визуализатор должен сделать больше работы, чтобы выяснить, может ли примитив быть упакован или нет, и из-за перекрытий с другими элементами сцены это также может привести к меньшему дозированию, что может повлиять на производительность.


чтобы применить сглаживание вершин к пользовательскому элементу QML, производному от QQuickItem выполните следующие шаги:

1) Создайте пользовательский материал и шейдер OpenGL программа.

smoothcolormaterial.h

#include <QSGMaterial>
#include <QSGMaterialShader>

//----------------------------------------------------------------------

class QSGSmoothColorMaterial : public QSGMaterial
{
public:
    QSGSmoothColorMaterial();
    int compare(const QSGMaterial *other) const;
protected:
    virtual QSGMaterialType *type() const;
    virtual QSGMaterialShader *createShader() const;
};

//----------------------------------------------------------------------

class QSGSmoothColorMaterialShader : public QSGMaterialShader
{
public:
    QSGSmoothColorMaterialShader();
    virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect);
    virtual char const *const *attributeNames() const;
private:
    void initialize();
    int m_matrixLoc;
    int m_opacityLoc;
    int m_pixelSizeLoc;
};

smoothcolormaterial.cpp

QSGSmoothColorMaterial::QSGSmoothColorMaterial()
{
    setFlag(RequiresFullMatrixExceptTranslate, true);
    setFlag(Blending, true);
}

int QSGSmoothColorMaterial::compare(const QSGMaterial *other) const
{
    Q_UNUSED(other)
    return 0;
}

QSGMaterialType *QSGSmoothColorMaterial::type() const
{
    static QSGMaterialType type;
    return &type;
}

QSGMaterialShader *QSGSmoothColorMaterial::createShader() const
{
    return new QSGSmoothColorMaterialShader();
}

//----------------------------------------------------------------------

QSGSmoothColorMaterialShader::QSGSmoothColorMaterialShader()
    : QSGMaterialShader()
{
    setShaderSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/shaders/smoothcolor.vert"));
    setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/shaders/smoothcolor.frag"));
}

void QSGSmoothColorMaterialShader::updateState(const QSGMaterialShader::RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect)
{
    Q_UNUSED(newEffect)

    if (state.isOpacityDirty())
        program()->setUniformValue(m_opacityLoc, state.opacity());

    if (state.isMatrixDirty())
        program()->setUniformValue(m_matrixLoc, state.combinedMatrix());

    if (oldEffect == 0) {
        // The viewport is constant, so set the pixel size uniform only once.
        QRect r = state.viewportRect();
        program()->setUniformValue(m_pixelSizeLoc, 2.0f / r.width(), 2.0f / r.height());
    }
}

const char * const *QSGSmoothColorMaterialShader::attributeNames() const
{
    static char const *const attributes[] = {
        "vertex",
        "vertexColor",
        "vertexOffset",
        0
    };
    return attributes;
}

void QSGSmoothColorMaterialShader::initialize()
{
    m_matrixLoc = program()->uniformLocation("matrix");
    m_opacityLoc = program()->uniformLocation("opacity");
    m_pixelSizeLoc = program()->uniformLocation("pixelSize");
}

Фрагмент Шейдеров

varying lowp vec4 color;

void main()
{
    gl_FragColor = color;
}

Вершинный Шейдер

uniform highp vec2 pixelSize;
uniform highp mat4 matrix;
uniform lowp float opacity;

attribute highp vec4 vertex;
attribute lowp vec4 vertexColor;
attribute highp vec2 vertexOffset;

varying lowp vec4 color;

void main()
{
    highp vec4 pos = matrix * vertex;
    gl_Position = pos;

    if (vertexOffset.x != 0.) {
        highp vec4 delta = matrix[0] * vertexOffset.x;
        highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
        highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);
        dir -= ndir * delta.w * pos.w;
        highp float numerator = dot(dir, ndir * pos.w * pos.w);
        highp float scale = 0.0;
        if (numerator < 0.0)
            scale = 1.0;
        else
            scale = min(1.0, numerator / dot(dir, dir));
        gl_Position += scale * delta;
    }

    if (vertexOffset.y != 0.) {
        highp vec4 delta = matrix[1] * vertexOffset.y;
        highp vec2 dir = delta.xy * pos.w - pos.xy * delta.w;
        highp vec2 ndir = .5 * pixelSize * normalize(dir / pixelSize);
        dir -= ndir * delta.w * pos.w;
        highp float numerator = dot(dir, ndir * pos.w * pos.w);
        highp float scale = 0.0;
        if (numerator < 0.0)
            scale = 1.0;
        else
            scale = min(1.0, numerator / dot(dir, dir));
        gl_Position += scale * delta;
    }

    color = vertexColor * opacity;
}

2) создать пользовательский AttributeSet на QSGGeometry.

myquickitem.cpp

namespace
{
    struct Color4ub
    {
        unsigned char r, g, b, a;
    };

    inline Color4ub colorToColor4ub(const QColor &c)
    {
        Color4ub color = { uchar(c.redF() * c.alphaF() * 255),
                           uchar(c.greenF() * c.alphaF() * 255),
                           uchar(c.blueF() * c.alphaF() * 255),
                           uchar(c.alphaF() * 255)
                         };
        return color;
    }

    struct SmoothVertex
    {
        float x, y;
        Color4ub color;
        float dx, dy;
        void set(float nx, float ny, Color4ub ncolor, float ndx, float ndy)
        {
            x = nx; y = ny; color = ncolor;
            dx = ndx; dy = ndy;
        }
    };

    const QSGGeometry::AttributeSet &smoothAttributeSet()
    {
        static QSGGeometry::Attribute data[] = {
            QSGGeometry::Attribute::create(0, 2, GL_FLOAT, true),
            QSGGeometry::Attribute::create(1, 4, GL_UNSIGNED_BYTE, false),
            QSGGeometry::Attribute::create(2, 2, GL_FLOAT, false)
        };
        static QSGGeometry::AttributeSet attrs = { 3, sizeof(SmoothVertex), data };
        return attrs;
    }
}

3) применить пользовательские материал и нестандартная геометрия QSGGeometryNode.

myquickitem.cpp

QSGNode *MyQuickItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *data)
{
     QSGGeometryNode *node = 0;

     QSGGeometry *geometry;
     QSGSmoothColorMaterial *material;
     node = static_cast<QSGGeometryNode *>(oldNode);
     if(!node) {
         node = new QSGGeometryNode;
         geometry = new QSGGeometry(smoothAttributeSet(), 0);
         geometry->setDrawingMode(GL_TRIANGLE_STRIP);
         material = new QSGSmoothColorMaterial();
         node->setGeometry(geometry);
         node->setFlag(QSGNode::OwnsGeometry);
         node->setMaterial(material);
         node->setFlag(QSGNode::OwnsMaterial);
     } else {
         geometry = node->geometry();
         material = static_cast<QSGSmoothColorMaterial *>(node->material());
     }

4) получить указатель на данные вершины.

 int vertexStride = geometry->sizeOfVertex();
 int vertexCount = 8;

 geometry->allocate(vertexCount, 0);
 SmoothVertex *smoothVertices = reinterpret_cast<SmoothVertex *>(geometry->vertexData());
 memset(smoothVertices, 0, vertexCount * vertexStride);

5) Установить данные вершин.

нужно 4 очка.

 float lineWidth = 4;
 float tlX = 0;   float tlY = 0;               //top-left
 float blX = 0;   float blY = 0 + lineWidth;   //bottom-left
 float trX = 500; float trY = 100;             //top-right
 float brX = 500; float brY = 100 + lineWidth; //bottom-right
 float delta = lineWidth * 0.5f;

 Color4ub fillColor = colorToColor4ub(QColor(255,0,0,255));
 Color4ub transparent = { 0, 0, 0, 0 };

рисовать линии сглажены. вы должны установить 8 вершин нарисовать 6 треугольников(2 линии, 4 для сглаживания). Вершины 0 и 2, 1 и 3, 4 и 6, 5 и 7 имеют одинаковые координаты, но разные цвета и противоположные вершины сдвиг.

enter image description here

 smoothVertices[0].set(trX, trY, transparent, delta, -delta);
 smoothVertices[1].set(tlX, tlY, transparent, -delta, -delta);

 smoothVertices[2].set(trX, trY, fillColor, -delta, delta);
 smoothVertices[3].set(tlX, tlY, fillColor, delta, delta);
 smoothVertices[4].set(brX, brY, fillColor, -delta, -delta);
 smoothVertices[5].set(blX, blY, fillColor, delta, -delta);

 smoothVertices[6].set(brX, brY, transparent, delta, delta);
 smoothVertices[7].set(blX, blY, transparent, -delta, delta);


 node->markDirty(QSGNode::DirtyGeometry);

 return node;
 }