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

16.1. Пишем шейдер на HLSL

Мы можем написать код нашего HLSL-шейдера непосредственно в исходном коде приложения в виде длинной строки символов. Однако более удобный и правильный подход — разделить код шейдеров и код приложения. Поэтому мы будем писать наши шейдеры в программе Notepad и сохранять их как обычные текстовые файлы ASCII. Затем для компиляции наших шейдеров мы воспользуемся функцией D3DXCompileShaderFromFile (раздел 16.2.2).

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

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

/////////////////////////////////////////////////////////////////////
//
// Файл: transform.txt
//
// Автор: Фрэнк Д. Луна (C) All Rights Reserved
//
// Система: AMD Athlon 1800+ XP, 512 DDR, Geforce 3, Windows XP,
// MSVC++ 7.0
//
// Описание: Вершинный шейдер, преобразующий вершину с помощью
// комбинации матриц преобразования вида и проекции и устанавливающий
// для вершины синий цвет
//
/////////////////////////////////////////////////////////////////////

//
// Глобальные переменные
//

// Глобальная переменная для хранения комбинации
// матриц преобразования вида и проекции.
// Мы инициализируем эту переменную в приложении.
matrix ViewProjMatrix;

// Инициализация глобального вектора для синего цвета
vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};

//
// Структуры
//

// Входная структура описывает вершины, которые будут
// передаваться в шейдер. Здесь входная вершина содержит
// только данные о местоположении.
struct VS_INPUT
{
     vector position : POSITION;
};

// Выходная структура описывает вершину, которая
// возвращается шейдером. Здесь выходная вершина
// содержит данные о местоположении и цвет
struct VS_OUTPUT
{
     vector position : POSITION;
     vector diffuse : COLOR;
};

//
// Главная Точка Входа. Обратите внимание,
// что функция получает в своем параметре копию
// входной вершины и возвращает копию вычисленной
// выходной вершины.
//

VS_OUTPUT Main(VS_INPUT input)
{
     // Обнуляем данные выходной вершины
     VS_OUTPUT output = (VS_OUTPUT)0;

     // Преобразование пространства вида и проекция
     output.position = mul(input.position, ViewProjMatrix);

     // Делаем рассеиваемую составляющую цвета синей
     output.diffuse = Blue;

     // Возвращаем спроецированную и окрашенную вершину
     return output;
}

16.1.1. Глобальные переменные

Сначала мы объявляем две глобальные переменные:

matrix ViewProjMatrix;
vector Blue = {0.0f, 0.0f, 1.0f, 1.0f};

Первая переменная, ViewProjMatrix, относится к типу matrix, который представляет матрицы 4 × 4 и является встроенным типом HLSL. Эта переменная хранит комбинацию матриц вида и проекции и, следовательно, описывает оба эти преобразования. Благодаря комбинированию преобразований мы обойдемся одной операцией умножения вектора на матрицу вместо двух. Обратите внимание, что нигде в исходном коде шейдера нет инициализации этой переменной. Это объясняется тем, что мы инициализируем данную переменную из приложения, а не в шейдере. Обмен данными между приложением и программой шейдера является одной из наиболее часто используемых операций, и ее исследованию посвящен раздел 16.2.1.

Вторая переменная, Blue, относится к встроенному типу vector, который представляет четырехмерный вектор. Мы просто инициализируем его компоненты для синего цвета, рассматривая его как цветовой вектор RGBA.

16.1.2. Входная и выходная структуры

За объявлением глобальных переменных следует объявление двух специальных структур, которые мы будем называть входной (input) и выходной (output) структурами. Для вершинных шейдеров эти структуры описывают данные вершины, которые соответственно, получает и возвращает наш шейдер.

struct VS_INPUT
{
     vector position : POSITION;
};

struct VS_OUTPUT
{
     vector position : POSITION;
     vector diffuse : COLOR;
};

 

ПРИМЕЧАНИЕ
Входная и выходная структура пиксельного шейдера описывают данные пикселя.

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

Синтаксическая конструкция с двоеточием применяется для указания способа использования переменной. Это похоже на поле настраиваемого формата вершин (FVF) в структуре данных вершины. Например, во входной структуре данных VS_INPUT, у нас есть член

vector position : POSITION;

Конструкция : POSITION означает, что переменная position типа vector используется для описания местоположения передаваемой шейдеру вершины. В качестве другого примера можно рассмотреть описание члена структуры VS_OUTPUT:

vector diffuse : COLOR;

Здесь : COLOR означает, что переменная diffuse типа vector применяется для описания цвета возвращаемой шейдером вершины. О доступных идентификаторах способов использования переменных, применяемых для вершинных и пиксельных шейдеров, мы поговорим в следующих двух главах.

ПРИМЕЧАНИЕ
С низкоуровневой точки зрения данная конструкция связывает переменную шейдера с регистром аппаратуры. Входные переменные связываются с входными регистрами, а выходные — с выходными. Например, член position структуры VS_INPUT связан с входным регистром местоположения вершины. Аналогично, член diffuse связан с выходным регистром цвета вершины.

16.1.3. Точка входа

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

VS_OUTPUT Main(VS_INPUT input)
{

 

ПРИМЕЧАНИЕ
В действительности вы не обязаны использовать входные и выходные структуры. Например, в особенности для пиксельных шейдеров, вы часто будете встречаться с синтаксисом, похожим на приведенный ниже:
float4 Main(in float2 base : TEXCOORD0,
            in float2 spot : TEXCOORD1,
            in float2 text : TEXCOORD2) : COLOR
{
     ...
}
Параметры передаются в шейдер; в данном примере мы передаем шейдеру три набора координат текстуры. Шейдер возвращает единственное значение цвета, на что указывает констркукия : COLOR следующая за сигнатурой функции. Это определение эквивалентно следующему:
struct INPUT
{
     float2 base : TEXCOORD0;
     float2 spot : TEXCOORD1;
     float2 text : TEXCOORD2;
};

struct OUTPUT
{
     float4 c : COLOR;
};

OUTPUT Main(INPUT input)
{
     ...
}

Код функции, являющейся входной точкой, отвечает за вычисление данных возвращаемой вершины на основе полученных данных исходной вершины. Рассматриваемый в примере шейдер просто преобразует координаты вершины в пространство вида и пространство проекции, устанавливает для вершины синий цвет и возвращает полученную в результате вершину. Сперва мы создаем экземпляр выходной структуры VS_OUTPUT и присваиваем всем ее членам 0.

VS_OUTPUT output = (VS_OUTPUT)0; // обнуляем все члены

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

// Преобразование и проекция
output.position = mul(input.position, ViewProjMatrix);

Затем мы устанавливаем член данных, задающий рассеиваемую составляющую цвета, равной вектору Blue:

// Делаем рассеиваемую составляющую цвета синей
output.diffuse = Blue;

И, наконец, мы возвращаем полученную вершину:

return output;
}

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

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