Программирование графических процессоров с использованием 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)
Pascal
type
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>
Pascal uses …, D3DX9,…

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

C++ D3DXCreateTextureFromFile( device, "texture.bmp", tex )
Pascal D3DXCreateTextureFromFile( device, 'texture.bmp', tex );

Последний шаг в работе с текстурами заключается в активации нужной текстуры перед выводом примитивов. Реализуется это с помощью вызова метода SetTexture интерфейса IDirect3DDevice9. Данный метод имеет два параметра: первый определяет номер так называемого текстурного уровня, а второй – указатель на загруженную текстуру. Библиотека Direct3D поддерживает до восьми текстурных уровней, но если вы работаете только с одной текстурой, то номер текстурного уровня всегда должен быть нулем. Установка (загрузка текстуры в текстурный уровень) текущей текстуры осуществляется, как правило, в теле функции вывода (рендеринга).

C++ device->SetTexture ( 0, tex );
Pascal device.SetTexture( 0, tex );

Если в сцене присутствуют несколько объектов с различными текстурами, то процесс установки текстуры и вывода примитивов должен быть последовательным:

SetTexture ( 0, tex1 );
drawObject1();

SetTexture ( 0, tex2 );
drawObject2();

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

C++ device->SetTexture ( 0, 0 );
Pascal device.SetTexture( 0, nil );

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

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

  • точечная фильтрация (используется по умолчанию) – самая быстрая по скорости, но самая низкая по качеству;
  • линейная фильтрация – приемлемое качество и скорость;
  • анизотропная – самая медленная, но самая качественная.
  • Программно установить тот или ной тип фильтрации можно с помощью вызова метода SetSamplerState интерфейса IDirect3DDevice9:

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

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

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

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

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

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

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

    D3DTEXF_POINT D3DTEXF_LINEAR

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

  • wrap
  • border color
  • clamp
  • 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)
    Pascal
    type
    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

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

    Библиотека Direct3D позволяет накладывать на один полигон не одну, а сразу несколько текстур. Наложение на грань нескольких текстур называется мультитекстурированием. На текущий момент поддерживается до 8 наложений (уровней) текстур на одну грань. Схему (принцип) мультитекстурирования можно описать следующим образом. Первый текстурный уровень (с индексом 0) принимает на вход два значения: цвет текселя и диффузный цвет вершины; производит с ними указанные операции и передает результат на следующий (нижний) уровень. Полученное на предыдущем уровне значение цвета используется в качестве одного из аргумента текущего уровня и т.д. Схематично данные шаги мультитекстурирования можно представить так:

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

    SetTextureStageState(<уровень>, D3DTSS_TEXCOORDINDEX, <номер координат>);

    Так, например, чтобы указать, что второй уровень будет использовать текстурные координаты первого текстурного уровня, достаточно воспользоваться следующим вызовом метода: SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 ). И затем нужно загрузить (установить) текстуры в соответствующие текстурные уровни.

    SetTexture( 0, tex1 );
    SetTexture( 1, tex2 );

    Можно указать для каждого текстурного уровня свои собственные текстурные координаты. В этом случае должен корректно измениться формат вершины при описании и набор FVF флагов. Ниже приведен пример описания вершины и FVF флагов для мультитекстурирования с двумя уровнями (текстурами) и текстурными координатами.

    C++
    struct CUSTOMVERTEX
    {
        FLOAT x,  y,  z, rhw;      // координаты вершины
        FLOAT u1, v1;         // текстурные координаты первого уровня
        FLOAT u2, v2;         // текстурные координаты второго уровня
    };
    
    #define MY_FVF (D3DFVF_XYZ | D3DFVF_TEX2)
    Pascal
    type
    MyVertex = packed record
          x, y, z, rhw: Single; // координаты вершины
          u1,v1: Single; // текстурные координаты первого уровня
          u2,v2: Single; // текстурные координаты второго уровня
    end;
    
    const
    MY_FVF = D3DFVF_XYZRHW or D3DFVF_TEX2;

    Вообще, число во флаге D3DFVF_TEX как раз и показывает, сколько текстурных уровней планируется задействовать при мультитекстурировании (D3DFVF_TEX1 … D3DFVF_TEX8). Для хранения второй и последующих текстур необходимо объявить соответствующее количество нужных переменных, произвести загрузку текстур с помощью функции D3DXCreateTextureFromFile() и установить текстуры в соответствующие текстурные уровни вызовов метода SetTexture (<номер уровня>, <текстура>). Ниже приведен пример возможного использования двух текстур при мультитекстурировании.

    C++
    LPDIRECT3DTEXTURE9 tex, tex2;
    …
    D3DXCreateTextureFromFile( device, "texture.bmp", tex );
    D3DXCreateTextureFromFile( device, "texture2.bmp", tex2 );
    …
    device->SetTexture ( 0, tex );
    device->SetTexture ( 1, tex2 );
    Pascal
    var
       tex, tex2: IDirect3DTexture9;
    …
    D3DXCreateTextureFromFile( device, 'texture.bmp', tex );
    D3DXCreateTextureFromFile( device, 'texture2.bmp', tex2 );
    …
    device.SetTexture( 0, tex );
    device.SetTexture( 1, tex2 );

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

    SetTextureStageState( <номер уровня>, D3DTSS_COLORARG1, <значение> ), 
    SetTextureStageState( <номер уровня>, D3DTSS_COLORARG2, <значение> ), 
    SetTextureStageState( <номер уровня>, D3DTSS_COLOROP, <операция> ).

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

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

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

    …
    points[0].u1 = 0.0f;
    points[0].v1 = 1.0f;
    points[0].u2 = 0.0f;
    points[0].v2 = 3.0f;
    …
    device->SetTexture( 0, tex );
    device->SetTexture( 1, tex );
    …

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

    device->SetTexture(0, tex);
    device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
    device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    device->SetTexture(1, tex);
    device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1);
    device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
    device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
    device->SetTextureStageState(1, D3DTSS_COLOROP, <operation>);

    operation = D3DTOP_MODULATE4X

    operation = D3DTOP_ADD

    Страницы:

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

    До сих пор выводимые на экран примитивы строились только с помощью синтетических цветов. Для того чтобы придать им большей реалистичности можно прибегнуть к помощи текстур. Текстура представляет собой двумерное растровое изображение, которое накладывается (натягивается) на поверхность объекта, например на плоский треугольник. Текстуры, как правило, хранятся в графических файлах форматов 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)
    Pascal
    type
    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>
    Pascal uses …, D3DX9,…

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

    C++ D3DXCreateTextureFromFile( device, "texture.bmp", tex )
    Pascal D3DXCreateTextureFromFile( device, 'texture.bmp', tex );

    Последний шаг в работе с текстурами заключается в активации нужной текстуры перед выводом примитивов. Реализуется это с помощью вызова метода SetTexture интерфейса IDirect3DDevice9. Данный метод имеет два параметра: первый определяет номер так называемого текстурного уровня, а второй – указатель на загруженную текстуру. Библиотека Direct3D поддерживает до восьми текстурных уровней, но если вы работаете только с одной текстурой, то номер текстурного уровня всегда должен быть нулем. Установка (загрузка текстуры в текстурный уровень) текущей текстуры осуществляется, как правило, в теле функции вывода (рендеринга).

    C++ device->SetTexture ( 0, tex );
    Pascal device.SetTexture( 0, tex );

    Если в сцене присутствуют несколько объектов с различными текстурами, то процесс установки текстуры и вывода примитивов должен быть последовательным:

    SetTexture ( 0, tex1 );
    drawObject1();
    
    SetTexture ( 0, tex2 );
    drawObject2();

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

    C++ device->SetTexture ( 0, 0 );
    Pascal device.SetTexture( 0, nil );

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

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

  • точечная фильтрация (используется по умолчанию) – самая быстрая по скорости, но самая низкая по качеству;
  • линейная фильтрация – приемлемое качество и скорость;
  • анизотропная – самая медленная, но самая качественная.
  • Программно установить тот или ной тип фильтрации можно с помощью вызова метода SetSamplerState интерфейса IDirect3DDevice9:

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

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

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

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

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

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

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

    D3DTEXF_POINT D3DTEXF_LINEAR

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

  • wrap
  • border color
  • clamp
  • 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)
    Pascal
    type
    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

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

    Библиотека Direct3D позволяет накладывать на один полигон не одну, а сразу несколько текстур. Наложение на грань нескольких текстур называется мультитекстурированием. На текущий момент поддерживается до 8 наложений (уровней) текстур на одну грань. Схему (принцип) мультитекстурирования можно описать следующим образом. Первый текстурный уровень (с индексом 0) принимает на вход два значения: цвет текселя и диффузный цвет вершины; производит с ними указанные операции и передает результат на следующий (нижний) уровень. Полученное на предыдущем уровне значение цвета используется в качестве одного из аргумента текущего уровня и т.д. Схематично данные шаги мультитекстурирования можно представить так:

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

    SetTextureStageState(<уровень>, D3DTSS_TEXCOORDINDEX, <номер координат>);

    Так, например, чтобы указать, что второй уровень будет использовать текстурные координаты первого текстурного уровня, достаточно воспользоваться следующим вызовом метода: SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 0 ). И затем нужно загрузить (установить) текстуры в соответствующие текстурные уровни.

    SetTexture( 0, tex1 );
    SetTexture( 1, tex2 );

    Можно указать для каждого текстурного уровня свои собственные текстурные координаты. В этом случае должен корректно измениться формат вершины при описании и набор FVF флагов. Ниже приведен пример описания вершины и FVF флагов для мультитекстурирования с двумя уровнями (текстурами) и текстурными координатами.

    C++
    struct CUSTOMVERTEX
    {
        FLOAT x,  y,  z, rhw;      // координаты вершины
        FLOAT u1, v1;         // текстурные координаты первого уровня
        FLOAT u2, v2;         // текстурные координаты второго уровня
    };
    
    #define MY_FVF (D3DFVF_XYZ | D3DFVF_TEX2)
    Pascal
    type
    MyVertex = packed record
          x, y, z, rhw: Single; // координаты вершины
          u1,v1: Single; // текстурные координаты первого уровня
          u2,v2: Single; // текстурные координаты второго уровня
    end;
    
    const
    MY_FVF = D3DFVF_XYZRHW or D3DFVF_TEX2;

    Вообще, число во флаге D3DFVF_TEX как раз и показывает, сколько текстурных уровней планируется задействовать при мультитекстурировании (D3DFVF_TEX1 … D3DFVF_TEX8). Для хранения второй и последующих текстур необходимо объявить соответствующее количество нужных переменных, произвести загрузку текстур с помощью функции D3DXCreateTextureFromFile() и установить текстуры в соответствующие текстурные уровни вызовов метода SetTexture (<номер уровня>, <текстура>). Ниже приведен пример возможного использования двух текстур при мультитекстурировании.

    C++
    LPDIRECT3DTEXTURE9 tex, tex2;
    …
    D3DXCreateTextureFromFile( device, "texture.bmp", tex );
    D3DXCreateTextureFromFile( device, "texture2.bmp", tex2 );
    …
    device->SetTexture ( 0, tex );
    device->SetTexture ( 1, tex2 );
    Pascal
    var
       tex, tex2: IDirect3DTexture9;
    …
    D3DXCreateTextureFromFile( device, 'texture.bmp', tex );
    D3DXCreateTextureFromFile( device, 'texture2.bmp', tex2 );
    …
    device.SetTexture( 0, tex );
    device.SetTexture( 1, tex2 );

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

    SetTextureStageState( <номер уровня>, D3DTSS_COLORARG1, <значение> ), 
    SetTextureStageState( <номер уровня>, D3DTSS_COLORARG2, <значение> ), 
    SetTextureStageState( <номер уровня>, D3DTSS_COLOROP, <операция> ).

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

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

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

    …
    points[0].u1 = 0.0f;
    points[0].v1 = 1.0f;
    points[0].u2 = 0.0f;
    points[0].v2 = 3.0f;
    …
    device->SetTexture( 0, tex );
    device->SetTexture( 1, tex );
    …

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

    device->SetTexture(0, tex);
    device->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0);
    device->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);
    device->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
    device->SetTexture(1, tex);
    device->SetTextureStageState(1, D3DTSS_TEXCOORDINDEX, 1);
    device->SetTextureStageState(1, D3DTSS_COLORARG2, D3DTA_TEXTURE);
    device->SetTextureStageState(1, D3DTSS_COLORARG1, D3DTA_CURRENT);
    device->SetTextureStageState(1, D3DTSS_COLOROP, <operation>);

    operation = D3DTOP_MODULATE4X

    operation = D3DTOP_ADD

    Вернуться к учебному плану