Я хотел бы использовать QOpenGLWidget в QT для создания быстрых линейных графиков. Эти графики должны быть снабжены некоторыми метками, для чего я сейчас использую QPainter с drawText(). В различных примерах команды OpenGL сначала используются в методе PaintGL, а затем в Qpainter. Например. здесь:
Как использовать QPainter в PaintGL QOpenGlWidget
Собственный код QPainter и OpenGL в классе QOpenGLWidget
Собственный код QPainter и OpenGL в классе QOpenGLWidget
Я написал небольшой пример виджета (C++), в котором строки, а затем текст рисуются в PaintGL (см. ниже). Моя проблема сейчас в том, что и линии, и текст можно увидеть правильно при первом рисовании. Если метод PaintGL вызывается второй раз (например, после изменения размера), по-прежнему виден только текст, линии больше не видны.
Я использую QT 6.4.2 с OpenGL 4.6.0 под Windows 10 с 64-разрядным приложением.
Вот объявление виджета:
struct ColorVertex_t {
float x = 0, y = 0, z = 0, r = 0, g = 0, b = 0, a = 0.8f;
// OpenGL Helpers
static const int PositionTupleSize = 3;
static const int ColorTupleSize = 4;
static Q_DECL_CONSTEXPR inline int positionOffset() { return offsetof(ColorVertex_t, x); }
static Q_DECL_CONSTEXPR inline int colorOffset() { return offsetof(ColorVertex_t, r); }
static Q_DECL_CONSTEXPR inline int stride() { return sizeof(ColorVertex_t); }
};
class OpenGLLinePlotWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
QOpenGLShaderProgram* m_program;
QOpenGLVertexArrayObject vao_lines_; // vertex array object for the lines
unsigned int vbo_lines_ = 0; // vertex buffer object for the lines, type GL_ARRAY_BUFFER
/// vector of vertex coordinates and colors (RGB) for the lines
std::vector<ColorVertex_t> vertices_lines_;
public:
OpenGLLinePlotWidget(QWidget* parent = nullptr);
~OpenGLLinePlotWidget();
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
};
А вот определения:
const char* vertexShaderSourceLine = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec4 aColor; // the color variable has attribute position 1\n"
"\n"
"out vec4 ourColor; // output a color to the fragment shader\n"
"\n"
"void main()\n"
"{\n"
" gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
" ourColor = aColor;\n"
"}\0";
const char* fragmentShaderSourceLine = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec4 ourColor;\n"
"void main()\n"
"{\n"
" FragColor = ourColor;\n"
"}\n\0";
OpenGLLinePlotWidget::OpenGLLinePlotWidget(QWidget* parent) : QOpenGLWidget(parent), m_program(nullptr)
{
// Define some line coordinates
size_t num_vertices = 100;
vertices_lines_.resize(num_vertices);
ColorVertex_t vertex;
for (size_t i = 0; i < num_vertices; ++i) {
vertex.x = -1. + i * 0.016;
vertex.y = sin(i * 0.314);
vertices_lines_[i] = vertex;
}
}
OpenGLLinePlotWidget::~OpenGLLinePlotWidget()
{
// Make sure the context is current and then explicitly destroy all underlying OpenGL resources.
makeCurrent();
// Actually destroy our OpenGL information
vao_lines_.destroy();
delete m_program;
doneCurrent();
}
/* This function is called by OpenGL and must not be called manually! */
void OpenGLLinePlotWidget::initializeGL()
{
// Initialize OpenGL Backend
initializeOpenGLFunctions();
glClearColor(1.f, 1.f, 1.f, 1.0f); // white background
// Create Shader (Do not release until VAO is created)
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSourceLine);
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSourceLine);
m_program->link();
m_program->bind();
// ------------- cursor sample mesh buffers ---------------
vao_lines_.create();
vao_lines_.bind();
glGenBuffers(1, &vbo_lines_);
glBindBuffer(GL_ARRAY_BUFFER, vbo_lines_);
// position attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, ColorVertex_t::stride(), (void*)0);
glEnableVertexAttribArray(0);
// color attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, ColorVertex_t::stride(), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
vao_lines_.release();
}
void OpenGLLinePlotWidget::resizeGL(int w, int h)
{
}
void OpenGLLinePlotWidget::paintGL()
{
if (!isValid()) return;
glClear(GL_COLOR_BUFFER_BIT);
// draw lines with OpenGL
vao_lines_.bind();
glLineWidth(2.0f);
glBindBuffer(GL_ARRAY_BUFFER, vbo_lines_);
glBufferData(GL_ARRAY_BUFFER, sizeof(ColorVertex_t) * vertices_lines_.size(), vertices_lines_.data(), GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINE_STRIP, 0, static_cast<GLsizei>(vertices_lines_.size()));
vao_lines_.release();
// draw some text with QPainter
QPainter painter(this);
painter.drawText(10, 20, "Hello World from QPainter in OpenGL!");
}
Я пробовал следующее:
После долгих поисков, проб и ошибок я в растерянности и буду признателен за любую помощь! Спасибо!
Ваша реализация paintGL связывает только VAO; попробуйте также перепривязать m_program.
Не перепривязка m_program была ошибкой — теперь она работает! :-) Большое большое спасибо!
@Botje: Спасибо RenderDoc за подсказку! Я не знал об этом инструменте, и я посмотрю на него!
@Г.М. хорошо подмечено! Не могли бы вы сформулировать это как ответ?





[Перенесено из комментария]
При использовании QPainter изнутри paintGL важно отметить, что базовый код Qt может выполнять все виды операций в текущем контексте OpenGL. Следовательно, важно восстановить все соответствующее состояние. В показанном коде...
vao_lines_.bind();
glLineWidth(2.0f);
glBindBuffer(GL_ARRAY_BUFFER, vbo_lines_);
glBufferData(GL_ARRAY_BUFFER, sizeof(ColorVertex_t) * vertices_lines_.size(), vertices_lines_.data(), GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINE_STRIP, 0, static_cast<GLsizei>(vertices_lines_.size()));
vao_lines_.release();
восстанавливается только ВАО. В этом случае также необходимо восстановить шейдерную программу либо...
m_program->bind();
или...
glUseProgram(m_program->programId());
Еще раз спасибо! Кстати: перепривязав m_program, он теперь также работает с Begin/endNativePainting...
Я только что увидел, что все еще реализовал функцию PaintEvent в своем тестовом коде следующим образом:
void OpenGLLinePlotWidget::paintEvent(QPaintEvent* e) {
paintGL();
}
Без этой реализации ничего не рисуется.
Судя по документации, подход
beginNativePaintingдолжен работать. Пробовали ли вы запустить свое приложение под RenderDoc, чтобы увидеть, как QPainter меняет ваше состояние OpenGL?