* Что такое мультитекстуринг? *
Мультитекстуринг — это 2 и более слоя текстур, наложенных друг на друга.
Он используется для создания световых карт, теней, и других графических техник.
В принципе, вы можете достигнуть тех же эффектов и без мультитекстуринга, но вам придется перерисовывать одну и ту же геометрию более одного раза.
Например, вы можете отрисовать стену, а потом чуть-чуть впереди неё нарисовать световую карту.

Мультитекстуринг был доступен в OpenGL не сразу.
API для мультитекстуринга называется «расширение» (extension).
Чтобы включить расширение, вы можете либо включить его в ваш проект, либо включить прилагаемый «glext.h», содержащий все существующие расширения. Если вы заметили, их там тонны. Расширения — это такие штуки, которые хотя и находятся вне стандартного API, но добавлены в его возможности. Для мультитекстурирования вам нужна последняя версия OpenGL. Чтобы узнать, можете ли вы работать с мультитекстурированием, попробуйте использовать функцию glGetString(GL_EXTENSIONS).
Она возвращает (char *), который содержит все доступные вам расширения.

Код этого урока взят из урока «Загрузка текстур».

Изменения коснутся только файла main.cpp:

// В начале добавляем ещё один хидер:
#include «glext.h»// Потом увеличиваем количество загружаемых текстур до четырех:
TextureImage textures[4];
//                    ^/*
Эта программа предлагает 2 примера мультитекстуринга.
Первый — простая карта света, а второй — статичный бэкграунд с ползущей
по нему картой облаков.
Чтобы использовать расширения, нам нужно загрузить функции из OpenGL DLL.
Давайте взглянем на код.

Существуют функции, управляющие мультитекстурированием. Они будут загружены
вызовом wglGetProcAddress(). Вы должны включить «glext.h», или дефайнить эти
типы в вашем коде. Если вы этого не сделаете, у вас не будет прототипов для
функций мультитекстуринга.
ARB == Architecture Review Board. Подробности вы можете найти в интернете, но
основное, что они делают — помогают обьявлять стандарты OpenGL.

Следующие указатели на функции позволяют нам указать, какие текстуры с какими
мы хотим связать. Сам процесс вы увидите в RenderScene().
*/

/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
PFNGLMULTITEXCOORD2FARBPROC     glMultiTexCoord2fARB    = NULL;
PFNGLACTIVETEXTUREARBPROC       glActiveTextureARB  = NULL;
/////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *

// Теперь переходим к функции инициализации (Init()):
void Init(HWND hWnd)
{
g_hWnd = hWnd;
GetClientRect(g_hWnd, &g_rRect);
InitializeOpenGL(g_rRect.right, g_rRect.bottom);

// Инициализируем класс
Texture = new CTexture();

// Здесь, после инициализации OpenGL, мы загружаем функции из wglGetProcAddress
// и сохраняем в указателях функций. wglGetProcAddress возвращает адрес функции
// расширения OpenGL.
glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress(«glActiveTextureARB»);
glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) wglGetProcAddress(«glMultiTexCoord2fARB»);

// Нам нужны обьявленные и готовые к употреблению функции мультитекстуринга,
// давайте убедимся, что на машине присутствует нужная версия OpenGL.
// Если функция расширения не найдена, указатели функций будут равны NULL
if(!glActiveTextureARB || !glMultiTexCoord2fARB)
{
// Print a error message and quit.
MessageBox(NULL, «Your version of OpenGL does not support multitexturing», «Error», MB_OK);
PostQuitMessage(0);
}

// У нас будет 4 текстуры. Используем класс CTexture для их загрузки.
Texture->LoadTexture(IL_BMP,«Bitmap.bmp», &textures[0]);
Texture->LoadTexture(IL_BMP,«LightMap.bmp», &textures[1]);
Texture->LoadTexture(IL_BMP,«Cove.bmp», &textures[2]);
Texture->LoadTexture(IL_BMP,«Fog.bmp», &textures[3]);
}

// Ну и наконец приступим к отрисовке.
// Функция RenderScene():

void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

// Переместимся назад, чтобы видеть наш квадрат:
glTranslatef(0,0,-7);

// А вот и самое интересное. Нам нужно определить, какая текстура относится к
// какому ID. ID указывается в указателе «GL_TEXTUREx_ARB», где «x» — и есть ID.
// Вы можете указить вплоть до 32 текстур, но ваша видеокарта может ненароком охренеть.
// Мы будем использовать только 2.
// Чтобы привязать текстуру к ID, мы просто делаем активным ID нужной текстуры
// функцией glActiveTexture(), затем вызываем glBindTexture() с индексом нашей текстуры.
// Теперь каждый раз, используя glMultiTexCoord2fARB(), мы просто передаём ей
// ID текстуры, и она знает, что текстурные координаты — для текстуры, найденной по
// этому ID.

