【问题标题】:glGenTextures works but returns the same texture name every single timeglGenTextures 有效,但每次都返回相同的纹理名称
【发布时间】:2025-11-24 12:40:02
【问题描述】:

我有一个名为Texture 的课程。此类负责管理纹理。在程序启动时,OpenGL 上下文被正确初始化(这就是这个问题与大多数涉及意外 glGenTextures 行为的问题不同的原因)。 TextureglGenTextures() 获取纹理名称,然后将图像数据加载并绑定到内存中,该纹理名称在函数 texGLInit() 中。 这行得通,并且纹理完全​​按预期显示。

但是,我还希望我的纹理能够更改它在用户单击按钮并从OpenFileDiaglog 中的 HDD 中选择一个按钮时显示的纹理。用于此的函数称为reloadTexture()。此函数尝试从内存中删除旧的图像/像素数据,并用用户选择的文件中的新数据替换它。 但是,当这种情况发生时,它会使用glDeleteTextures 删除纹理名称,然后分配一个新的纹理名称并使用函数texGLInit() 将新的像素数据加载到内存中。但是纹理名称在 100% 的情况下与之前的名称完全相同(通常是“1”)。

发生这种情况后显示的图像很奇怪。它具有新的图像尺寸,但仍然是旧图像像素。简而言之,它将旧图像扭曲到新图像大小。它仍在使用据称已删除的像素数据进行绘制。应该发生的是屏幕现在在屏幕上显示新的图像文件。我相信这与纹理名称不唯一有关。

代码如下:

Texture::Texture(string filename)//---Constructor loads in the initial image. Works fine!
{
    textureID[0]=0;

    const char* fnPtr = filename.c_str(); //our image loader accepts a ptr to a char, not a string
    //printf(fnPtr);

    lodepng::load_file(buffer, fnPtr);//load the file into a buffer

    unsigned error = lodepng::decode(image,w,h,buffer);//lodepng's decode function will load the pixel data into image vector from the buffer
    //display any errors with the texture
    if(error)
    {
        cout << "\ndecoder error " << error << ": " << lodepng_error_text(error) <<endl;
    }
    //execute the code that'll throw exceptions to do with the images size
    checkPOT(w);
    checkPOT(h);


    //image now contains our pixeldata. All ready for OpenGL to do its thing

    //let's get this texture up in the video memory
    texGLInit();

    Draw_From_Corner = CENTER;  
}

void Texture::reloadTexture(string filename)//Reload texture replaces the texture name and image/pixel data bound to this texture
{
    //first and foremost clear the image and buffer vectors back down to nothing so we can start afresh 
    buffer.clear();
    image.clear();
    w = 0;
    h = 0;
    //also delete the texture name we were using before
    glDeleteTextures(1, &textureID[0]);




    const char* fnPtr = filename.c_str(); //our image loader accepts a ptr to a char, not a string
    //printf(fnPtr);

    lodepng::load_file(buffer, fnPtr);//load the file into a buffer

    unsigned error = lodepng::decode(image,w,h,buffer);//lodepng's decode function will load the pixel data into image vector from the buffer
    //display any errors with the texture
    if(error)
    {
        cout << "\ndecoder error " << error << ": " << lodepng_error_text(error) <<endl;
    }
    //execute the code that'll throw exceptions to do with the images size
    checkPOT(w);
    checkPOT(h);

    //image now contains our pixeldata. All ready for  to do its thing

    //let's get this texture up in the video memoryOpenGL
    texGLInit();

    Draw_From_Corner = CENTER;
}

void Texture::texGLInit()//Actually gets the new texture name loads the pixeldata into openGL
{
    glGenTextures(1, &textureID[0]);
    ////printf("\ntextureID = %u", textureID[0]);
    glBindTexture(GL_TEXTURE_2D, textureID[0]);//evrything we're about to do is about this texture
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    //glDisable(GL_COLOR_MATERIAL);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,w,h,0, GL_RGBA, GL_UNSIGNED_BYTE, &image[0]);
    //we COULD free the image vectors memory right about now. But we'll do it when there's a need to. At the beginning of the reloadtexture func, makes sure it happens when we need it to.



}

这里值得一提的是 Texture 类中的绘制函数。

