2012-01-28 24 views
1

正如标题所示,我一直在加载.tga文件时遇到问题。我一直在使用this作为参考,据我所知(除了使用C++函数而不是c函数),我正在做同样的事情。我确实得到了一个纹理,但它应该是一个乱码版本,我不确定为什么。请原谅所有错误,我只是想让它工作。加载Targa文件的问题文件C++

页眉:

struct TGA 
{ 
    GLuint Load(const char* filename); 
    unsigned char header_[12]; 
    unsigned char bpp_; 
    unsigned char id_; 
    unsigned short width_; 
    unsigned short height_; 
    unsigned char* data_; 
}; 

.cpp的:

GLuint TGA::Load(const char* filename) 
{ 
width_ = 0; height_ = 0; 
bpp_ = 32; id_ = 8; 
data_ = 0; 

std::ifstream file; 

file.open(filename, std::ios::binary | std::ios::ate); 

if(file.fail()) 
{ 
    std::cout<<filename<<" could not be opened."<<std::endl; 
    return 0; 
} 

file.seekg(0, std::ios::beg); 
file.read((char*)header_, sizeof(unsigned char)*12); 
file.read((char*)&width_, sizeof(unsigned short)); 
file.read((char*)&height_, sizeof(unsigned short)); 
file.read((char*)&bpp_, sizeof(unsigned char)); 
file.read((char*)&id_, sizeof(unsigned char)); 

int imageSize = width_ * height_; 
std::cout<<imageSize<<std::endl; 

data_ = new unsigned char[imageSize * 3]; //3 means RGB 
file.read((char*)data_, imageSize * 3); 

file.close(); 

GLuint texture; 
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 
glGenTextures(1, &texture); 
glBindTexture(GL_TEXTURE_2D, texture); 

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width_, height_, 0, GL_BGRA, GL_UNSIGNED_BYTE, data_); 

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); 

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 

gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, data_); 


delete [] data_; 

return texture; 
} 
+0

不是一个答案..但.. ..确保填充和一次读取!如果你想让它变得可移植,请不要忘记endianess .. – 2012-01-28 17:06:12

+0

阅读文件头后是什么'bpp_'? (弗拉德:我相信TGA不像BMP那样填充)。 – user7116 2012-01-28 17:07:37

+0

bpp应该是每像素或像素深度的位数。 RGB图像为24。我试着检查各个位的添加/对齐,但它们似乎没有问题。我不确定如何在阅读数据后将其解析为一大块。 – Sullivan 2012-01-28 17:11:57

回答

3

当在数据读出,则假定TARGA文件具有每像素(file.read((char*)data_, imageSize * 3))24位而不检查的bpp_值。然后,您将图像数据传递给OpenGL,并说您的数据采用32-bpp BGRA格式。

您应该检查bpp_的值是什么,根据该值分配和读取数据,并将正确的数据格式传递给OpenGL(BGR或BGRA,具体取决于图像中是否包含alpha通道)。

+0

非常感谢你!我无法相信当opengl期待RGBA图像时,我试图使用RGB图像。 谢谢大家的帮助和建议。 – Sullivan 2012-01-28 17:21:52

1

是的,你在做同样的事情:)

如果你看一下你使用一个代码下面的评论参考,它读取数据的代码假设运行它的计算机是大端(如PS3)。我在这里猜测,但是你在PC上运行(使用小端)?

如果endian-ness错误会导致所有数据类型大于一个字节会得到错误的值(height_/width_),那么您需要检查运行该程序的计算机是使用大小写还是正确的相应的值。

要将height_和width_转换为正确的字节顺序,您应该只能使用; ntohs and ntohl(网络字节顺序是大端)

height_ = ntohs(height_); 
width_ = ntohs(width_); 
+0

我的确了解了这篇文章中的endian问题。我在PC上运行这个代码(Ubuntu Linux 64位),但我不知道它是哪种类型的端口... – Sullivan 2012-01-28 17:14:23

+0

无论操作系统如何,x86 PC都是小端。 – 2012-01-28 17:17:26

+0

我对此有一个模糊的记忆,但我不确定。 – Sullivan 2012-01-28 17:22:35

1

首先让我注意到,你正在做的事情比最糟糕的方法更好(读入一个打包的结构)。对于健壮的文件解析器,请始终逐一读取文件。这是避免错误和漏洞的唯一途径。

遗憾的是仍然有一些问题,但这些都是容易解决:

struct TGA 
{ 
    GLuint Load(const char* filename); 
    unsigned char header_[12]; 
    unsigned char bpp_; 
    unsigned char id_; 

    unsigned short width_; 
    unsigned short height_; 

宽度和高度做空可能会成为一个问题。魔鬼在细节中,但我会及时解释。现在我们应该尊重一些架构short对我们来说实际上可能太短了。为了安全起见,使用uint16_tstdint.h(C99标准的一部分)或更好的uint32_t。我们会看到为什么。

unsigned char* data_; 
}; 

