netlib.narod.ru< Назад | Оглавление | Далее >

Использование наложения текстур

Хотя вы и научились рисовать трехмерные объекты на экране, одноцветные полигоны достаточно скучны. Пришло время сделать вещи интереснее и добавить несколько деталей. Один из простейших способов увеличить детализацию трехмерных объектов — использование техники, известной как наложение текстур. Наложение текстур (texture mapping) — это техника, применяемая для раскраски полигона с использованием изображения (как показано на рис 2.14), что улучшает визуальное оформление рисуемых объектов. Растровые изображения обычно называют текстурами (texture), поэтому при обсуждении трехмерной визуализации я буду использовать оба эти термина как взаимозаменяемые.


Рис. 2.14. При наложении текстуры вы берете плоские полигоны и рисуете на их поверхности картинку

Рис. 2.14. При наложении текстуры вы берете плоские полигоны и рисуете на их поверхности картинку


При наложении текстур вы назначаете каждой вершине полиона пару координат. Эти координаты (называемые координаты U, V) определяют точку внутри изображения текстуры. Координаты U, V являются аналогом координат X, Y изображения текстуры, но вместо того, чтобы указывать координаты основанные на ширине и высоте изображения текстуры в пикселях, вы задаете координаты в диапазоне от 0.0 до 1.0.

Как правило, координаты X и Y находятся в диапазоне от нуля до ширины и высоты изображения соответственно, так что если у вас есть изображение размером 640 × 480, X будет в диапазоне от 0 до 639, а Y — в диапазоне от 0 до 479. Для доступа к пикселю, находящемуся в середине изображения, вы указываете координаты X = 319 и Y = 239.

Координаты U и V находятся в диапазоне от 0 (верхний или левый край изображения) до 1 (правый или нижний край изображения), как показано на рис. 2.15. Для доступа к пикселю, находящемуся в центре изображения размером 640 × 480 точек, вы используете координаты U = 0.5 и V = 0.5.


Рис. 2.15. Координаты U и V для изображения текстуры остаются постоянными, независимо от размера изображения

Рис. 2.15. Координаты U и V для изображения текстуры остаются постоянными, независимо от размера изображения


На первый взгляд договоренность относительно координат U и V может показаться странной. Однако она замечательно работает, поскольку вы можете быстро заменить текстуру на новую другого размера и не беспокоиться об изменении координат.

СОВЕТ
Вот изящный трюк. Вы можете задавать для координат U и V значения больше, чем 1.0. В результате при визуализации текстура будет дублироваться. Например, если вы зададите для координаты U значение 2.0, текстура будет нарисована дважды, дублируясь по горизонтали. Если задать для координаты U значение 3.0, текстура будет отображена трижды. То же самое справедливо и для координаты V.

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

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

Использование наложения текстур в Direct3D

Управление текстурами в Direct3D осуществляется через объект IDirect3DTexture9. Этот объект хранит информацию о текстуре и предоставляет доступ к ней (включая указатель на пиксели, образующие изображение текстуры).

Начав использовать текстуры, вы столкнетесь с некоторыми ограничениями, накладываемыми на них Direct3D и производителями видеокарт. Во-первых, ограничены размеры текстур, которые к тому же должны быть степенями двойки (например, 8, 32, 128 или 256). Обычно ширина текстуры равна ее высоте, то есть используются размеры 128 × 128 или 256 × 256. Будьте внимательны, поскольку при работе с трехмерной графикой здесь может крыться ловушка: некоторые видеокарты не позволяют применять текстуры, у которых высота отличается от ширины (то есть, размером 128 × 64 или 32 × 256), и большинство (на момент написания книги) не разрешают использовать текстуры, размеры которых не являются степенями двойки. Поэтому вы всегда должны стараться использовать текстуры у которых ширина равна высоте. Кроме того, вы должны гарантировать, что размеры ваших текстур не превышают 256 × 256, поскольку это максимальный размер текстур с которыми могут работать большинство видеокарт (а вы должны удостовериться, что ваша игра совместима с большей частью существующего оборудования).

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

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