void Texture::draw(point* origin, ANCHOR drawFrom)
{
    //let us set the DFC enum here.
    Draw_From_Corner = drawFrom;

    glEnable(GL_TEXTURE_2D);
    //printf("\nDrawing texture at (%f, %f)",centerPoint.x, centerPoint.y);
    glBindTexture(GL_TEXTURE_2D, textureID[0]);//bind the texture
    //create a quick vertex array for the primitive we're going to bind the texture to
    ////printf("TexID = %u",textureID[0]);
    GLfloat vArray[8];

#pragma region anchor switch
    switch (Draw_From_Corner)
    {
    case CENTER:

        vArray[0] = origin->x-(w/2); vArray[1] = origin->y-(h/2);//bottom left i0
        vArray[2] = origin->x-(w/2); vArray[3] = origin->y+(h/2);//top left i1
        vArray[4] = origin->x+(w/2); vArray[5] = origin->y+(h/2);//top right i2
        vArray[6] = origin->x+(w/2); vArray[7] = origin->y-(h/2);//bottom right i3
        break;

    case BOTTOMLEFT:

        vArray[0] = origin->x; vArray[1] = origin->y;//bottom left i0
        vArray[2] = origin->x; vArray[3] = origin->y+h;//top left i1
        vArray[4] = origin->x+w; vArray[5] = origin->y+h;//top right i2
        vArray[6] = origin->x+w; vArray[7] = origin->y;//bottom right i3

        break;

    case TOPLEFT:

        vArray[0] = origin->x; vArray[1] = origin->y-h;//bottom left i0
        vArray[2] = origin->x; vArray[3] = origin->y;//top left i1
        vArray[4] = origin->x+w; vArray[5] = origin->y;//top right i2
        vArray[6] = origin->x+w; vArray[7] = origin->y-h;//bottom right i3

        break;

    case TOPRIGHT:

        vArray[0] = origin->x-w; vArray[1] = origin->y-h;//bottom left i0
        vArray[2] = origin->x-w; vArray[3] = origin->y;//top left i1
        vArray[4] = origin->x; vArray[5] = origin->y;//top right i2
        vArray[6] = origin->x; vArray[7] = origin->y-h;//bottom right i3

        break;

    case BOTTOMRIGHT:

        vArray[0] = origin->x-w; vArray[1] = origin->y;//bottom left i0
        vArray[2] = origin->x-w; vArray[3] = origin->y+h;//top left i1
        vArray[4] = origin->x-h; vArray[5] = origin->y;//top right i2
        vArray[6] = origin->x; vArray[7] = origin->y;//bottom right i3

        break;

    default: //same as center

        vArray[0] = origin->x-(w/2); vArray[1] = origin->y-(h/2);//bottom left i0
        vArray[2] = origin->x-(w/2); vArray[3] = origin->y+(h/2);//top left i1
        vArray[4] = origin->x+(w/2); vArray[5] = origin->y+(h/2);//top right i2
        vArray[6] = origin->x+(w/2); vArray[7] = origin->y-(h/2);//bottom right i3

        break;


    }
#pragma  endregion          

    //create a quick texture array (we COULD create this on the heap rather than creating/destoying every cycle)
    GLfloat tArray[8] = 
    {
        //this has been tinkered with from my normal order. I think LodePNG is bringing the PD upside down. SO A QUICK FIX HERE WAS NECESSARY.
        0.0f,1.0f,//0
        0.0f,0.0f,//1
        1.0f,0.0f,//2
        1.0f,1.0f//3
    };

    //and finally.. the index array...remember, we draw in triangles....(and we'll go CW)
    GLubyte iArray[6] =
    {
        0,1,2,
        0,2,3
    };

    //Activate arrays
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    //Give openGL a pointer to our vArray and tArray
    glVertexPointer(2, GL_FLOAT, 0, &vArray[0]);
    glTexCoordPointer(2, GL_FLOAT, 0, &tArray[0]);

    //Draw it all
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, &iArray[0]);

    //glDrawArrays(GL_TRIANGLES,0,6);

    //Disable the vertex arrays
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisable(GL_TEXTURE_2D);
}

谁能告诉我为什么 OpenGL 不会从我加载到其中的新像素数据加载和绘图?就像我说的,我怀疑这与 glGenTextures 没有给我一个新的纹理名称有关。

【问题讨论】:

  • 我看不出 glGenTextures 给你旧的纹理名称有什么问题。 documentation for glDeleteTextures 明确表示“纹理被删除后,它没有内容或维度,并且它的名称可以免费重复使用(例如通过 glGenTextures)”。
  • 我不明白为什么 textureID 是一个数组。嗯,是的,它有效……但是……为什么?一个简单的变量有什么问题?
  • @datenwolf 我曾经使用简单的 GLUint,当我看到其他人在示例中使用它们时,我尝试使用数组代替。毕竟,文档确实说第二个参数指定了一个数组。我只是还没有切换回来:]
  • @dfan 我可能应该提到。我还尝试过从构造中创建一个全新的纹理,即不使用myTex-&gt;reloadTexture(filename.png),而是直接使用myTex = new Texture(filename.png)。这不会在任何时候调用 glDeleteTextures。仍然我得到了相同的纹理名称 (1)。
  • @GuyJoelMcLean:就 C 而言,单个变量可以视为 0 维数组,即只有一个元素的数组。

标签: c++ opengl


【解决方案1】:

您正在调用glTexImage2D 并将指针传递给客户端内存。当心,文档说:

如果在指定纹理图像时将非零命名缓冲区对象绑定到GL_PIXEL_UNPACK_BUFFER 目标(请参阅glBindBuffer),则data 将被视为缓冲区对象数据存储中的字节偏移量。

为了安全起见,您可能希望调用glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0) 取消绑定任何缓冲区对象。

【讨论】:

  • 您是否建议在尝试重新绑定之前执行此操作?
  • @Guy:不,我会在 glTexImage2D(..., &amp;image[0]); 之前使用该调用,以确保 OpenGL 不会在某些绑定缓冲区中查找像素数据,而不是从 image 读取数组。
  • 我终于搞定了一些工作。但是,如上使用的 glBindBuffer 会导致崩溃。 An unhandled exception of type 'System.AccessViolationException' occurred in Spritey.exe Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt. 我很确定这次我不会取消分配任何使用内存的对象。
  • @Guy:啊,那么您有一些使用像素缓冲区的代码和一些使用客户端内存中的数组的代码。您需要致电glBindBuffer 来根据需要来回切换。在您的代码中查找其他 glBindBuffer 调用。
  • 我只有你建议的那个。我必须包含 glew 库并创建 .lib 文件的路径,只是为了使 Visual Studio 链接到它。你知道 openGL 中的其他调用与 glBindBuffer 做类似的事情吗?
最近更新 更多