C++ OpenGL glTexImage2D 访问冲突

Posted

技术标签:

【中文标题】C++ OpenGL glTexImage2D 访问冲突【英文标题】:C++ OpenGL glTexImage2D Access Violation 【发布时间】:2012-03-30 21:17:43 【问题描述】:

我正在使用 OpenGL(freeglut 和 glew)编写应用程序。

我还想要纹理,所以我对位图文件格式进行了一些研究,并为主标头编写了一个结构,为 DIB 标头(信息标头)编写了另一个结构。

然后我开始编写加载程序。它会自动将纹理绑定到 OpenGL。这是函数:

static unsigned int ReadInteger(FILE *fp)

    int a, b, c, d;

    // Integer is 4 bytes long.
    a = getc(fp);  
    b = getc(fp);  
    c = getc(fp);  
    d = getc(fp);

    // Convert the 4 bytes to an integer.
    return ((unsigned int) a) + (((unsigned int) b) << 8) +
           (((unsigned int) c) << 16) + (((unsigned int) d) << 24);


static unsigned int ReadShort(FILE *fp)

    int a, b;

    // Short is 2 bytes long.
    a = getc(fp);  
    b = getc(fp);

    // Convert the 2 bytes to a short (int16).
    return ((unsigned int) a) + (((unsigned int) b) << 8);


    GLuint LoadBMP(const char* filename)

    FILE* file;

    // Check if a file name was provided.
    if (!filename)
        return 0;

    // Try to open file.
    fopen_s(&file, filename, "rb");

    // Return if the file could not be open.
    if (!file)
    
        cout << "Warning: Could not find texture '" << filename << "'." << endl;
        return 0;
    

    // Read signature.
    unsigned char signature[2];
    fread(&signature, 2, 1, file);

    // Use signature to identify a valid bitmap.
    if (signature[0] != BMPSignature[0] || signature[1] != BMPSignature[1])
    
        fclose(file);
        return 0;
    

    // Read width and height.
    unsigned long width, height;
    fseek(file, 16, SEEK_CUR); // After the signature we have 16bytes until the width.
    width = ReadInteger(file);
    height = ReadInteger(file);

    // Calculate data size (we'll only support 24bpp).
    unsigned long dataSize;
    dataSize = width * height * 3;

    // Make sure planes is 1.
    if (ReadShort(file) != 1)
    
        cout << "Error: Could not load texture '" << filename << "' (planes is not 1)." << endl;
        return 0;
    

    // Make sure bpp is 24.
    if (ReadShort(file) != 24)
    
        cout << "Error: Could not load texture '" << filename << "' (bits per pixel is not 24)." << endl;
        return 0;
    

    // Move pointer to beggining of data. (after the bpp we have 24 bytes until the data)
    fseek(file, 24, SEEK_CUR);

    // Allocate memory and read the image data.
    unsigned char* data = new unsigned char[dataSize];

    if (!data)
    
        fclose(file);
        cout << "Warning: Could not allocate memory to store data of '" << filename << "'." << endl;
        return 0;
    

    fread(data, dataSize, 1, file);

    if (data == NULL)
    
        fclose(file);
        cout << "Warning: Could no load data from '" << filename << "'." << endl;
        return 0;
    

    // Close the file.
    fclose(file);

    // Create the texture.
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    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_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); //NEAREST);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 

    gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, data);

    return texture;

我知道位图的数据被正确读取,因为我将它的数据输出到控制台并与在绘画中打开的图像进行比较。

这里的问题是这一行:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dibheader.width,
    dibheader.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

大多数时候我运行应用程序时,这条线会因错误而崩溃:

GunsGL.exe 中 0x008ffee9 处未处理的异常:0xC0000005:访问冲突读取位置 0x00af7002。

这是错误发生位置的反汇编:

movzx       ebx,byte ptr [esi+2]

这不是我的加载器的错误,因为我已经下载了其他加载器。 我使用的下载加载程序是来自 NeHe 的 this one。

编辑:(以上代码更新)

我重写了加载程序,但仍然在同一行发生崩溃。有时我会在 mlock.c 上崩溃而不是崩溃(我没记错):

void __cdecl _lock (
        int locknum
        )


        /*
         * Create/open the lock, if necessary
         */
        if ( _locktable[locknum].lock == NULL ) 

            if ( !_mtinitlocknum(locknum) )
                _amsg_exit( _RT_LOCK );
        

        /*
         * Enter the critical section.
         */

        EnterCriticalSection( _locktable[locknum].lock );

