2012-01-28 34 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