Пятница, 29 Ноября 2024, 06:14

Приветствую Вас Гость

[ Новые сообщения · Игроделы · Правила · Поиск ]
  • Страница 1 из 1
  • 1
Tutorial [EN/RU]: Загрузка текстур формата TGA, OpenGL
WXZRWДата: Понедельник, 14 Декабря 2009, 19:29 | Сообщение # 1
Thousand faces conspiration
Сейчас нет на сайте
Обновлен 14/12/2009

Исправлено : возвращение результата функцией get_tga_file, поскольку это тип unsigned int, то он не может быть меньше 0.

Все как обычно, комментарии к коду на английском, обьяснение в статье на русском. Описания функций WINAPI или OpenGL, использованных в коде, смотрите в MSDN.

Лицензия :

Любая информация которая здесь предоставлена, может содержать те или иные неточности, код может не компилиться при некоторых условиях. Все что здесь есть, предоставлено мною без каких-либо гарантий и ответственности, и я не несу никакой ответственности за любой возможный ущерб, который может быть вызван использованием любой части данного поста, кода или информации в любом виде и при любых обстоятельствах.

Туториал также не продвигает и не рекламирует ни один графический АПИ, ни один формат файла, ни что либо еще. Все форматы файлов и прочее имущество третьих лиц может быть использовано согласно закону. Все торговые марки принадлежат их владельцам.

Примечание : туториал основан на данном туториале - http://gcup.ru/forum/62-1590-1. Новый код помечен тегами /* NEW */ и /* END */

Код :

* Обьявим функции и переменные, т.е. функцию для загрузки данных перед пуском программы, функцию загрузки TGA файла и идентификатор текстуры.

Code
void load_data();

unsigned int get_tga_file(char* t_file);

unsigned int t_id;

* Функция для загрузки данных при запуске программы. Здесь также можно настроить что-либо, вобщем сделать все то что надо сделать перед рендерингом. Мы загрузим нашу текстуру здесь. Поскольку функция загрузки TGA возвращает результат, ты подаем его в переменную t_id. Это идентификатор текстуры.

Code

void load_data()
{
t_id  = get_tga_file("texture.tga");   

// Report if error occured while loading TGA file, using t_id value

if(t_id == 0)
{
MessageBox(NULL,"Can't load texture",NULL,MB_OK);
}

}

* Теперь нам надо вызвать функцию загрузки данных. Мы делаем это в функции WinMain, начало функции для наглядности :

Code
int WINAPI WinMain(HINSTANCE inst,HINSTANCE prev_inst,LPSTR line,int show)
{

* Наш новый код внутри :

Code
/* NEW */

load_data();

/* END */

* Закрываем функцию

Code

return _process(w_handle); // return result back to, after it's handled by _process function
}

* Теперь рисуем квадрат и накладываем на него текстуру. Обьяснение нового кода - мы включаем 2Д текстурирование, проверяем верный ли идентификатор текстуры (0 или положительное число), и если да, то включаем текстуру через glBindTexture в которую мы подаем идентификатор текстуры. Далее, мы включаем цвет (белый) на тот случай если текстуру не удалось загрузить и мы не смогли ее включить. Вообще стоит включать текстуринг (в glEnable(....)) только если идентификатор текстуры правилен, но поскольку текстура только 1, мы не заботимся об этом в данном случае.

Далее, мы рисуем квадрат, выбрав режим GL_QUADS, чтобы рисовать квадрат мы подаем 4 вершины (4 точки) квадрата в glVertex и также назначаем текстурные координаты для правильного наложения текстуры. Обратите внимание, точка отсчета текстурных координат разная для DirectX и OpenGL, у нас это левый нижний угол текстуры. Текстурные координаты могут быть на интервале от 0 до 1, пропорционально высоте и ширине текстуры. То есть, пространство текстурных координат приводится к величине высоты и ширины, например если высота = 256 пикселов, то масимальная текстурная координата = 1, тогда для задания координаты в позиции 128 пикселя текстурная координа равна 0.5, т.е. пропорционально, и так далее для любых высоты и ширины текстуры.