ПРИМЕЧАНИЕ
Большинство видеокарт поддерживают прямой доступ к памяти или быструю передачу данных, что ускоряет загрузку текстур в видеопамять. Можно спокойно предполагать, что большинство видеокарт используют интерфейс AGP, поддерживающий сверхбыструю передачу данных, а это значит, что ваши текстуры и другие графические данные будут загружаться в память видеокарты настолько быстро, насколько возможно.

 

СОВЕТ
Чтобы уменьшить время инициализации, затрачиваемое видеокартой на подготовку текстуры, вы можете упаковать несколько изображений в одну текстуру. Сделав так, вы гарантируете, что подготовку текстуры необходимо будет выполнить только один раз; потом, по мере необходимости, отдельные изображения будут извлекаться из текстуры. Пример использования этой техники будет показан в книге.

Загрузка текстуры

Чтобы получить изображение текстуры, вы обычно загружаете его с диска или из другого ресурса. Фактически, библиотека D3DX содержит ряд функций для загрузки и управления текстурами значительно облегчающих эту работу. Функции загрузки текстур из библиотеки D3DX перечислены в таблице 2.5.


Таблица 2.5. Функции загрузки текстур библиотеки D3DX



Функция Описание

D3DXCreateTextureFromFile Загружает текстуру из файла с растровыми изображением
D3DXCreateTextureFromFileEx Более сложная версия функции D3DXCreateTextureFromFile
D3DXCreateTextureFromFileInMemory Загружает текстуру из файла, который уже загружен в память
D3DXCreateTextureFromFileInMemoryEx Более сложная версия функции D3DXCreateTextureFromFileInMemory
D3DXCreateTextureFromResource Загружает изображение текстуры из ресурсов приложения
D3DXCreateTextureFromResourceEx Более сложная версия функции D3DXCreateTextureFromResource


Как видно в таблице 2.5, у каждой функции загрузки текстур есть две версии: одна быстрая и простая версия для загрузки текстур, и другая, с суффиксом Ex, более сложная, которая предоставляет больше контроля над процессом создания текстуры. Одиссею с наложением текстур мы начнем со знакомства с первой и наиболее простой для использования функцией D3DXCreateTextureFromFile:

HRESULT D3DXCreateTextureFromFile(
    IDirect3DDevice9  *pDevice,     // ранее инициализированный объект устройства
    LPCSTR            pSrcFile,     // имя загружаемого файла с изображением
    IDirect3DTexture9 **ppTexture); // создаваемый объект текстуры

И снова с функцией не сложно иметь дело; просто передайте ей ранее инициализированный объект устройства, имя файла с изображением, который вы хотите загрузить, и указатель на созданный вами объект IDirect3DTexture9.

Вот пример использования функции D3DXCreateTextureFromFile для загрузки растрового изображения из файла с именем texture.bmp в объект текстуры:

// g_pD3DDevice = ранее инициализированный объект устройства
IDirect3DTexture9 *pD3DTexture;

if(FAILED(D3DXCreateTextureFromFile(g_pD3DDevice,
                "texture.bmp", (void**)&pD3DTexture))) {
    // Произошла ошибка
}

Самое лучшее в этой функции то, что она выполняет всю инициализацию за вас и помещает структуру в память D3DPOOL_MANAGED, а это значит, что текстура будет постоянно находиться в памяти (потерянные текстуры были главной головной болью для программистов до выхода DirectX 8).

Установка текстуры

Как говорилось раньше, в разделе «Использование наложения текстур в Direct3D», видеокарта должна подготовиться к использованию текстуры для визуализации. Эта подготовка должна быть выполнена перед тем, как будет визуализироваться полигон с текстурой. Если у вас 1000 полигонов и для каждого полигона используется своя текстура, вы должны в цикле перебирать полигоны, устанавливать для каждого полигона его текстуру и визуализировать его.

