I want to place in QGraphicsScene several items that draw lines using opengl, when I draw these lines with painter or without shaders, using glBegin(GL_LINES) everything works fine, and the lines are drawn each in its own item. But when I draw with glDrawArrays, the lines of all the items overlap each other and are located in the center of the scene.
I have simple line drawing shaders:
Vertex shader:
attribute vec2 position;
attribute vec4 f_color;
uniform float drw;
uniform float drh;
varying vec4 color;
void main(void) {
color = f_color;
gl_Position = vec4((position.x / drw) * 2.0, (position.y / drh) * 2.0, -1.0, 1.0);
}
Fragment shader:
varying vec4 color;
void main(void) {
gl_FragColor = color;
}
Next I create my graphics item class:
#include <QGraphicsItem>
#include <QGraphicsLayoutItem>
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
class MyItem : public QGraphicsItem, public QGraphicsLayoutItem {
public:
MyItem(QGraphicsItem *parent = nullptr);
~ MyItem();
QRectF boundingRect() const override;
protected:
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
// Inherited from QGraphicsLayoutItem
void setGeometry(const QRectF &geom) override;
QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const override;
private:
QOpenGLBuffer m_vertexBuffer;
QVector<QVector2D> m_vertices;
QOpenGLVertexArrayObject m_vao;
QOpenGLShaderProgram *m_program;
QOpenGLBuffer m_colorBuffer;
QVector<QVector4D> m_colors;
QVector<QVector4D> m_colorVBuffer;
};
Constructor:
MyItem::MyItem(QGraphicsItem *parent): QGraphicsItem(parent)
{
m_program = new QOpenGLShaderProgram();
m_vertices = QVector<QVector2D>();
m_vao.create();
m_vao.bind();
m_vertexBuffer = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
m_vertexBuffer.create();
m_vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vertexBuffer.bind();
m_vertexBuffer.allocate(m_vertices.constData(), m_vertices.size() * sizeof(QVector2D));
m_colorBuffer = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
m_colorBuffer.create();
m_colorBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_colorBuffer.bind();
m_colorBuffer.allocate(m_vertices.constData(), m_colorVBuffer.size() * sizeof(QVector4D));
m_vao.release();
m_program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertexShader.vert")
m_program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragmentShader.frag")
m_program->link()
m_program->bind()
program->enableAttributeArray(0);
program->setAttributeBuffer(0, GL_FLOAT, 0, 2);
program->release();
setGraphicsItem(this);
}
And in the paint function I draw an array of lines:
void MyItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Q_UNUSED(option);
Q_UNUSED(widget);
painter->beginNativePainting();
m_program->bind();
m_vao.bind();
m_vertexBuffer.bind();
m_vertexBuffer.allocate(m_vertices.constData(), m_vertices.size() * sizeof(QVector2D));
m_program->enableAttributeArray("position");
m_program->setAttributeBuffer("position", GL_FLOAT, 0, 2);
m_program->setUniformValue("drw", static_cast<GLfloat>(boundingRect().width()));
m_program->setUniformValue("drh", static_cast<GLfloat>(boundingRect().height()));
m_program->enableAttributeArray("f_color");
m_program->setAttributeBuffer("f_color", GL_FLOAT, 0, 4);
glLineWidth(2);
glDrawArrays(GL_LINES, 0, m_vertices.size());
m_vao.release();
m_program->release();
painter->endNativePainting();
}
Overrided methods:
void PolarItem::setGeometry(const QRectF &geom)
{
prepareGeometryChange();
QGraphicsLayoutItem::setGeometry(geom);
setPos(geom.topLeft());
}
QSizeF PolarItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
switch (which) {
case Qt::MinimumSize:
case Qt::PreferredSize:
return QSize(400, 400);
case Qt::MaximumSize:
return QSizeF(400, 400);
default:
break;
}
return constraint;
}
QRectF PolarItem::boundingRect() const
{
return QRectF(0, 0, 400, 400);
}
Then I add 5 of these items to the scene and then to the graphics view:
#include "mainwindow.h"
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QOpenGLWidget>
#include <QGraphicsWidget>
#include <QGraphicsLinearLayout>
#include <QSurfaceFormat>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QGraphicsScene * scene = new QGraphicsScene(this);
QGraphicsView * view = new QGraphicsView(scene);
QGraphicsWidget *form = new QGraphicsWidget();
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(form);
layout->setOrientation(Qt::Vertical);
for (int i(0); i < 5; ++i) {
QSharedPointer<MyItem> item(new MyItem());
layout->addItem(item.data());
}
scene->addItem(form);
QOpenGLWidget *myGLWidget = new QOpenGLWidget();
QSurfaceFormat format;
format.setProfile(QSurfaceFormat::CompatibilityProfile);
format.setSamples(8);
QSurfaceFormat::setDefaultFormat(format);
view->setBackgroundBrush(QColor(255, 255, 255));
myGLWidget->setFormat(format);
view->setViewport(myGLWidget);
setCentralWidget(view);
}
And in the end I get this result, where the lines from all the items overlap each other in the center of the scene: Result
I tried to use glViewport with scenePos() before glDrawArrays but that also doesn't work correctly. What am I doing wrong?