C OpenGL glTexImage2D访问冲突

C++ OpenGL glTexImage2D Access Violation

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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;
}

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

这里的问题是这一行:

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

在大多数情况下,我运行该应用程序时,该行崩溃并显示错误:

Unhandled exception at 0x008ffee9 in GunsGL.exe: 0xC0000005: Access violation reading location 0x00af7002.

这是发生错误的地方:

1
movzx       ebx,byte ptr [esi+2]

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

编辑:(以上代码已更新)

我重写了装载程序,但仍然在同一行发生崩溃。而不是那次崩溃,有时我在mlock.c上崩溃了(我可以正确地回忆起相同的错误消息):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 );
}

在线:

1
EnterCriticalSection( _locktable[locknum].lock );

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

Edit2:

使用新的工作代码更新代码。
(标记为答案的答复不包含此工作所需的全部内容,但这很重要)


glTexImage2D()调用之前尝试glPixelStorei(GL_UNPACK_ALIGNMENT, 1)


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

1
2
3
4
5
6
7
8
BitmapHeader header;
BitmapInfoHeader dibheader;
/*...*/
// Read header.
fread(&header, sizeof(BitmapHeader), 1, file);

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

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

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

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

如果这是C,则使用new运算符。如果这是C,则不要将其从void *强制转换为L值类型,这是不好的样式,并且可能包含有用的编译器警告。

1
2
3
4
// Verify memory allocation.
if (!data)
{
    free(data);

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

1
2
3
4
5
6
7
// 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

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

好吧,这会错过所有像素存储参数的设置。在进行像素传输之前,请务必先设置每个像素存储参数,它们可能会因之前的操作而处于某些不良状态。安全胜过遗憾。