Нарисуйте пунктирную и пунктирную кривую Безье в QML

я видел, что есть пример реализации кривая Безье в QML, но я ищу подсказку, как реализовать пунктирную или пунктирную кривую Безье. Насколько я вижу, авторы примера кривой Безье используют QSGGeometryNode хранить внутри QSGGeometry С QSGFlatColorMaterial материал применяется на нем. Затем они просто создают список точек и рисуют сегменты между ними.

можно писать shader и применить его к QSGFlatColorMaterial (отображать строке dashed, dotted, etc)?

В конце концов, можно ли хранить более одного QSGGeometry внутри QSGGeometryNode?

обновление

я хотел бы реализовать это в "чистом QtQuick" - не в" старых " интерфейсах (например,QPainter etc) - потому что я не хочу использовать что-то, что переключает контекст (openGL и CPU). Я предпочитаю решение с пользовательским шейдером ( если это выполнимо) - потому что у меня будет больше возможностей в реализации пользовательского внешнего вида (пунктир, doted, цветной, может быть, анимированный и т. д.).

если это невозможно, я буду использовать QPainter.

5 ответов


я не думаю, что эта задача является хорошим кандидатом для реализации, используя QSGGeometryNode, было бы намного проще реализовать его с помощью QPainter основанный чертеж и QQuickPaintedItem. Вы все равно получите преимущества OpenGL, так как QPainter поддерживает чертеж ГЛ также и оно все еще быстре чем программное обеспечение. Вы можете использовать запас QPen С запасом пунктирными или пунктирными узорами или сделайте свой собственный с помощью простого QVector.

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

и последнее, но не менее важное: вы можете использовать элемент холста QML, который поддерживает почти те же операции, что и QPainter и, вероятно, предлагает ту же производительность.

EDIT: как следует из вашего обновления, вы пропустили часть, где я сказал QPainter смогите нарисовать как в програмном обеспечении, так и в GL, при GL рисуя часто значительно более быстро. Кроме того, при рисовании в контексте GL вам не нужно перемещать фреймбуфер из CPU в память GPU, он хранится в памяти GPU. Так что никаких накладных расходов. Что касается анимации и других вещей, конечно, это невозможно с QPainter вы ограничены тем, что QPen обеспечивает-различные соединения, крышки и так далее можно использовать для того чтобы доработать форму в некоторой степени, но никакие чудеса... Не получится с шейдерами тоже, это будет возможно только с нестандартной геометрией. И если вы используете QObject на основе объекта для каждого элемента тире / точки, чтобы независимо анимировать их, он будет в конечном итоге довольно дорогостоящим,QObject очень тяжелый и не должен использоваться с такой легкой рукой. Таким образом, пользовательский рендеринг GL в FBO-это в значительной степени способ пойти, если вы хотите такую гибкость, но вам придется полностью выйти из API QtQuick и перейти в GL land.

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

Что касается" чистой " реализации QtQuick, API на самом деле не был разработан для обработки такого типа задач рисования, поэтому элемент Canvas был предоставлен для заполнения пробела и получите расширенную функциональность боли от QML / JS. Холст фактически является оберткой вокруг QPainter это обращается на FBO.

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


Почему бы вам не использовать этот подход:

пример Beziercurve изменен для использования QSGVertexColorMaterial

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

вот важные части:

 geometry = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), m_segmentCount);
 //geometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), m_segmentCount);

QSGVertexColorMaterial *material = new QSGVertexColorMaterial;
//material->setColor(QColor(255, 0, 0));

//QSGGeometry::Point2D *vertices = geometry->vertexDataAsPoint2D();
QSGGeometry::ColoredPoint2D *vertices = geometry->vertexDataAsColoredPoint2D();

vertices[i].set(x, y, 0, 0, 0, 0);
//vertices[i].set(x, y);

С Qt 5.10 элемент Shape был введен и, похоже, делает именно то, что вы хотите.

https://doc.qt.io/qt-5.10/qml-qtquick-shapes-shape.html

Shape {
    width: 20
    ShapePath {
        strokeColor: "blue"
        strokeWidth: 2
        strokeStyle: ShapePath.DashLine
        startX: 0
        startY: 0
        PathLine { x: parent.width; y: 0 }
    }
}

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

остальная часть вопроса на самом деле не завершена, и даже если была предоставлена реализация на основе шейдеров, она не будет очень полезной изначально. Это будет просто преждевременно оптимизация. Давайте посмотрим, что вы пропали.

пример элемента BezierCurve-это просто доказательство концепции. Это не полезно само по себе, так как вам нужен способ цепочки нескольких элементов, которые гладятся с помощью одного и того же пера. Вам нужно что-то сродни простому QPainterPath. Фактически, сама геометрия может быть порождена QPainterPath и QPainterPathStroker.

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


import QtQuick 2.0

Rectangle {
    width : 1024
    height: 600

    Rectangle {
        x: -3 + 158
        y: 355
        width: 4; height: 4;
        color: "black";
        }

    Rectangle {
        x: 359 + 158
        y: 220
        width: 4; height: 4;
        color: "black";
        }

    Rectangle {
        x: 175 + 158
        y: 238
        width: 2; height: 2;
        color: "black";
        }

    Rectangle {
        x: 711 + 158
        y: 355
        width: 4; height: 4;
        color: "black";
        }

    Rectangle {
        x: 533 + 158
        y: 238
        width: 2; height: 2;
        color: "black";
        }

    Rectangle {
        x: -3 + 118
        y: 355
        width: 4; height: 4;
        color: "darkBlue";
        }

    Rectangle {
        x: 399 + 118
        y: 220
        width: 4; height: 4;
        color: "darkBlue";
        }

    Rectangle {
        x: 196 + 118
        y: 238
        width: 4; height: 4;
        color: "darkBlue";
        }

    Rectangle {
        x: 791 + 118
        y: 355
        width: 4; height: 4;
        color: "darkBlue";
        }

    Rectangle {
        x: 592 + 118
        y: 238
        width: 4; height: 4;
        color: "darkBlue";
        }


    Path {
        id: path
        startX: -3
        startY: 355
        PathQuad { x: 359; y:220; controlX: 175; controlY:238 }
        PathQuad { x: 711; y:355; controlX: 533; controlY:238 }
    }

    Path {
        id: path2
        startX: -3
        startY: 355

        PathQuad { x: 399; y:220; controlX: 196; controlY:238 }
        PathQuad { x: 791; y:355; controlX: 592; controlY:238 }
    }


    PathView {
    id: pathView;
    x: 158
    width: 708
    model: 300;
    path: path
    delegate: Rectangle {
    id: dot;
    width: 1; height: 1;
    color: "red";
    }
    }

    PathView {
    id: pathView2;
    x: 118
    width: 788
    model: 300;
    path: path2
    delegate: Rectangle {
    id: dot2;
    width: 1; height: 1;
    color: "green";
    }
    }
}