ПрограммированиеСтатьиГрафика

Работа с расширениями OpenGL с использованием NVIDIA OpenGL SDK 5.1. (Часть 2)

Автор:

Использование расширения ARB_multitexture
Использование расширения EXT_texture_env_add
Влияние расширения ARB_multitexture на производительность

Использование расширения ARB_multitexture

Известно, что в стандартном OpenGL на объект можно наложить не более одной текстуры за один проход. Но в реальных приложениях редко можно обойтись только одной текстурой - например при использовании карт освещения приходится поверх основной текстуры объекта накладывать еще и текстуру освещения. В случае использования хромированных поверхностей без второй текстуры тоже никак не обойтись.

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

Перед использованием расширения его нужно инициализировать. Это можно сделать командой glh_init_extensions("GL_ARB_multitexture") библиотеки OpenGL Helper Library. После этого желательно определить количество доступных текстурных модулей. Это делает команда glGetIntegerv с параметром GL_MAX_TEXTURE_UNITS_ARB. Следует учитывать, что большинство современных 3D-ускорителей, включая GeForce2, оборудовано двумя текстурными модулями, поэтому если вы хотите, чтобы ваша программа работала на всех современных видеокартах, старайтесь не задействовать больше двух текстурных модулей.

Выбор активного текстурного модуля осуществляется командой glActiveTextureARB. В качестве параметра она принимает константы, например GL_TEXTURE0_ARB, GL_TEXTURE1_ARB и т.д., которые делают активным первый или второй текстурный модуль соответственно. Всего в текущей реализации OpenGL предусмотрено до 32 текстурных модулей (т.е. до GL_TEXTURE31_ARB). По умолчанию активен первый текстурный модуль. После выбора текстурного модуля с ним можно работать, как обычно, используя команды glEnable(), glTexParameter(), glTexEnv() и т.д. При этом следует учитывать, что эти команды меняют состояние только активного текстурного модуля. Матрица текстуры тоже уникальна для каждого модуля. При наложении текстуры сначала вычисляется цвет поверхности на основе параметров материала и источников света, затем накладывается текстура первого модуля, потом второго и т.д. Причем, чтобы текстура второго и последующих модулей не замещала предыдущие, надо с помощью команды glTexEnv установить режим наложения, например GL_ADD, GL_BLEND, GL_MODULATE и т.д.

При использовании мультитекстурирования, необходимо для каждого текстурного модуля  указывать свой набор текстурных координат. Для этого используется команда MultiTexCoordARB, которая является расширенной версией команды TexCoord. В качестве первого параметра указывается идентификатор текстурного модуля, остальные параметры - такие же, как в команде TexCoord.

Рассмотрим пример (в архиве, который можно скачать на сайте, его можно найти в директории Ex01), в котором на прямоугольную площадку накладываются две текстуры, взятые из NVIDIA OpenGL SDK (см. рис. 1). На первой из них нарисована шахматная доска, на второй - логотип NVIDIA. Основой для этого примера послужил пример SimpleMultitexture из NVIDIA SDK. Оригинальный пример довольно сложен для понимания, т.к. он, как и все остальные примеры NVIDIA, использует не чистый OpenGL, а объектно-ориентированную надстройку над OpenGL, разработанную NVIDIA.

Изображение
Рисунок 1.

Сначала мы, используя библиотеку NV_UTIL, загружаем карты текстуры в память.

pTexture0=tga::read("checker2.tga"); //Загрузка текстур
pTexture1=tga::read("nvlogo.tga");

Затем создаем два списка текстур, для каждого из которых устанавливаем карту текстуры и параметры фильтрации.

glGenTextures(2, &Texture0);
Texture1=Texture0+1;

glBindTexture(GL_TEXTURE_2D, Texture0);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pTexture0->width, pTexture0->height,
             0, pTexture0->format, GL_UNSIGNED_BYTE, pTexture0->pixels);

glBindTexture(GL_TEXTURE_2D, Texture1);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pTexture1->width, pTexture1->height, 
             0, pTexture1->format, GL_UNSIGNED_BYTE, pTexture1->pixels);

Теперь можно переходить непосредственно к настройке текстурных модулей: 
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Texture0);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Texture1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);

Сначала командой glActiveTextureARB() мы устанавливаем активный текстурный модуль, затем командами glEnable() и glBindTexture() включаем режим наложения текстуры и привязываем текстуру к текущему текстурному модулю. Последней командой мы устанавливаем режим наложения текстуры: для первого модуля устанавливаем режим замещения, т.е. параметры материала игнорируются, а для второго - режим сложения пикселей первого текстурного модуля со вторым (т.е. в точках, где логотип NVIDIA имеет черный цвет, он будет прозрачным).