Code
void draw()    
{
// Clear the screen and load identity matrix
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    
glLoadIdentity();           

// View transform
// Variables : eye position (3 vars), view direction (3 vars), up vector (3 vars)
// Explanations : eye position is point from we're looking at, view direction is point we are    
// looking at, and up vector describes our coordinate system, we set Y to be pointed to the up
gluLookAt(0,0,2.5,0,0,0,0,1,0);

/* NEW */

// Depth test to grab info how far objects are located from
glEnable(GL_DEPTH_TEST);
// Enable 2D texturing
glEnable(GL_TEXTURE_2D);

// Check is texture id valid
if(t_id > 0)
{
glBindTexture(GL_TEXTURE_2D,t_id);
}

// Start to draw quads
glBegin(GL_QUADS);
       
// Below we set up color for quad. We draw the quad by passing it's 4 vertices to GL
       
glColor3ub(255,255,255);

glTexCoord2f(0.0, 1.0);    
glVertex3f(-1, 1, 0);

glTexCoord2f(0.0, 0.0);    
glVertex3f(-1, -1, 0);

glTexCoord2f(1.0, 0.0);    
glVertex3f(1, -1, 0);

glTexCoord2f(1.0, 1.0);
glVertex3f(1, 1, 0);
        
// After we 're done with drawing, we need to stop it.
glEnd();
       
// And disable depth testing then
glDisable(GL_DEPTH_TEST);
glDisable(GL_TEXTURE_2D);

/* END */
}

* Последний этап это загрузка самой текстуры. Для начала мы пытаемся открыть файл функцией fopen(...) и назначаем бинарный режим чтения. Если файл есть, мы продолжаем, либо если его нет, мы выходим и возвращаем ошибку в виде переменной равной -1. Если все ОК, мы читаем заголовок TGA файла, это 18 байт. Мы читаем весь заголовок в массив типа unsigned char размером на 18 элементов. Вообще можно читать заголовок частями, получая только нужные данные, но мы для простоты читаем все.

Когда мы получили заголовок, мы определяем тип текстуры. Мы грузим только несжатый формат TGA, т.е. если параметр в 3 элементе заголовка равен 2, что значит тип RGB. Если это не так, тогда мы выходим и выдаем ошибку в виде переменной равной -2. Если все удачно, тогда мы находим высоту и ширину текстуры, используя данные из заголовка. Также мы находим количество каналов в текстуре и пишем это в переменную color. Чтобы найти количество каналов, мы берем предпоследний элемент заголовка и делим на 8. Этот элемент хранит в себе количество байт на пиксель текстуры, т.е. 8, 16, 24 или 32. Поделив этот параметр на 8, мы получим число каналов в текстуре, т.е. - R,RG,RGB,RGBA. Далее, нам надо ввыделить место под данные текстуры, т.е. нам нужен массив размером на width*height*channels элементов. Выделив память, мы читаем в этот массив всю текстуру, т.е. каждый ее пиксель. Для этого используем - fread(image,sizeof(unsigned char),size,_file);.

После того как у нас есть текстура, возникают проблемы. Дело в том, что в файле TGA данные лежат в виде BGR, но нам надо RGB. Поэтому в цикле мы просто пересобираем массив как нам нужно, меняя элементы местами. Потом мы закрываем файл.

Теперь когда все готово, мы должны создать текстуру в OpenGL, для этого - назначаем переменную для идентификатора текстуры и генерируем данный идентификатор используя функцию glGenTextures(1,&tex_id); после чего включаем эту текстуру подавая идентификатор в glBindTexture(GL_TEXTURE_2D,tex_id); . Мы также устанавливаем режим наложения текстур в glTexEnvi(...), указывая GL_REPLACE чтобы не допустить смешивания цвета текстуры с цветом в OpenGL. (т.е. тем который задан при помощи например glColor3f(...) или glColor3ub(...) и т.д.). Если же нужно смешивание, тогда нужно передать параметр GL_MODULATE в glTexEnvi(....).

Далее мы настраиваем фильтрацию текстуры и способ ее наложения. После этого, мы проверяем сколько каналов доступно, если это не 3 или 4 канала (т.е. RGB/RGBA), то возвращаем ошибку в виде переменной -3. Если все прошло успешно, мы заливаем текстуру в OpenGL с помощью gluBuild2DMipmaps и возращаем идентификатор текстуры.

