多次调用glGetTexImage时发生缓冲区溢出错误

Buffer overflow error when calling glGetTexImage multiple times

本文关键字:缓冲区 溢出 错误 调用 glGetTexImage      更新时间:2023-10-16

我正在创建一个类来使用OpenGL管理图像。为了加载图像,我使用SOIL加载PNG。因为我需要检查碰撞,所以我想得到透明的像素。所以我尝试使用:glGetTexImage 获取数据

构造函数加载图像:

Image::Image(string imagePath, unsigned int flags){
    this->imagePath=imagePath;
    this->pxData=NULL;
    texture=SOIL_load_OGL_texture
    (
     imagePath.c_str(),
     SOIL_LOAD_AUTO,
     SOIL_CREATE_NEW_ID,
     flags
    );
    //Use that to subdivide in sprites the texture
    glGetTexLevelParameteriv(GL_TEXTURE_2D, texture, GL_TEXTURE_WIDTH, &textureWidth);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, texture, GL_TEXTURE_HEIGHT, &textureHeight);
    glBindTexture(GL_TEXTURE_2D, this->texture);
    //cout << "Real Dimensions - " << textureWidth << "-" << textureHeight;
    loadPixels(); //multiple instances of image generates error here
}

LoadPixels在我的结构中加载像素数据。

typedef struct PixelData{
    unsigned char r;
    unsigned char g;
    unsigned char b;
    unsigned char a;
} PixelData;
PixelData* pxData;

然后在loadPixels中我存储数据:

void Image::loadPixels(){
    pxData = new PixelData[textureHeight*textureWidth];
    glGetTexImage(GL_TEXTURE_2D, 1, GL_RGBA, GL_UNSIGNED_BYTE , pxData);
}

问题是,如果我创建一个图像:

Image img("/Developer/img.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);

它运行良好。

如果我创建了很多图像:

    Image img("/Developer/img.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img2("/Developer/img2.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img3("/Developer/img3.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img4("/Developer/img4.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img5("/Developer/img5.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img6("/Developer/img6.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img7("/Developer/img7.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img8("/Developer/img8.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img9("/Developer/img9.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);
    Image img10("/Developer/img10.png", SOIL_FLAG_MIPMAPS | SOIL_FLAG_INVERT_Y | SOIL_FLAG_NTSC_SAFE_RGB | SOIL_FLAG_COMPRESS_TO_DXT);

然后我的程序崩溃:

错误为:

OpenGL Game Engine(35463,0x7fff7b121310) malloc: *** error for object 0x104017808: incorrect checksum for freed object - object was probably modified after being freed.
*** set a breakpoint in malloc_error_break to debug

如果我注释glGetTexImage(GL_TEXTURE_2D,1,GL_RGBA,GL_UNSIGNED_BYTE,pxData);它运行良好,所以我想问题不在于分配。

我只需要得到像素数据来存储它,我想用这些像素创建一些数据,所以我需要得到纹理的像素。

注:

如果我取消对loadPixels的注释,程序将在带有sigabort的imagePath.c_str()处停止,如果我对其进行注释,它将加载所有纹理

多亏了Audrey,我得到了更多的信息。

glGetTexLevelParameteriv(GL_TEXTURE_2D, this->texture, GL_TEXTURE_WIDTH, &textureWidth);
glGetTexLevelParameteriv(GL_TEXTURE_2D, this->texture, GL_TEXTURE_HEIGHT, &textureHeight);
Real Dimensions - 128-128
Real Dimensions - 64-64
Real Dimensions - 32-32
Real Dimensions - 16-16

这是同一张照片,但尺寸越来越小。

我检查了id,它每次都会创建一个新的id,那么为什么尺寸会变得一团糟呢?

有没有其他方法可以获得这些数据,我在这里缺少了什么?另一个问题是,这个方法获取绑定纹理,有没有办法使用纹理id获取纹理数据?

根据评论中的讨论,getGlTexImage似乎溢出了pxData缓冲区。如果你发布给你textureWidthtextureHeight的函数的输出是什么,我们可能会找出问题所在。(例如,您确定getter函数的返回类型与变量textureWidth等之间的兼容性吗?)

编辑:我会检查你的<Gl.h>,看看函数glGetTexLevelParametersiv的签名。我在网上看了一下,微软WINAPI版本的签名与OpenGL.org上的签名不同,两者都不同意你使用的签名。

尝试重新排序并重写为:

glBindTexture(GL_TEXTURE_2D, this->texture);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &textureWidth);
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &textureHeight);
glGetTexLevelParameteriv(GL_TEXTURE_2D, texture, GL_TEXTURE_HEIGHT, &textureHeight);

第二个参数不采用纹理ID。它是一个mipmap级别。您需要首先绑定纹理,然后在放置纹理的位置使用0调用此函数。

具体来说,你的纹理ID几乎肯定会被实例化为"0"、"1"、"2"等。每次你用连续值调用glGetTexLevelParameteriv时,你都会要求在当前绑定的纹理上有一个较小的mipmap级别。您的第一个调用没有失败,这让我怀疑SOIL_load_OGL_texture实际上并没有在返回之前解除绑定。

请记住,几乎没有OpenGL函数将纹理、缓冲区或着色器ID作为参数,而是作用于当前绑定的对象。通常与实际ID一起使用的唯一函数是创建、销毁或绑定对象的函数,例如glGenTexturesglDeleteTexturesglBindTexture