Программирование графических процессоров с использованием Direct3D и HLSL

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


До сих пор выводимые на экран примитивы строились только с помощью синтетических цветов. Для того чтобы придать им большей реалистичности можно прибегнуть к помощи текстур. Текстура представляет собой двумерное растровое изображение, которое накладывается (натягивается) на поверхность объекта, например на плоский треугольник. Текстуры, как правило, хранятся в графических файлах форматов bmp, jpeg, tiff, tga, gif. Библиотека Direct3D содержит богатый набор функций для работы с текстурами. Процесс наложения текстуры на объект называют текстурированием. Текстуры накладываются на объект с помощью так называемых текстурных координат. Текстурные координаты представляют собой пару чисел (u,v), изменяющихся в пределах от 0 до 1, и определяются в своей системе координат. Ось u направлена горизонтально вправо, ось v - вертикально вниз. Пара величин (u,v) однозначно указывает на элемент текстуры, называемый текселем.


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


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

C++ struct MYVERTEX { FLOAT x, y, z, rhw; FLOAT u, v; // текстурные координаты };

#define MY_FVF (D3DFVF_XYZRHW|D3DFVF_TEX1)

Pascaltype MyVertex = packed record x, y, z, rhw: Single; u,v: Single; // текстурные координаты end;

const MY_FVF = D3DFVF_XYZRHW or D3DFVF_TEX1;

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

C++LPDIRECT3DTEXTURE9 tex = NULL;
Pascal

var tex: IDirect3DTexture9;

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


C++#include <d3dx9.h>
Pascaluses …, D3DX9,…
Загрузка же текстуры из графического файла в память осуществляется с помощью функции D3DXCreateTextureFromFile, у которой имеется три параметра. Первый параметр определяет указатель на устройство вывода, второй аргумент представляет собой строку, содержащую путь до загружаемого файла на диске, и третий – определяет переменную, в которой будет храниться указатель на текстуру.

C++D3DXCreateTextureFromFile( device, "texture.bmp", &tex )
PascalD3DXCreateTextureFromFile( device, 'texture.bmp', tex );
Последний шаг в работе с текстурами заключается в активации нужной текстуры перед выводом примитивов. Реализуется это с помощью вызова метода SetTexture интерфейса IDirect3DDevice9. Данный метод имеет два параметра: первый определяет номер так называемого текстурного уровня, а второй – указатель на загруженную текстуру. Библиотека Direct3D поддерживает до восьми текстурных уровней, но если вы работаете только с одной текстурой, то номер текстурного уровня всегда должен быть нулем. Установка (загрузка текстуры в текстурный уровень) текущей текстуры осуществляется, как правило, в теле функции вывода (рендеринга).

C++device->SetTexture ( 0, tex );
Pascaldevice.SetTexture( 0, tex );
Если в сцене присутствуют несколько объектов с различными текстурами, то процесс установки текстуры и вывода примитивов должен быть последовательным:

SetTexture ( 0, tex1 ); drawObject1();

SetTexture ( 0, tex2 ); drawObject2();



Для деактивации текстуры в некотором текстурном уровне достаточно вызвать метод SetTexture с нулевым (пустым) значением второго параметра.

C++device->SetTexture ( 0, 0 );
Pascaldevice.SetTexture( 0, nil );
В большинстве случаях размер накладываемой текстуры и размер текстурируемого полигона (треугольника) не совпадают. Поэтому когда исходная текстура меньше чем полигон, на который она накладывается, то текстура должна быть увеличена (растянута) до размеров полигона. И наоборот, когда текстура больше чем полигон, то она должна быть уменьшена (сжата) до размеров полигона.





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

  1. точечная фильтрация (используется по умолчанию) – самая быстрая по скорости, но самая низкая по качеству;
  2. линейная фильтрация – приемлемое качество и скорость;
  3. анизотропная – самая медленная, но самая качественная.


Программно установить тот или ной тип фильтрации можно с помощью вызова метода SetSamplerState интерфейса IDirect3DDevice9:

SetSamplerState( 0, D3DSAMP_MINFILTER, <тип фильтрации> ) SetSamplerState( 0, D3DSAMP_MAGFILTER, <тип фильтрации> ),

где <тип фильтрации> – одна из следующих констант:

D3DTEXF_POINT – точечная фильтрация,

D3DTEXF_LINEAR – линейная фильтрация,

D3DTEXF_ANISOTROPIC – анизотропная фильтрация;

константы D3DSAMP_MAGFILTER и D3DSAMP_MINFILTER указывают на то, что размер текстуры меньше и больше размеров полигона соответственно.

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





D3DTEXF_POINTD3DTEXF_LINEAR
На самом деле значения текстурных координат могут выходить за пределы отрезка [0,1]. Как при этом будет себя "вести" текстура зависит от установленного режима адресации. Существует 4 типа адресации текстур:

  1. wrap
  2. border color
  3. clamp
  4. mirror


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



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



SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP ); SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );





SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER ); SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER ); SetSamplerState( 0, D3DSAMP_BORDERCOLOR, D3DCOLOR_XRGB(64,64,0) );





SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP ); SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP );





SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_MIRROR ); SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_MIRROR );



<


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

C++ struct CUSTOMVERTEX { FLOAT x, y, z, rhw; // координаты вершины DWORD color; // цвет вершин FLOAT tu, tv; // текстурные координаты }; #define MY_FVF (D3DFVF_XYZRHW | D3DFVF_ DIFFUSE | D3DFVF_TEX1)
Pascaltype MyVertex = packed record x, y, z, rhw: Single; // координаты вершины color: DWORD; // цвет вершин u,v: Single; // текстурные координаты end;

const MY_FVF = D3DFVF_XYZRHW or D3DFVF_DIFFUSE or D3DFVF_TEX1;
Пример вывода квадрата, состоящего из двух треугольников, у которых вершины содержат как цвет, так и текстурные координаты показан ниже.



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

Первый аргумент указывает номер текстурного уровня, второй аргумент задает название изменяемого параметра, а третий – его значение. Определить значения двух цветовых аргументов для нулевого текстурного уровня можно следующим образом:

SetTextureStageState( 0, D3DTSS_COLORARG{1,2}, <значение> ),

где <значение> в нашем случае может быть константой D3DTA_DIFFUSE либо D3DTA_TEXTURE. Определить операцию взаимодействия двух цветовых аргументов позволит следующий вызов:

SetTextureStageState( 0, D3DTSS_COLOROP, <операция> ),

где <операция> может принимать следующие значения:

Вид операцииОписание
D3DTOP_SELECTARG1выбор в качестве результата первого цветового аргумента, при этом значение второго аргумента в рассмотрение не берется
D3DTOP_SELECTARG2выбор в качестве результата второго цветового аргумента, при этом значение первого аргумента в рассмотрение не берется
D3DTOP_MODULATEпокомпонентное перемножение первого и второго цветового аргумента
D3DTOP_MODULATE2Xпокомпонентное перемножение первого и второго цветового аргумента и битовый сдвиг на 1 бит влево (умножение на 2)
D3DTOP_MODULATE4Xпокомпонентное перемножение первого и второго цветового аргумента и битовый сдвиг на 2 бита влево (умножение на 4)
D3DTOP_ADDпокомпонентное сложение первого и второго цветового аргумента
D3DTOP_SUBTRACTпокомпонентное вычитание из первого цветового аргумента второго


Сам процесс смешивания цветов может быть описан следующей блок-схемой.



Следует отметить, что все цветовые операции над пикселями производятся покомпонентно для каждого оттенка. Отдельно для красного, зеленого и синего цветов, причем диапазон принимаемых значений каждого цветового канала ограничен в пределах [0,1]. Пусть формат вершины содержит цвет и текстурные координаты. Ниже показаны примеры смешивания цветовой и текстурной составляющей вершины при использовании только нулевого текстурного уровня.



D3DTSS_COLORARG1 = D3DTA_TEXTURE D3DTSS_COLORARG2 = D3DTA_DIFFUSE D3DTSS_COLOROP = D3DTOP_SELECTARG1




D3DTSS_COLORARG1 = D3DTA_TEXTURE D3DTSS_COLORARG2 = D3DTA_DIFFUSE D3DTSS_COLOROP = D3DTOP_SELECTARG2




D3DTSS_COLORARG1 = D3DTA_TEXTURE D3DTSS_COLORARG2 = D3DTA_DIFFUSE D3DTSS_COLOROP = D3DTOP_MODULATE




D3DTSS_COLORARG1 = D3DTA_TEXTURE D3DTSS_COLORARG2 = D3DTA_DIFFUSE D3DTSS_COLOROP = D3DTOP_MODULATE2X




D3DTSS_COLORARG1 = D3DTA_TEXTURE D3DTSS_COLORARG2 = D3DTA_DIFFUSE D3DTSS_COLOROP = D3DTOP_MODULATE4X




D3DTSS_COLORARG1 = D3DTA_TEXTURE D3DTSS_COLORARG2 = D3DTA_DIFFUSE D3DTSS_COLOROP = D3DTOP_ADD




D3DTSS_COLORARG1 = D3DTA_TEXTURE D3DTSS_COLORARG2 = D3DTA_DIFFUSE D3DTSS_COLOROP = D3DTOP_SUBTRACT




D3DTSS_COLORARG1 = D3DTA_DIFFUSE D3DTSS_COLORARG2 = D3DTA_TEXTURE D3DTSS_COLOROP = D3DTOP_SUBTRACT


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



D3DTSS_COLORARG1 = D3DTA_DIFFUSE (белый) D3DTSS_COLORARG2 = D3DTA_TEXTURE D3DTSS_COLOROP = D3DTOP_SUBTRACT



Содержание раздела