Работа с расширениями OpenGL с использованием NVIDIA OpenGL SDK 5.1. (Часть 8) (2 стр)
Автор: Сергей Гайдуков
Примеры
Для начала, рассмотрим простую задачу. Пусть нам надо реализовать наложение двух текстур на поверхность по формуле:
C=2*(Cf*Ct1+(1-Ct2)-0.5),
A=Af,
где t1 и t2 – две текстуры, а f – первичный цвет поверхности.
Для реализации такого наложения текстур достаточно двух текстурных блоков.
На нулевом блоке надо будет запрограммировать наложение текстуры t1 с использованием режима GL_MODULATE:
glActiveTextureARB(GL_TEXTURE0_ARB);
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
В результате, после работы первого текстурного блока текущий цвет поверхности станет следующим:
Cp=Cf*Ct1
Ap=Af*At1
Теперь перейдём к настройке второго текстурного блока. После анализа таблицы мы приходим к выводу, что для RGB-составляющей лучше всего подойдёт выражение Arg0+Arg1-0.5. Поэтому включаем режим GL_COMBINE_ARB и включаем использование этого выражения для RGB-составляющей цвета (константа GL_ADD_SIGNED_ARB):
glActiveTextureARB(GL_TEXTURE1_ARB); glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD_SIGNED_ARB);
Но выражение Arg0+Arg1-0.5 нас не совсем устраивает – нам необходимо использовать формулу 2*(Arg0+Arg1-0.5). Поэтому мы включаем умножение конечного результата на 2:
glTexEnvi(GL_TEXTURE_ENV, GL_RGB_SCALE_ARB, 2);
Теперь нам необходимо сопоставить параметрам Arg0 и Arg1. Если мы внимательно посмотрим на формулу, которая нам нужна, то увидим, что нам надо подставить вместо Arg0 выражение Cf*Ct1. Но ведь это результат работы первого текстурного блока, который доступен нам как Cp. Следовательно, нам надо сопоставить параметру Arg0 величину Cp.
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR);
Проведя аналогичные рассуждения для второго параметра, мы увидим, что ему необходимо сопоставить величину Ct. Но нам нужна не сама Ct, а 1-Ct. Поэтому мы должны настроить второй операнд на предварительное вычитание из единицы этой величины перед подстановкой в Arg2:
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_ONE_MINUS_SRC_COLOR);
Остались последние штрихи – настроить расчёт альфа канала, который должен быть равен альфа каналу первичного цвета поверхности (Af):
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
Вот и всё. Как видите всё довольно просто, надо лишь привыкнуть к немного запутанному синтаксису этого расширения.
Рассмотрим пример использования этого расширения из реальной жизни. Пусть нам надо нарисовать стеклянный глобус, на который наклеены материки бумаги или аналогичного материала (см. рисунок 2):
Рисунок 2. Стеклянный глобус. Текстура земли взята из NVIDIA SDK
Т.е. текстура с отражением (в данном случае это текстура неба из 3DS MAX), использующая сферические координаты, должна отражаться только в районе океанов, морей и рек.
Для того, что бы вы смогли лучше понять, что даёт нам расширение GL_ARB_texture_env_combine, мы напишем сначала программу без использования этого расширения (Ex04), а затем перепишем её с использованием этого расширения (Ex05).
Сначала нам надо сформировать текстуру земли, которая была полупрозрачной в районах, содержащих воду, т.е. в областях синего цвета. Для этого надо создать функцию, которая сканировала бы поверхность и, если в текущей точке яркость синего цвета превышало на 25% яркость остальных цветов, устанавливало байт прозрачности равным 175 вместо 255.
Bool MakeSeaTexture(tga::tgaImage* pTextureImage) { const double k=0.8; if ( pTextureImage->components!=3) return false; //Выделяем память под новую текстуру GLubyte* pixels=new GLubyte[pTextureImage->width*pTextureImage->height*4]; GLubyte* p1; GLubyte* p2; //Обходим все пиксели текстуры for ( p1=pTextureImage->pixels, p2=pixels; p1<pTextureImage->pixels+pTextureImage->width* pTextureImage->height*pTextureImage->components; p1++, p2++) { //Копируем RGB-блок без изменений *p2=*p1; p1++; p2++; *p2=*p1; p1++; p2++; *p2=*p1; p2++; // Если синий цвет ярче других более, чем в 1/k раз, // делаем пиксель полупрозрачным if ( ( *( p1)*k>*( p1-1)) && ( *( p1)*k>*( p1-2))) *p2=175; else *p2=255; } //удаляем образ старой текстуры delete[] pTextureImage->pixels; pTextureImage->pixels=pixels; pTextureImage->components=4; pTextureImage->format=GL_RGBA; return true; }
На землю должны накладываться две текстуры: текстура земли (t1) и текстура отражения (t2), причём результирующий цвет должен быть следующим:
R=Ct1*At1+Ct2*(1-At1)
A=Cf
Просмотрев спецификацию OpenGL, мы приходим к выводу, что такое наложение можно запрограммировать только следующим способом: загрузить в нулевой текстурный блок текстуру неба в режиме GL_REPLACE, а в первый – текстуру земли в режиме GL_DECAL:
textureSky.bind(); gluBuild2DMipmaps( textureSky.target,pTextureImageSky->components, pTextureImageSky->width, pTextureImageSky->height, pTextureImageSky->format, GL_UNSIGNED_BYTE, pTextureImageSky->pixels); textureEarth.parameter( GL_TEXTURE_MIN_FILTER, GL_LINEAR); textureEarth.parameter( GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glActiveTextureARB( GL_TEXTURE1_ARB); textureEarth.bind( ); gluBuild2DMipmaps( textureEarth.target,pTextureImageEarth->components, pTextureImageEarth->width, pTextureImageEarth->height, pTextureImageEarth->format, GL_UNSIGNED_BYTE, pTextureImageEarth->pixels); textureEarth.parameter( GL_TEXTURE_MIN_FILTER, GL_LINEAR); textureEarth.parameter( GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); glActiveTextureARB( GL_TEXTURE0_ARB);
Осталось нарисовать шар и наложить на него текстуры. Но здесь возникает маленькая заминка – мы не сможем нарисовать этот шар с использованием функции gluSphere библиотеки GLU. Дело в том, что на нулевом текстурном модуле накладывается сферическая текстура отражения, а на первом – собственно текстура земли. Т.е. текстуры координат объекта должны выводится на второй текстурный блок! А функции библиотеки GLU умеют генерировать координаты только для нулевого текстурного блока. Следовательно, у нас не остаётся иного выбора, как рисовать шар вручную, а это занятие не из приятных. К счастью, в NVIDIA OpenGL SDK имеется пример функции рисования шара с текстурными координатами (drawSphere), которая находится в файле “\src\demos\vtxprog_refract\shapes.cpp”. Правда эта функция также создаёт текстурные координаты для нулевого текстурного блока, но т.к. она распространяется в исходных кодах, исправить код функции можно менее, чем за минуту: достаточно заменить все вызовы glTexCoord2f(…) на glMultiTexCoord2fARB(GL_TEXTURE1_ARB, …).
Теперь остаётся только вывести шар на экран:
textureSky.enable(); 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); glActiveTextureARB( GL_TEXTURE1_ARB); textureEarth.enable( ); glActiveTextureARB( GL_TEXTURE0_ARB); list0.call_list( ); textureSky.disable( ); glDisable( GL_TEXTURE_GEN_S); glDisable( GL_TEXTURE_GEN_T); glActiveTextureARB( GL_TEXTURE1_ARB); textureEarth.disable( ); glActiveTextureARB( GL_TEXTURE0_ARB);
Если же мы воспользуемся расширением GL_ARB_texture_env_combine, то наша задача заметно упростится – мы сможем запрограммировать наложение текстур таким образом, что текстура земли будет находиться на нулевом текстурном блоке, а отражения – на первом:
glActiveTextureARB(GL_TEXTURE0_ARB); //Накладываем текстуру земли в режиме GL_REPLACE glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glActiveTextureARB( GL_TEXTURE1_ARB); //Накладываем текстуру неба в режиме GL_COMBINE glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB); //Используем формулу RGB=Arg0*Arg2+Arg1*(1-Arg2) glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_INTERPOLATE_ARB); //Arg0=Cp glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_RGB_ARB, GL_SRC_COLOR); //Arg1=Ct glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_TEXTURE); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND1_RGB_ARB, GL_SRC_COLOR); //Arg2=At glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE2_RGB_ARB, GL_PREVIOUS_ARB); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND2_RGB_ARB, GL_SRC_ALPHA); //Alpha=Arg0 glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE); //Arg0=Cf glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB); glTexEnvi( GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_ARB, GL_SRC_ALPHA);
Теперь для вывода земли можно воспользоваться стандартной функцией gluSphere, что заметно уменьшит размер программы.
Вот и всё на этот раз. Позже мы продолжим рассмотрение расширения ARB_texture_env_combine, а так же научимся моделировать освещение с использованием карт освещения (lightmap).
Исходный код к статье: 20031010_1.rar
10 октября 2003
Комментарии [4]