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

Direct3D: Мультитекстурирование.

Автор:

Здравствуйте, дорогие мои детишечки 8-)

В предыдущем уроке мы рассмотрели, как работать с текстурами в Direct3D. Сегодня же мы рассмотрим такую возможность, как мультитекстурирование. Что же это такое? Мультитекстурирование - это наложение нескольких текстур на один полигон. Наиболее часто такой метод применяется для имитации освещения. То есть, заранее рассчитывается степень освещенности той или иной части полигональной модели, а потом составляется карта освещенности (light map), которая накладывается "поверх" основной текстуры объекта, в результате чего создается эффект неравномерного освещения поверхности. Такой прием гораздо быстрее, чем расчет освещения "на лету", поэтому он нашел широкое применение в играх. Также существует множество областей применения мультитекстурирования, например, для имитации некоторых спецэффектов. В данном уроке мы рассмотрим имитацию освещения полигона. Этот пример основан на предыдущем уроке, так что для не читавших, рекомендую его прочесть: Direct3D: Текстурирование.

Ну что ж, поехали!

Для начала, я немного изменил формат наших вершин, добавив туда параметр, определяющий их цвет. В принципе, это действие не обязательно, но оно предоставляет широкое поле для экспериментов и позволяет добиться красивых эффектов. С помощью него мы сможем изменить цвет нашего "прожектора" (света). Причем конечный цвет получается путем интерполяции цветовой составляющей каждой из вершин. По умолчанию (если цвет не задан), используется белый цвет. Теперь наш формат вершин выглядит следующим образом:

 
#define MY_VERTEX_FORMAT (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1 | D3DFVF_DIFFUSE)

struct SMyVertex
{
  D3DXVECTOR3 pos;    // Положение вершины в пространстве
  D3DXVECTOR3 norm;   // Нормаль
  DWORD dwColor;      // Цвет
  FLOAT tu, tv;       // Текстурные координаты
};

Для второй текстуры можно использовать свои текстурные координаты, для этого надо добавить в описание вершин еще пару координат tu и tv, а также изменить флаг D3DFVF_TEX1 на D3DFVF_TEX2. Второй набор текстурных координат можно использовать, например, для анимации перемещения источника света. Для этого первый набор остается прежним, а второй набор изменяется. Естественно, можно использовать и более 2 текстур. Direct3D допускает использование одновременно до 8 текстур.

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

if (FAILED(D3DXCreateTextureFromFile(g_pd3dDevice, "Spotlight.bmp",&g_pSpotLight)))
  return false;

Для реализации мульти-текстурирования в Direct3D используются текстурные уровни (texture stages). Каждая из максимально восьми текстур может быть помещена в один из этих уровней. Делается это при помощи функции SetTexture следующим образом:

  g_pd3dDevice->SetTexture(0, g_pTexture);
  g_pd3dDevice->SetTexture(1, g_pSpotLight);

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

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

 
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, 0 );
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_MODULATE );
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
  g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1 );

В первой строке мы устанавливаем тот набор текстурных координат, который мы хотим использовать для соответствующей текстуры (текстура определяется по номеру уровня - первый параметр функции). В нашем примере мы используем один набор координат, поэтому устанавливаем D3DTSS_TEXCOORDINDEX в 0. Во второй строке мы устанавливаем режим фильтрации текстур. Я установил билинейную фильтрацию.

То же самое проделываем для второй текстуры:

 
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 );
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR );
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG2, D3DTA_DIFFUSE );
  g_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP,   D3DTOP_SELECTARG1);

Рассмотрим оставшиеся текстурные состояния:

D3DTSS_COLORARG1(2) - данное состояние определяет первый (второй) цветовой аргумент для текстурного уровня, может принимать следующие значений:
- D3DTA_CURRENT - аргумент текстуры - состояние, с которым отображался предыдущий текстурный уровень. Если это первый текстурный уровень (0), то этот аргумент равносилен D3DTA_DIFFUSE.
- D3DTA_DIFFUSE - аргумент текстуры - цвет, полученный интерполяцией цвета вершин с использованием затенения по Гуро. Если вершины не содержат цветовой составляющей, то выбирается белый (0xffffff) цвет.
- D3DTA_TEXTURE - аргумент текстуры - цвет текстуры для данного текстурного уровня.

D3DTSS_ALPHAARG1(2) - определяет первый (второй) альфа аргумент для текстурного уровня может принимать те же значения, что и D3DTSS_COLORARG1(2). Значение по умолчанию - D3DTA_TEXTURE. Если для данного уровня текстура не установлена, то используется D3DTA_DIFFUSE.

D3DTSS_COLOROP - 'это состояние устанавливает каким образом будет смешиваться цвета текстур. Может принимать следующие значения:
- D3DTOP_DISABLE - отключает вывод текстур из текущего уровня (stage) и для всех высших уровней. Чтобы отключить вывод текстур установите это значение для первого текстурного уровня.
- D3DTOP_SELECTARG1(2) - для вывода будет использоваться первый (второй) цветовой или альфа аргумент.
- D3DTOP_MODULATE - перемножает цветовые компоненты из ARG1 и ARG2
- D3DTOP_MODULATE2X(4Х) - перемножает цветовые компоненты и сдвигает результат на 1(2) бит влево, в результате чего получается умножение результата на 2(4), т.е. изображение становится ярче.
- D3DTOP_ADD - складывает цветовые компоненты ARG1 и ARG2
- D3DTOP_SUBTRACT - результирующий цвет получается путем вычитания второго аргумента ARG2 из первого ARG1.

По умолчанию D3DTSS_COLOROP для первого текстурного уровня имеет значение D3DTOP_MODULATE, а для всех остальных D3DTOP_DISABLE.

Помимо перечисленных существует еще несколько состояний, о них можно прочитать в DirectX8 SDK Help раздел D3DTEXTURESTAGESTATETYPE

После всех этих операций остается только нарисовать наш примитив. Делается это стандартным образом:

 
  g_pd3dDevice->SetStreamSource(0, g_pVB, sizeof(SMyVertex));
  g_pd3dDevice->SetVertexShader(MY_VERTEX_FORMAT);
  g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

Если все сделано правильно, то мы должны получить якобы освещенный прожектором полигон. Вот что получилось у меня:

Изображение

Скачать исходник (222 kБ)

#Direct3D, #текстурирование

4 апреля 2002 (Обновление: 25 июня 2009)