Reputation: 280
I'm developing a simple QQuickItem implementation in C++ based on the "openglunderqml" example that came with Qt. I made some modifications to use different shaders and two textures that I load in. The idea is that the shaders will crossfade between the two textures (which are essentially just images I have loaded into the textures).
When I put this QQuickItem alone inside a QML file and run it, everything works fine. The images crossfade between each other (I've setup a property animation to keep them crossfading) and everything appears fine. However if I put other elements such as text, the text doesn't render properly -- just little oddly shaped blocks. If I put an image in, things get really weird. Instead of the QQuickItem rendering the the box that its supposed to render in, it renders full screen and upside down. As far as I can tell the other image is never loaded.
I think I must be not doing something that I should be, but I've no idea what. Note that the first code block contains the shaders and rendering stuff, the second contains the function loadNewTexture() which loads a new image into a texture (only called once per texture -- not every rendering) and the third contains the QtQuick .qml file.
Heres the opengl code (within 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();
The 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;
}
The .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
}
}
Upvotes: 2
Views: 4042
Reputation: 1119
I recently did pretty much the same exercise and (not having actually ran your code and only having dealt with one texture) I think I might have an idea what you missed: You have to make sure your OpenGL state machine is left at the end of your paint function (more or less) exactly as you found it at the beginning. You did release the shader program and disabled the array attribute but did not unbind the two textures in your two texture units. The following at the end of your paint member function should do the trick:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
Other than that, two further comments:
Please note that your image1 Image from the QML file would hide your ImageCrossfader item completely (if I am not mistaken interpreting the anchors property). Everything you add to your QML scene will get painted over your opengl underlay (hence the name ;)).
You can safely remove all glEnable(), glDisable() and glBlendFunc() calls. Actually removing them should make your code safer, because the less you change, the less changes you have to remember to revert.
Upvotes: 3