Введение в программирование трехмерных игр с DX9

         

Цепочка текстур с понижаемой детализацией;



Рисунок 6.4. Цепочка текстур с понижаемой детализацией; обратите внимание, что каждое последующее изображение в цепочке в два раза меньше предыдущего




Детализируемые текстуры


Как говорилось в разделе6.3, треугольник на экране зачастую не совпадает по размерам с треугольным фрагментом текстуры. Чтобы уменьшить различие размеров, можно создать цепочку текстур с постепенно понижаемой детализацией (mipmap). Идея состоит в том, чтобы взять текстуру и на ее основе создать ряд изображений меньшего размера и с меньшим разрешением и для каждого из этих уровней индивидуально настроить фильтрацию, чтобы на изображении сохранялись важные для нас детали (Рисунок  6.4).



Фильтр детализации текстур



6.4.1. Фильтр детализации текстур

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

Device->SetSamplerState(0, D3DSAMP_MIPFILTER, Filter);

где Filter может принимать одно из следующих значений:

D3DTEXF_NONE— Детализация выключена.

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

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



Фильтры


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

Direct3D предоставляет три различных метода фильтрации, от выбора которых зависит качество итогового изображения. Чем выше качество, тем меньше скорость визуализации, так что вам придется выбирать между качеством и скоростью. Устанавливаются фильтры текстур с помощью метода IDirect3DDevice9::SetSamplerState.

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

Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT);



Линейная фильтрация (linear filtering) — Этот тип фильтра обеспечивает хорошее качество изображения и на современном оборудовании выполняется достаточно быстро. Рекомендуется использовать в качестве минимального варианта именно этот метод. Чтобы использовать для увеличения и уменьшения текстур линейную фильтрацию, добавьте в приложение следующий код:

Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);

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

Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);

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

Device->SetSamplerState(0, D3DSAMP_MAXANISOTROPY, 4);



Текстурирование


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



Использование детализируемых текстур в Direct



6.4.2. Использование детализируемых текстур в Direct3D

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



Координаты текстуры используются для задания


Координаты текстуры используются для задания треугольного фрагмента текстуры, который будет наложен на треугольную грань трехмерного объекта.
Мы можем создавать текстуры из хранящихся на диске файлов изображений с помощью метода D3DXCreateTextureFromFile.
Фильтрация текстур выполняется путем задания фильтров для увеличения, уменьшения и детализации текстуры.
Режим адресации определяет поведение Direct3D в тех случаях, когда координаты текстуры выходят за диапазон [0, 1]. Текстура может копироваться, отражаться, обрезаться и т.д.

Координаты текстур


Direct3D использует для текстур систему координат, образованную горизонтальной осьюU и вертикальной осью V. Пара координат (u, v) идентифицирует элемент текстуры, называемый текселем (texel). Обратите внимание, что ось V направлена вниз (Рисунок  6.2).

Для добавления к сцене текстур объектов необходимо выполнить следующие действия:

Создать вершины объектов, содержащие заданные координаты текстур.

Загрузить текстуру в интерфейс IDirect3DTexture9 с помошью метода D3DXCreateTextureFromFile.

Установить фильтры для увеличения, уменьшения и детализации текстур.

Перед тем, как рисовать объект, указать связанную с объектом текстуру с помощью метода IDirect3DDevice9::SetTexture.

Мы начинаем с объявления нескольких глобальных переменных — одной для глобального буфера вершин, хранящего данные вершин квадрата, и другой для текстуры, которая будет накладываться на квадрат:

IDirect3DVertexBuffer9* Quad = 0; IDirect3DTexture9* Tex = 0;

Функция Setup достаточно прямолинейна; мы создаем квадрат из двух треугольников и задаем для них координаты текстуры. Затем мы загружаем файл с растровым изображением dx5_logo.bmp в интерфейс IDirect3DTexture9. Теперь мы можем разрешить использование текстур с помощью метода SetTexture. После вышеописанных действий мы указываем, что для уменьшения и увеличения текстур используется линейная фильтрация, и устанавливаем фильтр детализации текстур D3DTEXF_POINT:

bool Setup() { Device->CreateVertexBuffer( 6 * sizeof(Vertex), D3DUSAGE_WRITEONLY, Vertex::FVF, D3DPOOL_MANAGED, &Quad, 0);

Vertex* v; Quad->Lock(0, 0, (void**)&v, 0);

// Квадрат состоит из двух треугольников, // обратите внимание на координаты текстур: v[0] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[1] = Vertex(-1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f); v[2] = Vertex( 1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);

v[3] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f); v[4] = Vertex( 1.0f, 1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f); v[5] = Vertex( 1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);




Quad->Unlock();

// Загрузка данных текстуры D3DXCreateTextureFromFile( Device, "dx5_logo.bmp", &Tex);

// Разрешаем текстурирование Device->SetTexture(0, Tex);

// Устанавливаем фильтры текстуры Device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); Device->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_POINT);

// Задаем матрицу проекции D3DXMATRIX proj; D3DXMatrixPerspectiveFovLH( &proj, D3DX_PI * 0.5f, // 90 градусов (float)Width / (float)Height, 1.0f, 1000.0f); Device->SetTransform(D3DTS_PROJECTION, &proj);

// Не использовать в этом примере освещение Device->SetRenderState(D3DRS_LIGHTING, false);

return true; }

Теперь мы можем визуализировать наш квадрат обычным способом и на его будет наложена указанная текстура:

