Search code examples
c++qtopenglqglwidget

QOpenGLShaderProgram - multiple instances yield corrupt results


I have a QGLWidget (Qt 5.2.1).

The GLWidget instantiates two other classes that each contain their own QOpenGLShaderPrograms, load their own shaders, and handle their own drawing. This makes for a nice encapsulation.

HOWEVER -- the issue is that the data from the axis mesh (first one being initialized) is appearing in the second class's shader. So, it never draws its own object with its own shader. If I reverse the order of init() calls, the same thing happens in reverse.

The two drawing classes are structured identically, so I'm including only the Mesh class for brevity.

Here's the result. The AxisMesh, which draws lines, is drawing them over the Scatter class, with the same color, instead of in their own color and where they're supposed to be drawn.

The lines in this picture should be a different color and a different location: The lines in this picture should be a different color and a different location

QUESTIONS:

Is it allowed to have two QOpenGLShaderPrograms in a QGLWidget?

Is there something wrong with the approach below?

GLWidget initialzeGL method:

void GLWidget::initializeGL()
{
    // Instantiate our drawing objects
    m_axisMesh       = new PlotItemAxisMesh(m_plotManager, m_plotSelection, &m_axisScale);
    m_surfaceScatter = new PlotItemSurfaceScatter(m_plotManager, m_plotSelection, &m_axisScale);

...

    // Initialize the axis mesh class
    m_axisMesh->init();

    // Initialize the scatter points class
    m_surfaceScatter->init();
}

GLWidget paintGL method:

void GLWidget::paintGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

...

    // Draw the mesh
    m_axisMesh->draw(m_camera.data(), modelMatrix);

    // Draw the points
    m_surfaceScatter->draw(m_camera.data(), modelMatrix);
}

Axis Mesh init method:

void PlotItemAxisMesh::init()
{
    initializeOpenGLFunctions();

    // Initialize the shaders
    initShaders();

    m_program->link();
    m_program->bind();

    // Load the data into the local VBO
    build();

    // Release (unbind) all
    m_program->release();
}

Axis Mesh build method:

void PlotItemAxisMesh::build()
{
    ShmooPlotMatrix *matrix = m_plotManager->getPlotPointMatrix();

    // Calculate the y-axis height in OpenGL terms
    uint32_t yHeight = (m_xMax + m_yMax)/2;
    float yScale     = yHeight / fabs(m_axisScale->getMax() - m_axisScale->getMin());
    float yOffset    = 0 ? m_axisScale->getMin() > 0 : -m_axisScale->getMin();

    // Since we swept X/Y but are plotting the points as X/Z, then Y becomes the value
    m_xMax = matrix->getXMax();
    m_yMax = yHeight;
    m_zMax = matrix->getYMax();

    m_vertexArray.clear();
    m_vertexArray.reserve(4*(m_xMax + m_yMax));

... (load vertexAray with data)

    m_vertexBuffer.create();
    m_vertexBuffer.bind();
    m_vertexBuffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
    m_vertexBuffer.allocate(&(m_vertexArray.front()), vertexSize);

    // Tell VBO how to read the data
    m_positionAttrIndex = m_program->attributeLocation(m_positionAttr);
    m_colorAttrIndex    = m_program->attributeLocation(m_colorAttr);

    int offset = 0;
    m_program->enableAttributeArray(m_positionAttrIndex);
    m_program->setAttributeBuffer(m_positionAttrIndex, GL_FLOAT, offset, 3, sizeof(VertexData));

    offset = sizeof(QVector3D);
    m_program->enableAttributeArray(m_colorAttrIndex);
    m_program->setAttributeBuffer(m_colorAttrIndex, GL_FLOAT, offset, 3, sizeof(VertexData));

    // Release (unbind) all
    m_vertexBuffer.release();
}

Axis Mesh draw method:

void PlotItemAxisMesh::draw(PlotCamera *camera, const QMatrix4x4 &modelMatrix)
{

    m_program->bind();
    {
        // Set modelview-projection matrix
        m_program->setUniformValue("mvpMatrix",  camera->getProjection() * camera->getView() * modelMatrix);
        m_vertexBuffer.bind();
        glDrawArrays(GL_LINES, 0, m_vertexArray.size());
        m_vertexBuffer.release();
    }
    m_program->release();
}

Solution

  • Thank you to @Reto Koradi for the solution

    The fix was to change the draw methods to enable/set the attribute buffers at draw-time.

    void PlotItemAxisMesh::draw(PlotCamera *camera, const QMatrix4x4 &modelMatrix)
    {
        m_program->bind();
        {
            m_vertexBuffer.bind();
    
            int offset = 0;
            m_program->enableAttributeArray(m_positionAttrIndex);
            m_program->setAttributeBuffer(m_positionAttrIndex, GL_FLOAT, offset, 3, sizeof(VertexData));
    
            offset = sizeof(QVector3D);
            m_program->enableAttributeArray(m_colorAttrIndex);
            m_program->setAttributeBuffer(m_colorAttrIndex, GL_FLOAT, offset, 3, sizeof(VertexData));
    
            // Set modelview-projection matrix
            m_program->setUniformValue("mvpMatrix",  camera->getProjection() * camera->getView() * modelMatrix);
    
            glDrawArrays(GL_LINES, 0, m_vertexArray.size());
            m_vertexBuffer.release();
        }
        m_program->release();
    }
    

    Proper drawing

    enter image description here