Code
unsigned int get_tga_file(char* t_file)
{
// Below variables to hold data we will need.
FILE *_file;

long size;

int color;
        
unsigned char header[18];

// Open texture file in RB mode (Read/Binary)
_file = fopen(t_file, "rb");

// If unavailable, then return 0
if(!_file)
{
return 0;
}

// Otherwise, get header from TGA
fread(header,1,sizeof(header),_file);

// If not RGB image, return 0
if(header[2] != 2) { return 0;}

int width = header[13] * 256 + header[12];
int height = header[15] * 256 + header[14];

// Compute how many bits per texture pixel this TGA has available.
// Convert this data to channels amount, e.g. 3 or 4 channels (RGB or RGBA)
color = header[16] / 8;

// Get array size to hold image. We compute total amount of pixels, i.e. width * height, after that we
// need to allocate space for each pixel, so we multiply previous result with bits per pixel variable
size = width * height * color;

// After we know the size of image data array, we declare it
unsigned char *image = new unsigned char[sizeof(unsigned char) * size];

// Read pixel data from image to our image array
fread(image,sizeof(unsigned char),size,_file);

// TGA holds BGR data, but we want it to be RGB instead. So we need to swap channels.
for(long i = 0; i < size; i += color)
{
unsigned char tmp = image[i];

image[i] = image[i+2];

image[i+2] = tmp;
}

// Close file after done
fclose(_file);

// Declare variable to hold texture ID
unsigned int tex_id;

// Generate texture and return it's ID
glGenTextures(1,&tex_id);
         
// And bind it
glBindTexture(GL_TEXTURE_2D,tex_id);

// We want only texture colors to operate with, i.e. no color mixing required
// You can set GL_MODULATE though, to mix texture color with color you want.
glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);

// Set texture filtering
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
         
// Set texture mapping mode
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);

// Select texture channels info using data we got from texture file. Shulda be 3 or 4 channels (RGB or RGBA)
// Otherwise return 0
if(color == 3) { color = GL_RGB;}
else if(color == 4) { color = GL_RGBA;}
else { return 0;}

// Build mipmaps by passing data we collected above
gluBuild2DMipmaps(GL_TEXTURE_2D,color,width,height,color,GL_UNSIGNED_BYTE,image);

// If everything is OK, return txeture ID then.
return tex_id;
}

Скриншот :

Также выложены .cpp исходник и текстура в формате TGA, для компиляции в Visual C++ нужно создать проект типа Win32 Application.

Прикрепления: core.cpp (14.3 Kb) · texture.rar (1.7 Kb)
PesetsДата: Понедельник, 11 Января 2010, 20:31 | Сообщение # 2
постоянный участник
Сейчас нет на сайте
Я в свое время разбирал формат TGA... то, что меня бесило - во всех туториалах, что я читал, авторы рассматривают только определенный TGA с определенным заголовком. Если заголовок не такой, как надо, то "все плохо" и программа уныло хлопается... И здесь то же самое...
Когда-же-наконец-кто-нибудь-напишет-статью-в-которой-полностью-расшифровывается-формат-TGA?!

Как пример, насколько помню, в GIMPе по умолчанию в TGA начало координат - правый нижний угол... и человек, использовавший материал урока, будет в недоумении, когда попытается загрузить нарисованный в GIMPе рисунок, а он получится "трех ногами")



WXZRWДата: Понедельник, 11 Января 2010, 22:42 | Сообщение # 3
Thousand faces conspiration
Сейчас нет на сайте
TGA формат прекрасно расписан и широко известен. То есть для достаточно опытного программиста это не проблема. Кроме того, полноценный коммерческий вариант использования формата может перевалить за 10 (или больше) страниц кода в виде А4, шрифт размера 10 (Arial или Tahome). Я сильно сомневаюсь что кто-либо будет выкладывать коммерческие исходники в сеть. То есть такой туториал может потянуть на главу книги по обьему.

В основном все что есть в сети бесплатно и для массового читателя - это основы, то есть начальные сведения для начинающих программистов.