Вы повторяете этот процесс, пока не будут визуализированы все полигоны. Если несколько полигонов используют одну и ту же текстуру, более эффективно установить текстуру и визуализировать все полигоны, которые используют ее, вместо того, чтобы использовать цикл установки текстуры и визуализации для каждого полигона.

Для установки текстуры используют функцию IDirect3DDevice9::SetTexture:

HRESULT IDirect3DDevice9::SetTexture(
    DWORD                 Stage,      // Этап текстурирования 0 - 7
    IDirect3DBaseTexture9 *pTexture); // Устанавливаемый объект текстуры

Вы видите, где передается созданный вами объект текстуры (как pTexture), но что за параметр Stage? Он называется этапом текстурирования (texture stage) и это одна из самых захватывающих техник наложения текстур в Direct3D.

Процесс наложения текстур в Direct3D очень гибкий. Текстура не берется из единственного источника, а может быть построена с использованием целых восьми различных источников. Эти источники, называемые этапами текстурирования (texture stage), нумеруются от 0 до 7. При визуализации полигона для каждого рисуемого пикселя Direct3D начинает с этапа 1 и запрашивает пиксель текстуры. Затем Direct3D переходит к этапу 2 и запрашивает другой пиксель текстуры или позволяет вам модифицировать пиксель предыдущей текстуры. Процесс продолжается, пока не будут пройдены все восемь этапов.

Каждый этап может изменить пиксель текстуры как требуется, включая смешивание пикселя с пикселем новой текстуры, увеличение или уменьшение насыщенности цвета или яркости и даже выполнение специального эффекта, известного как альфа-смешивание (техника, которая смешивает цвета нескольких пикселей). Этот процесс изображен на рис. 2.16, где исходный пиксель проходит через каждый этап, начиная с этапа 0, на котором из текстуры извлекается рассеиваемая составляющая цвета пикселя (уровни красного, зеленого и синего цветов). Затем для пикселя выполняется альфа-смешивание, после чего производится затемнение, и в результате получается итоговый пиксель, который рисуется на экране.


Рис. 2.16. Каждый этап текстурирования модифицирует исходный пиксель различными способами

Рис. 2.16. Каждый этап текстурирования модифицирует исходный пиксель различными способами. Здесь исходный пиксель проходит через ряд изменений для получения итогового пикселя


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

Приведенный ниже фрагмент кода устанавливает текстуру для использования на этапе 0 и сообщает визуализатору о необходимости извлечь пиксель текстуры, применить информацию о цветах вершин и запретить альфа-смешивание:

// g_pD3DDevice = ранее инициализированный объект устройства
// pD3DTexture  = загруженный объект текстуры

// Устанавливаем текстуру для этапа 0
g_pD3DDevice->SetTexture(0, pTexture);

// Устанавливаем параметр этапа - это необходимо делать один раз в программе
g_pD3DDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE);
g_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
g_pD3DDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);
g_pD3DDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);

Это основной набор операций с текстурами и вы будете встречать его достаточно часто. Обратите внимание, что вы устанавливаете параметры этапа текстурирования только одни раз, и затем полагаетесь на вызов SetTexture. Чтобы узнать больше об использовании этапов текстурирования, обратитесь к документации DX SDK.

Когда вы закончили использовать текстуру (после визуализации полигонов), вы вызываете функцию SetTexture еще раз, указав в параметре pTexture значение NULL:

g_pD3DDevice->SetTexture(0, NULL);

Этот вызов удаляет данные текстуры из памяти и видеопроцессора. Если не сделать этого, в программе появятся утечки памяти, которые могут привести даже к краху вашей игры.

Использование выборок

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

По этим (и по многим другим) причинам были созданы выборки, сглаживающие эти небольшие дефекты. Direct3D использует различные выборки, которые гарантируют, что ваша графика будет выглядеть более чисто.

Чтобы использовать выборку в Direct3D необходимо воспользоваться функцией IDirect3DDevice9::SetSamplerState:

HRESULT IDirect3DDevice9::SetSamplerState(
    DWORD               Sampler, // Этап текстурирования/выборки 0-7
    D3DSAMPLERSTATETYPE Type,    // Устанавливаемый режим
    DWORD               Value);  // Используемое значение

