OpenGL and QtQuick texture issues
I am developing a simple QQuickItem implementation in C ++ based on the "openglunderqml" example that ships with Qt. I made some changes to use different shaders and two textures that I load. The idea is that the shaders will overlap between the two textures (which are essentially just images that I have loaded into textures).
When I put this QQuickItem on my own in a QML file and run it, everything works fine. The images are crossfading with each other (I tweaked the properties to be crossfaded) and everything looks great. However, if I place other elements such as text, the text does not display as expected - only small, odd-shaped blocks. If I place an image, things get really weird. Instead of rendering the QQuickItem it should display, it displays full screen and upside down. As far as I can tell, the other image is never loaded.
I think I must be not doing what I should be, but I have no idea what. Note that the first block of code contains shaders and render objects, the second contains the loadNewTexture () function that loads a new image into the texture (called only once for each texture, not for each render), and the third contains the QtQuick.qml file.
Here is the opengl code (in the QQuckItem :: Paint method):
// Builds the OpenGL shaders that handle the crossfade
if (!m_program) {
m_program = new QOpenGLShaderProgram();
// Shader loads coordinate positions
m_program->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute vec2 position;"
"varying vec2 texcoord;"
"void main() {"
" gl_Position = vec4(position, 0.0, 1.0);"
" texcoord = position * vec2(0.5) + vec2(0.5);"
"}");
// Shader does the crossfade
m_program->addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform lowp float xfade;"
"uniform sampler2D textures[2];"
"varying vec2 texcoord;"
"void main() {"
" gl_FragColor = mix("
" texture2D(textures[0], texcoord),"
" texture2D(textures[1], texcoord),"
" xfade"
" );"
"}");
m_program->bindAttributeLocation("vertices", 0);
m_program->link();
connect(window()->openglContext(), SIGNAL(aboutToBeDestroyed()),
this, SLOT(cleanup()), Qt::DirectConnection);
}
m_program->bind();
// Loads corner vertices as triangle strip
m_program->enableAttributeArray(0);
float values[] = {
-1, -1,
1, -1,
-1, 1,
1, 1
};
m_program->setAttributeArray(0, GL_FLOAT, values, 2);
// Loads the fade value
m_program->setUniformValue("xfade", (float) m_thread_xfade);
glEnable(GL_TEXTURE_2D);
// Check if a new texture needs to be loaded
if (!new_source_loaded && !m_adSource.isEmpty())
new_source_loaded = loadNewTexture(m_adSource);
// Loads texture 0 into the shader
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures[0]);
m_program->setUniformValue("textures[0]", 0);
// Loads texture 1 into the shader
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, textures[1]);
m_program->setUniformValue("textures[1]", 1);
// Sets the OpenGL render area to the space given to this components
glViewport((GLint) this->x(), (GLint) this->y(), (GLint) this->width(), (GLint) this->height());
// Sets some parameters
glDisable(GL_DEPTH_TEST);
// Sets the clear color (backround color) to black
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
// Draws triangle strip
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
// Cleans up vertices
m_program->disableAttributeArray(0);
m_program->release();
LoadNewTexture () function:
bool AdRotator::loadNewTexture(QUrl source) {
// Load the image from source url
QImage image(source.path());
// Check that the image was loaded properly
if (image.isNull()) {
qDebug() << QString("AdRotator::loadTexture: Loading image from source: ") << source.toString() << QString(" failed.");
return false;
}
// Update this as the active texture
active_texture = !active_texture;
// Convert into GL-friendly format
QImage GL_formatted_image = QGLWidget::convertToGLFormat(image);
// Check that the image was converted properly
if (image.isNull()) {
qDebug() << QString("AdRotator::loadTexture: Converting image from source: ") << source.toString() << QString(" failed.");
return false;
}
// Generate the texture base
glGenTextures(1, &textures[active_texture]);
glBindTexture(GL_TEXTURE_2D, textures[active_texture]);
// Give texture parameters (scaling and edging options)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// Load pixels from image into texture
glTexImage2D(
GL_TEXTURE_2D, 0, /* target, level of detail */
GL_RGBA, /* internal format */
GL_formatted_image.width(), GL_formatted_image.height(), 0, /* width, height, border */
GL_RGBA, GL_UNSIGNED_BYTE, /* external format, type */
GL_formatted_image.bits() /* pixels */
);
if (textures[active_texture] == 0) qDebug() << QString("New Texture post-load failed.");
return true;
}
Qml file:
import QtQuick 2.0
Item {
width: 1920
height: 1080
/* Image{} element breaks things
Image {
id: image1
x: 0
y: 0
anchors.rightMargin: 0
anchors.bottomMargin: 0
anchors.leftMargin: 0
anchors.topMargin: 0
sourceSize.height: 1080
sourceSize.width: 1920
anchors.fill: parent
fillMode: Image.PreserveAspectCrop
source: "images/background.png"
}*/
/* The QQuickItem */
ImageCrossfader {
x: 753
y: 107
width: 1150
height: 865
}
}
source to share
I recently did almost the same exercise and (without executing my code and only processing one texture). I think I have an idea of what you missed: you have to make sure that your final OpenGL autoserver stays at the end of your draw function (more or less) exactly as you found it at the beginning . You released your shader program and turned off the array attribute, but you did not cancel two textures in two texture units. The following at the end of your drawing element function should do the trick:
glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);
In addition, two more comments:
-
Note that your image image1 from the QML file will hide the ImageCrossfader element entirely (if I'm not mistaken when interpreting the anchors property). Anything you add to your QML scene will be drawn over your opengl lining (hence the name;)).
-
You can safely remove all calls to glEnable (), glDisable (), and glBlendFunc (). In fact, removing them should make your code safer, because the less you change, the fewer changes you have to remember to come back.
source to share