2012-01-28 2 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, если вы когда-нибудь захотите, чтобы она была переносимой. –

+0

Что такое 'bpp_' после прочтения заголовка файла? (Влад: Я считаю, что TGA не накладывается, как BMP.) – user7116

+0

bpp должен быть бит на пиксель или глубину пикселя. Это 24 изображения RGB. Я попытался проверить добавление/выравнивание различных битов, но, похоже, все в порядке. Я не уверен, как анализировать данные после прочтения его как одного большого фрагмента. – Sullivan

ответ

3

Когда вы читаете в данных, вы берете файл тарга имеет 24 бита на пиксель (file.read((char*)data_, imageSize * 3)) без проверки значения bpp_. Затем вы передаете данные изображения в OpenGL и говорите, что ваши данные находятся в формате BGRA формата 32 бит.

Вы должны проверить, что такое значение bpp_, выделить и прочитать данные на основе этого значения, а также передать правильный формат данных в OpenGL (BGR или BGRA, в зависимости от того, включен ли альфа-канал в изображение или нет).

+0

Большое вам спасибо! Я не могу поверить, что пытался использовать RGB-образ, когда opengl ожидал изображение RGBA. Спасибо всем за помощь и совет. – Sullivan

1

Да, вы делаете то же самое :)

Если вы посмотрите на комментарии ниже код, который вы использовали для ссылка, код, в котором он считывает данные, предполагая, что компьютер, работающий с ним, является большим endian (например, PS3). Я угадываю здесь, но вы работаете на ПК (который использует немного endian)?

Если вы ошиблись в конце концов, все ваши типы данных, превышающие байты, получат неправильные значения (height_/width_), вам нужно будет проверить, использует ли компьютер, на котором запущена программа, big-or little-endian и correct значения соответственно.

Чтобы преобразовать height_ и width_, чтобы исправить endian-ness, вы должны просто использовать; ntohs and ntohl (порядок байт сети является большой обратный порядок байт)

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

Я читал об этой проблеме. Я запускаю этот код на ПК (Ubuntu Linux 64 бит), но я понятия не имею, какой тип endian это ... – Sullivan

+0

ПК x86 мало ориентирован, независимо от операционной системы. –

+0

У меня была смутная память об этом, но я не был уверен. – Sullivan

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_t от stdint.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 байта из нераспределенного хранилища и, следовательно, вызвать segfault. Хитрость, чтобы избежать этого, - это превратить отдельные переменные в тип, достаточно большой в вычислении. Или вы делаете так, как я предложил, и используйте для этого значение также значение uint32_t. Я кодирую по правилу, что каждая переменная должна содержать как минимум два бита, как требуется для наибольшего разрешенного значения в нем. К сожалению, стандарт C не может быть «исправлен» для принуждения к размеру L-типа инструкции, поскольку это нарушит много существующего кода.

Другая проблема, с которой вы сталкиваетесь, заключается в том, что вы запрограммировали 3 канала. Зачем? У вас есть bpp-заголовок, который сообщает вам формат. Итак, давайте изменим это: uint32_t imageSize = (uint_32)width_ * height_ * bpp_/8;. Обратите внимание, что необходимо приложить только одну переменную выражения к большему типу. Однако не делайте эту ошибку: (uint32_t)(width_ * height_ * bpp_/8), которая выдаст короткий типизированный результат uint32_t - умьте круглые скобки.

Последнее, но не менее важное, мы можем передать это glTexImage2D или gluBuildMipMap2D. Я настоятельно рекомендую не использовать gluBuildMipmap, потому что он преобразует вашу текстуру в параметры мощности-2, которые больше не нужны с OpenGL-2. Вместо этого вызывается glGenerateMipmap Параметром формата должно быть не количество каналов, а маркер 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

+0

Что касается использования opengl 2, я использую много старых руководств для справки (думаю, NeHe и т. П.), Поскольку поиск примеров с использованием более новых версий opengl - это немного и далеко. Я стараюсь быть как можно более последовательным (за исключением того, что избегаю рисования в режиме немедленного режима, если я могу ему помочь), и я решительно потружу, чтобы узнать «лучше», как только я пойму, что его основная работа лучше. – Sullivan

+0

@Sullivan: Либо будьте осторожны, чтобы превратить переменные в нужный размер для операции или использовать в два раза больше. Честно говоря, если мы говорим о заголовках структуры, а не о массовых данных, добавленная безопасность в значительной степени компенсирует дополнительные затраты на память. Переменные, такие как ширина, высота и т. Д., Учитывают только небольшой объем потребляемой памяти, но для многих из возможных ошибок. Массовые данные, которые вы будете хранить в массиве соответствующего размера элемента, в любом случае. – datenwolf

Смежные вопросы