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

Работа с расширениями 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

Страницы: 1 2

#OpenGL, #расширения

10 октября 2003

Комментарии [4]