GLuint TGA::Load(const char* filename) 
{ 
width_ = 0; height_ = 0; 
bpp_ = 32; id_ = 8; 
data_ = 0; 

无论如何我们会覆盖那些,所以不需要清除它们,但也没有害处。

std::ifstream file; 

file.open(filename, std::ios::binary | std::ios::ate); 

if(file.fail()) 
{ 
    std::cout<<filename<<" could not be opened."<<std::endl; 
    return 0; 
} 

file.seekg(0, std::ios::beg); 
file.read((char*)header_, sizeof(unsigned char)*12); 

file.read((char*)&width_, sizeof(unsigned short)); 
file.read((char*)&height_, sizeof(unsigned short)); 

好的,这些有点问题,因为他们假设程序运行在小端机器上。假设你要为PowerPC开发(认为PlayStation 3,XBox或以大端模式运行的ARM),这段代码就会中断。解决方案:

char buf[2]; file.read(buf, 2); width = buf[0] | buf[1]<<8;和高度相同。如果在字符大小不是8的计算机上运行,​​该代码仍然存在问题,但这些日期之间的差距很小。如果您想保持安全,请在某处添加#if CHAR_BITS!=8 #error "char bits not 8" #endif

file.read((char*)&bpp_, sizeof(unsigned char)); 
file.read((char*)&id_, sizeof(unsigned char)); 

现在下面的行是错误的:

int imageSize = width_ * height_; // <<<<<<<<< 

由于width_height_都是short类型的乘法将coerece短宽度。 C和C++的这种特殊行为是令人讨厌的可利用漏洞的最大来源之一。假设我会给你一张头像被宣布为width = height 2^15-1的图片,这个数字很适合预期的16位。如果你把这两个数字相乘并将其压缩,你会得到... 1.然后还有一点小问题。

std::cout<<imageSize<<std::endl; 

data_ = new unsigned char[imageSize * 3]; //3 means RGB 
file.read((char*)data_, imageSize * 3); 

的好处是,你只读您是通过代理变量分配的字节数,所以我不能注入代码到你的程序。但是我可以将它崩溃,因为稍后您的glTexImage2D调用将尝试从未分配的存储中读取(2^15 -1)^ 2个字节,从而导致段错误。避免这种情况的技巧是将单个变量转换为计算中足够大的类型。或者你按照我的建议做,并且使用uint32_t来表示宽度和高度。我按照规则进行编码,即每个变量应至少保存两倍于所需最大允许值的位。不幸的是,C标准不能被“固定”来强制L语句的大小,因为这会破坏很多现有的代码。

您遇到的另一个问题是您硬编码了3个通道。为什么?你已经有了bpp-header,它告诉你这个格式。所以让我们改变这个:uint32_t imageSize = (uint_32)width_ * height_ * bpp_/8;。请注意,仅将一个表达式的变量强制转换为较大的类型才是必要的。但是不要犯这个错误:(uint32_t)(width_ * height_ * bpp_/8)这会将简短的输入结果转换为uint32_t - 介意括号。

最后但并非最不重要,我们可以将此传递给glTexImage2D或gluBuildMipMap2D。我强烈建议不要使用gluBuildMipmap,因为它会将纹理转换为2维的幂次,这是自OpenGL-2以来不再需要的。相反,请调用glGenerateMipmap format参数不应该是通道数,而应该是GL_RED,GL_RG,GL_RGB,GL_RGBA的标记。因此,使用这个小开关语句:

GLenum internal_format; 
GLenum format; 
GLenum buffer_format; 
switch(bpp_) { 
case 8: 
    internal_format = GL_R8; 
    format = GL_RED; 
    buffer_format = GL_UNSIGNED_BYTE; 
    break; 
case 16: 
    internal_format = GL_RGB5; 
    format = GL_RGB; 
    buffer_format = GL_UNSIGNED_SHORT_5_6_5; 
    break; 
case 24: 
    internal_format = GL_RGB8; 
    format = RGB; 
    buffer_format = GL_UNSIGNED_BYTE; 
    break; 
case 32: 
    internal_format = GL_RGBA8; 
    format = RGB; 
    buffer_format = GL_UNSIGNED_BYTE; 
    break; 
default: 
    format_unsupported_error(); return; 
} 

glTexImage2D(..., internal_format, ..., internal_format, format, ...); 
+0

谢谢你的建议。我不完全没有经验,但我仍然有很多东西需要学习。使用两倍数据大小变量的概念听起来很有趣,但是这不是对内存的无谓使用吗?当我测试时,我已经更新了魔术数字。 – Sullivan 2012-01-29 21:21:58

+0

至于使用opengl 2,我使用了很多老的教程作为参考(想想NeHe之类的),因为使用较新版本的opengl找到示例很少见。我试图尽可能地保持一致(除了避免即时模式绘图,如果我可以帮助的话),一旦我更好地理解它的基本工作,我就会冒险学习“更好”的opengl。 – Sullivan 2012-01-29 21:22:22

+0

@Sullivan:要么小心地将变量转换为正确的操作大小,要么使用两倍的大小。坦率地说,如果我们正在讨论结构头部而不是批量数据,那么增加的安全性很大程度上补偿了增加的内存成本。像宽度,高度等变量只占用少量内存,但对于许多可利用的漏洞。批量数据无论如何都会保存在一个匹配元素大小的数组中。 – datenwolf 2012-01-29 21:46:55