Использование расширения EXT_texture_env_add

Обратите внимание, что в команде glTexEnvi() используется новый режим наложения текстуры — GL_ADD. Включение этого режима приводит к тому, что результирующий цвет вычисляется как сумма одноименных цветовых компонентов поверхности, на которую накладывается текстура, и текстуры. Т.е.

Cv=min(1, Cf+Ct). 
За поддержку этого режима отвечает расширение EXT_texture_env_add. Это расширение было включено в состав OpenGL 1.3 под названием ARB_texture_env_add. Но т.к. NVIDIA OpenGL SDK 5.1 не содержит заголовочных файлов для OpenGL 1.3, я буду использовать расширение EXT_texture_env_add.

Стоит отметить, что указанный выше фрагмент нельзя переписать без использования списков текстур следующим образом:

glActiveTextureARB(GL_TEXTURE0_ARB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pTexture0->width, pTexture0->height,
             0, pTexture0->format, GL_UNSIGNED_BYTE, pTexture0->pixels);
//--------------------
glActiveTextureARB(GL_TEXTURE1_ARB);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, pTexture1->width, pTexture1->height, 
             0, pTexture1->format, GL_UNSIGNED_BYTE, pTexture1->pixels);

Это связанно с тем, что команда glTexImage меняет состояние всех текстурных модулей одновременно. Следовательно, вышеуказанный код нарисует логотип NVIDIA с яркостью, умноженной на 2.

Теперь можно создать дисплейный список, рисующий квадратную площадку:

List=glGenLists(1);
glNewList(List, GL_COMPILE);
  glBegin(GL_QUADS);
    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
    glVertex3f(-1, -1, 0);

    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
    glVertex3f(-1, 1, 0);
    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
    glVertex3f( 1,  1, 0);

    glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
    glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
    glVertex3f( 1, -1, 0);
  glEnd();
glEndList();

Здесь нет ничего необычного за исключением того, что координаты текстур первого и второго модуля задаются командой glMultiTexCoord2fARB(). В остальных частях программы нет ничего особенного.

Давайте немного доработаем пример (см. рис. 2), добавив вращение текстуры с логотипом NVIDIA относительно шахматной доски (пример Ex02 в архиве). Вращать текстуру поможет использование матрицы текстуры. Задача облегчается тем, что каждый модуль имеет свою, независимую матрицу текстуры.

Изображение
Рисунок 2.

Для добавления этой возможности нам достаточно включить всего несколько строк в функцию Display():

glActiveTextureARB(GL_TEXTURE1_ARB);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glTranslatef( 0.5, 0.5, 0);
glRotatef(rr, 0, 0, 1);
glTranslatef(-0.5,-0.5, 0);
glMatrixMode(GL_MODELVIEW);
glActiveTextureARB(GL_TEXTURE0_ARB);

Как видите, все достаточно просто: активизируем второй текстурный модуль и матрицу текстуры, переносим центр системы координат текстуры в центр площадки (с координатами 0.5, 0.5), выполняем поворот и осуществляем обратный перенос (если не выполнять перенос, то текстура будет вращаться вокруг левого нижнего угла).

Собственно, анимация организуется традиционным для GLUT образом, с использованием функции установки фонового обработчика glutIdleFunc():

void Idle()
{
  rr+=(GetTickCount()-OldTick)*0.04;
  OldTick=GetTickCount();
  if (rr>360)
    rr-=360;

  glutPostRedisplay();
}

Теперь, после того, как мы разобрались с основами мультитекстурирования, посмотрим, как можно организовать работу с хромированными поверхностями. В качестве упражнения попробуем изменить пример Ex04 из прошлой статьи - сделаем деревянный чайник хромированным (см. рис. 3). В качестве зеркальной текстуры будем использовать файл chromic.tga из NVIDIA OpenGL SDK. Исходный текст примера находится в каталоге Ex03.

Изображение
Рисунок 3.

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

glActiveTextureARB(GL_TEXTURE1_ARB); // Настройки второго текстурного блока
glEnable(GL_TEXTURE_2D);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glBindTexture(GL_TEXTURE_2D, Texture1);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
Страницы: 1 2 Следующая »

#расширения OpenGL, #NVIDIA, #OpenGL, #SDK, #текстурирование

23 февраля 2002 (Обновление: 17 сен 2009)