QPainter and OpenGL native code in QOpenGLWidget c

2019-07-20 20:45发布

问题:

I am preparing a desktop application to draw some 2D plots. I am using C++: Qt 5 and visual studio 2013. I created a simple GUI and put myQOpenGLWidget in it. I am already drawing the plots, axes and ticks using my own openGl shaders. Everything works fine and now I want to add the description to my axes and ticks to make the graph possible to analysis. As in OpenGL itself there is no dedicated functions to render text, I came up with using QPainter object just to add the desired description to already created plot. And here are problems starting...

Here is my functions of my QopenGLWidget implementation:

void GLWidget::initializeGL() {
initializeOpenGLFunctions();
glEnable(GL_POINT_SPRITE);
glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

vertexShad = new QOpenGLShader(QOpenGLShader::Vertex);
fragmentShad = new QOpenGLShader(QOpenGLShader::Fragment);

bool flag = vertexShad->compileSourceFile(QString("vs.glsl"));
if (flag) printf("compiled vertex Shader\n");
flag = fragmentShad->compileSourceFile(QString("fs.glsl"));
if (flag) printf("compiled fragment Shader\n");
flag = program.addShader(vertexShad);
if (flag) printf("linked vertex Shader\n");
flag = false;
flag =program.addShader(fragmentShad);
if (flag) printf("linked fragment Shader\n");

program.link();
program.bind();
}

paintGL:

void GLWidget::paintGL() {
painter = new QPainter(this);

painter->beginNativePainting();
glViewport(0, 0, this->width(), this->height());
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
drawAxes();
glViewport(MARGIN, MARGIN, this->width() - MARGIN * 2, this->height() - MARGIN * 2);
glScissor(MARGIN, MARGIN, this->width() - MARGIN * 2, this->height() - MARGIN * 2);
glEnable(GL_SCISSOR_TEST);
  // here are the functions which use native openGL code
  // ...
  // disabling openGL settings
glDisable(GL_SCISSOR_TEST);
glDisable(GL_POINT_SPRITE);
glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
glDisable(GL_BLEND);
painter->endNativePainting();

drawTicksValues(); // function that uses QPainter
painter->end();
 }

drawTicksValues:

void GLWidget::drawTicksValues() {
char * maxText = new char[4];
sprintf(maxText, "%d", maxY);
char * minText = new char[4];
sprintf(minText, "%d", minY);
painter->drawText(0, MARGIN, QString(maxText));
painter->drawText(0, height() - MARGIN, QString(minText));
painter->drawText(MARGIN, height() - MARGIN + 20, QString("0"));
painter->drawText(width() - MARGIN, height() - MARGIN + 20, QString(100));
}

main:

int main(int argc, char *argv[])
{
 QApplication a(argc, argv);
 QSurfaceFormat  format;
 format.setSamples(4);
 format.setDepthBufferSize(24);
 format.setStencilBufferSize(8);

 QSurfaceFormat::setDefaultFormat(format);
 SymmGaitModels w;
 w.show();
 return a.exec();
}

Unfortunately, when I run the code, with uncommented QPainter part, the axis and plots are cleared, only the numbers I paint with QPainter are shown. But if I comment the QPainter part, the plots and axes are drawed normally. Any suggestions how I can add the text with QPainter to my plot instead of clearing it?

回答1:

I finally managed to get things work.

First of all, I had to change and set the format (you have to change it before widget is shown), for example in main function , before my app was shown

main:

int main(int argc, char *argv[])
{
 QApplication a(argc, argv);

 SymmGaitModels w;
   // GLWidget is my implemantation of QOpenGLWidget
 GLWidget* gl = w.findChild<GLWidget*>("OpenGLWidget");
 QSurfaceFormat  format;
 //format.setSamples(4);
 //format.setDepthBufferSize(24);
 //format.setStencilBufferSize(8);
   format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
 QSurfaceFormat::setDefaultFormat(format);

 gl->setFormat(format);
 w.show();
 return a.exec();
}

Moreover, I had to move all (even enabling and disabling things like blend, point_sprite etc, which caused my problems) of my openGL functions between

QPainter-> beginNativePainting()
 ...
QPainter->endNativePainting();

And I had to implement paintEvent function. I just put paintGL() expression there. Now my functions look like that:

void GLWidget::initializeGL() {
 printf("autoFILL: %d\n",this->autoFillBackground());
 initializeOpenGLFunctions(); // this function has to stay in initializeGL
 // commented functions below has to be removed and put after beginNativePainting
     //glEnable(GL_POINT_SPRITE);
 //glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
 //glEnable(GL_BLEND);
 //glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

   // shaders may be compiled and added here, but program has to be linked and built after beginNativePainting()
 vertexShad = new QOpenGLShader(QOpenGLShader::Vertex);
 fragmentShad = new QOpenGLShader(QOpenGLShader::Fragment);

 bool flag = vertexShad->compileSourceFile(QString("vs.glsl"));
 //bool flag = vertexShad->compileSourceCode(vertexShadSrc);
 if (flag) printf("compiled vertex Shader\n");
 flag = fragmentShad->compileSourceFile(QString("fs.glsl"));
 //flag = fragmentShad->compileSourceCode(fragmentShadSrc);
 if (flag) printf("compiled fragment Shader\n");
 flag = program.addShader(vertexShad);
 if (flag) printf("linked vertex Shader\n");
 flag = false;
 flag =program.addShader(fragmentShad);
 if (flag) printf("linked fragment Shader\n");
}

void GLWidget::paintEvent(QPaintEvent *e) {
 paintGL(); // still this, widget has to be now refreshed by widget->update()
 }

void GLWidget::paintGL() {
 painter = new QPainter(this);
 painter->beginNativePainting();
 //
 glEnable(GL_POINT_SPRITE);
 glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);
 glEnable(GL_BLEND);
 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

 program.link();
 program.bind();
 //
 glViewport(0, 0, this->width(), this->height());
 glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
 glClear(GL_COLOR_BUFFER_BIT);
 drawAxes();
 glViewport(MARGIN, MARGIN, this->width() - MARGIN * 2, this->height() - MARGIN * 2);
 glScissor(MARGIN, MARGIN, this->width() - MARGIN * 2, this->height() - MARGIN * 2);
 glEnable(GL_SCISSOR_TEST);
 // methods using native openGL functions here
 glDisable(GL_SCISSOR_TEST);
 glDisable(GL_POINT_SPRITE);
 glDisable(GL_VERTEX_PROGRAM_POINT_SIZE);
 glDisable(GL_BLEND);
 program.disconnect();
 painter->endNativePainting();
 drawTicksValues();
 painter->end();
}

After adding paintEvent implementation, the openGL widget has to be now refreshed using widget->update().

Hope it will be helpful for other people who are struggling with using both QPainter and native openGL functions, as Qt doc seems to be a little shallow on this topic.

Thanks! Adam