OpenGL和QtQuick纹理问题(OpenGL and QtQuick Texture Problems)

我正在基于Qt附带的“openglunderqml”示例在C ++中开发一个简单的QQuickItem实现。 我做了一些修改,使用不同的着色器和两个纹理加载。想法是着色器将在两个纹理之间交叉渐变(这基本上只是我加载到纹理中的图像)。

当我将这个QQuickItem单独放在QML文件中并运行它时,一切正常。 图像之间相互交叉淡入淡出(我设置了一个属性动画来保持交叉渐变),一切都很好。 但是,如果我放置其他元素(如文本),则文本无法正确呈现 - 只是形状奇特的块。 如果我把图像放入,事情变得非常奇怪。 而不是QQuickItem渲染它应该呈现的框,它呈现全屏和颠倒。 据我所知,其他图像永远不会被加载。

我想我一定不会做我应该做的事,但我不知道是什么。 请注意,第一个代码块包含着色器和渲染内容,第二个代码块包含函数loadNewTexture(),它将新图像加载到纹理中(每个纹理只调用一次 - 而不是每次渲染),第三个包含QtQuick .qml文件。

继承了opengl代码(在QQuckItem :: Paint方法中):

// 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()函数:

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文件:

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 } }

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 } }

最满意答案

我最近做了几乎相同的练习(并没有实际运行你的代码,只处理了一个纹理)我想我可能知道你错过了什么:你必须确保你的OpenGL状态机留在最后你的绘画功能 (或多或少) 与你在开始时发现的完全一样 。 您确实发布了着色器程序并禁用了数组属性,但没有取消绑定两个纹理单元中的两个纹理。 在paint成员函数结束时的下列内容应该可以解决问题:

glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0);

除此之外,还有两条评论:

请注意,QML文件中的image1图像将完全隐藏您的ImageCrossfader项目(如果我没有错误地解释anchors属性)。 您添加到QML场景中的所有内容都将绘制您的opengl底层上 (因此名称;))。

您可以安全地删除所有glEnable()glDisable()glBlendFunc()调用。 实际上删除它们应该会使您的代码更安全,因为您更改的次数越少,您需要记住的更改就越少。

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.

更多推荐