Reputation: 3341
I need to make it so part of my Qml view is “taken over” by some non-Qt OpenGL rendering, and I was having issues getting a texture to display properly so I thought I would just draw a line and get that to work before moving on to more complicated code.
For those not familiar with Qt5, the entire window is drawn using OpenGL, and I'm hooking into Qt's OpenGL drawing mechanism using their QQuickWindow::beforeRendering() signal which means my painting code is executed every redraw (every vertical sync).
I took the Squircle sample code ( http://qt-project.org/doc/qt-5/qtquick-scenegraph-openglunderqml-example.html ) and modified it slightly so it would draw in a specified portion of the screen (instead of the entire screen) and that is working perfectly. Then, I modified just the renderer::paint() function to initially draw three green lines, and after 2 seconds to instead draw one blue line:
void CtRenderer::paint()
{
static int n = 0;
if (n == 0)
{
glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glEnable(GL_SCISSOR_TEST);
glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// glClearColor(1, 0, 0, 1);
// glClear(GL_COLOR_BUFFER_BIT);
glLineWidth(10);
glColor4f(0.0, 1.0, 0.0, 1);
glBegin(GL_LINES);
glVertex3f(0.0, 0.0, 1); glVertex3f(1, 0.5, 1);
glVertex3f(0.0, 0.0, 1); glVertex3f(0.5, 0.5, 1);
glVertex3f(0.0, 0.0, 1); glVertex3f(0.5, 1, 1);
glEnd();
}
else if (n == 120)
{
glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glEnable(GL_SCISSOR_TEST);
glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
// glClearColor(1, 0, 0, 1);
// glClear(GL_COLOR_BUFFER_BIT);
glLineWidth(10);
glColor4f(0.0, 0.0, 1.0, 1);
glBegin(GL_LINES);
glVertex3f(0.0, 0.0, 1); glVertex3f(0.4, 0.8, 1);
glEnd();
}
n++;
return;
}`
What I get instead are three gray lines that flicker continuously and never change to being a single line. After some online research, I thought that maybe I should not use glBegin()/glEnd() so I changed the code :
void CtRenderer::paint()
{
static bool bOnce = true;
if (bOnce)
{
glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glEnable(GL_SCISSOR_TEST);
glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
float vertices[] = {-0.5f, -0.5f, 0.5f, 0.5f};
glLineWidth(10);
glColor4f(0.0, 1.0, 0.0, 1);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, vertices);
glDrawArrays(GL_LINES, 0, 2);
glDisableClientState(GL_VERTEX_ARRAY);
bOnce = false;
}
return;
}
This still gives me a flickering gray line.
When I try this code in a simple GLUT application, outside of Qt, it works just fine, so it seems to be some interaction between OpenGL and Qt5 Qml. What can I try next?
p.s. I'm using Qt version 5.3 on a Linux Ubuntu box
p.p.s. In response to some comments, I updated my code to look like this:
void dumpGlErrors(int iLine)
{
for (;;)
{
GLenum err = glGetError();
if (err == GL_NO_ERROR)
return;
std::cout << "GL error " << err << " detected in line " << iLine << std::endl;
}
}
void CtRenderer::paint()
{
glPushMatrix();
glLoadIdentity();
glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glEnable(GL_SCISSOR_TEST);
glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
dumpGlErrors(__LINE__);
glClearColor(1, 0, 1, 1); // Magenta
glClear(GL_COLOR_BUFFER_BIT);
bool bDepth = glIsEnabled(GL_DEPTH_TEST);
glDisable(GL_DEPTH_TEST);
bool bLighting = glIsEnabled(GL_LIGHTING);
glDisable(GL_LIGHTING);
bool bTexture = glIsEnabled(GL_TEXTURE_2D);
glDisable(GL_TEXTURE_2D);
dumpGlErrors(__LINE__);
float vertices[] = { -0.5f, -0.5f, 0.1f, 0.5f, 0.5f, 0.1f, 0.5f, 0.5f, 1.0f, 0.5f, -0.5f, 1.0f };
float colors[] = { 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f };
glLineWidth(10);
glColor4f(0.0, 0.0, 1.0, 1);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vertices);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, 0, colors);
dumpGlErrors(__LINE__);
glDrawArrays(GL_LINES, 0, 4); // 4, not 2.
dumpGlErrors(__LINE__);
// glFlush();
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
if (bTexture)
glEnable(GL_TEXTURE_2D);
if (bLighting)
glEnable(GL_LIGHTING);
if (bDepth)
glEnable(GL_DEPTH_TEST);
glPopMatrix();
dumpGlErrors(__LINE__);
// In case somebody else calls glClear()
glClearColor(0, 1, 1, 1); // Cyan
}
Now I get a nice magenta background and I see my line flash once and then it goes away and I'm left with only magenta.
p.p.p.s. I tried using VBO type functions:
class Point
{
public:
float m_vertex[3];
float m_color[4];
};
void SquircleRenderer::drawBuffer()
{
QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
if (!m_bBufInit)
{
std::cout << "SquircleRenderer::drawBuffer()" << std::endl;
// Adding these two lines doesn't change anything
/*
glFuncs.initializeOpenGLFunctions();
glFuncs.glUseProgram(0);
*/
// Create a new VBO
glFuncs.glGenBuffers(1, &m_buf);
// Make the new VBO active
glFuncs.glBindBuffer(GL_ARRAY_BUFFER, m_buf);
static const Point points[4] = {
{ { -0.5f, -0.5f, 0.1f }, { 0.0f, 1.0f, 0.0f, 1.0f } },
{ { 0.5f, 0.5f, 0.1f }, { 1.0f, 0.0f, 0.0f, 1.0f } },
{ { 0.5f, 0.5f, 1.0f }, { 0.0f, 0.0f, 1.0f, 1.0f } },
{ { 0.5f, -0.5f, 1.0f }, { 1.0f, 1.0f, 0.0f, 1.0f } }
};
// Upload vertex data to the video device
glFuncs.glBufferData(GL_ARRAY_BUFFER, 4 * sizeof(Point), points, GL_STATIC_DRAW);
dumpGlErrors(__LINE__);
m_bBufInit = true;
}
glPushMatrix();
glLoadIdentity();
glViewport(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glEnable(GL_SCISSOR_TEST);
glScissor(m_rect.x() + 10, m_rect.y() + 10, m_rect.width() - 20, m_rect.height() - 20);
glClearColor(1, 1, 0, 1); // Yellow
glClear(GL_COLOR_BUFFER_BIT);
glFuncs.glBindBuffer(GL_ARRAY_BUFFER, m_buf);
dumpGlErrors(__LINE__);
/*
* "If a non-zero named buffer object is bound to the GL_ARRAY_BUFFER target
* (see glBindBuffer) while a vertex array is
* specified, pointer is treated as a byte offset into the buffer object's data store"
*/
glLineWidth(10);
glColor4f(0.0, 0.0, 1.0, 1);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(Point), 0);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_FLOAT, sizeof(Point), (void*)offsetof(Point, m_color));
dumpGlErrors(__LINE__);
glDrawArrays(GL_LINES, 0, 4); // 4, not 2.
dumpGlErrors(__LINE__);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
// In case somebody else calls glClear()
glClearColor(0, 1, 1, 1); // Cyan
glPopMatrix();
dumpGlErrors(__LINE__);
}
As with all my other efforts, I get a yellow background and my lines appear for what I assume is 1/60 second before the lines disappear and all I have is my yellow background.
Upvotes: 4
Views: 6478
Reputation: 3341
I discovered the answer: I have to tell Qt that I'm using the old fixed function pipeline. Making one function call before doing any drawing does the trick:
QOpenGLFunctions glFuncs(QOpenGLContext::currentContext());
glFuncs.glUseProgram(0);
Upvotes: 4
Reputation: 397
void SquircleRenderer::paint()
{
if (!m_program) {
m_program = new QOpenGLShaderProgram();
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute highp vec4 aVertices;"
"attribute highp vec4 aColors;"
"varying highp vec4 vColors;"
"void main() {"
" gl_Position = aVertices;"
" vColors= aColors;"
"}");
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"varying highp vec4 vColors;"
"void main() {"
" gl_FragColor = vColors;"
"}");
m_program->bindAttributeLocation("aVertices", 0);
m_program->bindAttributeLocation("aColors", 1);
m_program->link();
}
m_program->bind();
m_program->enableAttributeArray(0);
m_program->enableAttributeArray(1);
float vertices[] = {
-1, -1, //Diag bottom left to top right
1, 1,
-1, 1, //Diag top left to bottom right
1, -1,
-1, 0, //Horizontal line
1, 0
};
float colors[] = {
1, 1, 0, 1,
1, 0, 1, 1,
0, 1, 1, 1,
1, 0, 0, 1,
0, 0, 1, 1,
0, 1, 0, 1
};
m_program->setAttributeArray(0, GL_FLOAT, vertices, 2); //3rd to 0, 4th to 1 by default
m_program->setAttributeArray(1, GL_FLOAT, colors, 4);
glViewport(0, 0, m_viewportSize.width(), m_viewportSize.height());
glDisable(GL_DEPTH_TEST);
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
//Here I draw 3 lines, reduce to 2 instead of 6 to draw only one.
//Change second param to choose which line to draw
glDrawArrays(GL_LINES, 0, 6);
m_program->disableAttributeArray(0);
m_program->disableAttributeArray(1);
m_program->release();
}
My conclusion would be to leave the Legacy version of OpenGL in the history book. Even if it's for dummy shaders like those one, it saves you the trouble of managing any matrices and you know what is happening.
PS : The code hasn't been tested, it's just a tweak of the Squircle code. Let me know if there is trouble, but I'd rather continue the troubleshoot session from this piece of code.
Upvotes: 0
Reputation: 6333
With Qt Quick there are three ways you can mix OpenGL:
Alternatively you can build a QWidget application and use OpenGL in a QWidget window and QML in another (QML widget window).
Upvotes: 1