Нарисуйте пунктирную и пунктирную кривую Безье в QML
Я видел пример реализации кривой Безье в QML, но я ищу подсказку, как реализовать пунктир или пунктирная линия кривой безье. Насколько я вижу, авторы примера кривой Безье используют QSGGeometryNode
для хранения внутри QSGGeometry
с помощью материала QSGFlatColorMaterial
, применяемого к нему. Затем они просто создают список точек и рисуют сегменты между ними.
Можно ли написать shader
и применить его к QSGFlatColorMaterial
(для отображения строки как dashed
, dotted
и т.д.)?
В конце концов, можно ли хранить более одного QSGGeometry
внутри QSGGeometryNode
?
UPDATE
Я хотел бы реализовать это в "pure QtQuick
" - не в "старых" интерфейсах (например, QPainter etc
)), потому что я не хочу использовать что-то, что переключает контекст (openGL и CPU). Я предпочитаю решение с пользовательским шейдером (если это выполнимо), потому что у меня будет больше возможностей для реализации пользовательского внешнего вида (пунктирная, придуманная, цветная, анимированная и т.д.).
Если это невозможно, я буду использовать QPainter
.
Ответы
Ответ 1
Я не думаю, что эта задача является хорошим кандидатом для реализации с использованием QSGGeometryNode
, было бы намного проще реализовать ее с использованием чертежа QPainter
и QQuickPaintedItem
. Вы по-прежнему получаете преимущества OpenGL, поскольку QPainter
поддерживает также чертеж GL, и он все же быстрее, чем программное обеспечение. Вы можете использовать запас QPen
со скопированными пунктирными или пунктирными узорами или сделать свой собственный с помощью простого QVector
.
В качестве альтернативы вы можете перейти к пользовательскому подходу GL-рисования вместо использования классов Qt, которые довольно ограничены, когда речь заходит о представлении передовой сложной геометрии. Вы даже можете использовать instancing (если есть), чтобы улучшить производительность еще больше, и просто поместите тире или точную геометрию вдоль кривой пути.
И последнее, но не менее важное: вы можете использовать элемент QML Canvas, который поддерживает почти те же операции, что и QPainter
, и, вероятно, предлагает ту же производительность.
РЕДАКТИРОВАТЬ: Как показывает ваше обновление, вы пропустили ту часть, в которой я сказал, что QPainter
может рисовать как в программном обеспечении, так и в GL, при этом GL-рисунок часто значительно быстрее. Кроме того, при рисовании в контексте GL вам не нужно перемещать фреймбуфер из CPU в память GPU, он хранится в памяти графического процессора. Так что никаких накладных расходов. Что касается анимаций и других вещей, конечно, это невозможно с QPainter
, вы ограничены тем, что QPen
обеспечивает - различные объединения, кепки и т.д. Можно использовать для изменения формы до некоторой степени, но не чудес... Это тоже будет невозможно с шейдерами, это возможно только с пользовательской геометрией. И если вы используете объект QObject
для каждого элемента штриховки/точки для того, чтобы независимо анимировать их, он будет довольно дорогостоящим, QObject
очень тяжелым и не должен использоваться с такой легкой рукой. Таким образом, настраиваемый GL-рендеринг для FBO - это в значительной степени способ, если вы хотите такую гибкость, но вам придется полностью отказаться от API QtQuick и в землю GL.
Во всяком случае, штриховой линейный шейдер не должен быть таким сложным, в основном вы окрашиваете фрагмент на основе расстояния от кривой и "периода" вдоль его длины. Я нашел этот пример, сам не пробовал. Вы можете анимировать пороги, даже использовать синусоидальную функцию, чтобы получить фанки выглядящий стиль.
Что касается "чистой" реализации QtQuick, API на самом деле не был предназначен для обработки таких задач рисования, поэтому элемент Canvas был предоставлен для заполнения пробела и получения расширенной функциональности для обработки из QML/JS. Canvas фактически является оберткой вокруг QPainter
, которая рисует FBO.
В конце концов, это не сводится к тому, что возможно/невозможно, но какой подход имеет наибольший смысл и наиболее эффективен при выполнении работы. Сначала попробуйте подход QQuickPaintedItem
, так как он является самым легким, если вас не устраивает производительность, вы можете реализовать еще одно более сложное решение и профиль для первого. В конце концов, именно поэтому QQuickPaintedItem
был введен в первую очередь - обрабатывать устаревшую картину, что не удобно делать с классом QQuickItem
.
Ответ 2
Почему бы вам не использовать этот подход:
Пример 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);
Ответ 3
Нет, вы не можете хранить несколько геометрий в геометрии node. API очень откровенен в этом. Там нет оснований хранить несколько геометрий там, поскольку node соединяет геометрию и материал. Вы можете повторно использовать геометрию и материалы между узлами - то, как оно предназначено для использования, на самом деле.
Остальная часть вопроса не совсем завершена, и даже если бы была реализована реализация на основе шейдеров, сначала это будет не очень полезно. Это будет преждевременная оптимизация. Давайте посмотрим, что вам не хватает.
Пример "Безье" - это просто доказательство концепции. Это не полезно само по себе, так как вам нужен способ связывания нескольких элементов, которые гладят с помощью одного и того же пера. Вам нужно что-то похожее на простой QPainterPath
. Фактически, сама геометрия могла быть сгенерирована с помощью QPainterPath
и QPainterPathStroker
.
После того, как у вас есть полная тесселированная геометрия для объекта с непрерывным поглаживанием, вы можете либо отрезать его по стилю линии, либо использовать шейдер. Это потребует профилирования, чтобы показать, что линейный шейдер сам по себе является большой победой. Вполне возможно, что вам нужен геометрический шейдер для выполнения поглаживания и т.д., И все усиление производительности будет сосредоточено там. Подумайте о количестве вычислений, которые необходимо выполнить, стиль линии относительно прост.
Ответ 4
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";
}
}
}