Home > Net >  How to switch texture display quickly in opengl
How to switch texture display quickly in opengl

Time:12-03

I use opengl in qt. I have several pictures with a size of 30000 * 20000, including single channel, three channel and four channel pictures. The function I want to achieve is to switch and display different pictures through the keyboard, and support moving and zooming. I have implemented this function now, but I encountered some problems. I want to bind them to different texture units and switch them when painting. But it seems to have no effect.

void MyOpenGLWidget::initializeGL()
{
    initializeOpenGLFunctions();        
    glGenVertexArrays(2, VAO);
    glGenVertexArrays(2, VBO);
    glEnable(GL_DEPTH_TEST);
    {
        if (!bindShaderProgram(m_shaderProgram, vertexSourceBG, fragmentSourceBG))
            return;
        glBindVertexArray(VAO[0]);
        glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
        glPixelStorei(GL_PACK_ALIGNMENT, 1);
        glBufferData(GL_ARRAY_BUFFER, m_vBGVertices.size() * sizeof(GLfloat), m_vBGVertices.data(), GL_STREAM_DRAW);

        readImage();
        glGenTextures(IMAGE_NUM, texture);
        for (int i = 0; i < IMAGE_NUM; i  ) {
            glActiveTexture(GL_TEXTURE0 i);
            glBindTexture(GL_TEXTURE_2D, texture[i]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            int channel = m_vImage[m_imageIdx].channels();
            int internalFormat = GL_RGB, format = GL_RGB;
            getImageFormat(channel, internalFormat, format);
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_imageWidth, m_imageHeight, 0, format, GL_UNSIGNED_BYTE, m_vImage[m_imageIdx].data);
            glBindTexture(GL_TEXTURE_2D, texture[i]);
        }
        m_shaderProgram.setAttributeBuffer("aPos", GL_FLOAT, 0, 3, sizeof(GLfloat) * 5);//8
        m_shaderProgram.enableAttributeArray("aPos");
        m_shaderProgram.setAttributeBuffer("aTexCoord", GL_FLOAT, sizeof(GLfloat) * 3, 2, sizeof(GLfloat) * 5);
        m_shaderProgram.enableAttributeArray("aTexCoord");
        m_shaderProgram.release();

        glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
        glBindVertexArray(VAO[0]);
    }
}
void MyOpenGLWidget::paintGL()
{
    glClearColor(0.1f, 0.5f, 0.7f, 1.0f);  
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);       
    {
        glBindVertexArray(VAO[0]);

        m_shaderProgram.bind();                    
        createCameraTransformMatrix();
        m_shaderProgram.setUniformValue("model", m_mModel);
        m_shaderProgram.setUniformValue("view", m_mView);
        m_shaderProgram.setUniformValue("projection", m_mProjection);

        glActiveTexture(GL_TEXTURE0 m_imageIdx); 
        glBindTexture(GL_TEXTURE_2D, texture[m_imageIdx]);
        m_shaderProgram.setUniformValue("ourTexture", GL_TEXTURE0 m_imageIdx);
        m_shaderProgram.setUniformValue("imgWidth", m_imageWidth);
        m_shaderProgram.setUniformValue("imgHeight", m_imageHeight);
        glDrawArrays(GL_POLYGON, 0, 4);     
        m_shaderProgram.release();
        glBindTexture(GL_TEXTURE_2D, texture[m_imageIdx]);
        glBindVertexArray(VAO[0]);
    }
}

Switch the currently displayed texture by switching m_imageIdx. But no response. I bound them to the same texture unit, and then loaded them when refreshing. Now they can be loaded, but the speed is too slow to accept.

void MyOpenGLWidget::paintGL()
{
    glClearColor(0.1f, 0.5f, 0.7f, 1.0f); 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    {
        glBindVertexArray(VAO[0]);

        m_shaderProgram.bind();                    
        createCameraTransformMatrix();
        m_shaderProgram.setUniformValue("model", m_mModel);
        m_shaderProgram.setUniformValue("view", m_mView);
        m_shaderProgram.setUniformValue("projection", m_mProjection);

        glActiveTexture(GL_TEXTURE0); 
        glBindTexture(GL_TEXTURE_2D, texture[m_imageIdx]);
        updateImage();
        if (!m_vImage[m_imageIdx].empty()) {
            int channel = m_vImage[m_imageIdx].channels();
            int internalFormat = GL_RGB, format = GL_RGB;
            getImageFormat(channel, internalFormat, format);

            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_imageWidth, m_imageHeight, 0, format, GL_UNSIGNED_BYTE, m_vImage[m_imageIdx].data);
        }
        m_shaderProgram.setUniformValue("imgWidth", m_imageWidth);
        m_shaderProgram.setUniformValue("imgHeight", m_imageHeight);
        glDrawArrays(GL_POLYGON, 0, 4);
        m_shaderProgram.release();
        glBindTexture(GL_TEXTURE_2D, texture[m_imageIdx]);
        glBindVertexArray(VAO[0]);
    }

I referred to some similar problems and tried glTexSubImage2D, but I needed to refresh the whole picture, so the efficiency was not improved. I can't reduce the texture precision because the texture display needs high precision. I hope someone can give me some advice.

Available code snippets.

//init
for (int i = 0; i < IMAGE_NUM; i  ) {
            glActiveTexture(GL_TEXTURE0   i);
            glBindTexture(GL_TEXTURE_2D, texture[i]);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            int channel = m_vImage[m_imageIdx].channels();
            int internalFormat = GL_RGB, format = GL_RGB;
            getImageFormat(channel, internalFormat, format);
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
            glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_imageWidth, m_imageHeight, 0, format, GL_UNSIGNED_BYTE, m_vImage[i].data);
            glBindTexture(GL_TEXTURE_2D, texture[i]);
        }
//paintGL
glBindTexture(GL_TEXTURE_2D, texture[m_imageIdx]);
m_shaderProgram.setUniformValue("ourTexture", m_imageIdx);
m_shaderProgram.setUniformValue("imgWidth", m_imageWidth);
m_shaderProgram.setUniformValue("imgHeight", m_imageHeight);
glDrawArrays(GL_POLYGON, 0, 4); 
m_shaderProgram.release();
glBindTexture(GL_TEXTURE_2D, texture[m_imageIdx]);

CodePudding user response:

You definitely do not need to upload them every frame. What you did forget was to set the correct value for sampler2D uniform variable in the fragment shader (which you did not post).

m_shaderProgram.setUniformValue("ourTexture", m_imageIdx); must be called in the draw loop

  • with correctly bound shader
  • Do not use GL_TEXTURE0 here, as pointed by @HolyBlackCat . By default, it uses data from GL_TEXTURE0. You can imagine GL_TEXTURE as an array of pointers to the actual textures. glActiveTexture sets which pointer is used for the subsequent GL calls and glBindTexture assigns to this active pointer.

The uniform sampler acts exactly the same, the index you assign to it is the array element it will work with.

This also means that you need to just bind all textures to separate units only once, e.g. how you do (needlessly) during initialization and then vary only the uniform sampler, no binds, not activates needing.

  • Related