bool Display(float timeDelta) { if (Device) { Device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xffffffff, 1.0f, 0); Device->BeginScene();

Device->SetStreamSource(0, Quad, 0, sizeof(Vertex)); Device->SetFVF(Vertex::FVF);

// Рисуем примитив с наложением указанной текстуры Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

Device->EndScene(); Device->Present(0, 0, 0, 0); } return true; }


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


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



Режим отражения



Рисунок 6.8. Режим отражения



Куб с текстурой деревянного ящика



Рисунок 6.1. Куб с текстурой деревянного ящика


В Direct3D текстура представляется с помощью интерфейса IDirect3DTexture9. Текстура, подобно поверхности, является двухмерной матрицей пикселей и может быть наложена на треугольную грань.

Цели

Узнать, как задать фрагмент текстуры, который будет наложен на треугольную грань.

Изучить способы создания текстур.

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



Окно с изображением текстурированого квадрата, полученное в приложении TexQuad


Рисунок 6.9. Окно с изображением текстурированого квадрата, полученное в приложении TexQuad


ПРИМЕЧАНИЕ

В сопроводительных файлах есть еще два примера приложений для данной главы. Один пример отображает куб с наложенной текстурой деревянного ящика (Рисунок  6.1). Другой пример демонстрирует различные режимы адресации.
<
На этих рисунках координаты текстур для четырех вершин квадрата определены следующим образом: (0, 0), (0, 3), (3, 0) и (3, 3). Поскольку по осям U и V размер равен трем единицам, квадрат делится на матрицу 3 × 3. Если, к примеру, вы хотите, чтобы текстура накладывалась в виде матрицы размером 5 × 5, включите режим обертывания и задайте координаты текстур (0, 0), (0, 5), (5, 0) и (5, 5).

Приведенный ниже фрагмент кода взят из программы AddressModes и показывает, как устанавливаются четыре режима адресации:

// Установка режима обертывания if (::GetAsyncKeyState('W') & 0x8000f) { Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP); Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP); }

// Установка режима цвета рамки if (::GetAsyncKeyState('B') & 0x8000f) { Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); Device->SetSamplerState(0, D3DSAMP_BORDERCOLOR, 0x000000ff); }

// Установка режима отсечения if (::GetAsyncKeyState('C') & 0x8000f) { Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP); Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP); }

// Установка режима отражения if (::GetAsyncKeyState('M') & 0x8000f) { Device->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR); Device->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR); }


Режимы адресации


Раньше мы утверждали, что координаты текстур должны находиться в диапазоне [0,1]. С технической точки зрения это неверно, и координаты могут выходить за указанный диапазон. Поведение Direct3D в том случае, если координаты выходят за диапазон [0, 1] определяется установленным режимом адресации. Поддерживаются четыре режима адресаци: обертывание (wrap), цвет рамки (border color), одиночное наложение (clamp) и отражение (mirror), которые показаны на Рисунок  6.5, 6.6, 6.7 и 6.8 соответственно.



Система координат текстуры иногда называемая пространством текстуры



Рисунок 6.2. Система координат текстуры иногда называемая пространством текстуры

Кроме того, обратите внимание, на нормализованные координаты в диапазоне [0, 1], которые обеспечивают для Direct3D работу со значениями из фиксированного диапазона, не зависящего от размеров конкретной текстуры.

Для каждой треугольной грани в трехмерном пространстве мы должны определить соответствующий треугольный фрагмент текстуры, который будет наложен на эту грань (Рисунок  6.3).



Слева изображена треугольная грань


Рисунок 6.3. Слева изображена треугольная грань в трехмерном пространстве, а справа — двухмерный треугольный фрагмент текстуры, который должен быть наложен на данную грань


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

struct Vertex { float _x, _y, _z; float _nx, _ny, _nz; float _u, _v; // координаты текстуры

static const DWORD FVF; }; const DWORD Vertex::FVF = D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX1;

Обратите внимание, что к описанию формата вершины мы добавили константу D3DFVF_TEX1, которая говорит о том, что наша структура данных вершины содержит пару координат текстуры.

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

ПРИМЕЧАНИЕ

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

Создание текстур и разрешение текстурирования

Данные текстур обычно считываются из хранящихся на диске файлов изображений и загружаются в объект IDirect3DTexture9. Для этого используется следующая функция библиотеки D3DX:

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

Данная функция позволяет загружать изображения в форматах BMP, DDS, DIB, JPG, PNG и TGA.

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

IDirect3Dtexture9* _stonewall; D3DXCreateTextureFromFile(_device, "stonewall.bmp", &_stonewall);

Для установки текущей текстуры используется следующий метод:

HRESULT IDirect3DDevice9::SetTexture( DWORD Stage, // Значение в диапазоне 0-7, идентифицирующее // этап текстурирования (см. примечание ниже) IDirect3DBaseTexture9* pTexture // Указатель на устанавливаемую текстуру );

Вот пример использования данного метода:

Device->SetTexture(0, _stonewall);

ПРИМЕЧАНИЕ

В Direct3D вы можете устанавливать до восьми текстур, которые будут объединяться для получения более детализированного изображения. Этот процесс называется мультитекстурированием (multitexturing). В этой книге до четвертой части мы не будем пользоваться мультитекстурированием, и поэтому номер этапа текстурирования будет равен 0.

Чтобы запретить наложение текстуры на конкретном этапе текстурирования следует установить значение pTexture для соответствующего этапа равным 0.

Например, если мы не хотим использовать при отображении объекта текстуры, следует написать:

Device->SetTexture(0, 0); renderObjectWithoutTexture();

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

Device->SetTexture(0, _tex0); drawTrisUsingTex0();

Device->SetTexture(0, _tex1); drawTrisUsingTex1();