Программирование игр, создание игрового движка, OpenGL, DirectX, физика, форум
GameDev.ru / Программирование / Статьи / Создание детальных текстур c помощью mip-map уровней в Direct3D8.

Создание детальных текстур c помощью mip-map уровней в Direct3D8.

Автор: Михаил Хабров


Начнем с определения самого термина "detail texture".
Детальная текстура - это текстура, накладываемая поверх основной таким образом, чтобы при приближении камеры к полигону, она начинала плавно "проступать".

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

Вот пример детальной текстуры:

Изображение

Тут позволю себе одно замечание - так как детальная текстура накладывается ПОВЕРХ основной, то естественно, для нее должны быть установлены некие параметры смешивания (обычно это MODULATE2X).

Таким образом, при рисовании детальной текстуры необходимо помнить, что средний уровень значений цветов должен быть около 128 - то есть середины, а сама картинка должна быть нарисована в градации серого цвета - grayscale.

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

Для этого можно воспользоваться mipmap уровнями текстуры, но тут опять не обойтись без краткой справки.

Mipmap levels - это некое подобие слоев. То есть при создании\загрузки текстуры, создается n-ое количество изображений (где максимальное значение n определяется количеством mipmap уровней, поддерживаемых видеокартой), причем первое изображение по размеру совпадает с оригиналом (изображением, которое Вы загружаете), а последующие являются копией, но меньшего размера.

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

Этим мы и воспользуемся, при построении детальной текстуры.

Что же нам надо сделать? Вот краткий алгоритм:
1. Создаем пустую текстуру размером, соответствующим размеру изображения, которое будет у нас микрофактурой, с двумя mipmap уровнями (в принципе, уровней может быть и больше, но в нашем случае используется только одна детальная текстура, зачем нужен второй уровень Вы узнаете чуть позже).
2. Грузим изображение детальной текстуры в отдельный дескриптор (LPDIRECT3DTEXTURE8) и копируем его содержимое в первый mip-уровень.
3. Второй mip-уровень полностью закрашиваем 50% серым цветом RGB(128, 128, 128).
4. Задаем для второй текстуры параметр смешивания D3DTOP_MODULATE2X и тип фильтрации для mip-уровней - D3DTEXF_LINEAR.
5. Накладываем вторую текстуру и рендерим.

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

Все. В результате Вы должны получить что-то типа этого:

Изображение

А теперь перейдем к реализации этого алгоритма. Для написания кода использовалась D3DX библиотека.

//
// Шаг первый - создание пустой текстуры
// LPDIRECT3DTEXTURE8 g_DetTex - глобальная переменная
//

bool Step1()
{
  if (FAILED(D3DXCreateTexture(D3DDevice,    // Дескриптор Direct3D Device'a
        256, 256,     // Пусть изображение у нас - 256х256 пикселей
        2,            // Количество mip-уровней
        0,         // Usage
        D3DFMT_A8R8G8B8, // Формат текстуры - 24bit
        D3DPOOL_MANAGED, // Храним, есессно, в видео памяти
        &g_DetTex)))   // Ну и сам дескриптор детальной текстуры
  {
     return false;
  }

  return true;
}

//
// Шаг второй - подгрузка изображения в первый mip-уровень
//

bool Step2()
{
  LPDIRECT3DTEXTURE8 surf; // Промежуточная текстура
  UINT mip = 0;      // Текущий mip-уровень, в который будет 
         // скопировано изображение детальной текстуры

  // Грузим "detail1.jpg" - изображение детальной текстуры
  if (FAILED(D3DXCreateTextureFromFileEx(D3DDevice, "detail1.jpg", 0, 0, 0, 0,
          D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, D3DX_DEFAULT,
          D3DX_DEFAULT, 0xff, 0, 0, &surf)))
  {
     return false;
  }

  
  D3DLOCKED_RECT  d3dlr;
  D3DSURFACE_DESC    d3dsd;

  g_DetTex->GetLevelDesc( mip, &d3dsd );

  // Нам необходимо скопировать каждый пиксель из загруженной текстуры в заготовку
  if (FAILED(surf->LockRect( mip, &d3dlr, 0, 0 )))
     return false;
  DWORD* pSrcPixel = (DWORD*)d3dlr.pBits;
  
  if (FAILED(g_DetTex->LockRect( mip, &d3dlr, 0, 0 )))
     return false;
  DWORD* pDestPixel = (DWORD*)d3dlr.pBits;
  
  for( DWORD j=0; j<d3dsd.Height; j++  )
  {
     for( DWORD i=0; i<d3dsd.Width; i++ )
     {
        pDestPixel[j*d3dsd.Height+i] = pSrcPixel[j*d3dsd.Height+i];
     }
  }
    
  surf->UnlockRect(mip);
  g_DetTex->UnlockRect(mip);
  
  // Освобождаем временную текстуру
  surf->Release();

  return true;
}

//
// Шаг третий - заливка второго mip-уровня серым цветом
//

bool Step3()
{
  // Получаем размер изображения во втором mip-уровне
  g_DetTex->GetLevelDesc( 1, &d3dsd );

  // Теперь каждому пикселу изображения присваиваем значение серого цвета
  if (FAILED(g_DetTex->LockRect( 1, &d3dlr, 0, 0 )))
     return false;
  DWORD* pPixel = (DWORD*)d3dlr.pBits;

  for( DWORD j=0; j<d3dsd.Height; j++  )
  {
     for( DWORD i=0; i<d3dsd.Width; i++ )
     {
        pPixel[j*d3dsd.Height+i] = 0xff7f7f7f; // RGB(128, 128, 128)
     }
  }

  g_Dettex->UnlockRect(1);
  return true;
}

//
// Шаг четвертый - непосредственно наложение второй текстуры на объект
// Эта процедура должна быть вызвана в цикле прорисовки до отрисовки объекта
//

void Step4()
{
  D3DDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1 );
  D3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
  D3DDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
  D3DDevice->SetTextureStageState( 1, D3DTSS_ADDRESSU,  D3DTADDRESS_WRAP);
  D3DDevice->SetTextureStageState( 1, D3DTSS_ADDRESSV,  D3DTADDRESS_WRAP);
  D3DDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE2X );
  D3DDevice->SetTextureStageState( 1, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
}

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

Как это сделать можно на OpenGL:
Detail textures: теория и практика.

29 мая 2002


Обновление: 21 августа 2010

2001—2018 © GameDev.ru — Разработка игр