В этой книге я буду задавать только две выборки: D3DSAMP_MINFILTER и D3DSAMP_MAGFILTER. Оба этих состояния определяют как Direct3D смешивает близлежащие пиксели внутри текстуры перед выводом пикселя на экран. Первое состояние, D3DSAMP_MAGFILTER, используется когда текстура растягивается на полигон (увеличивается), а D3DSAMP_MINFILTER — когда текстура сжимается (уменьшается).

Аргумент Value может принимать одно из значений, перечисленных в таблице 2.6.

Таблица 2.6. Типы фильтров выборки текстуры в Direct3D



Значение Описание

D3DTEXF_NONE Фильтр не используется
D3DTEXF_POINT Самый быстрый режим фильтрации. Используется цвет единственной точки из текстуры
D3DTEXF_LINEAR Режим билинейной интерполяции. Этот режим для получения итогового смешанного пикселя комбинирует четыре пикселя текстуры. Достаточно быстрый режим наложения текстуры, позволяющий получить сглаженное изображение
D3DTEXF_ANISOTROPIC Анизотропная фильтрация компенсирует угловые различия между экраном и текстурируемым полигоном. Хорошо, но медленно


Как правило, вы будете использовать режимы фильтрации D3DTEXF_POINT или D3DTEXF_LINEAR; они быстрые, а линейный режим еще и сглаживает результат. Чтобы использовать какой-нибудь режим фильтрации просто добавьте следующий код:

// g_pD3DDevice = ранее инициализированный объект устройства
// Установка фильтра увеличения
if(FAILED(g_pD3DDevice->SetSamplerState(0,
                    D3DSAMP_MAGFILTER, D3DTEXF_POINT))) {
    // Произошла ошибка
}

// Установка фильтра уменьшения
if(FAILED(g_pD3DDevice->SetSamplerState(0,
                    D3DTSS_MINFILTER, D3DTEXF_POINT))) {
    // Произошла ошибка
}

Визуализация текстурированных объектов

Перед тем, как вы сможете рисовать объект (полигон или набор полигонов) с текстурой, вы должны убедиться, что в данные вершин полигона включена пара координат U, V. Структура данных вершины, содержащая только набор трехмерных координат и координаты текстуры, выглядит так:

typedef struct {
    D3DVECTOR3 Position; // вектор местоположения вершины
    float      tu, tv;   // Здесь добавляем координаты текстуры!
} sVertex;

Теперь вы конструируете макроопределение настраиваемого формата вершин, чтобы информировать Direct3D о том, какие компоненты данных вершины используются. В данном случае компоненты — это непреобразованные трехмерные координаты и пара координат текстуры.

В описании используются значения D3DFVF_XYZ и D3DFVF_TEX1:

#define VERTEXFMT (D3DFVF_XYZ | D3DFVF_TEX1)

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

// g_pD3DDevice = ранее инициализированный объект устройства
// NumPolys     = количество рисуемых полигональных примитивов
// g_pD3DVertexBuffer = ранее созданный буфер вершин
//                      с информацией о полигонах

IDirect3DTexture9 *pD3DTexture;     // Объект текстуры

// Загрузка текстуры
D3DXCreateTextureFromFile(g_pD3DDevice, "texture.bmp",
                          (void**)&pD3DTexture);

if(SUCCEEDED(g_pD3DDevice->BeginScene())) {

    // Установка текстуры
    g_pD3DDevice->SetTexture(0, pD3DTexture);

    // Установка источника потоковых данных
    // и вершинного шейдера
    g_pD3DDevice->SetStreamSource(0, g_pD3DVertexBuffer, 0,
                                  sizeof(sVertex));
    g_pD3DDevice->SetFVF(VERTEXFMT);

    // Рисуем список треугольников
    g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, NumPolys);

    // Завершение сцены
    g_pD3DDevice->EndScene();

    // Освобождение ресурсов текстуры
    g_pD3DDevice->SetTexture(0, NULL);
}

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz