OpenGL ES glTexImage2D 优化

OpenGL ES glTexImage2D optimization

我的任务是展示几张图片。我将其实现为 class 以创建多个实例。每个实例代表一张图片。它编译着色器,设置两个三角形并在构造函数中加载图片数据。主程序创建实例然后循环切换prigramid并为每个实例调用render()方法。

while(true)
    for (uint g = 0; g < pictures.size(); g++){
        glUseProgram(pictures[g]->ProgramId);
        pictures[g]->render();
    }

效果不错,能显示图片,但我不喜欢。它可以做得更好。

这是class

的部分代码
Picture::Picture(picPosition* pPosition, const char * fileName)
:BasePicture(pPosition)
{
    pos = glGetAttribLocation(ProgramId, "position");
    uv = glGetAttribLocation(ProgramId, "texture_vert");

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glGenBuffers(1, &uvbuffer);
    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);

    int n;
    textureData = stbi_load(fileName, &picWidth, &picHeight, &n, STBI_rgb_alpha);
    TextureID  = glGetUniformLocation(ProgramId, "myTextureSampler");
    glBindTexture(GL_TEXTURE_2D, TextureID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glActiveTexture(GL_TEXTURE0);

    glDepthMask(GL_FALSE);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    //calculating the vertex matrix using MVP calculated in parent class
    for (int i = 0; i < 6; i++)
        ModeledVerts.push_back(MVP * verts[i]);
    v = glm::value_ptr(ModeledVerts[0]);
}

Picture::~Picture()
{
    stbi_image_free(textureData);
    glDeleteBuffers(1, &vbo);
    glDeleteBuffers(1, &uvbuffer);
}

void Picture::render()
{
    glBufferData(GL_ARRAY_BUFFER, 96, v, GL_STATIC_DRAW);
    glVertexAttribPointer(pos, 4, GL_FLOAT, GL_FALSE, 0, (GLvoid*) 0);
    glEnableVertexAttribArray(pos);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);

    glBufferData(GL_ARRAY_BUFFER, sizeof(verticesUV), verticesUV, GL_STATIC_DRAW);
    glVertexAttribPointer(uv, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*) 0);
    glEnableVertexAttribArray(uv);
    glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, picWidth, picHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData);

    glDrawArrays(GL_TRIANGLES, 0, 6);
}

我玩了很多代码,使 render() 函数尽可能轻,但我不能让它比现在更轻。

最大的问题是每次都发送textureData(glTexImage2D)。数据永远不会改变。我试图将它移动到构造函数,但在这种情况下,所有图片对象都显示最新加载的相同图片。看起来有一个实例覆盖了之前上传的纹理数据。我正在寻找一种在构造函数中加载图片数据而不是每次渲染时加载图片数据的方法。看起来 OpenGL API 中有一些东西,但我还不知道。

另一个改进可能是从 render() 获取顶点数据设置。该数据永远不会改变。但它并不像 glTexImage2D 在 render() 中调用那么重要。

你能告诉我 OpenGL API 来分离着色器的数据吗?或者告诉我我做错了什么...

您说:

It works well and shows the pictures but I do not like it. It could be done much better.

从设计角度来看,我认为这可能对您有所帮助。

将打开、读取和解析纹理数据图像文件的功能与实际纹理结构或 class 分开。这将是一个示例伪代码:

struct Texture {
    unsigned int width;
    unsigned int height;
    bool         hasTransparency; 

    GLint id; // ID that is used by OpenGL to setActive, bind, and pass to shaders as either a uniform or sampler2D.
    std::string filenameAndPath; // Keep this filename associated with texture so you can prevent trying to reload the same file over and over.
    GLuchar*  data; // the actual color - pixel texture data. 
}

// This function will handle the opening and reading in of the texture data
// it would return back the ID value generated by OpenGL which will also be
// stored into the texture struct. The texture struct is returned by reference so that it can be populated with data.
GLuint loadTexture( const char* filenameAndPath, Texture& texture, /*loading parameters & flags*/ ) {
    // Check to see if file exists or is already loaded
    if ( fileanameAndPath already exists ) {
        // get the existing ID from the already loaded texture
        // and just return that. 
    } else {
        // Try to open the new file for reading.

        // parse the data for general purposes that will support
        // your application. You can simply use `stbi_load` as it is a fairly
        // decent third party library.

        // Check the internal formatting of how the data is stored
        // Compression, pixel orientation etc.

        // configure the data to your needs (color format),
        // (flipping the pixels either horizontally, vertically or both),

        // now copy the actual pixel data into your buffer.

        // close the file handle

        // save all the information into your struct

        // return the ID value that was generated by OpenGL
    }
}

render loop 之前的主引擎代码中,您需要从文件加载纹理,然后可以在需要的地方使用此纹理对象。最后在您的渲染循环中,您可能希望将纹理设置为活动并将它们绑定到渲染目标并将它们传递给您的着色器。在某些情况下,您可能希望将它们设置为活动状态并在 render loop 之前绑定它们,具体取决于您正在实施的 shader-technique 类型。

回答我自己的问题。 解决方案是使用 atlas 地图。软件生成包含所有图片的图集,上传一次(glTexImage2D)并为每张图片使用坐标。这极大地提高了性能,因为 glTexImage2D 只被调用了一次。