Мультитекстурирование в OpenGL
Для получения ряда визуальных эффектов (карты освещенности, туман, микрофактурные текстуры и т.п.) часто возникает необходимость наложения на грань не одной текстуры, а сразу нескольких.
К сожалению, стандартный OpenGL позволяет накладывать только одну текстуру за раз (проход), что
приводит к необходимости использовать несколько проходов для вывода граней. При этом временные затраты на рендеринг объектов возрастают в соответствующее число раз.
Поэтому естественным является желание добавить возможность за один проход накладывать сразу
несколько текстур. И не случайно, что первым официальным ARB-расширением, принятым в сентябре 1998 года является
именно расширение ARB_multitexture.
Данное расширение позволяет за один проход накладывать несколько текстур, при этом для каждой
выводимой текстуры можно задать свои параметры наложения, свой набор текстурных координат, свою
матрицу преобразования текстурных координат и т.п.
Общая схема мультитекстурирования представлена на следующем рисунке:
Рис. 1. Общая схема мультитекстурирования в OpenGL.
Для проверки подержки данного расширения можно использовать функцию isExtensionSupported,
рассмотренную в предыдущей статье.
Если
isExtensionSupported ( "GL_ARB_multitexture" )
возвращает значение true, то данное расширение поддерживается.
Различные видеоускорители поддерживают разное число текстурных блоков (различных текстур, которые
можно наложить за один проход). Их число для данного ускорителя (и драйвера) можно определить при
помощи функции glGetIntegerv.
int maxTextureUnits;
glGetIntegerv ( GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits );
Максимальное число текстурных блоков, поддерживаемых GL_ARB_multitexture равно 32.
Для использования данного расширения следует получить адреса новых функций. Это можно
сделать следующим образом:
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL;
PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL;
PFNGLMULTITEXCOORD1FARBPROC glMultiTexCoord1f = NULL;
PFNGLMULTITEXCOORD1FVARBPROC glMultiTexCoord1fv = NULL;
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2f = NULL;
PFNGLMULTITEXCOORD2FVARBPROC glMultiTexCoord2fv = NULL;
bool initMultitexture ()
{
glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)
wglGetProcAddress ( "glActiveTextureARB" );
glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)
wglGetProcAddress ( "glClientActiveTextureARB" );
glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FARBPROC)
wglGetProcAddress ( "glMultiTexCoord1fARB" );
glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVARBPROC)
wglGetProcAddress ( "glMultiTexCoord1fvARB" );
glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FARBPROC)
wglGetProcAddress ( "glMultiTexCoord2fARB" );
glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVARBPROC)
wglGetProcAddress ( "glMultiTexCoord2fvARB" );
return glActiveTextureARB != NULL &&
glClientActiveTextureARB != NULL &&
glMultiTexCoord1f != NULL &&
glMultiTexCoord1fv != NULL &&
glMultiTexCoord2f != NULL &&
glMultiTexCoord2fv != NULL;
}
Приведенная функция инициализирует указатели на часто используемые функции для работы с
мультитекстурированием и возрващает значение true, если инициализация прошла успешно.
После того, как указатели на функции получены, можно (после загрузки текстур, естественно) задать
используемые текстуры.
Сначала следует при помощи функции glActiveTextureARB задать активный текстурный модуль (используемую текстуру и способ ее применения:
glActiveTextureARB ( texture );
Параметр этой функции задает номер текстурного модуля и может принимать одно из следующих значений - GL_TEXTURE0_ARB, GL_TEXTURE1_ARB, GL_TEXTURE2_ARB, ..., GL_TEXTURE31_ARB.
После задания используемого текстурного модуля следует разрешить использование текстуры ( при помощи команды glEnable ( GL_TEXTURE_2D ) ), задать используемую текстуру и ее параметры (при помощи функций glBindTexture и glTexParameter), а также задать способ применения этой текстуры.
Ниже приводится простой пример задания двух текстурных модулей:
glBindTexture ( GL_TEXTURE_2D, texture1 );
glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_MAX_FILTER, GL_LINEAR );
glTexImage2D ( GL_TEXTURE_2D, GL_RGBA, width1, height1, 0, format1,
GL_UNSIGNED_BYTE, pixels1 );
glBindTexture ( GL_TEXTURE_2D, texture2 );
glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameterf ( GL_TEXTURE_2D, GL_TEXTURE_MAX_FILTER, GL_LINEAR );
glTexImage2D ( GL_TEXTURE_2D, GL_RGBA, width2, height2, 0, format2,
GL_UNSIGNED_BYTE, pixels2 );
glActiveTextureARB ( GL_TEXTURE0_ARB );
glEnable ( GL_TEXTURE_2D );
glBindTexture ( GL_TEXTURE_2D, texture1 );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
glActiveTextureARB ( GL_TEXTURE1_ARB );
glEnable ( GL_TEXTURE_2D );
glBindTexture ( GL_TEXTURE_2D, texture2 );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
При задании вершин выводимого объекта для каждой задаваемой вершины следует задать свой набор
текстурных координат при помощи функции glMultiTexCoord:
glMultiTexCoord2f ( texture, u, v );
glMultiTexCoord2fv ( texture, ptr );
Первый параметр задает один из текстурных модулей (т.е. принимает одно из значений GL_TEXTUREn_ARB, n = 0,...,31). Дальше задаются текстурные координаты либо как набор координат, либо как указатель на массив координат).
Обратите внимание, что по аналогии с функцией glTexCoord существуют варианты функции glMultiTexCoord для разных типов входных данных и разного числа текстурных координат (от 1 до 4).
На следующем примере показывается задание текстурных координат при использовании двух текстурных модулей:
glBegin ( GL_TRIANGLES );
glMultiTexCoord2fv ( GL_TEXTURE0_ARB, &t0 [0] );
glMultiTexCoord2fv ( GL_TEXTURE1_ARB, &t1 [0] );
glVertex3fv ( &v [0] );
glMultiTexCoord2fv ( GL_TEXTURE0_ARB, &t0 [1] );
glMultiTexCoord2fv ( GL_TEXTURE1_ARB, &t1 [1] );
glVertex3fv ( &v [1] );
glMultiTexCoord2fv ( GL_TEXTURE0_ARB, &t0 [2] );
glMultiTexCoord2fv ( GL_TEXTURE1_ARB, &t1 [2] );
glVertex3fv ( &v [2] );
glEnd ();
В следующем примере выводится грань с двумя наложенными на нее текстурами.
void init ()
{
glClearColor ( 0.0, 0.0, 0.0, 1.0 );
glEnable ( GL_DEPTH_TEST );
glActiveTextureARB ( GL_TEXTURE0_ARB );
glEnable ( GL_TEXTURE_2D );
glBindTexture ( GL_TEXTURE_2D, texture1 );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL );
glActiveTextureARB ( GL_TEXTURE1_ARB );
glEnable ( GL_TEXTURE_2D );
glBindTexture ( GL_TEXTURE_2D, texture2 );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
}
void display ()
{
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glBegin ( GL_QUADS );
glMultiTexCoord2f ( GL_TEXTURE0_ARB, 0, 0 );
glMultiTexCoord2f ( GL_TEXTURE1_ARB, 0, 0 );
glVertex3f ( -1, -1, 0 );
glMultiTexCoord2f ( GL_TEXTURE0_ARB, 1, 0 );
glMultiTexCoord2f ( GL_TEXTURE1_ARB, 5, 0 );
glVertex3f ( 1, -1, 0 );
glMultiTexCoord2f ( GL_TEXTURE0_ARB, 1, 1 );
glMultiTexCoord2f ( GL_TEXTURE1_ARB, 5, 5 );
glVertex3f ( 1, 1, 0 );
glMultiTexCoord2f ( GL_TEXTURE0_ARB, 0, 1 );
glMultiTexCoord2f ( GL_TEXTURE1_ARB, 0, 5 );
glVertex3f ( -1, 1, 0 );
glEnd ();
glutSwapBuffers ();
}
Поскольку текстурные координаты для каждого текстурного модуля задаются независимо, то для каждого текстурного модуля можно задать свою матрицу преобразования текстурных координат, а также способ автоматической генерации текстурных координат и свой закон наложения текстуры.
В следующем примере выводится один из стандартных объектов объектов glut с наложением как обычной текстуры, так и текстуры, имитирующей отражение (environment mapping).
void init ()
{
glClearColor ( 0, 0, 0, 1 );
glEnable ( GL_DEPTH_TEST );
glActiveTextureARB ( GL_TEXTURE0_ARB );
glEnable ( GL_TEXTURE_2D );
glBindTexture ( GL_TEXTURE_2D, texture1 );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE );
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, texture2 );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
}
void display ()
{
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glPushMatrix ();
glTranslatef ( 2, 2, 2 );
glRotatef ( angle, 1, 1, 0 );
glRotatef ( angle2, 0, 1, 1 );
glutSolidTeapot ( 1 );
glPopMatrix ();
glutSwapBuffers ();
}
void reshape ( int w, int h )
{
glViewport ( 0, 0, (GLsizei)w, (GLsizei)h );
glMatrixMode ( GL_PROJECTION );
glLoadIdentity ();
gluPerspective ( 60.0, (GLfloat)w/(GLfloat)h, 1.0, 60.0 );
glMatrixMode ( GL_MODELVIEW );
glLoadIdentity ();
gluLookAt ( 0.0, 0.0, 0.0, // eye
5.0, 5.0, 5.0, // center
0.0, 1.0, 0.0 ); // up
}
void animate ()
{
angle = 0.04f * glutGet ( GLUT_ELAPSED_TIME );
angle2 = 0.01f * glutGet ( GLUT_ELAPSED_TIME );
glutPostRedisplay ();
}
Также существует возможность использовать мультитекстурирования при работе с вершинными массивами (vertex array).
Для этого служит функция glClientActiveTextureARB. Она берет в качестве входного параметра номер текстурного модуля (GL_TEXTURE0_ARB, ..., GL_TEXTURE31_ARB) и относит к нему следующие команды:
glTexCoordPointer
glEnableClientState
glDisableClientState
Так задать массивы текстурных координат для первых двух текстурных модулей можно при помощи следующего фрагмента кода:
// setup 1st texture unit
glClientActiveTextureARB ( GL_TEXTURE0_ARB );
glTexCoordPointer ( 2, GL_FLOAT, 0, p0 );
glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
// setup 2nd texture unit
glClientActiveTextureARB ( GL_TEXTURE1_ARB );
glTexCoordPointer ( 2, GL_FLOAT, 0, p1 );
glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
Примеры кода к этой статье можно скачать здесь
Рис 2. Пример использования мультитекстурирования.
Дополнительную информацию по мультитекстурированию можно найти на следующих сайтах
developer.nvidia.com,
www.berkelium.com/OpenGL/examples,
nomad.openglforums.com,
www.gamedev.ru.
|