问题描述
我正在使用OpenGL(freeglut和glew)编写一个应用程序。
我也想要纹理,所以我做了一些研究Bitmap文件格式,对于主标头和另一个对于DIB标头(信息标头)。
然后我开始编写加载程序。它自动将纹理绑定到OpenGL。这里是函数:
static unsigned int ReadInteger(FILE * fp)
{
int a, b,c,d;
//整数为4个字节长。
a = getc(fp)
b = getc(fp);
c = getc(fp);
d = getc(fp);
//将4个字节转换为整数。
return((unsigned int)a)+(((unsigned int)b)((unsigned int)c)}
static unsigned int ReadShort(FILE * fp)
{
int a,b;
//短为2个字节长。
a = getc(fp);
b = getc(fp);
//将2个字节转换为短整型(int16)。
return((unsigned int)a)+(((unsigned int)b)}
GLuint LoadBMP(const char * filename)
{
FILE * file;
//检查是否提供了文件名。
if(!filename)
return 0;
//尝试打开文件。
fopen_s(& file,filename,rb);
//如果文件无法打开则返回。
if(!file)
{
cout< 警告:找不到纹理< filename<< 。 << endl;
return 0;
}
//读签名。
unsigned char signature [2];
fread(& signature,2,1,file);
//使用签名来标识有效的位图。
if(signature [0]!= BMPSignature [0] || signature [1]!= BMPSignature [1])$ b $ b {
fclose
return 0;
}
//读取宽度和高度。
unsigned long width,height;
fseek(file,16,SEEK_CUR); //签名后,我们有16bytes直到宽度。
width = ReadInteger(file);
height = ReadInteger(file);
//计算数据大小(我们只支持24bpp)。
unsigned long dataSize;
dataSize = width * height * 3;
//确保飞机是1.
if(ReadShort(file)!= 1)
{
cout< 错误:无法加载纹理< filename<< '(飞机不是1)。 << endl;
return 0;
}
//确保bpp是24。
if(ReadShort(file)!= 24)
{
cout< 错误:无法加载纹理< filename<< '(每像素的位不是24)。 << endl;
return 0;
}
//将指针移动到数据的起始位置。 (在bpp之后我们有24个字节直到数据)
fseek(file,24,SEEK_CUR);
//分配内存并读取图像数据。
unsigned char * data = new unsigned char [dataSize];
if(!data)
{
fclose(file);
cout<< 警告:无法分配内存来存储<< filename<< 。 << endl;
return 0;
}
fread(data,dataSize,1,file);
if(data == NULL)
{
fclose(file);
cout<< Warning:Can not load data from'<< filename<< 。 << endl;
return 0;
}
//关闭文件。
fclose(file);
//创建纹理。
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);
大多数时候,我运行应用程序时,此行崩溃时出现错误:
这是错误发生的地方的反汇编:
movzx ebx,byte ptr [esi + 2]
这不是我的装载器的错误,因为我已经下载了其他装载器。
我使用的下载加载程序是来自NeHe的。 p>
编辑:(CODE UPDATED ABOVE)
我重写了装载程序,但我仍然在同一行。而不是那个崩溃,有时我得到一个崩溃mlock.c(同样的错误消息是我记得正确):
void __cdecl _lock(
int locknum
)
{
/ *
*如有必要,创建/打开锁
* /
if(_locktable [locknum] .lock == NULL){
if(!_mtinitlocknum(locknum))
_amsg_exit(_RT_LOCK);
}
/ *
*输入临界区。
* /
EnterCriticalSection(_locktable [locknum] .lock);
}
在行上:
EnterCriticalSection(_locktable [locknum] .lock);
此外,这里是一个屏幕截图,其中一个应用程序不崩溃显然不对):
Edit2:
工作一个。
(标记为答案的答复不包含这项工作所需的全部内容,但它是至关重要的)
我知道,很容易读这样的二进制数据
但你真的不应该这样做。为什么?因为结构的存储器布局可以被填充以满足对齐约束(是的,我知道关于打包pragmas),所使用的编译器的类型大小可能不匹配二进制文件中的数据大小,并且最后但不是最小endianess可能不匹配。
始终将二进制数据读入中间缓冲区,在其中以明确定义的方式提取字段并完全指定偏移量并输入。
如果是C ++,请使用 new
运算符。如果这是C,那么不要从 void *
转换为L值类型,它的样式不好,可能会覆盖有用的编译器警告。
如果 data $ c $
OpenGL确实支持BGR对齐。格式参数是,惊奇,GL_BGR
好吧,这错过了全部的像素存储参数。在进行像素传输之前始终设置每个像素存储参数,它们可能保留在来自先前操作的某些不期望的状态中。更安全,比对不起。
I'm writing an application using OpenGL (freeglut and glew).
I also wanted textures so I did some research on the Bitmap file format and wrote a struct for the main header and another for the DIB header (info header).
Then I started writing the loader. It automatically binds the texture to OpenGL. Here is the function:
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;
}
I know that the bitmap's data is correctly read because I outputted it's data to the console and compared with the image opened in paint.
The problem here is this line:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dibheader.width,
dibheader.height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
Most of the times I run the application this line crashes with the error:
This is the Disassembly of where the error occurs:
movzx ebx,byte ptr [esi+2]
It's not an error with my loader, because I've downloaded other loaders.A downloaded loader that I used was this one from NeHe.
EDIT: (CODE UPDATED ABOVE)
I rewrote the loader, but I still get the crash on the same line. Instead of that crash, sometimes I get a crash on mlock.c (same error message is I recall correctly):
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 );
}
On the line:
EnterCriticalSection( _locktable[locknum].lock );
Also, here is a screen shot of one of those times the applications doesn't crash (the texture is obviously not right):http://i.stack.imgur.com/4Mtso.jpg
Edit2:
Updated code with the new working one.(The reply marked as an answer does not contain all that was needed for this to work, but it was vital)
I know, it's tempting to read binary data like this
but you really shouldn't do it that way. Why? Because the memory layout of structures may be padded to meet alignment constraints (yes, I know about packing pragmas), the type size of the used compiler may not match the data size in the binary file, and last but not least endianess may not match.
Always read binary data into a intermediary buffer of which you extract the fields in a well defined way with exactly specified offsets and typing.
If this is C++, then use the new
operator. If this is C, then don't cast from void *
to the L value type, it's bad style and may cover usefull compiler warnings.
If data
is NULL you mustn't free it.
OpenGL does indeed support BGR alignment. The format parameter is, surprise, GL_BGR
Well, and this misses setting all of the pixel store parameters. Always set every pixel store parameter before doing pixel transfers, they may be left in some undesired state from a previous operation. Better safe than sorry.
这篇关于C ++ OpenGL glTexImage2D访问冲突的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!