Более того, все мои западные коллеги сделали в своих аналогичных туториалах не больше чем я, т.е. примерно столько же... и сам я решил не делать больше чем они сделали... все мои туторы сделаны соотносительно равноценными по сравнению с наиболее распространенными туториалами западных авторов. Я не намерен с ними конкурировать и делать продукт лучше чем у них. Кого из авторов предпочесть - выбор читателя.

Левый нижний угол - это так изначально в OpenGL, можно сделать функцию для ремаппинга текстурных координат, но это усложнит туториал, я не думаю что это реально нужно начинающим. Опытный программер решит этот вопрос без моей помощи.

PesetsДата: Вторник, 12 Января 2010, 11:30 | Сообщение # 4
постоянный участник
Сейчас нет на сайте
Quote (WXZRW)
и сам я решил не делать больше чем они сделали... все мои туторы сделаны соотносительно равноценными по сравнению с наиболее распространенными туториалами западных авторов. Я не намерен с ними конкурировать и делать продукт лучше чем у них.

Не понимаю, зачем тогда вообще писать, если в итоге получится то же самое, что уже написано других туториалах...
Quote (WXZRW)
я не думаю что это реально нужно начинающим

А я не думаю, что начинающим нужно вообще разбирать формат TGA. Библиотек для работы с изображениями в интернетах пруд пруди. И бесплатных тоже. Так что, если человек решится писать свой движок, то ему гораздо выгоднее будет подобные задачи оставить сторонним библиотекам, а самому сосредоточиться на тех вещах, которые его не устроили в существующих движках...




WXZRWДата: Вторник, 12 Января 2010, 12:20 | Сообщение # 5
Thousand faces conspiration
Сейчас нет на сайте
Quote (Pesets)
Не понимаю, зачем тогда вообще писать, если в итоге получится то же самое, что уже написано других туториалах...

Чтобы у нас здесь на форуме были свои туторы. Да на других сайтах в РФ тоже туторы есть, а ведь если посмотреть, то все это в МСДН есть и так, зачем же переписывать. Пусть читатель МСДН смотрит, первоисточник же, тем более. Зачем например у вас в РФ спички делают, покупали бы спички в ЕС, что ли.. такие же самые тем более, не хуже, не лучше.

Quote (Pesets)
А я не думаю, что начинающим нужно вообще разбирать формат TGA. Библиотек для работы с изображениями в интернетах пруд пруди. И бесплатных тоже. Так что, если человек решится писать свой движок, то ему гораздо выгоднее будет подобные задачи оставить сторонним библиотекам, а самому сосредоточиться на тех вещах, которые его не устроили в существующих движках...

Вообще я задумывал тутор не по ТГА, а по наложению текстур вообще, с 0 то есть. Для читателей, которые раньше с текстурами вообще не работали. Я думал сделать руками пустую текстуру и заполнить ее одним цветом целиком, но потом решил что можно и ТГА взять, код тем более небольшой и не сложный. Также и с бинарным файлом можно поработать немного, кто этого не делал еще. Можно было бы и БМП грузить, только там не сильно проще. А если devIL использовать например, так ведь надо нужные файлы приложить или предлагать читателю их найти самому. Здесь же все в одном файле и сразу.

Вообще, да, может быть это здесь не очень нужно, хотя не факт что совсем бесполезно. Я думаю, главное, что есть, потому что лучше когда что-то есть, чем вообще нет ничего.

SaiteiДата: Понедельник, 20 Апреля 2015, 21:00 | Сообщение # 6
старожил
Сейчас нет на сайте
Тему открепляю. Программистов хочу предостеречь, что использование функций по типу glBegin/End/Vertex... не рекомендуется в новых версиях OpenGL.
Нынче это является устаревшим функционалом и вывод картинки производится с помощью шейдеров. Рекомендую начинать знакомство с OpenGL версии 3.3 и старше smile

Если автор урока захочет переписать или написать новый урок - убедительно прошу написать мне в ЛС!
  • Страница 1 из 1
  • 1
Поиск:

Все права сохранены. GcUp.ru © 2008-2024 Рейтинг