// Установим активным ID первой текстуры, затем забиндим текстуру кирпичей к этому ID.
// Перед тем, как биндить текстуру, вы ДОЛЖНЫ вызвать: glEnable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,textures[0].texID);

// Сделаем id второй текстуры активным, и забиндим нашу карту света на этот ID.
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,textures[1].texID);

// Так как мы хотим перемещать текстурированные квадраты в различные координаты,
// мы вызываем новую матрицу, так что изменения не коснутся других квадратов.
glPushMatrix();
// В текущей матрице квадрат переместится в левую часть экрана
glTranslatef(2,0,0);

// Отобразим мультитекстурированный квадрат в левой части экрана
glBegin(GL_QUADS);
// Теперь, вместо glTextCoord2f() воспользуемся ф-ей glMultiTextCoord2fARB(),
// чтобы указать текстурные координаты для каждой текстуры. Это позволит
// нам передвинуть нашу текстуру в совершенно другие координаты.
// Превосходный пример этого — тени. glMultiTexCoord2fARB() принимает
// ID текстуры, который мы указываем, с нужными текстурными координатами.
// Сейчас мы используем одинаковые координаты для каждой текстуры.

// Левый верхний угол
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 1.0f);
glVertex3f(1, 1, 0);

// Нижний левый угол
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f, 0.0f);
glVertex3f(1, 1, 0);

// Нижний правый угол
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 0.0f);
glVertex3f(1, 1, 0);

// Верхний правый угол
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f, 1.0f);
glVertex3f(1, 1, 0);
glEnd();
// И выходим из матрицы, чтобы перемещения не затронули следующий квадрат.
glPopMatrix();

// Теперь напишем второй пример мультитекстуринга в правой части экрана.
// Мы можем переназначать новые текстуры к использованным GL_TEXTURE*_ARB.
// Мы могли бы использовать GL_TEXTURE2_ARB и GL_TEXTURE3_ARB, но это не нужно.

// Активируем первый ID и забиндим на него бэкграунд:
glActiveTextureARB(GL_TEXTURE0_ARB);
glBindTexture(GL_TEXTURE_2D,  textures[2].texID);

// На второй ID забиндим текстуру дыма.
glActiveTextureARB(GL_TEXTURE1_ARB);
glBindTexture(GL_TEXTURE_2D,  textures[3].texID);

// Чтобы сделать дым более реалистичным, заставим его плыть над фоном.
// Для этого создадим static float, содержащий счетчик перемещения.
// Так как наша текстура — GL_WRAP, будет эффект непрерывности.

// Создадим счетчик
static float wrap=0;

// Сдвинем квадрат направо
glTranslatef(2,0,0);

// И начинаем рисовать
glBegin(GL_QUADS);
// Рисуем левую верхнюю вершину, вычитая из координаты текстуры счетчик,
// чтобы добится эффекта скроллинга.
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f wrap, 1.0f);
glVertex3f(1, 1, 0);

// Нижняя левая вершина
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 0.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f wrap, 0.0f);
glVertex3f(1, 1, 0);

// Нижняя правая вершина
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 0.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f wrap, 0.0f);
glVertex3f(1, 1, 0);

// верхняя правая вершина
glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1.0f, 1.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1.0f wrap, 1.0f);
glVertex3f(1, 1, 0);
glEnd();

// Увеличим счетчик перемещения
wrap += 0.0015f;

SwapBuffers(g_hDC);
}

 

Вот и всё! Держу пари, вы думали, что придется поработать побольше, не так ли?
В будущем вы сможете затенять любые поверхности, делать ярче бэкграунд, и ещё много всяких вкуснотей. Вам понадобится расширение EXT_texture_env_combine.
Больше информации о нем будет в будущих уроках. Это расширение позволит вам осуществлять бамп-маппинг очень и очень быстро.

Давайте обобщим:

1) Прежде чем что-нибуть делать, необходимо создать указатели на функции, прочитанные из расширения. Чтобы использовать эти типы данных, нужно либо скопировать их прототипы себе в программу, либо просто включить хидер «glext.h». Как вариант, вы можете поместить этот файл в директорию инклудов VC++.

PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = NULL;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL;

2) Теперь нужно прочитать и сохранить указатели функций, и проверить, правильные ли адреса мы получили.

glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress(«glActiveTextureARB»);
glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) wglGetProcAddress(«glMultiTexCoord2fARB»);

3) Когда всё готово к рендеру, нам нужно привязать нашу текстуру к мультитекстурному ID.
Этот ID мы можем потом использовать для указания, какой именно мультитекстуре назначаем координаты.

glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textures[0].texID);

4) Теперь, чтобы задать текстурные координаты для нашей текстуры, мы просто передаём мультитекстурный ID, который нам нужен.

glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0.0f, 1.0f);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0.0f — wrap, 1.0f);
glVertex3f(-1, 1, 0);

Вот и всё! Наслаждайтесь 😉

Исходные коды к уроку