上线:

EnterCriticalSection( _locktable[locknum].lock );

另外,这是应用程序不会崩溃的其中一个时间的屏幕截图(纹理显然不正确): http://i.stack.imgur.com/4Mtso.jpg

编辑2:

用新的工作代码更新了代码。 (标记为答案的回复不包含此操作所需的所有内容,但它很重要)

【问题讨论】:

那个内存位置似乎不是一个指示未初始化指针的位置...我的下一个猜测是 OpenGL 正试图读取超出data 的范围。您能否验证您指定的要读取的数据量是否正确? 这看起来更像 C 而不是 C++ @ktodisco 数据大小其实是正确的。 @Pubby 也许,我真的不知道它们之间的区别,我只是编码。 xD 【参考方案1】:

在拨打glTexImage2D() 之前尝试glPixelStorei(GL_UNPACK_ALIGNMENT, 1)

【讨论】:

我忘了说我已经试过了。图像为 24bpp 512x512。它因同样的错误而崩溃(只是再次尝试)。【参考方案2】:

我知道,像这样读取二进制数据很诱人

BitmapHeader header;
BitmapInfoHeader dibheader;
/*...*/
// Read header.
fread(&header, sizeof(BitmapHeader), 1, file);

// Read info header.
fread(&dibheader, sizeof(BitmapInfoHeader), 1, file);

但你真的不应该那样做。为什么?因为结构的内存布局可能会被填充以满足对齐约束(是的,我知道打包编译指示),使用的编译器的类型大小可能与二进制文件中的数据大小不匹配,最后但并非最不重要的字节序可能不匹配.

始终将二进制数据读入中间缓冲区,您可以通过明确定义的方式提取其中的字段,并使用精确指定的偏移量和类型。

// Allocate memory for the image data.
data = (unsigned char*)malloc(dibheader.dataSize);

如果这是 C++,则使用 new 运算符。如果这是 C,那么不要将 void * 转换为 L 值类型,这是不好的风格,可能会覆盖有用的编译器警告。

// Verify memory allocation.
if (!data)

    free(data);

如果 data 为 NULL,则不能释放它。

// Swap R and B because bitmaps are BGR and OpenGL uses RGB.
for (unsigned int i = 0; i < dibheader.dataSize; i += 3)

    B = data[i]; // Backup Blue.
    data[i] = data[i + 2]; // Place red in right place.
    data[i + 2] = B; // Place blue in right place.

OpenGL 确实支持 BGR 对齐。格式参数是,令人惊讶的是,GL_BGR

// Generate texture image.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dibheader.width, dibheader.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

嗯,这错过了设置像素存储参数的所有。始终在进行像素传输之前设置每个像素存储参数,它们可能会因先前的操作而处于某些不希望的状态。安全总比后悔好。

【讨论】:

我很久以前尝试过 GL_BGR,我只是得到一个未定义的错误。无论哪种方式,我都重写了加载程序,我将使用新代码更新问题(并且不时出现不同的错误)。 @100GPing100: GL_BGR 是在 OpenGL-1.1 之后引入的,所以你需要一些扩展头来使用它。我推荐 GLEW,尽管您不需要初始化所有函数指针。 我实际上正在使用 glew(在第一篇文章中说)。我需要从中“导入”类型吗? (就像在 C# 中,如果我们需要来自 dll 的函数,我们可以导入它) @100GPing100:你需要包含它的头文件才能让编译器看到它。也试试 GL_BGR_EXT。请注意,只有倒数第三个参数采用此参数,即glTexImage(…, GL_RGB, …, GL_BGR[_EXT], GL_UNSIGNED_BYTE, data); @100GPing100:嗯,你设置了像素存储参数吗?他们都是。请参阅 glPixelStorei 的手册页,GL_UNPACK_ 参数是您必须注意的。 opengl.org/sdk/docs/man/xhtml/glPixelStore.xml 并查看此文本以了解这些参数的含义fly.cc.fer.hr/~unreal/theredbook/chapter08.html

以上是关于C++ OpenGL glTexImage2D 访问冲突的主要内容,如果未能解决你的问题,请参考以下文章

在 OpenGL glTexImage2D() 中,如果“数据”指针为 NULL,最后两个枚举是不是执行任何操作?

RGBA 整数值到 OpenGL 纹理

glTexImage2D与glDeleteTextures 求助

关于OpenGL纹理 装载位图的问题

OpenGL 像素在内存中的排列方式

对符号 glTexImage2D 的未定义引用