带有 Qpainter 的 QOpenGLWidget:OpenGL 绘图仅在第一次 PaintGL 调用后可见

问题描述 投票:0回答:2

我想在 QT 中使用 QOpenGLWidget 来创建快速线图。这些图将带有一些标签,我目前正在使用带有drawText()的QPainter。在各种示例中,OpenGL命令首先在paintGL方法中使用,然后在Qpainter中使用。 例如。这里:

如何在QOpenGlWidget的paintGL中使用QPainter

QOpenGLWidget 类中的 QPainter 和 OpenGL 原生代码

QOpenGLWidget 类中的 QPainter 和 OpenGL 原生代码

我编写了一个小示例小部件(C++),其中在paintGL中绘制线条,然后绘制文本(见下文)。我现在的问题是,第一次绘制线条和文本时都可以正确看到。 如果第二次调用paintGL方法(例如调整大小后),则只有文本仍然可见,线条不再可见。

我在 Windows 10 下使用带有 OpenGL 4.6.0 的 QT 6.4.2 和 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!");
}

我尝试过以下方法:

  • 如果 QPainter 指令被注释掉,即使在调整大小之后,线条也会始终正确绘制。只有使用 QPainter 才会导致这种不正确的行为。
  • 我使用 glGetError 和 glCheckFramebufferStatus 来检查是否发生了任何错误 - 但事实并非如此。
  • 我在OpenGL绘图之前定义了QPainter,并在beginNativePainting()和endNativePainting()之间设置OpenGL指令。但我在这里没有看到任何线条(无论出于何种原因......)。
  • 我实现了paintEvent函数,我只是将paintGL()调用放在那里。
  • 我设置了整个应用程序的默认表面格式,并且在使用“new”创建小部件后也设置了它的表面格式,但这没有帮助。

经过大量搜索和反复试验,我不知所措,希望得到任何帮助!谢谢!

c++ resize vertex-shader qpainter qopenglwidget
2个回答
1
投票

[移自评论]

当在

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();

仅恢复 VAO。在这种情况下,还需要使用以下任一方法恢复着色器程序...

m_program->bind();

或者...

glUseProgram(m_program->programId());



0
投票

void OpenGLLinePlotWidget::paintEvent(QPaintEvent* e) { paintGL(); }

如果没有这个实现,什么也画不出来。

© www.soinside.com 2019 - 2024. All rights reserved.